Home | History | Annotate | Download | only in dexlib
      1 /*
      2  * [The "BSD licence"]
      3  * Copyright (c) 2010 Ben Gruver (JesusFreke)
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 package org.jf.dexlib;
     30 
     31 import com.google.common.base.Preconditions;
     32 import org.jf.dexlib.Util.*;
     33 
     34 import javax.annotation.Nonnull;
     35 import javax.annotation.Nullable;
     36 import java.util.*;
     37 
     38 public class ClassDataItem extends Item<ClassDataItem> {
     39     @Nullable
     40     private EncodedField[] staticFields = null;
     41     @Nullable
     42     private EncodedField[] instanceFields = null;
     43     @Nullable
     44     private EncodedMethod[] directMethods = null;
     45     @Nullable
     46     private EncodedMethod[] virtualMethods = null;
     47 
     48     /**
     49      * Creates a new uninitialized <code>ClassDataItem</code>
     50      * @param dexFile The <code>DexFile</code> that this item belongs to
     51      */
     52     public ClassDataItem(final DexFile dexFile) {
     53         super(dexFile);
     54     }
     55 
     56     /**
     57      * Creates a new <code>ClassDataItem</code> with the given values
     58      * @param dexFile The <code>DexFile</code> that this item belongs to
     59      * @param staticFields The static fields for this class
     60      * @param instanceFields The instance fields for this class
     61      * @param directMethods The direct methods for this class
     62      * @param virtualMethods The virtual methods for this class
     63      */
     64     private ClassDataItem(DexFile dexFile, @Nullable EncodedField[] staticFields,
     65                           @Nullable EncodedField[] instanceFields, @Nullable EncodedMethod[] directMethods,
     66                           @Nullable EncodedMethod[] virtualMethods) {
     67         super(dexFile);
     68         this.staticFields = staticFields;
     69         this.instanceFields = instanceFields;
     70         this.directMethods = directMethods;
     71         this.virtualMethods = virtualMethods;
     72     }
     73 
     74     /**
     75      * Creates a new <code>ClassDataItem</code> with the given values
     76      * @param dexFile The <code>DexFile</code> that this item belongs to
     77      * @param staticFields The static fields for this class
     78      * @param instanceFields The instance fields for this class
     79      * @param directMethods The direct methods for this class
     80      * @param virtualMethods The virtual methods for this class
     81      * @return a new <code>ClassDataItem</code> with the given values
     82      */
     83     public static ClassDataItem internClassDataItem(DexFile dexFile, @Nullable List<EncodedField> staticFields,
     84                                                     @Nullable List<EncodedField> instanceFields,
     85                                                     @Nullable List<EncodedMethod> directMethods,
     86                                                     @Nullable List<EncodedMethod> virtualMethods) {
     87         EncodedField[] staticFieldsArray = null;
     88         EncodedField[] instanceFieldsArray = null;
     89         EncodedMethod[] directMethodsArray = null;
     90         EncodedMethod[] virtualMethodsArray = null;
     91 
     92         if (staticFields != null && staticFields.size() > 0) {
     93             SortedSet<EncodedField> staticFieldsSet = new TreeSet<EncodedField>();
     94             for (EncodedField staticField: staticFields) {
     95                 if (staticFieldsSet.contains(staticField)) {
     96                     System.err.println(String.format("Ignoring duplicate static field definition: %s",
     97                             staticField.field.getFieldString()));
     98                     continue;
     99                 }
    100                 staticFieldsSet.add(staticField);
    101             }
    102 
    103             staticFieldsArray = new EncodedField[staticFieldsSet.size()];
    104             staticFieldsArray = staticFieldsSet.toArray(staticFieldsArray);
    105         }
    106 
    107         if (instanceFields != null && instanceFields.size() > 0) {
    108             SortedSet<EncodedField> instanceFieldsSet = new TreeSet<EncodedField>();
    109             for (EncodedField instanceField: instanceFields) {
    110                 if (instanceFieldsSet.contains(instanceField)) {
    111                     System.err.println(String.format("Ignoring duplicate instance field definition: %s",
    112                             instanceField.field.getFieldString()));
    113                     continue;
    114                 }
    115                 instanceFieldsSet.add(instanceField);
    116             }
    117 
    118             instanceFieldsArray = new EncodedField[instanceFieldsSet.size()];
    119             instanceFieldsArray = instanceFieldsSet.toArray(instanceFieldsArray);
    120         }
    121 
    122         TreeSet<EncodedMethod> directMethodSet = new TreeSet<EncodedMethod>();
    123 
    124         if (directMethods != null && directMethods.size() > 0) {
    125             for (EncodedMethod directMethod: directMethods) {
    126                 if (directMethodSet.contains(directMethod)) {
    127                     System.err.println(String.format("Ignoring duplicate direct method definition: %s",
    128                             directMethod.method.getMethodString()));
    129                     continue;
    130                 }
    131                 directMethodSet.add(directMethod);
    132             }
    133 
    134             directMethodsArray = new EncodedMethod[directMethodSet.size()];
    135             directMethodsArray = directMethodSet.toArray(directMethodsArray);
    136         }
    137 
    138         if (virtualMethods != null && virtualMethods.size() > 0) {
    139             TreeSet<EncodedMethod> virtualMethodSet = new TreeSet<EncodedMethod>();
    140             for (EncodedMethod virtualMethod: virtualMethods) {
    141                 if (directMethodSet.contains(virtualMethod)) {
    142                     // If both a direct and virtual definition is present, dalvik's behavior seems to be undefined,
    143                     // so we can't gracefully handle this case, like we can if the duplicates are all direct or all
    144                     // virtual -- in which case, we ignore all but the first definition
    145                     throw new RuntimeException(String.format("Duplicate direct+virtual method definition: %s",
    146                             virtualMethod.method.getMethodString()));
    147                 }
    148                 if (virtualMethodSet.contains(virtualMethod)) {
    149                     System.err.println(String.format("Ignoring duplicate virtual method definition: %s",
    150                             virtualMethod.method.getMethodString()));
    151                     continue;
    152                 }
    153                 virtualMethodSet.add(virtualMethod);
    154             }
    155 
    156             virtualMethodsArray = new EncodedMethod[virtualMethodSet.size()];
    157             virtualMethodsArray = virtualMethodSet.toArray(virtualMethodsArray);
    158         }
    159 
    160         ClassDataItem classDataItem = new ClassDataItem(dexFile, staticFieldsArray, instanceFieldsArray,
    161                 directMethodsArray, virtualMethodsArray);
    162         return dexFile.ClassDataSection.intern(classDataItem);
    163     }
    164 
    165     /** {@inheritDoc} */
    166     protected void readItem(Input in, ReadContext readContext) {
    167         int staticFieldsCount = in.readUnsignedLeb128();
    168         int instanceFieldsCount = in.readUnsignedLeb128();
    169         int directMethodsCount = in.readUnsignedLeb128();
    170         int virtualMethodsCount = in.readUnsignedLeb128();
    171 
    172         if (staticFieldsCount > 0) {
    173             staticFields = new EncodedField[staticFieldsCount];
    174             EncodedField previousEncodedField = null;
    175             for (int i=0; i<staticFieldsCount; i++) {
    176                 try {
    177                     staticFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField);
    178                 } catch (Exception ex) {
    179                     throw ExceptionWithContext.withContext(ex, "Error while reading static field at index " + i);
    180                 }
    181             }
    182         }
    183 
    184         if (instanceFieldsCount > 0) {
    185             instanceFields = new EncodedField[instanceFieldsCount];
    186             EncodedField previousEncodedField = null;
    187             for (int i=0; i<instanceFieldsCount; i++) {
    188                 try {
    189                     instanceFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField);
    190                 } catch (Exception ex) {
    191                     throw ExceptionWithContext.withContext(ex, "Error while reading instance field at index " + i);
    192                 }
    193             }
    194         }
    195 
    196         if (directMethodsCount > 0) {
    197             directMethods = new EncodedMethod[directMethodsCount];
    198             EncodedMethod previousEncodedMethod = null;
    199             for (int i=0; i<directMethodsCount; i++) {
    200                 try {
    201                     directMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in,
    202                             previousEncodedMethod);
    203                 } catch (Exception ex) {
    204                     throw ExceptionWithContext.withContext(ex, "Error while reading direct method at index " + i);
    205                 }
    206             }
    207         }
    208 
    209         if (virtualMethodsCount > 0) {
    210             virtualMethods = new EncodedMethod[virtualMethodsCount];
    211             EncodedMethod previousEncodedMethod = null;
    212             for (int i=0; i<virtualMethodsCount; i++) {
    213                 try {
    214                     virtualMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in,
    215                             previousEncodedMethod);
    216                 } catch (Exception ex) {
    217                     throw ExceptionWithContext.withContext(ex, "Error while reading virtual method at index " + i);
    218                 }
    219             }
    220         }
    221     }
    222 
    223     /** {@inheritDoc} */
    224     protected int placeItem(int offset) {
    225         offset += Leb128Utils.unsignedLeb128Size(getStaticFieldCount());
    226         offset += Leb128Utils.unsignedLeb128Size(getInstanceFieldCount());
    227         offset += Leb128Utils.unsignedLeb128Size(getDirectMethodCount());
    228         offset += Leb128Utils.unsignedLeb128Size(getVirtualMethodCount());
    229 
    230         if (staticFields != null) {
    231             EncodedField previousEncodedField = null;
    232             for (EncodedField encodedField: staticFields) {
    233                 offset = encodedField.place(offset, previousEncodedField);
    234                 previousEncodedField = encodedField;
    235             }
    236         }
    237 
    238         if (instanceFields != null) {
    239             EncodedField previousEncodedField = null;
    240             for (EncodedField encodedField: instanceFields) {
    241                 offset = encodedField.place(offset, previousEncodedField);
    242                 previousEncodedField = encodedField;
    243             }
    244         }
    245 
    246         if (directMethods != null) {
    247             EncodedMethod previousEncodedMethod = null;
    248             for (EncodedMethod encodedMethod: directMethods) {
    249                 offset = encodedMethod.place(offset, previousEncodedMethod);
    250                 previousEncodedMethod = encodedMethod;
    251             }
    252         }
    253 
    254         if (virtualMethods != null) {
    255             EncodedMethod previousEncodedMethod = null;
    256             for (EncodedMethod encodedMethod: virtualMethods) {
    257                 offset = encodedMethod.place(offset, previousEncodedMethod);
    258                 previousEncodedMethod = encodedMethod;
    259             }
    260         }
    261 
    262         return offset;
    263     }
    264 
    265     /** {@inheritDoc} */
    266     protected void writeItem(AnnotatedOutput out) {
    267         if (out.annotates()) {
    268             int staticFieldCount = getStaticFieldCount();
    269             out.annotate("static_fields_size: 0x" + Integer.toHexString(staticFieldCount) + " (" +
    270                     staticFieldCount + ")");
    271             out.writeUnsignedLeb128(staticFieldCount);
    272 
    273             int instanceFieldCount = getInstanceFieldCount();
    274             out.annotate("instance_fields_size: 0x" + Integer.toHexString(instanceFieldCount) + " (" +
    275                     instanceFieldCount + ")");
    276             out.writeUnsignedLeb128(instanceFieldCount);
    277 
    278             int directMethodCount = getDirectMethodCount();
    279             out.annotate("direct_methods_size: 0x" + Integer.toHexString(directMethodCount) + " (" +
    280                     directMethodCount + ")");
    281             out.writeUnsignedLeb128(directMethodCount);
    282 
    283             int virtualMethodCount = getVirtualMethodCount();
    284             out.annotate("virtual_methods_size: 0x" + Integer.toHexString(virtualMethodCount) + " (" +
    285                     virtualMethodCount + ")");
    286             out.writeUnsignedLeb128(virtualMethodCount);
    287 
    288 
    289             if (staticFields != null) {
    290                 int index = 0;
    291                 EncodedField previousEncodedField = null;
    292                 for (EncodedField encodedField: staticFields) {
    293                     out.annotate("[" + index++ + "] static_field");
    294                     out.indent();
    295                     encodedField.writeTo(out, previousEncodedField);
    296                     out.deindent();
    297                     previousEncodedField = encodedField;
    298                 }
    299             }
    300 
    301             if (instanceFields != null) {
    302                 int index = 0;
    303                 EncodedField previousEncodedField = null;
    304                 for (EncodedField encodedField: instanceFields) {
    305                     out.annotate("[" + index++ + "] instance_field");
    306                     out.indent();
    307                     encodedField.writeTo(out, previousEncodedField);
    308                     out.deindent();
    309                     previousEncodedField = encodedField;
    310                 }
    311             }
    312 
    313             if (directMethods != null) {
    314                 int index = 0;
    315                 EncodedMethod previousEncodedMethod = null;
    316                 for (EncodedMethod encodedMethod: directMethods) {
    317                     out.annotate("[" + index++ + "] direct_method");
    318                     out.indent();
    319                     encodedMethod.writeTo(out, previousEncodedMethod);
    320                     out.deindent();
    321                     previousEncodedMethod = encodedMethod;
    322                 }
    323             }
    324 
    325             if (virtualMethods != null) {
    326                 int index = 0;
    327                 EncodedMethod previousEncodedMethod = null;
    328                 for (EncodedMethod encodedMethod: virtualMethods) {
    329                     out.annotate("[" + index++ + "] virtual_method");
    330                     out.indent();
    331                     encodedMethod.writeTo(out, previousEncodedMethod);
    332                     out.deindent();
    333                     previousEncodedMethod = encodedMethod;
    334                 }
    335             }
    336         } else {
    337             out.writeUnsignedLeb128(getStaticFieldCount());
    338             out.writeUnsignedLeb128(getInstanceFieldCount());
    339             out.writeUnsignedLeb128(getDirectMethodCount());
    340             out.writeUnsignedLeb128(getVirtualMethodCount());
    341 
    342             if (staticFields != null) {
    343                 EncodedField previousEncodedField = null;
    344                 for (EncodedField encodedField: staticFields) {
    345                     encodedField.writeTo(out, previousEncodedField);
    346                     previousEncodedField = encodedField;
    347                 }
    348             }
    349 
    350 
    351             if (instanceFields != null) {
    352                 EncodedField previousEncodedField = null;
    353                 for (EncodedField encodedField: instanceFields) {
    354                     encodedField.writeTo(out, previousEncodedField);
    355                     previousEncodedField = encodedField;
    356                 }
    357             }
    358 
    359             if (directMethods != null) {
    360                 EncodedMethod previousEncodedMethod = null;
    361                 for (EncodedMethod encodedMethod: directMethods) {
    362                     encodedMethod.writeTo(out, previousEncodedMethod);
    363                     previousEncodedMethod = encodedMethod;
    364                 }
    365             }
    366 
    367             if (virtualMethods != null) {
    368                 EncodedMethod previousEncodedMethod = null;
    369                 for (EncodedMethod encodedMethod: virtualMethods) {
    370                     encodedMethod.writeTo(out, previousEncodedMethod);
    371                     previousEncodedMethod = encodedMethod;
    372                 }
    373             }
    374         }
    375     }
    376 
    377     /** {@inheritDoc} */
    378     public ItemType getItemType() {
    379         return ItemType.TYPE_CLASS_DATA_ITEM;
    380     }
    381 
    382     /** {@inheritDoc} */
    383     public String getConciseIdentity() {
    384         TypeIdItem parentType = getParentType();
    385         if (parentType == null) {
    386             return "class_data_item @0x" + Integer.toHexString(getOffset());
    387         }
    388         return "class_data_item @0x" + Integer.toHexString(getOffset()) + " (" + parentType.getTypeDescriptor() +")";
    389     }
    390 
    391     /** {@inheritDoc} */
    392     public int compareTo(ClassDataItem other) {
    393         Preconditions.checkNotNull(other);
    394 
    395         // An empty CodeDataItem may be shared by multiple ClassDefItems, so we can't use parent in this case
    396         if (isEmpty()) {
    397             if (other.isEmpty()) {
    398                 return 0;
    399             }
    400             return -1;
    401         }
    402         if (other.isEmpty()) {
    403             return 1;
    404         }
    405 
    406         TypeIdItem parentType = getParentType();
    407         TypeIdItem otherParentType= other.getParentType();
    408         if (parentType == null) {
    409             if (otherParentType == null) {
    410                 return 0;
    411             }
    412             return -1;
    413         }
    414         if (otherParentType == null) {
    415             return 1;
    416         }
    417         return parentType.compareTo(otherParentType);
    418     }
    419 
    420     @Override
    421     public int hashCode() {
    422         // If the item has a single parent, we can use the re-use the identity (hash) of that parent
    423         TypeIdItem parentType = getParentType();
    424         if (parentType != null) {
    425             return parentType.hashCode();
    426         }
    427         return 0;
    428     }
    429 
    430     /**
    431      * Returns the parent type for a non-empty ClassDataItem, or null for an empty one (which could be referenced by
    432      * multiple ClassDefItem parents)
    433      *
    434      * Only an empty ClassDataItem may have multiple parents.
    435      *
    436      * @return The parent type for this ClassDefItem, or null if it may have multiple parents
    437      */
    438     @Nullable
    439     public TypeIdItem getParentType() {
    440         if (staticFields != null && staticFields.length > 0) {
    441             return staticFields[0].field.getContainingClass();
    442         }
    443         if (instanceFields != null && instanceFields.length > 0) {
    444             return instanceFields[0].field.getContainingClass();
    445         }
    446         if (directMethods != null && directMethods.length > 0) {
    447             return directMethods[0].method.getContainingClass();
    448         }
    449         if (virtualMethods != null && virtualMethods.length > 0) {
    450             return virtualMethods[0].method.getContainingClass();
    451         }
    452         return null;
    453     }
    454 
    455     /**
    456      * @return the static fields for this class
    457      */
    458     @Nonnull
    459     public List<EncodedField> getStaticFields() {
    460         if (staticFields == null) {
    461             return Collections.emptyList();
    462         }
    463         return ReadOnlyArrayList.of(staticFields);
    464     }
    465 
    466     /**
    467      * @return the instance fields for this class
    468      */
    469     @Nonnull
    470     public List<EncodedField> getInstanceFields() {
    471         if (instanceFields == null) {
    472             return Collections.emptyList();
    473         }
    474         return ReadOnlyArrayList.of(instanceFields);
    475     }
    476 
    477     /**
    478      * @return the direct methods for this class
    479      */
    480     @Nonnull
    481     public List<EncodedMethod> getDirectMethods() {
    482         if (directMethods == null) {
    483             return Collections.emptyList();
    484         }
    485         return ReadOnlyArrayList.of(directMethods);
    486     }
    487 
    488     /**
    489      * @return the virtual methods for this class
    490      */
    491     @Nonnull
    492     public List<EncodedMethod> getVirtualMethods() {
    493         if (virtualMethods == null) {
    494             return Collections.emptyList();
    495         }
    496         return ReadOnlyArrayList.of(virtualMethods);
    497     }
    498 
    499     /**
    500      * @return The number of static fields in this <code>ClassDataItem</code>
    501      */
    502     public int getStaticFieldCount() {
    503         if (staticFields == null) {
    504             return 0;
    505         }
    506         return staticFields.length;
    507     }
    508 
    509     /**
    510      * @return The number of instance fields in this <code>ClassDataItem</code>
    511      */
    512     public int getInstanceFieldCount() {
    513         if (instanceFields == null) {
    514             return 0;
    515         }
    516         return instanceFields.length;
    517     }
    518 
    519     /**
    520      * @return The number of direct methods in this <code>ClassDataItem</code>
    521      */
    522     public int getDirectMethodCount() {
    523         if (directMethods == null) {
    524             return 0;
    525         }
    526         return directMethods.length;
    527     }
    528 
    529     /**
    530      * @return The number of virtual methods in this <code>ClassDataItem</code>
    531      */
    532     public int getVirtualMethodCount() {
    533         if (virtualMethods == null) {
    534             return 0;
    535         }
    536         return virtualMethods.length;
    537     }
    538 
    539     /**
    540      * @return true if this is an empty ClassDataItem
    541      */
    542     public boolean isEmpty() {
    543         return (getStaticFieldCount() + getInstanceFieldCount() +
    544                 getDirectMethodCount() + getVirtualMethodCount()) == 0;
    545     }
    546 
    547     /**
    548      * Performs a binary search for the definition of the specified direct method
    549      * @param methodIdItem The MethodIdItem of the direct method to search for
    550      * @return The EncodedMethod for the specified direct method, or null if not found
    551      */
    552     public EncodedMethod findDirectMethodByMethodId(MethodIdItem methodIdItem) {
    553         return findMethodByMethodIdInternal(methodIdItem.index, directMethods);
    554     }
    555 
    556     /**
    557      * Performs a binary search for the definition of the specified virtual method
    558      * @param methodIdItem The MethodIdItem of the virtual method to search for
    559      * @return The EncodedMethod for the specified virtual method, or null if not found
    560      */
    561     public EncodedMethod findVirtualMethodByMethodId(MethodIdItem methodIdItem) {
    562         return findMethodByMethodIdInternal(methodIdItem.index, virtualMethods);
    563     }
    564 
    565     /**
    566      * Performs a binary search for the definition of the specified method. It can be either direct or virtual
    567      * @param methodIdItem The MethodIdItem of the virtual method to search for
    568      * @return The EncodedMethod for the specified virtual method, or null if not found
    569      */
    570     public EncodedMethod findMethodByMethodId(MethodIdItem methodIdItem) {
    571         EncodedMethod encodedMethod = findMethodByMethodIdInternal(methodIdItem.index, directMethods);
    572         if (encodedMethod != null) {
    573             return encodedMethod;
    574         }
    575 
    576         return findMethodByMethodIdInternal(methodIdItem.index, virtualMethods);
    577     }
    578 
    579     private static EncodedMethod findMethodByMethodIdInternal(int methodIdItemIndex, EncodedMethod[] encodedMethods) {
    580         int min = 0;
    581         int max = encodedMethods.length;
    582 
    583         while (min<max) {
    584             int index = (min+max)>>1;
    585 
    586             EncodedMethod encodedMethod = encodedMethods[index];
    587 
    588             int encodedMethodIndex = encodedMethod.method.getIndex();
    589             if (encodedMethodIndex == methodIdItemIndex) {
    590                 return encodedMethod;
    591             } else if (encodedMethodIndex < methodIdItemIndex) {
    592                 if (min == index) {
    593                     break;
    594                 }
    595                 min = index;
    596             } else {
    597                 if (max == index) {
    598                     break;
    599                 }
    600                 max = index;
    601             }
    602         }
    603 
    604         return null;
    605     }
    606 
    607     public static class EncodedField implements Comparable<EncodedField> {
    608         /**
    609          * The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with
    610          */
    611         public final FieldIdItem field;
    612 
    613         /**
    614          * The access flags for this field
    615          */
    616         public final int accessFlags;
    617 
    618         /**
    619          * Constructs a new <code>EncodedField</code> with the given values
    620          * @param field The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with
    621          * @param accessFlags The access flags for this field
    622          */
    623         public EncodedField(FieldIdItem field, int accessFlags) {
    624             this.field = field;
    625             this.accessFlags = accessFlags;
    626         }
    627 
    628         /**
    629          * This is used internally to construct a new <code>EncodedField</code> while reading in a <code>DexFile</code>
    630          * @param dexFile The <code>DexFile</code> that is being read in
    631          * @param in the Input object to read the <code>EncodedField</code> from
    632          * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this
    633          * <code>EncodedField</code>.
    634          */
    635         private EncodedField(DexFile dexFile, Input in, @Nullable EncodedField previousEncodedField) {
    636             int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
    637             field = dexFile.FieldIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex);
    638             accessFlags = in.readUnsignedLeb128();
    639         }
    640 
    641         /**
    642          * Writes the <code>EncodedField</code> to the given <code>AnnotatedOutput</code> object
    643          * @param out the <code>AnnotatedOutput</code> object to write to
    644          * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this
    645          * <code>EncodedField</code>.
    646          */
    647         private void writeTo(AnnotatedOutput out, EncodedField previousEncodedField) {
    648             int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
    649 
    650             if (out.annotates()) {
    651                 out.annotate("field: " + field.getFieldString());
    652                 out.writeUnsignedLeb128(field.getIndex() - previousIndex);
    653                 out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForField(accessFlags));
    654                 out.writeUnsignedLeb128(accessFlags);
    655             }else {
    656                 out.writeUnsignedLeb128(field.getIndex() - previousIndex);
    657                 out.writeUnsignedLeb128(accessFlags);
    658             }
    659         }
    660 
    661         /**
    662          * Calculates the size of this <code>EncodedField</code> and returns the offset
    663          * immediately following it
    664          * @param offset the offset of this <code>EncodedField</code> in the <code>DexFile</code>
    665          * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this
    666          * <code>EncodedField</code>.
    667          * @return the offset immediately following this <code>EncodedField</code>
    668          */
    669         private int place(int offset, EncodedField previousEncodedField) {
    670             int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
    671 
    672             offset += Leb128Utils.unsignedLeb128Size(field.getIndex() - previousIndex);
    673             offset += Leb128Utils.unsignedLeb128Size(accessFlags);
    674             return  offset;
    675         }
    676 
    677         /**
    678          * Compares this <code>EncodedField</code> to another, based on the comparison of the associated
    679          * <code>FieldIdItem</code>
    680          * @param other The <code>EncodedField</code> to compare against
    681          * @return a standard integer comparison value indicating the relationship
    682          */
    683         public int compareTo(EncodedField other)
    684         {
    685             return field.compareTo(other.field);
    686         }
    687 
    688         /**
    689          * Determines if this <code>EncodedField</code> is equal to other, based on the equality of the associated
    690          * <code>FieldIdItem</code>
    691          * @param other The <code>EncodedField</code> to test for equality
    692          * @return true if other is equal to this instance, otherwise false
    693          */
    694         public boolean equals(Object other) {
    695             if (other instanceof EncodedField) {
    696                 return compareTo((EncodedField)other) == 0;
    697             }
    698             return false;
    699         }
    700 
    701         /**
    702          * @return true if this is a static field
    703          */
    704         public boolean isStatic() {
    705             return (accessFlags & AccessFlags.STATIC.getValue()) != 0;
    706         }
    707     }
    708 
    709     public static class EncodedMethod implements Comparable<EncodedMethod> {
    710         /**
    711          * The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with
    712          */
    713         public final MethodIdItem method;
    714 
    715         /**
    716          * The access flags for this method
    717          */
    718         public final int accessFlags;
    719 
    720         /**
    721          * The <code>CodeItem</code> containing the code for this method, or null if there is no code for this method
    722          * (i.e. an abstract method)
    723          */
    724         public final CodeItem codeItem;
    725 
    726         /**
    727          * Constructs a new <code>EncodedMethod</code> with the given values
    728          * @param method The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with
    729          * @param accessFlags The access flags for this method
    730          * @param codeItem The <code>CodeItem</code> containing the code for this method, or null if there is no code
    731          * for this method (i.e. an abstract method)
    732          */
    733         public EncodedMethod(MethodIdItem method, int accessFlags, CodeItem codeItem) {
    734             this.method = method;
    735             this.accessFlags = accessFlags;
    736             this.codeItem = codeItem;
    737             if (codeItem != null) {
    738                 codeItem.setParent(this);
    739             }
    740         }
    741 
    742         /**
    743          * This is used internally to construct a new <code>EncodedMethod</code> while reading in a <code>DexFile</code>
    744          * @param dexFile The <code>DexFile</code> that is being read in
    745          * @param readContext a <code>ReadContext</code> object to hold information that is only needed while reading
    746          * in a file
    747          * @param in the Input object to read the <code>EncodedMethod</code> from
    748          * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this
    749          * <code>EncodedMethod</code>.
    750          */
    751         public EncodedMethod(DexFile dexFile, ReadContext readContext, Input in, EncodedMethod previousEncodedMethod) {
    752             int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
    753             method = dexFile.MethodIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex);
    754             accessFlags = in.readUnsignedLeb128();
    755             if (dexFile.skipInstructions()) {
    756                 in.readUnsignedLeb128();
    757                 codeItem = null;
    758             } else {
    759                 codeItem = (CodeItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CODE_ITEM,
    760                         in.readUnsignedLeb128());
    761             }
    762             if (codeItem != null) {
    763                 codeItem.setParent(this);
    764             }
    765         }
    766 
    767         /**
    768          * Writes the <code>EncodedMethod</code> to the given <code>AnnotatedOutput</code> object
    769          * @param out the <code>AnnotatedOutput</code> object to write to
    770          * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this
    771          * <code>EncodedMethod</code>.
    772          */
    773         private void writeTo(AnnotatedOutput out, EncodedMethod previousEncodedMethod) {
    774             int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
    775 
    776             if (out.annotates()) {
    777                 out.annotate("method: " + method.getMethodString());
    778                 out.writeUnsignedLeb128(method.getIndex() - previousIndex);
    779                 out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForMethod(accessFlags));
    780                 out.writeUnsignedLeb128(accessFlags);
    781                 if (codeItem != null) {
    782                     out.annotate("code_off: 0x" + Integer.toHexString(codeItem.getOffset()));
    783                     out.writeUnsignedLeb128(codeItem.getOffset());
    784                 } else {
    785                     out.annotate("code_off: 0x0");
    786                     out.writeUnsignedLeb128(0);
    787                 }
    788             }else {
    789                 out.writeUnsignedLeb128(method.getIndex() - previousIndex);
    790                 out.writeUnsignedLeb128(accessFlags);
    791                 out.writeUnsignedLeb128(codeItem==null?0:codeItem.getOffset());
    792             }
    793         }
    794 
    795         /**
    796          * Calculates the size of this <code>EncodedMethod</code> and returns the offset
    797          * immediately following it
    798          * @param offset the offset of this <code>EncodedMethod</code> in the <code>DexFile</code>
    799          * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this
    800          * <code>EncodedMethod</code>.
    801          * @return the offset immediately following this <code>EncodedField</code>
    802          */
    803         private int place(int offset, EncodedMethod previousEncodedMethod) {
    804             int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
    805 
    806             offset += Leb128Utils.unsignedLeb128Size(method.getIndex() - previousIndex);
    807             offset += Leb128Utils.unsignedLeb128Size(accessFlags);
    808             offset += codeItem==null?1:Leb128Utils.unsignedLeb128Size(codeItem.getOffset());
    809             return  offset;
    810         }
    811 
    812         /**
    813          * Compares this <code>EncodedMethod</code> to another, based on the comparison of the associated
    814          * <code>MethodIdItem</code>
    815          * @param other The <code>EncodedMethod</code> to compare against
    816          * @return a standard integer comparison value indicating the relationship
    817          */
    818         public int compareTo(EncodedMethod other) {
    819             return method.compareTo(other.method);
    820         }
    821 
    822         /**
    823          * Determines if this <code>EncodedMethod</code> is equal to other, based on the equality of the associated
    824          * <code>MethodIdItem</code>
    825          * @param other The <code>EncodedMethod</code> to test for equality
    826          * @return true if other is equal to this instance, otherwise false
    827          */
    828         public boolean equals(Object other) {
    829             if (other instanceof EncodedMethod) {
    830                 return compareTo((EncodedMethod)other) == 0;
    831             }
    832             return false;
    833         }
    834 
    835         /**
    836          * @return true if this is a direct method
    837          */
    838         public boolean isDirect() {
    839             return ((accessFlags & (AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() |
    840                     AccessFlags.CONSTRUCTOR.getValue())) != 0);
    841         }
    842     }
    843 }
    844