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
29package org.jf.dexlib;
30
31import com.google.common.base.Preconditions;
32import org.jf.dexlib.Util.*;
33
34import javax.annotation.Nonnull;
35import javax.annotation.Nullable;
36import java.util.*;
37
38public 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