/* 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; import java.util.List; import org.ow2.asmdex.Opcodes; import org.ow2.asmdex.lowLevelUtils.ByteVector; import org.ow2.asmdex.structureCommon.LocalVariable; /** * Simple class representing a Method. It's a mix between the method_id_item, but also encoded_method * structures. * * The equals and hashCode methods have been overridden in order to detect easily * duplicates in HashMaps. * * A Method is identified by its methodName, className and prototype. Access and ExceptionIndexes * are NOT taken in account to differentiate the methods. * * Access may be ACC_UNKNOWN if the method created is referenced, but not yet parsed (like when an * instruction refers to a not yet parsed method). * * A Method has a code_item. However, it may not if the method is abstract or interface. * * @author Julien Névo */ public class Method implements Comparable<Method>, IAnnotationsHolder { /** * Name of the Class owning the Method. */ final private String className; /** * Prototype of the Method, in TypeDescriptor format. */ final private Prototype prototype; /** * The name of the Method. */ final private String methodName; /** * The access flags of the Field. */ private int access; /** * The names of the exceptions. May be Null. */ private String[] exceptionNames; /** * Start in bytes from the beginning of the Dex file, of the bytecode to copy, in case the * "ConstantPool" optimization is used if the Writer is directly linked to the Reader. * It includes the code_item header. */ private int startBytecodeToCopy; /** * Start in bytes from the beginning of the Dex file, of the debug_info_item to copy, in case the * "ConstantPool" optimization is used if the Writer is directly linked to the Reader. */ private int startDebugInfoToCopy; /** * The code of the Method, its Instructions. May be Null if abstract/interface. */ private CodeItem codeItem; /** * Annotation_set_item, representing all the annotation_items for this Method. */ private AnnotationSetItem annotationSetItem = new AnnotationSetItem(); /** * Annotation_set_ref_item, representing the annotation_set_item for the Annotated Parameters * of this Method. */ private AnnotationSetRefList annotatedParameterSetRefList; /** * The name of the parameters (return parameter not included). */ private String[] parameters; /** * The Local Variables of this Method. */ private List<LocalVariable> localVariables = new ArrayList<LocalVariable>(); /** * The Signature of the Field. May be Null. */ private String[] signature; /** * The hashcode is cached. */ final private int hashcode; /** * * @param methodName name of the Method. * @param className name of the Class owning the Method. * @param methodDescriptor Prototype of the Method, in TypeDescriptor format. */ public Method(String methodName, String className, Prototype methodDescriptor) { this.methodName = methodName; this.className = className; prototype = methodDescriptor; hashcode = calculateHashCode(methodName, className, prototype); } /** * Full init if this is the witness kept. * @param access The access flags of the Field. * @param signature the signature of the method. May be Null. * @param exceptionNames the names of the exceptions. May be Null. * @param constantPool the Constant Pool of the Application. */ public void init(int access, String[] signature, String[] exceptionNames, ConstantPool constantPool) { this.access = access; this.exceptionNames = exceptionNames; this.signature = signature; if (!isUnknown() && supportsCodeItem()) { codeItem = new CodeItem(this, constantPool); } annotatedParameterSetRefList = new AnnotationSetRefList(prototype.getNbParameters(), this); } // ---------------------------------------------- // Public methods. // ---------------------------------------------- /** * Adds a Local Variable to this Method. */ public void addLocalVariable(LocalVariable localVariable) { localVariables.add(localVariable); } /** * Adds information to the current Method. This is useful <i>only</i> if this * Method has the ACC_UNKNOWN flag, which means Instructions referred to it while it has not yet been * visited, declaring it but not giving all the information it should have. */ public void completeInformation(int access, String[] exceptionNames, String[] signature, ConstantPool constantPool) { this.access = access; this.exceptionNames = exceptionNames; this.signature = signature; if ((codeItem == null) && !isUnknown() && supportsCodeItem()) { // It SHOULD be Null in all cases. codeItem = new CodeItem(this, constantPool); } } /** * Calculates the hashcode of the Method according to its given identifiers. * @param methodName the name of the Method. * @param className the Class it belongs to. * @param prototype the Prototype of the Method. * @return the hashcode of the Method. */ public static int calculateHashCode(String methodName, String className, Prototype prototype) { return methodName.hashCode() + className.hashCode() + prototype.hashCode(); } /** * Calculates the hashcode of the Method according to its given identifiers. * @param methodName the name of the Method. * @param className the Class it belongs to. * @param prototypeHashCode the hashcode of the Prototype. * @return the hashcode of the Method. */ public static int calculateHashCode(String methodName, String className, int prototypeHashCode) { return methodName.hashCode() + className.hashCode() + prototypeHashCode; } /** * Indicates whether the Method is virtual (not static, private, or constructor). * @return true if the Method is virtual. */ public boolean isVirtual() { return ((access & Opcodes.ACC_STATIC) + (access & Opcodes.ACC_PRIVATE) + (access & Opcodes.ACC_CONSTRUCTOR)) == 0; } /** * Indicates whether the Method is direct (static and/or private and/or constructor). * @return true if the Method is direct. */ public boolean isDirect() { return !isVirtual(); } /** * Indicates whether the Method is static. * @return true if the Method is static. */ public boolean isStatic() { return (access & Opcodes.ACC_STATIC) != 0; } /** * Indicates whether the Method is abstract or interface. * @return true if the Method is abstract or interface. */ public boolean isAbstractOrInterface() { return ((access & Opcodes.ACC_ABSTRACT) | (access & Opcodes.ACC_INTERFACE)) != 0; } /** * Indicates whether the Method is native. * @return true if the Method is static. */ public boolean isNative() { return (access & Opcodes.ACC_NATIVE) != 0; } /** * Indicates whether the Method is unknown (referred to, but not yet parsed). * @return true if the Method is unknown. */ public boolean isUnknown() { return (access & Opcodes.ACC_UNKNOWN) != 0; } /** * Indicates whether a method contains java code (a CodeItem). * @return true if the Method has code. */ public boolean supportsCodeItem() { return (access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE | Opcodes.ACC_NATIVE)) == 0; } /** * Indicates whether the Method uses the "this" parameter. * @return true if the Method uses the "this" parameter. */ public boolean isUsingThis() { // "This" is used if the Method is not static, and is not a Constructor and not abstract. // Patch for "not abstract" by Panxiaobo #316341 - patch 16. return (!isStatic()) && ((access & Opcodes.ACC_CONSTRUCTOR) == 0) && supportsCodeItem(); } /** * Adds an annotation_item to the annotations_set_items. * @param annotationItem the Annotation Item to add. */ public void addAnnotationItem(AnnotationItem annotationItem) { annotationSetItem.addAnnotationItem(annotationItem); } /** * Adds an annotation_item to a parameter of this Method. * @param parameterIndex zero-based index of the argument. * @param annotationItem the Annotation Item to add. */ public void addParameterAnnotationItem(int parameterIndex, AnnotationItem annotationItem) { annotatedParameterSetRefList.addAnnotationItem(parameterIndex, annotationItem); } /** * Closes and registers the annotation_set_items and annotation_set_ref_items of this Method. * This must only be done once when the Method has been fully visited. * @param constantPool the Constant Pool of the Application. */ public void closeAnnotations(ConstantPool constantPool) { annotatedParameterSetRefList.close(); // Now that the annotation_set_item are fully known, we can register it to the Constant Pool. constantPool.addAnnotationSetItemToConstantPool(annotationSetItem); // We do the same for the annotation_set_ref_list. constantPool.addAnnotationSetRefListToConstantPool(annotatedParameterSetRefList); } /** * Generates the Instructions code, as Dalvik bytecode, in the codeItem buffer, as well as its * header. It uses <i>symbolic</i> references, so must be parsed again to link them to the "real" * elements. * Also fills the debug_code_item in this instance. Note however that the debug_info_offset field * in the code_item header is not set, because we don't know where the debug_info_item is encoded * for now. Note that the debug_info_item is not written here, only built. * The alignment is not managed here, but must be by the calling method. */ public void generateCodeItemCode() { codeItem.generateCodeItemCode(); } /** * Frees all the structures (list of instructions...) of the code_item and the debug_info_item * so that they don't consume memory. This <i>MUST</i> be done after having generated the bytecode, once * the method has been parsed and its end visited. */ public void free() { codeItem.free(); } // ---------------------------------------------- // Getters and setters. // ---------------------------------------------- /** * Returns the code_item code (including the code_item header and the bytecode), <i>without</i> the * try/catch fields after the insns field. * It uses <i>symbolic</i> references, so must be parsed again to link them to the "real" * elements. * @return the code_item code, without the try/catch fields. */ public ByteVector getCodeItemCode() { return codeItem.getCodeItemCode(); } /** * Returns the try/catch section of the code_item, beginning by the possible padding after the insns field, * or Null if no try/catch is present. * It uses <i>symbolic</i> references, so must be parsed again to link them to the "real" * elements. * @return the try/catch code, or Null. */ public ByteVector getCodeItemTryCatch() { return codeItem.getCodeItemTryCatch(); } /** * Sets the start of the bytecode to copy from the input Dex file to the * output. This is only useful when using the optimization that consists in copying part of * the Constant Pool and the bytecode of methods that doesn't change, if the Reader is linked * to the Writer with no Adapter to modify the methods in between. * @param start start in bytes from the beginning of the Dex file where the bytecode is. This * includes the code_item header. */ public void setStartBytecodeToCopy(int start) { startBytecodeToCopy = start; } /** * Gets the start in byte of the bytecode to copy from the input Dex file. * This is only useful when using the optimization that consists in copying part of * the Constant Pool and the bytecode of methods that doesn't change, if the Reader is linked * to the Writer with no Adapter to modify the methods in between. * @return the start in byte of the bytecode, or 0 if the optimization is not used. */ public int getStartBytecodeToCopy() { return startBytecodeToCopy; } /** * Sets the start of the debug_info_item to copy from the input Dex file to the * output. This is only useful when using the optimization that consists in copying part of * the Constant Pool and the bytecode of methods that doesn't change, if the Reader is linked * to the Writer with no Adapter to modify the methods in between. * @param start start in bytes from the beginning of the Dex file where the debug_info_item is. */ public void setStartDebugInfoToCopy(int start) { startDebugInfoToCopy = start; } /** * Gets the start in byte of the debug_info_item to copy from the input Dex file. * This is only useful when using the optimization that consists in copying part of * the Constant Pool and the bytecode of methods that doesn't change, if the Reader is linked * to the Writer with no Adapter to modify the methods in between. * @return the start in byte of the debug_info_item, or 0 if the optimization is not used. */ public int getStartDebugInfoToCopy() { return startDebugInfoToCopy; } // ---------------------------------------------- // Getters and Setters. // ---------------------------------------------- /** * Returns the name of the Class owning the Method. * @return the name of the Class owning the Method. */ public String getClassName() { return className; } /** * Returns the prototype of the Method, in TypeDescriptor format. * @return the prototype of the Method, in TypeDescriptor format. */ public Prototype getPrototype() { return prototype; } /** * Returns the name of the Method. * @return the name of the Method. */ public String getMethodName() { return methodName; } /** * Returns the access flags. * @return the access flags. */ public int getAccess() { return access; } /** * Returns the exception names. May be Null. * @return the exception names, or Null. */ public String[] getExceptionNames() { return exceptionNames; } /** * Returns the Code Item linked to this Method. May be Null if it hasn't any (if abstract or interface). * @return the Code Item linked to this Method or Null. */ public CodeItem getCodeItem() { return codeItem; } /** * Returns the annotation_set_item this structure currently contains. * @return the annotation_set_item this structure currently contains. */ @Override public AnnotationSetItem getAnnotationSetItem() { return annotationSetItem; } /** * Returns the number of annotation_items this structure currently contains. Does not concern the * parameter annotations. * @return the number of annotation_items this structure currently contains. */ @Override public int getNbAnnotations() { return annotationSetItem.getNbAnnotationItems(); } /** * Return the annotation_set_ref_list of this Method, indicating what are the annotations on each * parameters. * @return the annotation_set_ref_list of this Method. */ public AnnotationSetRefList getAnnotatedParameterSetRefList() { return annotatedParameterSetRefList; } /** * Returns the number of annotation_set_items the Method has. They are only used by the * parameter annotations (one annotation_set_item per argument, even if this one in particular is * not annotated). * @return the number of annotation_set_items the Method has. */ public int getNbParameterAnnotations() { return annotatedParameterSetRefList.getNbAnnotationSetItemsUsed(); } /** * Returns the number of parameters of this Method (doesn't count the Return parameter). May be 0. * @return the number of parameters of this Method. */ public int getNbParameters() { return prototype.getNbParameters(); } /** * Returns the name of the parameters of this Method (excluding the Return parameter). May be null if * the Debug Info didn't provide them or was not visited. * @return the name of the parameters of this Method, or null. */ public String[] getParameters() { return parameters; } /** * Sets the name of the parameters used by this Method. * @param parameters name of the parameters. */ public void setParameters(String[] parameters) { this.parameters = parameters; } /** * Sets the first line number of the Method, but only if none has been found before. * @param firstLineNumber the first line number of this Method. */ public void setFirstLineNumberIfNeeded(int firstLineNumber) { codeItem.setFirstLineNumber(firstLineNumber); } /** * Returns the Signature of the Method. May be Null. * @return the Signature of the Method. May be Null. */ public String[] getSignature() { return signature; } /** * Returns the Local Variable list. It may be empty. * @return the Local Variable list. */ public List<LocalVariable> getLocalVariables() { return localVariables; } // ---------------------------------------------- // Overridden methods. // ---------------------------------------------- @Override public int hashCode() { return hashcode; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof Method) { Method mii = (Method)obj; return methodName.equals(mii.methodName) && (className.equals(mii.className) && prototype.equals(mii.prototype)); } return false; } @Override public int compareTo(Method method) { if (this == method) { return 0; } // Tests class owning name first. int compare = className.compareTo(method.className); if (compare != 0) { return compare; } // Tests the names. compare = methodName.compareTo(method.methodName); if (compare != 0) { return compare; } // Tests the prototype. return prototype.compareTo(method.prototype); } }