/* * [The "BSD licence"] * Copyright (c) 2010 Ben Gruver (JesusFreke) * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.jf.dexlib; import java.util.Collections; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import org.jf.dexlib.Util.AccessFlags; import org.jf.dexlib.Util.AnnotatedOutput; import org.jf.dexlib.Util.ExceptionWithContext; import org.jf.dexlib.Util.Input; import org.jf.dexlib.Util.Leb128Utils; import org.jf.dexlib.Util.ReadOnlyArrayList; public class ClassDataItem extends Item<ClassDataItem> { private EncodedField[] staticFields = null; private EncodedField[] instanceFields = null; private EncodedMethod[] directMethods = null; private EncodedMethod[] virtualMethods = null; /** * Creates a new uninitialized <code>ClassDataItem</code> * @param dexFile The <code>DexFile</code> that this item belongs to */ public ClassDataItem(final DexFile dexFile) { super(dexFile); } /** * Creates a new <code>ClassDataItem</code> with the given values * @param dexFile The <code>DexFile</code> that this item belongs to * @param staticFields The static fields for this class * @param instanceFields The instance fields for this class * @param directMethods The direct methods for this class * @param virtualMethods The virtual methods for this class */ private ClassDataItem(DexFile dexFile, EncodedField[] staticFields, EncodedField[] instanceFields, EncodedMethod[] directMethods, EncodedMethod[] virtualMethods) { super(dexFile); this.staticFields = staticFields; this.instanceFields = instanceFields; this.directMethods = directMethods; this.virtualMethods = virtualMethods; } /** * Creates a new <code>ClassDataItem</code> with the given values * @param dexFile The <code>DexFile</code> that this item belongs to * @param staticFields The static fields for this class * @param instanceFields The instance fields for this class * @param directMethods The direct methods for this class * @param virtualMethods The virtual methods for this class * @return a new <code>ClassDataItem</code> with the given values */ public static ClassDataItem internClassDataItem(DexFile dexFile, List<EncodedField> staticFields, List<EncodedField> instanceFields, List<EncodedMethod> directMethods, List<EncodedMethod> virtualMethods) { EncodedField[] staticFieldsArray = null; EncodedField[] instanceFieldsArray = null; EncodedMethod[] directMethodsArray = null; EncodedMethod[] virtualMethodsArray = null; if (staticFields != null && staticFields.size() > 0) { SortedSet<EncodedField> staticFieldsSet = new TreeSet<EncodedField>(); for (EncodedField staticField: staticFields) { if (staticFieldsSet.contains(staticField)) { System.err.println(String.format("Ignoring duplicate static field definition: %s", staticField.field.getFieldString())); continue; } staticFieldsSet.add(staticField); } staticFieldsArray = new EncodedField[staticFieldsSet.size()]; staticFieldsArray = staticFieldsSet.toArray(staticFieldsArray); } if (instanceFields != null && instanceFields.size() > 0) { SortedSet<EncodedField> instanceFieldsSet = new TreeSet<EncodedField>(); for (EncodedField instanceField: instanceFields) { if (instanceFieldsSet.contains(instanceField)) { System.err.println(String.format("Ignoring duplicate instance field definition: %s", instanceField.field.getFieldString())); continue; } instanceFieldsSet.add(instanceField); } instanceFieldsArray = new EncodedField[instanceFieldsSet.size()]; instanceFieldsArray = instanceFieldsSet.toArray(instanceFieldsArray); } TreeSet<EncodedMethod> directMethodSet = new TreeSet<EncodedMethod>(); if (directMethods != null && directMethods.size() > 0) { for (EncodedMethod directMethod: directMethods) { if (directMethodSet.contains(directMethod)) { System.err.println(String.format("Ignoring duplicate direct method definition: %s", directMethod.method.getMethodString())); continue; } directMethodSet.add(directMethod); } directMethodsArray = new EncodedMethod[directMethodSet.size()]; directMethodsArray = directMethodSet.toArray(directMethodsArray); } if (virtualMethods != null && virtualMethods.size() > 0) { TreeSet<EncodedMethod> virtualMethodSet = new TreeSet<EncodedMethod>(); for (EncodedMethod virtualMethod: virtualMethods) { if (directMethodSet.contains(virtualMethod)) { // If both a direct and virtual definition is present, dalvik's behavior seems to be undefined, // so we can't gracefully handle this case, like we can if the duplicates are all direct or all // virtual -- in which case, we ignore all but the first definition throw new RuntimeException(String.format("Duplicate direct+virtual method definition: %s", virtualMethod.method.getMethodString())); } if (virtualMethodSet.contains(virtualMethod)) { System.err.println(String.format("Ignoring duplicate virtual method definition: %s", virtualMethod.method.getMethodString())); continue; } virtualMethodSet.add(virtualMethod); } virtualMethodsArray = new EncodedMethod[virtualMethodSet.size()]; virtualMethodsArray = virtualMethodSet.toArray(virtualMethodsArray); } ClassDataItem classDataItem = new ClassDataItem(dexFile, staticFieldsArray, instanceFieldsArray, directMethodsArray, virtualMethodsArray); return dexFile.ClassDataSection.intern(classDataItem); } /** {@inheritDoc} */ protected void readItem(Input in, ReadContext readContext) { int staticFieldsCount = in.readUnsignedLeb128(); int instanceFieldsCount = in.readUnsignedLeb128(); int directMethodsCount = in.readUnsignedLeb128(); int virtualMethodsCount = in.readUnsignedLeb128(); if (staticFieldsCount > 0) { staticFields = new EncodedField[staticFieldsCount]; EncodedField previousEncodedField = null; for (int i=0; i<staticFieldsCount; i++) { try { staticFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField); } catch (Exception ex) { throw ExceptionWithContext.withContext(ex, "Error while reading static field at index " + i); } } } if (instanceFieldsCount > 0) { instanceFields = new EncodedField[instanceFieldsCount]; EncodedField previousEncodedField = null; for (int i=0; i<instanceFieldsCount; i++) { try { instanceFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField); } catch (Exception ex) { throw ExceptionWithContext.withContext(ex, "Error while reading instance field at index " + i); } } } if (directMethodsCount > 0) { directMethods = new EncodedMethod[directMethodsCount]; EncodedMethod previousEncodedMethod = null; for (int i=0; i<directMethodsCount; i++) { try { directMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in, previousEncodedMethod); } catch (Exception ex) { throw ExceptionWithContext.withContext(ex, "Error while reading direct method at index " + i); } } } if (virtualMethodsCount > 0) { virtualMethods = new EncodedMethod[virtualMethodsCount]; EncodedMethod previousEncodedMethod = null; for (int i=0; i<virtualMethodsCount; i++) { try { virtualMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in, previousEncodedMethod); } catch (Exception ex) { throw ExceptionWithContext.withContext(ex, "Error while reading virtual method at index " + i); } } } } /** {@inheritDoc} */ protected int placeItem(int offset) { offset += Leb128Utils.unsignedLeb128Size(getStaticFieldCount()); offset += Leb128Utils.unsignedLeb128Size(getInstanceFieldCount()); offset += Leb128Utils.unsignedLeb128Size(getDirectMethodCount()); offset += Leb128Utils.unsignedLeb128Size(getVirtualMethodCount()); if (staticFields != null) { EncodedField previousEncodedField = null; for (EncodedField encodedField: staticFields) { offset = encodedField.place(offset, previousEncodedField); previousEncodedField = encodedField; } } if (instanceFields != null) { EncodedField previousEncodedField = null; for (EncodedField encodedField: instanceFields) { offset = encodedField.place(offset, previousEncodedField); previousEncodedField = encodedField; } } if (directMethods != null) { EncodedMethod previousEncodedMethod = null; for (EncodedMethod encodedMethod: directMethods) { offset = encodedMethod.place(offset, previousEncodedMethod); previousEncodedMethod = encodedMethod; } } if (virtualMethods != null) { EncodedMethod previousEncodedMethod = null; for (EncodedMethod encodedMethod: virtualMethods) { offset = encodedMethod.place(offset, previousEncodedMethod); previousEncodedMethod = encodedMethod; } } return offset; } /** {@inheritDoc} */ protected void writeItem(AnnotatedOutput out) { if (out.annotates()) { int staticFieldCount = getStaticFieldCount(); out.annotate("static_fields_size: 0x" + Integer.toHexString(staticFieldCount) + " (" + staticFieldCount + ")"); out.writeUnsignedLeb128(staticFieldCount); int instanceFieldCount = getInstanceFieldCount(); out.annotate("instance_fields_size: 0x" + Integer.toHexString(instanceFieldCount) + " (" + instanceFieldCount + ")"); out.writeUnsignedLeb128(instanceFieldCount); int directMethodCount = getDirectMethodCount(); out.annotate("direct_methods_size: 0x" + Integer.toHexString(directMethodCount) + " (" + directMethodCount + ")"); out.writeUnsignedLeb128(directMethodCount); int virtualMethodCount = getVirtualMethodCount(); out.annotate("virtual_methods_size: 0x" + Integer.toHexString(virtualMethodCount) + " (" + virtualMethodCount + ")"); out.writeUnsignedLeb128(virtualMethodCount); if (staticFields != null) { int index = 0; EncodedField previousEncodedField = null; for (EncodedField encodedField: staticFields) { out.annotate("[" + index++ + "] static_field"); out.indent(); encodedField.writeTo(out, previousEncodedField); out.deindent(); previousEncodedField = encodedField; } } if (instanceFields != null) { int index = 0; EncodedField previousEncodedField = null; for (EncodedField encodedField: instanceFields) { out.annotate("[" + index++ + "] instance_field"); out.indent(); encodedField.writeTo(out, previousEncodedField); out.deindent(); previousEncodedField = encodedField; } } if (directMethods != null) { int index = 0; EncodedMethod previousEncodedMethod = null; for (EncodedMethod encodedMethod: directMethods) { out.annotate("[" + index++ + "] direct_method"); out.indent(); encodedMethod.writeTo(out, previousEncodedMethod); out.deindent(); previousEncodedMethod = encodedMethod; } } if (virtualMethods != null) { int index = 0; EncodedMethod previousEncodedMethod = null; for (EncodedMethod encodedMethod: virtualMethods) { out.annotate("[" + index++ + "] virtual_method"); out.indent(); encodedMethod.writeTo(out, previousEncodedMethod); out.deindent(); previousEncodedMethod = encodedMethod; } } } else { out.writeUnsignedLeb128(getStaticFieldCount()); out.writeUnsignedLeb128(getInstanceFieldCount()); out.writeUnsignedLeb128(getDirectMethodCount()); out.writeUnsignedLeb128(getVirtualMethodCount()); if (staticFields != null) { EncodedField previousEncodedField = null; for (EncodedField encodedField: staticFields) { encodedField.writeTo(out, previousEncodedField); previousEncodedField = encodedField; } } if (instanceFields != null) { EncodedField previousEncodedField = null; for (EncodedField encodedField: instanceFields) { encodedField.writeTo(out, previousEncodedField); previousEncodedField = encodedField; } } if (directMethods != null) { EncodedMethod previousEncodedMethod = null; for (EncodedMethod encodedMethod: directMethods) { encodedMethod.writeTo(out, previousEncodedMethod); previousEncodedMethod = encodedMethod; } } if (virtualMethods != null) { EncodedMethod previousEncodedMethod = null; for (EncodedMethod encodedMethod: virtualMethods) { encodedMethod.writeTo(out, previousEncodedMethod); previousEncodedMethod = encodedMethod; } } } } /** {@inheritDoc} */ public ItemType getItemType() { return ItemType.TYPE_CLASS_DATA_ITEM; } /** {@inheritDoc} */ public String getConciseIdentity() { TypeIdItem parentType = getParentType(); if (parentType == null) { return "class_data_item @0x" + Integer.toHexString(getOffset()); } return "class_data_item @0x" + Integer.toHexString(getOffset()) + " (" + parentType.getTypeDescriptor() +")"; } /** {@inheritDoc} */ public int compareTo(ClassDataItem other) { // An empty CodeDataItem may be shared by multiple ClassDefItems, so we can't use parent in this case if (isEmpty()) { if (other.isEmpty()) { return 0; } return -1; } if (other.isEmpty()) { return 1; } TypeIdItem parentType = getParentType(); TypeIdItem otherParentType= other.getParentType(); if (parentType == null) { if (otherParentType == null) { return 0; } return -1; } if (otherParentType == null) { return 1; } return parentType.compareTo(otherParentType); } @Override public int hashCode() { // If the item has a single parent, we can use the re-use the identity (hash) of that parent TypeIdItem parentType = getParentType(); if (parentType != null) { return parentType.hashCode(); } return 0; } /** * Returns the parent type for a non-empty ClassDataItem, or null for an empty one (which could be referenced by * multiple ClassDefItem parents) * * Only an empty ClassDataItem may have multiple parents. * * @return The parent type for this ClassDefItem, or null if it may have multiple parents */ public TypeIdItem getParentType() { if (staticFields != null && staticFields.length > 0) { return staticFields[0].field.getContainingClass(); } if (instanceFields != null && instanceFields.length > 0) { return instanceFields[0].field.getContainingClass(); } if (directMethods != null && directMethods.length > 0) { return directMethods[0].method.getContainingClass(); } if (virtualMethods != null && virtualMethods.length > 0) { return virtualMethods[0].method.getContainingClass(); } return null; } /** * @return the static fields for this class */ public List<EncodedField> getStaticFields() { if (staticFields == null) { return Collections.emptyList(); } return ReadOnlyArrayList.of(staticFields); } /** * @return the instance fields for this class */ public List<EncodedField> getInstanceFields() { if (instanceFields == null) { return Collections.emptyList(); } return ReadOnlyArrayList.of(instanceFields); } /** * @return the direct methods for this class */ public List<EncodedMethod> getDirectMethods() { if (directMethods == null) { return Collections.emptyList(); } return ReadOnlyArrayList.of(directMethods); } /** * @return the virtual methods for this class */ public List<EncodedMethod> getVirtualMethods() { if (virtualMethods == null) { return Collections.emptyList(); } return ReadOnlyArrayList.of(virtualMethods); } /** * @return The number of static fields in this <code>ClassDataItem</code> */ public int getStaticFieldCount() { if (staticFields == null) { return 0; } return staticFields.length; } /** * @return The number of instance fields in this <code>ClassDataItem</code> */ public int getInstanceFieldCount() { if (instanceFields == null) { return 0; } return instanceFields.length; } /** * @return The number of direct methods in this <code>ClassDataItem</code> */ public int getDirectMethodCount() { if (directMethods == null) { return 0; } return directMethods.length; } /** * @return The number of virtual methods in this <code>ClassDataItem</code> */ public int getVirtualMethodCount() { if (virtualMethods == null) { return 0; } return virtualMethods.length; } /** * @return true if this is an empty ClassDataItem */ public boolean isEmpty() { return (getStaticFieldCount() + getInstanceFieldCount() + getDirectMethodCount() + getVirtualMethodCount()) == 0; } /** * Performs a binary search for the definition of the specified direct method * @param methodIdItem The MethodIdItem of the direct method to search for * @return The EncodedMethod for the specified direct method, or null if not found */ public EncodedMethod findDirectMethodByMethodId(MethodIdItem methodIdItem) { return findMethodByMethodIdInternal(methodIdItem.index, directMethods); } /** * Performs a binary search for the definition of the specified virtual method * @param methodIdItem The MethodIdItem of the virtual method to search for * @return The EncodedMethod for the specified virtual method, or null if not found */ public EncodedMethod findVirtualMethodByMethodId(MethodIdItem methodIdItem) { return findMethodByMethodIdInternal(methodIdItem.index, virtualMethods); } /** * Performs a binary search for the definition of the specified method. It can be either direct or virtual * @param methodIdItem The MethodIdItem of the virtual method to search for * @return The EncodedMethod for the specified virtual method, or null if not found */ public EncodedMethod findMethodByMethodId(MethodIdItem methodIdItem) { EncodedMethod encodedMethod = findMethodByMethodIdInternal(methodIdItem.index, directMethods); if (encodedMethod != null) { return encodedMethod; } return findMethodByMethodIdInternal(methodIdItem.index, virtualMethods); } private static EncodedMethod findMethodByMethodIdInternal(int methodIdItemIndex, EncodedMethod[] encodedMethods) { int min = 0; int max = encodedMethods.length; while (min<max) { int index = (min+max)>>1; EncodedMethod encodedMethod = encodedMethods[index]; int encodedMethodIndex = encodedMethod.method.getIndex(); if (encodedMethodIndex == methodIdItemIndex) { return encodedMethod; } else if (encodedMethodIndex < methodIdItemIndex) { if (min == index) { break; } min = index; } else { if (max == index) { break; } max = index; } } return null; } public static class EncodedField implements Comparable<EncodedField> { /** * The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with */ public final FieldIdItem field; /** * The access flags for this field */ public final int accessFlags; /** * Constructs a new <code>EncodedField</code> with the given values * @param field The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with * @param accessFlags The access flags for this field */ public EncodedField(FieldIdItem field, int accessFlags) { this.field = field; this.accessFlags = accessFlags; } /** * This is used internally to construct a new <code>EncodedField</code> while reading in a <code>DexFile</code> * @param dexFile The <code>DexFile</code> that is being read in * @param in the Input object to read the <code>EncodedField</code> from * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this * <code>EncodedField</code>. */ private EncodedField(DexFile dexFile, Input in, EncodedField previousEncodedField) { int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); field = dexFile.FieldIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex); accessFlags = in.readUnsignedLeb128(); } /** * Writes the <code>EncodedField</code> to the given <code>AnnotatedOutput</code> object * @param out the <code>AnnotatedOutput</code> object to write to * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this * <code>EncodedField</code>. */ private void writeTo(AnnotatedOutput out, EncodedField previousEncodedField) { int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); if (out.annotates()) { out.annotate("field: " + field.getFieldString()); out.writeUnsignedLeb128(field.getIndex() - previousIndex); out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForField(accessFlags)); out.writeUnsignedLeb128(accessFlags); }else { out.writeUnsignedLeb128(field.getIndex() - previousIndex); out.writeUnsignedLeb128(accessFlags); } } /** * Calculates the size of this <code>EncodedField</code> and returns the offset * immediately following it * @param offset the offset of this <code>EncodedField</code> in the <code>DexFile</code> * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this * <code>EncodedField</code>. * @return the offset immediately following this <code>EncodedField</code> */ private int place(int offset, EncodedField previousEncodedField) { int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); offset += Leb128Utils.unsignedLeb128Size(field.getIndex() - previousIndex); offset += Leb128Utils.unsignedLeb128Size(accessFlags); return offset; } /** * Compares this <code>EncodedField</code> to another, based on the comparison of the associated * <code>FieldIdItem</code> * @param other The <code>EncodedField</code> to compare against * @return a standard integer comparison value indicating the relationship */ public int compareTo(EncodedField other) { return field.compareTo(other.field); } /** * Determines if this <code>EncodedField</code> is equal to other, based on the equality of the associated * <code>FieldIdItem</code> * @param other The <code>EncodedField</code> to test for equality * @return true if other is equal to this instance, otherwise false */ public boolean equals(Object other) { if (other instanceof EncodedField) { return compareTo((EncodedField)other) == 0; } return false; } /** * @return true if this is a static field */ public boolean isStatic() { return (accessFlags & AccessFlags.STATIC.getValue()) != 0; } } public static class EncodedMethod implements Comparable<EncodedMethod> { /** * The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with */ public final MethodIdItem method; /** * The access flags for this method */ public final int accessFlags; /** * The <code>CodeItem</code> containing the code for this method, or null if there is no code for this method * (i.e. an abstract method) */ public final CodeItem codeItem; /** * Constructs a new <code>EncodedMethod</code> with the given values * @param method The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with * @param accessFlags The access flags for this method * @param codeItem The <code>CodeItem</code> containing the code for this method, or null if there is no code * for this method (i.e. an abstract method) */ public EncodedMethod(MethodIdItem method, int accessFlags, CodeItem codeItem) { this.method = method; this.accessFlags = accessFlags; this.codeItem = codeItem; if (codeItem != null) { codeItem.setParent(this); } } /** * This is used internally to construct a new <code>EncodedMethod</code> while reading in a <code>DexFile</code> * @param dexFile The <code>DexFile</code> that is being read in * @param readContext a <code>ReadContext</code> object to hold information that is only needed while reading * in a file * @param in the Input object to read the <code>EncodedMethod</code> from * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this * <code>EncodedMethod</code>. */ public EncodedMethod(DexFile dexFile, ReadContext readContext, Input in, EncodedMethod previousEncodedMethod) { int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); method = dexFile.MethodIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex); accessFlags = in.readUnsignedLeb128(); if (dexFile.skipInstructions()) { in.readUnsignedLeb128(); codeItem = null; } else { codeItem = (CodeItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CODE_ITEM, in.readUnsignedLeb128()); } if (codeItem != null) { codeItem.setParent(this); } } /** * Writes the <code>EncodedMethod</code> to the given <code>AnnotatedOutput</code> object * @param out the <code>AnnotatedOutput</code> object to write to * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this * <code>EncodedMethod</code>. */ private void writeTo(AnnotatedOutput out, EncodedMethod previousEncodedMethod) { int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); if (out.annotates()) { out.annotate("method: " + method.getMethodString()); out.writeUnsignedLeb128(method.getIndex() - previousIndex); out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForMethod(accessFlags)); out.writeUnsignedLeb128(accessFlags); if (codeItem != null) { out.annotate("code_off: 0x" + Integer.toHexString(codeItem.getOffset())); out.writeUnsignedLeb128(codeItem.getOffset()); } else { out.annotate("code_off: 0x0"); out.writeUnsignedLeb128(0); } }else { out.writeUnsignedLeb128(method.getIndex() - previousIndex); out.writeUnsignedLeb128(accessFlags); out.writeUnsignedLeb128(codeItem==null?0:codeItem.getOffset()); } } /** * Calculates the size of this <code>EncodedMethod</code> and returns the offset * immediately following it * @param offset the offset of this <code>EncodedMethod</code> in the <code>DexFile</code> * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this * <code>EncodedMethod</code>. * @return the offset immediately following this <code>EncodedField</code> */ private int place(int offset, EncodedMethod previousEncodedMethod) { int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); offset += Leb128Utils.unsignedLeb128Size(method.getIndex() - previousIndex); offset += Leb128Utils.unsignedLeb128Size(accessFlags); offset += codeItem==null?1:Leb128Utils.unsignedLeb128Size(codeItem.getOffset()); return offset; } /** * Compares this <code>EncodedMethod</code> to another, based on the comparison of the associated * <code>MethodIdItem</code> * @param other The <code>EncodedMethod</code> to compare against * @return a standard integer comparison value indicating the relationship */ public int compareTo(EncodedMethod other) { return method.compareTo(other.method); } /** * Determines if this <code>EncodedMethod</code> is equal to other, based on the equality of the associated * <code>MethodIdItem</code> * @param other The <code>EncodedMethod</code> to test for equality * @return true if other is equal to this instance, otherwise false */ public boolean equals(Object other) { if (other instanceof EncodedMethod) { return compareTo((EncodedMethod)other) == 0; } return false; } /** * @return true if this is a direct method */ public boolean isDirect() { return ((accessFlags & (AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() | AccessFlags.CONSTRUCTOR.getValue())) != 0); } } }