• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
ClassDataItem(final DexFile dexFile)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      */
ClassDataItem(DexFile dexFile, @Nullable EncodedField[] staticFields, @Nullable EncodedField[] instanceFields, @Nullable EncodedMethod[] directMethods, @Nullable EncodedMethod[] virtualMethods)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      */
internClassDataItem(DexFile dexFile, @Nullable List<EncodedField> staticFields, @Nullable List<EncodedField> instanceFields, @Nullable List<EncodedMethod> directMethods, @Nullable List<EncodedMethod> virtualMethods)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} */
readItem(Input in, ReadContext readContext)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} */
placeItem(int offset)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} */
writeItem(AnnotatedOutput out)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} */
getItemType()378     public ItemType getItemType() {
379         return ItemType.TYPE_CLASS_DATA_ITEM;
380     }
381 
382     /** {@inheritDoc} */
getConciseIdentity()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} */
compareTo(ClassDataItem other)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
hashCode()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
getParentType()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
getStaticFields()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
getInstanceFields()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
getDirectMethods()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
getVirtualMethods()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      */
getStaticFieldCount()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      */
getInstanceFieldCount()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      */
getDirectMethodCount()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      */
getVirtualMethodCount()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      */
isEmpty()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      */
findDirectMethodByMethodId(MethodIdItem methodIdItem)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      */
findVirtualMethodByMethodId(MethodIdItem methodIdItem)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      */
findMethodByMethodId(MethodIdItem methodIdItem)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 
findMethodByMethodIdInternal(int methodIdItemIndex, EncodedMethod[] encodedMethods)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          */
EncodedField(FieldIdItem field, int accessFlags)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          */
EncodedField(DexFile dexFile, Input in, @Nullable EncodedField previousEncodedField)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          */
writeTo(AnnotatedOutput out, EncodedField previousEncodedField)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          */
place(int offset, EncodedField previousEncodedField)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          */
compareTo(EncodedField other)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          */
equals(Object other)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          */
isStatic()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          */
EncodedMethod(MethodIdItem method, int accessFlags, CodeItem codeItem)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          */
EncodedMethod(DexFile dexFile, ReadContext readContext, Input in, EncodedMethod previousEncodedMethod)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          */
writeTo(AnnotatedOutput out, EncodedMethod previousEncodedMethod)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          */
place(int offset, EncodedMethod previousEncodedMethod)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          */
compareTo(EncodedMethod other)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          */
equals(Object other)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          */
isDirect()838         public boolean isDirect() {
839             return ((accessFlags & (AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() |
840                     AccessFlags.CONSTRUCTOR.getValue())) != 0);
841         }
842     }
843 }
844