| /* |
| * [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 com.google.common.base.Preconditions; |
| import org.jf.dexlib.Util.*; |
| |
| import javax.annotation.Nonnull; |
| import javax.annotation.Nullable; |
| import java.util.*; |
| |
| public class ClassDataItem extends Item<ClassDataItem> { |
| @Nullable |
| private EncodedField[] staticFields = null; |
| @Nullable |
| private EncodedField[] instanceFields = null; |
| @Nullable |
| private EncodedMethod[] directMethods = null; |
| @Nullable |
| 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, @Nullable EncodedField[] staticFields, |
| @Nullable EncodedField[] instanceFields, @Nullable EncodedMethod[] directMethods, |
| @Nullable 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, @Nullable List<EncodedField> staticFields, |
| @Nullable List<EncodedField> instanceFields, |
| @Nullable List<EncodedMethod> directMethods, |
| @Nullable 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) { |
| Preconditions.checkNotNull(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 |
| */ |
| @Nullable |
| 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 |
| */ |
| @Nonnull |
| public List<EncodedField> getStaticFields() { |
| if (staticFields == null) { |
| return Collections.emptyList(); |
| } |
| return ReadOnlyArrayList.of(staticFields); |
| } |
| |
| /** |
| * @return the instance fields for this class |
| */ |
| @Nonnull |
| public List<EncodedField> getInstanceFields() { |
| if (instanceFields == null) { |
| return Collections.emptyList(); |
| } |
| return ReadOnlyArrayList.of(instanceFields); |
| } |
| |
| /** |
| * @return the direct methods for this class |
| */ |
| @Nonnull |
| public List<EncodedMethod> getDirectMethods() { |
| if (directMethods == null) { |
| return Collections.emptyList(); |
| } |
| return ReadOnlyArrayList.of(directMethods); |
| } |
| |
| /** |
| * @return the virtual methods for this class |
| */ |
| @Nonnull |
| 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, @Nullable 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); |
| } |
| } |
| } |