/* Software Name : AsmDex * Version : 1.0 * * Copyright © 2012 France Télécom * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ package org.ow2.asmdex.structureWriter; import java.util.ArrayList; /** * Prototype of a method, like described in the proto_id_item structure. * * Implements Comparable in order to easily sort the Prototypes. Like requested by the Dex * documentation, two prototypes are compared by their return types, and then their arguments. * * @author Julien Névo. */ public class Prototype implements Comparable<Prototype> { /** * Full descriptor of the Prototype, in the "TypeDescriptor" format. */ final private String descriptor; /** * String representing the whole prototype (return type and parameters) in the "ShortyDescriptor" * format. */ private String shortyDescriptor; /** * String representing the return type, in the "TypeDescriptor" format. */ private String returnType; /** * Strings of the parameter types, in the "TypeDescriptor" format. */ private TypeList parameterTypes; /** * The hashcode of the Prototype, calculated in the Constructor. */ final private int hashcode; /** * Constructor of a Prototype. Builds the structure from a descriptor in the TypeDescriptor * format. * @param descriptor full descriptor in the TypeDescriptor format. */ public Prototype(String descriptor) { this.descriptor = descriptor; hashcode = calculateHashCode(descriptor); } /** * Fix internal structure if this is the unique witness kept in the constantpool */ public void initialize() { shortyDescriptor = getShortyDescriptorFromTypeDescriptor(descriptor); returnType = getFirstDescriptor(descriptor); parameterTypes = getDescriptors(descriptor, true); } /** * Calculates the hashcode of the Prototype from its descriptor in the TypeDescriptor format. * @return the hashcode of the Prototype. */ public static int calculateHashCode(String descriptor) { return descriptor.hashCode(); } // ----------------------------------------- // Getters. // ----------------------------------------- /** * Returns a String representing the whole prototype (return type and parameters) in the * "TypeDescriptor" format. * @return a String representing the whole prototype (return type and parameters) in the * "TypeDescriptor" format. */ public String getDescriptor() { return descriptor; } /** * Returns a String representing the whole prototype (return type and parameters) in the * "ShortyDescriptor" format. * @return a String representing the whole prototype (return type and parameters) in the * "ShortyDescriptor" format. */ public String getShortyDescriptor() { return shortyDescriptor; } /** * Returns a String representing the return type, in the "TypeDescriptor" format. * @return a String representing the return type, in the "TypeDescriptor" format. */ public String getReturnType() { return returnType; } /** * Returns a TypeList of the parameter types, in the "TypeDescriptor" format. It may be an empty * array. * @return a TypeList of the parameter types, in the "TypeDescriptor" format. */ public TypeList getParameterTypes() { return parameterTypes; } /** * Returns the number of parameters this Prototype contains (doesn't count the return type). May be 0. * @return the number of parameters this Prototype contains. */ public int getNbParameters() { return parameterTypes.size(); } // ----------------------------------------- // Static utility methods // ----------------------------------------- /** * Returns the first Type found in the descriptor given. Returns Null if the String is empty, Null or * invalid. * @param descriptor descriptor. * @return the first Type found in the descriptor given, or Null if the String is empty, Null or * invalid. */ public static String getFirstDescriptor(String descriptor) { StringBuilder result = new StringBuilder(10); if ((descriptor != null) && (descriptor.length() > 0)) { char c = descriptor.charAt(0); switch (c) { case 'V': case 'Z': case 'B': case 'S': case 'C': case 'I': case 'J': case 'F': case 'D': result.append(c); break; case 'L': int i = descriptor.indexOf(';'); if (i > 0) { result.append(descriptor.substring(0, i + 1)); } break; case '[': result.append('['); result.append(getFirstDescriptor(descriptor.substring(1))); } } return result.toString(); } /** * Returns the size in bytes of a given type, or 0 for a Void or unknown type. * @param type type. * @return the size in bytes of a given type, or 0 for a Void or unknown type. */ public static int getSizeOfType(String type) { int result = 0; switch (type.charAt(0)) { case 'Z': case 'B': case 'S': case 'C': case 'I': case 'F': case 'L': case '[': result = 2; break; case 'J': case 'D': result = 4; break; } return result; } /** * Returns the sum in bytes of the size of every type of the given descriptor, skipping or not * the return type (the first information of the descriptor). * @param descriptor the descriptor. * @param skipReturnType true to skip the first type found. * @return the sum in bytes of the size of every type of the given descriptor. */ public static int getSizeOfDescriptor(String descriptor, boolean skipReturnType) { int result = 0; String foundDescriptor; while ((descriptor != null) && (descriptor.length() > 0)) { foundDescriptor = getFirstDescriptor(descriptor); if (foundDescriptor == null) { descriptor = null; } else { int foundResult = getSizeOfType(foundDescriptor); if (skipReturnType) { skipReturnType = false; } else { result += foundResult; } descriptor = descriptor.substring(foundDescriptor.length()); } } return result; } /** * Returns a ShortyDescriptor from a given TypeDescriptor. * @param desc the descriptor. * @return a ShortyDescriptor. */ public static String getShortyDescriptorFromTypeDescriptor(String desc) { StringBuilder result = new StringBuilder(5); while (!desc.equals("")) { String foundDesc = getFirstDescriptor(desc); desc = desc.substring(foundDesc.length()); // Any reference type would be "Lxxx;" or "[xx", which means the returned String would // have a length > 1. if (foundDesc.length() > 1) { result.append('L'); } else { result.append(foundDesc); } } return result.toString(); } /** * Returns the number of parameters (return parameter not included) in the given descriptor. * @param desc the descriptor. * @return the number of parameters (return parameter not included). */ public static int getNbParametersFromTypeDescriptor(String desc) { return getShortyDescriptorFromTypeDescriptor(desc).length() - 1; } /** * Returns a TypeList containing descriptors in the TypeDescriptor format from a descriptor also in the * TypeDescriptor format, or an empty array if nothing was found. * @param descriptor descriptors in the TypeDescriptor format. * @param skipFirst true to skip the first descriptor, usually the return type. * @return a TypeList of descriptors in the TypeDescriptor format. */ public static TypeList getDescriptors(String descriptor, boolean skipFirst) { ArrayList<String> types = new ArrayList<String>(); String foundDesc; while (!descriptor.equals("")) { foundDesc = getFirstDescriptor(descriptor); descriptor = descriptor.substring(foundDesc.length()); if (skipFirst) { skipFirst = false; } else { types.add(foundDesc); } } return new TypeList(types.toArray(new String[types.size()])); } // ----------------------------------------- // Overridden methods // ----------------------------------------- @Override public int compareTo(Prototype prototype) { if (this == prototype) { return 0; } // Tests Return type first, as Prototype are sorted by Return type. int compare = returnType.compareTo(prototype.returnType); if (compare != 0) { return compare; } // Tests the arguments. return parameterTypes.compareTo(prototype.parameterTypes); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof Prototype) { Prototype secondPrototype = (Prototype)o; return descriptor.equals(secondPrototype.descriptor); } return false; } @Override public int hashCode() { return hashcode; } }