1// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4package com.android.tools.r8.dex;
5
6import static com.android.tools.r8.utils.EncodedValueUtils.parseDouble;
7import static com.android.tools.r8.utils.EncodedValueUtils.parseFloat;
8import static com.android.tools.r8.utils.EncodedValueUtils.parseSigned;
9import static com.android.tools.r8.utils.EncodedValueUtils.parseUnsigned;
10
11import com.android.tools.r8.Resource;
12import com.android.tools.r8.code.Instruction;
13import com.android.tools.r8.code.InstructionFactory;
14import com.android.tools.r8.graph.ClassKind;
15import com.android.tools.r8.graph.Descriptor;
16import com.android.tools.r8.graph.DexAccessFlags;
17import com.android.tools.r8.graph.DexAnnotation;
18import com.android.tools.r8.graph.DexAnnotationElement;
19import com.android.tools.r8.graph.DexAnnotationSet;
20import com.android.tools.r8.graph.DexAnnotationSetRefList;
21import com.android.tools.r8.graph.DexCallSite;
22import com.android.tools.r8.graph.DexClass;
23import com.android.tools.r8.graph.DexCode;
24import com.android.tools.r8.graph.DexCode.Try;
25import com.android.tools.r8.graph.DexCode.TryHandler;
26import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
27import com.android.tools.r8.graph.DexDebugEvent;
28import com.android.tools.r8.graph.DexDebugInfo;
29import com.android.tools.r8.graph.DexEncodedAnnotation;
30import com.android.tools.r8.graph.DexEncodedArray;
31import com.android.tools.r8.graph.DexEncodedField;
32import com.android.tools.r8.graph.DexEncodedMethod;
33import com.android.tools.r8.graph.DexField;
34import com.android.tools.r8.graph.DexItem;
35import com.android.tools.r8.graph.DexItemFactory;
36import com.android.tools.r8.graph.DexMemberAnnotation;
37import com.android.tools.r8.graph.DexMemberAnnotation.DexFieldAnnotation;
38import com.android.tools.r8.graph.DexMemberAnnotation.DexMethodAnnotation;
39import com.android.tools.r8.graph.DexMemberAnnotation.DexParameterAnnotation;
40import com.android.tools.r8.graph.DexMethod;
41import com.android.tools.r8.graph.DexMethodHandle;
42import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
43import com.android.tools.r8.graph.DexProto;
44import com.android.tools.r8.graph.DexString;
45import com.android.tools.r8.graph.DexType;
46import com.android.tools.r8.graph.DexTypeList;
47import com.android.tools.r8.graph.DexValue;
48import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
49import com.android.tools.r8.graph.DexValue.DexValueMethodType;
50import com.android.tools.r8.graph.DexValue.DexValueNull;
51import com.android.tools.r8.graph.DexValue.DexValueString;
52import com.android.tools.r8.graph.OffsetToObjectMapping;
53import com.android.tools.r8.logging.Log;
54import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
55import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
56import java.io.ByteArrayOutputStream;
57import java.io.IOException;
58import java.io.InputStream;
59import java.nio.ShortBuffer;
60import java.nio.file.Path;
61import java.util.ArrayList;
62import java.util.Arrays;
63import java.util.Hashtable;
64import java.util.List;
65import java.util.function.Consumer;
66import java.util.function.Supplier;
67
68public class DexFileReader {
69
70  final int NO_INDEX = -1;
71  private DexFile file;
72  private final Segment[] segments;
73  private int[] stringIDs;
74  private final ClassKind classKind;
75
76  public static Segment[] parseMapFrom(Path file) throws IOException {
77    return parseMapFrom(new DexFile(file.toString()));
78  }
79
80  public static Segment[] parseMapFrom(InputStream stream) throws IOException {
81    return parseMapFrom(new DexFile(stream));
82  }
83
84  private static Segment[] parseMapFrom(DexFile dex) throws IOException {
85    DexFileReader reader = new DexFileReader(dex, ClassKind.PROGRAM, new DexItemFactory());
86    return reader.segments;
87  }
88
89  public void close() {
90    // This close behavior is needed to reduce peak memory usage of D8/R8.
91    indexedItems = null;
92    codes = null;
93    offsetMap = null;
94    file = null;
95    stringIDs = null;
96  }
97
98  // Mapping from indexes to indexable dex items.
99  private OffsetToObjectMapping indexedItems = new OffsetToObjectMapping();
100
101  // Mapping from offset to code item;
102  private Int2ObjectMap<DexCode> codes = new Int2ObjectOpenHashMap<>();
103
104  // Mapping from offset to dex item;
105  private Int2ObjectMap<Object> offsetMap = new Int2ObjectOpenHashMap<>();
106
107  // Factory to canonicalize certain dexitems.
108  private final DexItemFactory dexItemFactory;
109
110  public DexFileReader(DexFile file, ClassKind classKind, DexItemFactory dexItemFactory) {
111    this.file = file;
112    this.dexItemFactory = dexItemFactory;
113    file.setByteOrder();
114    segments = parseMap();
115    parseStringIDs();
116    this.classKind = classKind;
117  }
118
119  public OffsetToObjectMapping getIndexedItemsMap() {
120    return indexedItems;
121  }
122
123  void addCodeItemsTo() {
124    if (classKind == ClassKind.LIBRARY) {
125      // Ignore contents of library files.
126      return;
127    }
128    Segment segment = lookupSegment(Constants.TYPE_CODE_ITEM);
129    if (segment.length == 0) {
130      return;
131    }
132    file.position(segment.offset);
133    for (int i = 0; i < segment.length; i++) {
134      file.align(4);  // code items are 4 byte aligned.
135      int offset = file.position();
136      DexCode code = parseCodeItem();
137      codes.put(offset, code);  // Update the file local offset to code mapping.
138    }
139  }
140
141  private DexTypeList parseTypeList() {
142    DexType[] result = new DexType[file.getUint()];
143    for (int j = 0; j < result.length; j++) {
144      result[j] = indexedItems.getType(file.getUshort());
145    }
146    return new DexTypeList(result);
147  }
148
149  private DexTypeList typeListAt(int offset) {
150    if (offset == 0) {
151      return DexTypeList.empty();
152    }
153    return (DexTypeList) cacheAt(offset, this::parseTypeList);
154  }
155
156  public DexValue parseEncodedValue() {
157    int header = file.get() & 0xff;
158    int valueArg = header >> 5;
159    int valueType = header & 0x1f;
160    switch (valueType) {
161      case DexValue.VALUE_BYTE: {
162        assert valueArg == 0;
163        byte value = (byte) parseSigned(file, 1);
164        return DexValue.DexValueByte.create(value);
165      }
166      case DexValue.VALUE_SHORT: {
167        int size = valueArg + 1;
168        short value = (short) parseSigned(file, size);
169        return DexValue.DexValueShort.create(value);
170      }
171      case DexValue.VALUE_CHAR: {
172        int size = valueArg + 1;
173        char value = (char) parseUnsigned(file, size);
174        return DexValue.DexValueChar.create(value);
175      }
176      case DexValue.VALUE_INT: {
177        int size = valueArg + 1;
178        int value = (int) parseSigned(file, size);
179        return DexValue.DexValueInt.create(value);
180      }
181      case DexValue.VALUE_LONG: {
182        int size = valueArg + 1;
183        long value = parseSigned(file, size);
184        return DexValue.DexValueLong.create(value);
185      }
186      case DexValue.VALUE_FLOAT: {
187        int size = valueArg + 1;
188        return DexValue.DexValueFloat.create(parseFloat(file, size));
189      }
190      case DexValue.VALUE_DOUBLE: {
191        int size = valueArg + 1;
192        return DexValue.DexValueDouble.create(parseDouble(file, size));
193      }
194      case DexValue.VALUE_STRING: {
195        int size = valueArg + 1;
196        int index = (int) parseUnsigned(file, size);
197        DexString value = indexedItems.getString(index);
198        return new DexValue.DexValueString(value);
199      }
200      case DexValue.VALUE_TYPE: {
201        int size = valueArg + 1;
202        DexType value = indexedItems.getType((int) parseUnsigned(file, size));
203        return new DexValue.DexValueType(value);
204      }
205      case DexValue.VALUE_FIELD: {
206        int size = valueArg + 1;
207        DexField value = indexedItems.getField((int) parseUnsigned(file, size));
208        return new DexValue.DexValueField(value);
209      }
210      case DexValue.VALUE_METHOD: {
211        int size = valueArg + 1;
212        DexMethod value = indexedItems.getMethod((int) parseUnsigned(file, size));
213        return new DexValue.DexValueMethod(value);
214      }
215      case DexValue.VALUE_ENUM: {
216        int size = valueArg + 1;
217        DexField value = indexedItems.getField((int) parseUnsigned(file, size));
218        return new DexValue.DexValueEnum(value);
219      }
220      case DexValue.VALUE_ARRAY: {
221        assert valueArg == 0;
222        return new DexValue.DexValueArray(parseEncodedArrayValues());
223      }
224      case DexValue.VALUE_ANNOTATION: {
225        assert valueArg == 0;
226        return new DexValue.DexValueAnnotation(parseEncodedAnnotation());
227      }
228      case DexValue.VALUE_NULL: {
229        assert valueArg == 0;
230        return DexValueNull.NULL;
231      }
232      case DexValue.VALUE_BOOLEAN: {
233        // 0 is false, and 1 is true.
234        return DexValue.DexValueBoolean.create(valueArg != 0);
235      }
236      case DexValue.VALUE_METHOD_TYPE: {
237        int size = valueArg + 1;
238        DexProto value = indexedItems.getProto((int) parseUnsigned(file, size));
239        return new DexValue.DexValueMethodType(value);
240      }
241      case DexValue.VALUE_METHOD_HANDLE: {
242        int size = valueArg + 1;
243        DexMethodHandle value = indexedItems.getMethodHandle((int) parseUnsigned(file, size));
244        return new DexValue.DexValueMethodHandle(value);
245      }
246      default:
247        throw new IndexOutOfBoundsException();
248    }
249  }
250
251  private DexEncodedAnnotation parseEncodedAnnotation() {
252    int typeIdx = file.getUleb128();
253    int size = file.getUleb128();
254    DexAnnotationElement[] elements = new DexAnnotationElement[size];
255    for (int i = 0; i < size; i++) {
256      int nameIdx = file.getUleb128();
257      DexValue value = parseEncodedValue();
258      elements[i] = new DexAnnotationElement(indexedItems.getString(nameIdx), value);
259    }
260    return new DexEncodedAnnotation(indexedItems.getType(typeIdx), elements);
261  }
262
263  private DexValue[] parseEncodedArrayValues() {
264    int size = file.getUleb128();
265    DexValue[] values = new DexValue[size];
266    for (int i = 0; i < size; i++) {
267      values[i] = parseEncodedValue();
268    }
269    return values;
270  }
271
272
273  private DexEncodedArray parseEncodedArray() {
274    return new DexEncodedArray(parseEncodedArrayValues());
275  }
276
277  private DexEncodedArray encodedArrayAt(int offset) {
278    return (DexEncodedArray) cacheAt(offset, this::parseEncodedArray);
279  }
280
281  private DexFieldAnnotation[] parseFieldAnnotations(int size) {
282    if (size == 0) {
283      return null;
284    }
285    int[] fieldIndices = new int[size];
286    int[] annotationOffsets = new int[size];
287    for (int i = 0; i < size; i++) {
288      fieldIndices[i] = file.getUint();
289      annotationOffsets[i] = file.getUint();
290    }
291    int saved = file.position();
292    DexFieldAnnotation[] result = new DexFieldAnnotation[size];
293    for (int i = 0; i < size; i++) {
294      DexField field = indexedItems.getField(fieldIndices[i]);
295      DexAnnotationSet annotation = annotationSetAt(annotationOffsets[i]);
296      result[i] = new DexFieldAnnotation(field, annotation);
297    }
298    file.position(saved);
299    return result;
300  }
301
302  private DexMethodAnnotation[] parseMethodAnnotations(int size) {
303    if (size == 0) {
304      return null;
305    }
306    int[] methodIndices = new int[size];
307    int[] annotationOffsets = new int[size];
308    for (int i = 0; i < size; i++) {
309      methodIndices[i] = file.getUint();
310      annotationOffsets[i] = file.getUint();
311    }
312    int saved = file.position();
313    DexMethodAnnotation[] result = new DexMethodAnnotation[size];
314    for (int i = 0; i < size; i++) {
315      DexMethod method = indexedItems.getMethod(methodIndices[i]);
316      DexAnnotationSet annotation = annotationSetAt(annotationOffsets[i]);
317      result[i] = new DexMethodAnnotation(method, annotation);
318    }
319    file.position(saved);
320    return result;
321  }
322
323  private DexAnnotationSetRefList annotationSetRefListAt(int offset) {
324    return (DexAnnotationSetRefList) cacheAt(offset, this::parseAnnotationSetRefList);
325  }
326
327  private DexAnnotationSetRefList parseAnnotationSetRefList() {
328    int size = file.getUint();
329    int[] annotationOffsets = new int[size];
330    for (int i = 0; i < size; i++) {
331      annotationOffsets[i] = file.getUint();
332    }
333    DexAnnotationSet[] values = new DexAnnotationSet[size];
334    for (int i = 0; i < size; i++) {
335      values[i] = annotationSetAt(annotationOffsets[i]);
336    }
337    return new DexAnnotationSetRefList(values);
338  }
339
340  private DexParameterAnnotation[] parseParameterAnnotations(int size) {
341    if (size == 0) {
342      return null;
343    }
344    int[] methodIndices = new int[size];
345    int[] annotationOffsets = new int[size];
346    for (int i = 0; i < size; i++) {
347      methodIndices[i] = file.getUint();
348      annotationOffsets[i] = file.getUint();
349    }
350    int saved = file.position();
351    DexParameterAnnotation[] result = new DexParameterAnnotation[size];
352    for (int i = 0; i < size; i++) {
353      DexMethod method = indexedItems.getMethod(methodIndices[i]);
354      result[i] = new DexParameterAnnotation(
355          method,
356          annotationSetRefListAt(annotationOffsets[i]));
357    }
358    file.position(saved);
359    return result;
360  }
361
362  private <T> Object cacheAt(int offset, Supplier<T> function, Supplier<T> defaultValue) {
363    if (offset == 0) {
364      return defaultValue.get();
365    }
366    return cacheAt(offset, function);
367  }
368
369  private <T> Object cacheAt(int offset, Supplier<T> function) {
370    if (offset == 0) {
371      return null;  // return null for offset zero.
372    }
373    Object result = offsetMap.get(offset);
374    if (result != null) {
375      return result;  // return the cached result.
376    }
377    // Cache is empty so parse the structure.
378    file.position(offset);
379    result = function.get();
380    // Update the map.
381    offsetMap.put(offset, result);
382    assert offsetMap.get(offset) == result;
383    return result;
384  }
385
386  private DexAnnotation parseAnnotation() {
387    if (Log.ENABLED) {
388      Log.verbose(getClass(), "Reading Annotation @ 0x%08x.", file.position());
389    }
390    int visibility = file.get();
391    return new DexAnnotation(visibility, parseEncodedAnnotation());
392  }
393
394  private DexAnnotation annotationAt(int offset) {
395    return (DexAnnotation) cacheAt(offset, this::parseAnnotation);
396  }
397
398  private DexAnnotationSet parseAnnotationSet() {
399    if (Log.ENABLED) {
400      Log.verbose(getClass(), "Reading AnnotationSet @ 0x%08x.", file.position());
401    }
402    int size = file.getUint();
403    int[] annotationOffsets = new int[size];
404    for (int i = 0; i < size; i++) {
405      annotationOffsets[i] = file.getUint();
406    }
407    DexAnnotation[] result = new DexAnnotation[size];
408    for (int i = 0; i < size; i++) {
409      result[i] = annotationAt(annotationOffsets[i]);
410    }
411    return new DexAnnotationSet(result);
412  }
413
414  private DexAnnotationSet annotationSetAt(int offset) {
415    return (DexAnnotationSet) cacheAt(offset, this::parseAnnotationSet, DexAnnotationSet::empty);
416  }
417
418  private AnnotationsDirectory annotationsDirectoryAt(int offset) {
419    return (AnnotationsDirectory) cacheAt(offset, this::parseAnnotationsDirectory,
420        AnnotationsDirectory::empty);
421  }
422
423  private AnnotationsDirectory parseAnnotationsDirectory() {
424    int classAnnotationsOff = file.getUint();
425    int fieldsSize = file.getUint();
426    int methodsSize = file.getUint();
427    int parametersSize = file.getUint();
428    final DexFieldAnnotation[] fields = parseFieldAnnotations(fieldsSize);
429    final DexMethodAnnotation[] methods = parseMethodAnnotations(methodsSize);
430    final DexParameterAnnotation[] parameters = parseParameterAnnotations(parametersSize);
431    return new AnnotationsDirectory(
432        annotationSetAt(classAnnotationsOff),
433        fields,
434        methods,
435        parameters);
436  }
437
438  private DexDebugInfo debugInfoAt(int offset) {
439    return (DexDebugInfo) cacheAt(offset, this::parseDebugInfo);
440  }
441
442  private DexDebugInfo parseDebugInfo() {
443    int start = file.getUleb128();
444    int parametersSize = file.getUleb128();
445    DexString[] parameters = new DexString[parametersSize];
446    for (int i = 0; i < parametersSize; i++) {
447      int index = file.getUleb128p1();
448      if (index != NO_INDEX) {
449        parameters[i] = indexedItems.getString(index);
450      }
451    }
452    List<DexDebugEvent> events = new ArrayList<>();
453    for (int head = file.getUbyte(); head != Constants.DBG_END_SEQUENCE; head = file.getUbyte()) {
454      switch (head) {
455        case Constants.DBG_ADVANCE_PC:
456          events.add(dexItemFactory.createAdvancePC(file.getUleb128()));
457          break;
458        case Constants.DBG_ADVANCE_LINE:
459          events.add(dexItemFactory.createAdvanceLine(file.getSleb128()));
460          break;
461        case Constants.DBG_START_LOCAL: {
462          int registerNum = file.getUleb128();
463          int nameIdx = file.getUleb128p1();
464          int typeIdx = file.getUleb128p1();
465          events.add(new DexDebugEvent.StartLocal(
466              registerNum,
467              nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx),
468              typeIdx == NO_INDEX ? null : indexedItems.getType(typeIdx),
469              null));
470          break;
471        }
472        case Constants.DBG_START_LOCAL_EXTENDED: {
473          int registerNum = file.getUleb128();
474          int nameIdx = file.getUleb128p1();
475          int typeIdx = file.getUleb128p1();
476          int sigIdx = file.getUleb128p1();
477          events.add(new DexDebugEvent.StartLocal(
478              registerNum,
479              nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx),
480              typeIdx == NO_INDEX ? null : indexedItems.getType(typeIdx),
481              sigIdx == NO_INDEX ? null : indexedItems.getString(sigIdx)));
482          break;
483        }
484        case Constants.DBG_END_LOCAL: {
485          events.add(dexItemFactory.createEndLocal(file.getUleb128()));
486          break;
487        }
488        case Constants.DBG_RESTART_LOCAL: {
489          events.add(dexItemFactory.createRestartLocal(file.getUleb128()));
490          break;
491        }
492        case Constants.DBG_SET_PROLOGUE_END: {
493          events.add(dexItemFactory.createSetPrologueEnd());
494          break;
495        }
496        case Constants.DBG_SET_EPILOGUE_BEGIN: {
497          events.add(dexItemFactory.createSetEpilogueBegin());
498          break;
499        }
500        case Constants.DBG_SET_FILE: {
501          int nameIdx = file.getUleb128p1();
502          DexString sourceFile = nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx);
503          events.add(dexItemFactory.createSetFile(sourceFile));
504          break;
505        }
506        default: {
507          assert head >= 0x0a && head <= 0xff;
508          events.add(dexItemFactory.createDefault(head));
509        }
510      }
511    }
512    return new DexDebugInfo(start, parameters, events.toArray(new DexDebugEvent[events.size()]));
513  }
514
515  private static class MemberAnnotationIterator<S extends Descriptor<?, S>, T extends DexItem> {
516
517    private int index = 0;
518    private final DexMemberAnnotation<S, T>[] annotations;
519    private final Supplier<T> emptyValue;
520
521    private MemberAnnotationIterator(DexMemberAnnotation<S, T>[] annotations,
522        Supplier<T> emptyValue) {
523      this.annotations = annotations;
524      this.emptyValue = emptyValue;
525    }
526
527    // Get the annotation set for an item. This method assumes that it is always called with
528    // an item that is higher in the sorting order than the last item.
529    T getNextFor(S item) {
530      // TODO(ager): We could use the indices from the file to make this search faster using
531      // compareTo instead of slowCompareTo. That would require us to assign indices during
532      // reading. Those indices should be cleared after reading to make sure that we resort
533      // everything correctly at the end.
534      while (index < annotations.length && annotations[index].item.slowCompareTo(item) < 0) {
535        index++;
536      }
537      if (index >= annotations.length || !annotations[index].item.equals(item)) {
538        return emptyValue.get();
539      }
540      return annotations[index].annotations;
541    }
542  }
543
544  private DexEncodedField[] readFields(int size, DexFieldAnnotation[] annotations,
545      DexValue[] staticValues) {
546    DexEncodedField[] fields = new DexEncodedField[size];
547    int fieldIndex = 0;
548    MemberAnnotationIterator<DexField, DexAnnotationSet> annotationIterator =
549        new MemberAnnotationIterator<>(annotations, DexAnnotationSet::empty);
550    for (int i = 0; i < size; i++) {
551      fieldIndex += file.getUleb128();
552      DexField field = indexedItems.getField(fieldIndex);
553      DexAccessFlags accessFlags = new DexAccessFlags(file.getUleb128());
554      DexAnnotationSet fieldAnnotations = annotationIterator.getNextFor(field);
555      DexValue staticValue = null;
556      if (accessFlags.isStatic()) {
557        if (staticValues != null && i < staticValues.length) {
558          staticValue = staticValues[i];
559        } else {
560          staticValue = DexValue.defaultForType(field.type, dexItemFactory);
561        }
562      }
563      fields[i] = new DexEncodedField(field, accessFlags, fieldAnnotations, staticValue);
564    }
565    return fields;
566  }
567
568  private DexEncodedMethod[] readMethods(int size, DexMethodAnnotation[] annotations,
569      DexParameterAnnotation[] parameters, boolean skipCodes) {
570    DexEncodedMethod[] methods = new DexEncodedMethod[size];
571    int methodIndex = 0;
572    MemberAnnotationIterator<DexMethod, DexAnnotationSet> annotationIterator =
573        new MemberAnnotationIterator<>(annotations, DexAnnotationSet::empty);
574    MemberAnnotationIterator<DexMethod, DexAnnotationSetRefList> parameterAnnotationsIterator =
575        new MemberAnnotationIterator<>(parameters, DexAnnotationSetRefList::empty);
576    for (int i = 0; i < size; i++) {
577      methodIndex += file.getUleb128();
578      DexAccessFlags accessFlags = new DexAccessFlags(file.getUleb128());
579      int codeOff = file.getUleb128();
580      DexCode code = null;
581      if (!skipCodes) {
582        assert codeOff == 0 || codes.get(codeOff) != null;
583        code = codes.get(codeOff);
584      }
585      DexMethod method = indexedItems.getMethod(methodIndex);
586      methods[i] = new DexEncodedMethod(method, accessFlags, annotationIterator.getNextFor(method),
587          parameterAnnotationsIterator.getNextFor(method), code);
588    }
589    return methods;
590  }
591
592  void addClassDefsTo(Consumer<DexClass> classCollection) {
593    final Segment segment = lookupSegment(Constants.TYPE_CLASS_DEF_ITEM);
594    final int length = segment.length;
595    indexedItems.initializeClasses(length);
596    if (length == 0) {
597      return;
598    }
599    file.position(segment.offset);
600
601    int[] classIndices = new int[length];
602    int[] accessFlags = new int[length];
603    int[] superclassIndices = new int[length];
604    int[] interfacesOffsets = new int[length];
605    int[] sourceFileIndices = new int[length];
606    int[] annotationsOffsets = new int[length];
607    int[] classDataOffsets = new int[length];
608    int[] staticValuesOffsets = new int[length];
609
610    for (int i = 0; i < length; i++) {
611      if (Log.ENABLED) {
612        Log.verbose(getClass(), "Reading ClassDef @ 0x%08x.", file.position());
613      }
614      classIndices[i] = file.getUint();
615      accessFlags[i] = file.getUint();
616      superclassIndices[i] = file.getInt();
617      interfacesOffsets[i] = file.getUint();
618      sourceFileIndices[i] = file.getInt();
619      annotationsOffsets[i] = file.getUint();
620      classDataOffsets[i] = file.getUint();
621      staticValuesOffsets[i] = file.getUint();
622    }
623
624    for (int i = 0; i < length; i++) {
625      int superclassIdx = superclassIndices[i];
626      DexType superclass = superclassIdx == NO_INDEX ? null : indexedItems.getType(superclassIdx);
627      int srcIdx = sourceFileIndices[i];
628      DexString source = srcIdx == NO_INDEX ? null : indexedItems.getString(srcIdx);
629      // fix annotations.
630      DexType type = indexedItems.getType(classIndices[i]);
631      DexAccessFlags flags = new DexAccessFlags(accessFlags[i]);
632      DexClass clazz;
633      DexEncodedField[] staticFields = DexEncodedField.EMPTY_ARRAY;
634      DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY;
635      DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
636      DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
637      AnnotationsDirectory annotationsDirectory = annotationsDirectoryAt(annotationsOffsets[i]);
638      if (classDataOffsets[i] != 0) {
639        DexEncodedArray staticValues = encodedArrayAt(staticValuesOffsets[i]);
640
641        file.position(classDataOffsets[i]);
642        int staticFieldsSize = file.getUleb128();
643        int instanceFieldsSize = file.getUleb128();
644        int directMethodsSize = file.getUleb128();
645        int virtualMethodsSize = file.getUleb128();
646
647        staticFields = readFields(staticFieldsSize, annotationsDirectory.fields,
648            staticValues != null ? staticValues.values : null);
649        instanceFields = readFields(instanceFieldsSize, annotationsDirectory.fields, null);
650        directMethods =
651            readMethods(
652                directMethodsSize,
653                annotationsDirectory.methods,
654                annotationsDirectory.parameters,
655                classKind != ClassKind.PROGRAM);
656        virtualMethods =
657            readMethods(
658                virtualMethodsSize,
659                annotationsDirectory.methods,
660                annotationsDirectory.parameters,
661                classKind != ClassKind.PROGRAM);
662      }
663      clazz = classKind.create(
664          type,
665          Resource.Kind.DEX,
666          flags,
667          superclass,
668          typeListAt(interfacesOffsets[i]),
669          source,
670          annotationsDirectory.clazz,
671          staticFields,
672          instanceFields,
673          directMethods,
674          virtualMethods);
675      classCollection.accept(clazz);  // Update the application object.
676    }
677  }
678
679  private void parseStringIDs() {
680    Segment segment = lookupSegment(Constants.TYPE_STRING_ID_ITEM);
681    stringIDs = new int[segment.length];
682    if (segment.length == 0) {
683      return;
684    }
685    file.position(segment.offset);
686    for (int i = 0; i < segment.length; i++) {
687      stringIDs[i] = file.getUint();
688    }
689  }
690
691  private Segment lookupSegment(int type) {
692    for (Segment s : segments) {
693      if (s.type == type) {
694        return s;
695      }
696    }
697    // If the segment doesn't exist, return an empty segment of this type.
698    return new Segment(type, 0, 0, 0);
699  }
700
701  private Segment[] parseMap() {
702    // Read the segments information from the MAP.
703    int mapOffset = file.getUint(Constants.MAP_OFF_OFFSET);
704    file.position(mapOffset);
705    int mapSize = file.getUint();
706    final Segment[] result = new Segment[mapSize];
707    for (int i = 0; i < mapSize; i++) {
708      int type = file.getUshort();
709      int unused = file.getUshort();
710      int size = file.getUint();
711      int offset = file.getUint();
712      result[i] = new Segment(type, unused, size, offset);
713    }
714    if (Log.ENABLED) {
715      for (int i = 0; i < result.length; i++) {
716        Segment segment = result[i];
717        int nextOffset = i < result.length - 1 ? result[i + 1].offset : segment.offset;
718        Log.debug(this.getClass(), "Read segment 0x%04x @ 0x%08x #items %08d size 0x%08x.",
719            segment.type, segment.offset, segment.length, nextOffset - segment.offset);
720      }
721    }
722    for (int i = 0; i < mapSize - 1; i++) {
723      result[i].setEnd(result[i + 1].offset);
724    }
725    result[mapSize - 1].setEnd(file.end());
726    return result;
727  }
728
729  private DexCode parseCodeItem() {
730    int registerSize = file.getUshort();
731    int insSize = file.getUshort();
732    int outsSize = file.getUshort();
733    int triesSize = file.getUshort();
734    int debugInfoOff = file.getUint();
735    int insnsSize = file.getUint();
736    short[] code = new short[insnsSize];
737    Try[] tries = new Try[triesSize];
738    DexCode.TryHandler[] handlers = null;
739
740    if (insnsSize != 0) {
741      for (int i = 0; i < insnsSize; i++) {
742        code[i] = file.getShort();
743      }
744      if (insnsSize % 2 != 0) {
745        file.getUshort();  // Skip padding ushort
746      }
747      if (triesSize > 0) {
748        Hashtable<Integer, Integer> handlerMap = new Hashtable<>();
749        // tries: try_item[tries_size].
750        for (int i = 0; i < triesSize; i++) {
751          int startAddr = file.getUint();
752          int insnCount = file.getUshort();
753          int handlerOff = file.getUshort();
754          tries[i] = new Try(startAddr, insnCount, handlerOff);
755        }
756        // handlers: encoded_catch_handler_list
757        int encodedCatchHandlerListPosition = file.position();
758        // - size: uleb128
759        int size = file.getUleb128();
760        handlers = new TryHandler[size];
761        // - list: encoded_catch_handler[handlers_size]
762        for (int i = 0; i < size; i++) {
763          // encoded_catch_handler
764          int encodedCatchHandlerOffset = file.position() - encodedCatchHandlerListPosition;
765          handlerMap.put(encodedCatchHandlerOffset, i);
766          // - size:	sleb128
767          int hsize = file.getSleb128();
768          int realHsize = Math.abs(hsize);
769          // - handlers	encoded_type_addr_pair[abs(size)]
770          TryHandler.TypeAddrPair pairs[] = new TryHandler.TypeAddrPair[realHsize];
771          for (int j = 0; j < realHsize; j++) {
772            int typeIdx = file.getUleb128();
773            int addr = file.getUleb128();
774            pairs[j] = new TypeAddrPair(indexedItems.getType(typeIdx), addr,
775                encodedCatchHandlerOffset);
776          }
777          int catchAllAddr = -1;
778          if (hsize <= 0) {
779            catchAllAddr = file.getUleb128();
780          }
781          handlers[i] = new TryHandler(pairs, catchAllAddr);
782        }
783        // Convert the handler offsets inside the Try objects to indexes.
784        for (Try t : tries) {
785          t.setHandlerIndex(handlerMap);
786        }
787      }
788    }
789    // Store and restore offset information around reading debug info.
790    int saved = file.position();
791    DexDebugInfo debugInfo = debugInfoAt(debugInfoOff);
792    file.position(saved);
793    InstructionFactory factory = new InstructionFactory();
794    Instruction[] instructions =
795        factory.readSequenceFrom(ShortBuffer.wrap(code), 0, code.length, indexedItems);
796    return new DexCode(
797        registerSize,
798        insSize,
799        outsSize,
800        instructions,
801        tries,
802        handlers,
803        debugInfo,
804        factory.getHighestSortingString());
805  }
806
807  static void populateIndexTables(DexFileReader fileReader) {
808    // Populate structures that are already sorted upon read.
809    DexFileReader.populateStrings(fileReader);  // Depends on nothing.
810    DexFileReader.populateTypes(fileReader);  // Depends on Strings.
811    DexFileReader.populateFields(fileReader);  // Depends on Types, and Strings.
812    DexFileReader.populateProtos(fileReader);  // Depends on Types and Strings.
813    DexFileReader.populateMethods(fileReader);  // Depends on Protos, Types, and Strings.
814    DexFileReader.populateMethodHandles(fileReader); // Depends on Methods and Fields
815    DexFileReader.populateCallSites(fileReader); // Depends on MethodHandles
816  }
817
818  private static void populateStrings(DexFileReader reader) {
819    reader.indexedItems.initializeStrings(reader.stringIDs.length);
820    for (int i = 0; i < reader.stringIDs.length; i++) {
821      reader.indexedItems.setString(i, reader.stringAt(i));
822    }
823  }
824
825  private static void populateMethodHandles(DexFileReader reader) {
826    Segment segment = reader.lookupSegment(Constants.TYPE_METHOD_HANDLE_ITEM);
827    reader.indexedItems.initializeMethodHandles(segment.length);
828    for (int i = 0; i < segment.length; i++) {
829      reader.indexedItems.setMethodHandle(i, reader.methodHandleAt(i));
830    }
831  }
832
833  private static void populateCallSites(DexFileReader reader) {
834    Segment segment = reader.lookupSegment(Constants.TYPE_CALL_SITE_ID_ITEM);
835    reader.indexedItems.initializeCallSites(segment.length);
836    for (int i = 0; i < segment.length; i++) {
837      reader.indexedItems.setCallSites(i, reader.callSiteAt(i));
838    }
839  }
840
841  private static void populateTypes(DexFileReader reader) {
842    Segment segment = reader.lookupSegment(Constants.TYPE_TYPE_ID_ITEM);
843    reader.indexedItems.initializeTypes(segment.length);
844    for (int i = 0; i < segment.length; i++) {
845      reader.indexedItems.setType(i, reader.typeAt(i));
846    }
847  }
848
849  private static void populateFields(DexFileReader reader) {
850    Segment segment = reader.lookupSegment(Constants.TYPE_FIELD_ID_ITEM);
851    reader.indexedItems.initializeFields(segment.length);
852    for (int i = 0; i < segment.length; i++) {
853      reader.indexedItems.setField(i, reader.fieldAt(i));
854    }
855  }
856
857  private static void populateProtos(DexFileReader reader) {
858    Segment segment = reader.lookupSegment(Constants.TYPE_PROTO_ID_ITEM);
859    reader.indexedItems.initializeProtos(segment.length);
860    for (int i = 0; i < segment.length; i++) {
861      reader.indexedItems.setProto(i, reader.protoAt(i));
862    }
863  }
864
865  private static void populateMethods(DexFileReader reader) {
866    Segment segment = reader.lookupSegment(Constants.TYPE_METHOD_ID_ITEM);
867    reader.indexedItems.initializeMethods(segment.length);
868    for (int i = 0; i < segment.length; i++) {
869      reader.indexedItems.setMethod(i, reader.methodAt(i));
870    }
871  }
872
873  private DexString stringAt(int index) {
874    final int offset = stringIDs[index];
875    file.position(offset);
876    int size = file.getUleb128();
877    ByteArrayOutputStream os = new ByteArrayOutputStream();
878    byte read;
879    do {
880      read = file.get();
881      os.write(read);
882    } while (read != 0);
883    return dexItemFactory.createString(size, os.toByteArray());
884  }
885
886  private DexType typeAt(int index) {
887    Segment segment = lookupSegment(Constants.TYPE_TYPE_ID_ITEM);
888    if (index >= segment.length) {
889      return null;
890    }
891    int offset = segment.offset + (Constants.TYPE_TYPE_ID_ITEM_SIZE * index);
892    int stringIndex = file.getUint(offset);
893    return dexItemFactory.createType(indexedItems.getString(stringIndex));
894  }
895
896  private DexField fieldAt(int index) {
897    Segment segment = lookupSegment(Constants.TYPE_FIELD_ID_ITEM);
898    if (index >= segment.length) {
899      return null;
900    }
901    int offset = segment.offset + (Constants.TYPE_FIELD_ID_ITEM_SIZE * index);
902    file.position(offset);
903    int classIndex = file.getUshort();
904    int typeIndex = file.getUshort();
905    int nameIndex = file.getUint();
906    DexType clazz = indexedItems.getType(classIndex);
907    DexType type = indexedItems.getType(typeIndex);
908    DexString name = indexedItems.getString(nameIndex);
909    return dexItemFactory.createField(clazz, type, name);
910  }
911
912  private DexMethodHandle methodHandleAt(int index) {
913    Segment segment = lookupSegment(Constants.TYPE_METHOD_HANDLE_ITEM);
914    if (index >= segment.length) {
915      return null;
916    }
917    int offset = segment.offset + (Constants.TYPE_METHOD_HANDLE_ITEM_SIZE * index);
918    file.position(offset);
919    MethodHandleType type = MethodHandleType.getKind(file.getUshort());
920    file.getUshort(); // unused
921    int indexFieldOrMethod = file.getUshort();
922    Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod;
923    switch (type) {
924      case INSTANCE_GET:
925      case INSTANCE_PUT:
926      case STATIC_GET:
927      case STATIC_PUT: {
928        fieldOrMethod = indexedItems.getField(indexFieldOrMethod);
929        break;
930      }
931      case INVOKE_INSTANCE:
932      case INVOKE_STATIC: {
933        fieldOrMethod = indexedItems.getMethod(indexFieldOrMethod);
934        break;
935      }
936      default:
937        throw new AssertionError("Method handle type unsupported in a dex file.");
938    }
939    file.getUshort(); // unused
940
941    return dexItemFactory.createMethodHandle(type, fieldOrMethod);
942  }
943
944  private DexCallSite callSiteAt(int index) {
945    Segment segment = lookupSegment(Constants.TYPE_CALL_SITE_ID_ITEM);
946    if (index >= segment.length) {
947      return null;
948    }
949    int callSiteOffset =
950        file.getUint(segment.offset + (Constants.TYPE_CALL_SITE_ID_ITEM_SIZE * index));
951    DexEncodedArray callSiteEncodedArray = encodedArrayAt(callSiteOffset);
952    DexValue[] values = callSiteEncodedArray.values;
953    assert values[0] instanceof DexValueMethodHandle;
954    assert values[1] instanceof DexValueString;
955    assert values[2] instanceof DexValueMethodType;
956
957    return dexItemFactory.createCallSite(
958        ((DexValueString) values[1]).value,
959        ((DexValueMethodType) values[2]).value,
960        ((DexValueMethodHandle) values[0]).value,
961        // 3 means first extra args
962        Arrays.asList(Arrays.copyOfRange(values, 3, values.length)));
963  }
964
965  private DexProto protoAt(int index) {
966    Segment segment = lookupSegment(Constants.TYPE_PROTO_ID_ITEM);
967    if (index >= segment.length) {
968      return null;
969    }
970    int offset = segment.offset + (Constants.TYPE_PROTO_ID_ITEM_SIZE * index);
971    file.position(offset);
972    int shortyIndex = file.getUint();
973    int returnTypeIndex = file.getUint();
974    int parametersOffsetIndex = file.getUint();
975    DexString shorty = indexedItems.getString(shortyIndex);
976    DexType returnType = indexedItems.getType(returnTypeIndex);
977    DexTypeList parameters = typeListAt(parametersOffsetIndex);
978    return dexItemFactory.createProto(shorty, returnType, parameters);
979  }
980
981  private DexMethod methodAt(int index) {
982    Segment segment = lookupSegment(Constants.TYPE_METHOD_ID_ITEM);
983    if (index >= segment.length) {
984      return null;
985    }
986    int offset = segment.offset + (Constants.TYPE_METHOD_ID_ITEM_SIZE * index);
987    file.position(offset);
988    int classIndex = file.getUshort();
989    int protoIndex = file.getUshort();
990    int nameIndex = file.getUint();
991    return dexItemFactory.createMethod(
992        indexedItems.getType(classIndex),
993        indexedItems.getProto(protoIndex),
994        indexedItems.getString(nameIndex));
995  }
996
997  private static class AnnotationsDirectory {
998
999    private static final DexParameterAnnotation[] NO_PARAMETER_ANNOTATIONS =
1000        new DexParameterAnnotation[0];
1001
1002    private static final DexFieldAnnotation[] NO_FIELD_ANNOTATIONS =
1003        new DexFieldAnnotation[0];
1004
1005    private static final DexMethodAnnotation[] NO_METHOD_ANNOTATIONS =
1006        new DexMethodAnnotation[0];
1007
1008    private static final AnnotationsDirectory THE_EMPTY_ANNOTATIONS_DIRECTORY =
1009        new AnnotationsDirectory(DexAnnotationSet.empty(),
1010            NO_FIELD_ANNOTATIONS, new DexMethodAnnotation[0],
1011            NO_PARAMETER_ANNOTATIONS);
1012
1013    public final DexAnnotationSet clazz;
1014    public final DexFieldAnnotation[] fields;
1015    public final DexMethodAnnotation[] methods;
1016    public final DexParameterAnnotation[] parameters;
1017
1018    AnnotationsDirectory(DexAnnotationSet clazz,
1019        DexFieldAnnotation[] fields,
1020        DexMethodAnnotation[] methods,
1021        DexParameterAnnotation[] parameters) {
1022      this.clazz = clazz == null ? DexAnnotationSet.empty() : clazz;
1023      this.fields = fields == null ? NO_FIELD_ANNOTATIONS : fields;
1024      this.methods = methods == null ? NO_METHOD_ANNOTATIONS : methods;
1025      this.parameters = parameters == null ? NO_PARAMETER_ANNOTATIONS : parameters;
1026    }
1027
1028    public static AnnotationsDirectory empty() {
1029      return THE_EMPTY_ANNOTATIONS_DIRECTORY;
1030    }
1031  }
1032}
1033