• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dex;
18 
19 import com.android.dex.Code.CatchHandler;
20 import com.android.dex.Code.Try;
21 import com.android.dex.util.ByteInput;
22 import com.android.dex.util.ByteOutput;
23 import com.android.dex.util.FileUtils;
24 
25 import java.io.ByteArrayOutputStream;
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.OutputStream;
32 import java.io.UTFDataFormatException;
33 import java.nio.ByteBuffer;
34 import java.nio.ByteOrder;
35 import java.security.MessageDigest;
36 import java.security.NoSuchAlgorithmException;
37 import java.util.AbstractList;
38 import java.util.Collections;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.NoSuchElementException;
42 import java.util.RandomAccess;
43 import java.util.zip.Adler32;
44 import java.util.zip.ZipEntry;
45 import java.util.zip.ZipFile;
46 
47 /**
48  * The bytes of a dex file in memory for reading and writing. All int offsets
49  * are unsigned.
50  */
51 public final class Dex {
52     private static final int CHECKSUM_OFFSET = 8;
53     private static final int CHECKSUM_SIZE = 4;
54     private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE;
55     private static final int SIGNATURE_SIZE = 20;
56     // Provided as a convenience to avoid a memory allocation to benefit Dalvik.
57     // Note: libcore.util.EmptyArray cannot be accessed when this code isn't run on Dalvik.
58     static final short[] EMPTY_SHORT_ARRAY = new short[0];
59 
60     private ByteBuffer data;
61     private final TableOfContents tableOfContents = new TableOfContents();
62     private int nextSectionStart = 0;
63     private final StringTable strings = new StringTable();
64     private final TypeIndexToDescriptorIndexTable typeIds = new TypeIndexToDescriptorIndexTable();
65     private final TypeIndexToDescriptorTable typeNames = new TypeIndexToDescriptorTable();
66     private final ProtoIdTable protoIds = new ProtoIdTable();
67     private final FieldIdTable fieldIds = new FieldIdTable();
68     private final MethodIdTable methodIds = new MethodIdTable();
69 
70     /**
71      * Creates a new dex that reads from {@code data}. It is an error to modify
72      * {@code data} after using it to create a dex buffer.
73      */
Dex(byte[] data)74     public Dex(byte[] data) throws IOException {
75         this(ByteBuffer.wrap(data));
76     }
77 
Dex(ByteBuffer data)78     private Dex(ByteBuffer data) throws IOException {
79         this.data = data;
80         this.data.order(ByteOrder.LITTLE_ENDIAN);
81         this.tableOfContents.readFrom(this);
82     }
83 
84     /**
85      * Creates a new empty dex of the specified size.
86      */
Dex(int byteCount)87     public Dex(int byteCount) throws IOException {
88         this.data = ByteBuffer.wrap(new byte[byteCount]);
89         this.data.order(ByteOrder.LITTLE_ENDIAN);
90     }
91 
92     /**
93      * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}.
94      */
Dex(InputStream in)95     public Dex(InputStream in) throws IOException {
96         loadFrom(in);
97     }
98 
99     /**
100      * Creates a new dex buffer from the dex file {@code file}.
101      */
Dex(File file)102     public Dex(File file) throws IOException {
103         if (FileUtils.hasArchiveSuffix(file.getName())) {
104             ZipFile zipFile = new ZipFile(file);
105             ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME);
106             if (entry != null) {
107                 loadFrom(zipFile.getInputStream(entry));
108                 zipFile.close();
109             } else {
110                 throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file);
111             }
112         } else if (file.getName().endsWith(".dex")) {
113             loadFrom(new FileInputStream(file));
114         } else {
115             throw new DexException("unknown output extension: " + file);
116         }
117     }
118 
119     /**
120      * Creates a new dex from the contents of {@code bytes}. This API supports
121      * both {@code .dex} and {@code .odex} input. Calling this constructor
122      * transfers ownership of {@code bytes} to the returned Dex: it is an error
123      * to access the buffer after calling this method.
124      */
create(ByteBuffer data)125     public static Dex create(ByteBuffer data) throws IOException {
126         data.order(ByteOrder.LITTLE_ENDIAN);
127 
128         // if it's an .odex file, set position and limit to the .dex section
129         if (data.get(0) == 'd'
130                 && data.get(1) == 'e'
131                 && data.get(2) == 'y'
132                 && data.get(3) == '\n') {
133             data.position(8);
134             int offset = data.getInt();
135             int length = data.getInt();
136             data.position(offset);
137             data.limit(offset + length);
138             data = data.slice();
139         }
140 
141         return new Dex(data);
142     }
143 
loadFrom(InputStream in)144     private void loadFrom(InputStream in) throws IOException {
145         ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
146         byte[] buffer = new byte[8192];
147 
148         int count;
149         while ((count = in.read(buffer)) != -1) {
150             bytesOut.write(buffer, 0, count);
151         }
152         in.close();
153 
154         this.data = ByteBuffer.wrap(bytesOut.toByteArray());
155         this.data.order(ByteOrder.LITTLE_ENDIAN);
156         this.tableOfContents.readFrom(this);
157     }
158 
checkBounds(int index, int length)159     private static void checkBounds(int index, int length) {
160         if (index < 0 || index >= length) {
161             throw new IndexOutOfBoundsException("index:" + index + ", length=" + length);
162         }
163     }
164 
writeTo(OutputStream out)165     public void writeTo(OutputStream out) throws IOException {
166         byte[] buffer = new byte[8192];
167         ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
168         data.clear();
169         while (data.hasRemaining()) {
170             int count = Math.min(buffer.length, data.remaining());
171             data.get(buffer, 0, count);
172             out.write(buffer, 0, count);
173         }
174     }
175 
writeTo(File dexOut)176     public void writeTo(File dexOut) throws IOException {
177         OutputStream out = new FileOutputStream(dexOut);
178         writeTo(out);
179         out.close();
180     }
181 
getTableOfContents()182     public TableOfContents getTableOfContents() {
183         return tableOfContents;
184     }
185 
open(int position)186     public Section open(int position) {
187         if (position < 0 || position >= data.capacity()) {
188             throw new IllegalArgumentException("position=" + position
189                     + " length=" + data.capacity());
190         }
191         ByteBuffer sectionData = data.duplicate();
192         sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary?
193         sectionData.position(position);
194         sectionData.limit(data.capacity());
195         return new Section("section", sectionData);
196     }
197 
appendSection(int maxByteCount, String name)198     public Section appendSection(int maxByteCount, String name) {
199         if ((maxByteCount & 3) != 0) {
200             throw new IllegalStateException("Not four byte aligned!");
201         }
202         int limit = nextSectionStart + maxByteCount;
203         ByteBuffer sectionData = data.duplicate();
204         sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary?
205         sectionData.position(nextSectionStart);
206         sectionData.limit(limit);
207         Section result = new Section(name, sectionData);
208         nextSectionStart = limit;
209         return result;
210     }
211 
getLength()212     public int getLength() {
213         return data.capacity();
214     }
215 
getNextSectionStart()216     public int getNextSectionStart() {
217         return nextSectionStart;
218     }
219 
220     /**
221      * Returns a copy of the the bytes of this dex.
222      */
getBytes()223     public byte[] getBytes() {
224         ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
225         byte[] result = new byte[data.capacity()];
226         data.position(0);
227         data.get(result);
228         return result;
229     }
230 
strings()231     public List<String> strings() {
232         return strings;
233     }
234 
typeIds()235     public List<Integer> typeIds() {
236         return typeIds;
237     }
238 
typeNames()239     public List<String> typeNames() {
240         return typeNames;
241     }
242 
protoIds()243     public List<ProtoId> protoIds() {
244         return protoIds;
245     }
246 
fieldIds()247     public List<FieldId> fieldIds() {
248         return fieldIds;
249     }
250 
methodIds()251     public List<MethodId> methodIds() {
252         return methodIds;
253     }
254 
classDefs()255     public Iterable<ClassDef> classDefs() {
256         return new ClassDefIterable();
257     }
258 
readTypeList(int offset)259     public TypeList readTypeList(int offset) {
260         if (offset == 0) {
261             return TypeList.EMPTY;
262         }
263         return open(offset).readTypeList();
264     }
265 
readClassData(ClassDef classDef)266     public ClassData readClassData(ClassDef classDef) {
267         int offset = classDef.getClassDataOffset();
268         if (offset == 0) {
269             throw new IllegalArgumentException("offset == 0");
270         }
271         return open(offset).readClassData();
272     }
273 
readCode(ClassData.Method method)274     public Code readCode(ClassData.Method method) {
275         int offset = method.getCodeOffset();
276         if (offset == 0) {
277             throw new IllegalArgumentException("offset == 0");
278         }
279         return open(offset).readCode();
280     }
281 
282     /**
283      * Returns the signature of all but the first 32 bytes of this dex. The
284      * first 32 bytes of dex files are not specified to be included in the
285      * signature.
286      */
computeSignature()287     public byte[] computeSignature() throws IOException {
288         MessageDigest digest;
289         try {
290             digest = MessageDigest.getInstance("SHA-1");
291         } catch (NoSuchAlgorithmException e) {
292             throw new AssertionError();
293         }
294         byte[] buffer = new byte[8192];
295         ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
296         data.limit(data.capacity());
297         data.position(SIGNATURE_OFFSET + SIGNATURE_SIZE);
298         while (data.hasRemaining()) {
299             int count = Math.min(buffer.length, data.remaining());
300             data.get(buffer, 0, count);
301             digest.update(buffer, 0, count);
302         }
303         return digest.digest();
304     }
305 
306     /**
307      * Returns the checksum of all but the first 12 bytes of {@code dex}.
308      */
computeChecksum()309     public int computeChecksum() throws IOException {
310         Adler32 adler32 = new Adler32();
311         byte[] buffer = new byte[8192];
312         ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
313         data.limit(data.capacity());
314         data.position(CHECKSUM_OFFSET + CHECKSUM_SIZE);
315         while (data.hasRemaining()) {
316             int count = Math.min(buffer.length, data.remaining());
317             data.get(buffer, 0, count);
318             adler32.update(buffer, 0, count);
319         }
320         return (int) adler32.getValue();
321     }
322 
323     /**
324      * Generates the signature and checksum of the dex file {@code out} and
325      * writes them to the file.
326      */
writeHashes()327     public void writeHashes() throws IOException {
328         open(SIGNATURE_OFFSET).write(computeSignature());
329         open(CHECKSUM_OFFSET).writeInt(computeChecksum());
330     }
331 
332     /**
333      * Look up a field id name index from a field index. Cheaper than:
334      * {@code fieldIds().get(fieldDexIndex).getNameIndex();}
335      */
nameIndexFromFieldIndex(int fieldIndex)336     public int nameIndexFromFieldIndex(int fieldIndex) {
337         checkBounds(fieldIndex, tableOfContents.fieldIds.size);
338         int position = tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * fieldIndex);
339         position += SizeOf.USHORT;  // declaringClassIndex
340         position += SizeOf.USHORT;  // typeIndex
341         return data.getInt(position);  // nameIndex
342     }
343 
findStringIndex(String s)344     public int findStringIndex(String s) {
345         return Collections.binarySearch(strings, s);
346     }
347 
findTypeIndex(String descriptor)348     public int findTypeIndex(String descriptor) {
349         return Collections.binarySearch(typeNames, descriptor);
350     }
351 
findFieldIndex(FieldId fieldId)352     public int findFieldIndex(FieldId fieldId) {
353         return Collections.binarySearch(fieldIds, fieldId);
354     }
355 
findMethodIndex(MethodId methodId)356     public int findMethodIndex(MethodId methodId) {
357         return Collections.binarySearch(methodIds, methodId);
358     }
359 
findClassDefIndexFromTypeIndex(int typeIndex)360     public int findClassDefIndexFromTypeIndex(int typeIndex) {
361         checkBounds(typeIndex, tableOfContents.typeIds.size);
362         if (!tableOfContents.classDefs.exists()) {
363             return -1;
364         }
365         for (int i = 0; i < tableOfContents.classDefs.size; i++) {
366             if (typeIndexFromClassDefIndex(i) == typeIndex) {
367                 return i;
368             }
369         }
370         return -1;
371     }
372 
373     /**
374      * Look up a field id type index from a field index. Cheaper than:
375      * {@code fieldIds().get(fieldDexIndex).getTypeIndex();}
376      */
typeIndexFromFieldIndex(int fieldIndex)377     public int typeIndexFromFieldIndex(int fieldIndex) {
378         checkBounds(fieldIndex, tableOfContents.fieldIds.size);
379         int position = tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * fieldIndex);
380         position += SizeOf.USHORT;  // declaringClassIndex
381         return data.getShort(position) & 0xFFFF;  // typeIndex
382     }
383 
384     /**
385      * Look up a method id declaring class index from a method index. Cheaper than:
386      * {@code methodIds().get(methodIndex).getDeclaringClassIndex();}
387      */
declaringClassIndexFromMethodIndex(int methodIndex)388     public int declaringClassIndexFromMethodIndex(int methodIndex) {
389         checkBounds(methodIndex, tableOfContents.methodIds.size);
390         int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
391         return data.getShort(position) & 0xFFFF;  // declaringClassIndex
392     }
393 
394     /**
395      * Look up a method id name index from a method index. Cheaper than:
396      * {@code methodIds().get(methodIndex).getNameIndex();}
397      */
nameIndexFromMethodIndex(int methodIndex)398     public int nameIndexFromMethodIndex(int methodIndex) {
399         checkBounds(methodIndex, tableOfContents.methodIds.size);
400         int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
401         position += SizeOf.USHORT;  // declaringClassIndex
402         position += SizeOf.USHORT;  // protoIndex
403         return data.getInt(position);  // nameIndex
404     }
405 
406     /**
407      * Look up a parameter type ids from a method index. Cheaper than:
408      * {@code readTypeList(protoIds.get(methodIds().get(methodDexIndex).getProtoIndex()).getParametersOffset()).getTypes();}
409      */
parameterTypeIndicesFromMethodIndex(int methodIndex)410     public short[] parameterTypeIndicesFromMethodIndex(int methodIndex) {
411         checkBounds(methodIndex, tableOfContents.methodIds.size);
412         int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
413         position += SizeOf.USHORT;  // declaringClassIndex
414         int protoIndex = data.getShort(position) & 0xFFFF;
415         checkBounds(protoIndex, tableOfContents.protoIds.size);
416         position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex);
417         position += SizeOf.UINT;  // shortyIndex
418         position += SizeOf.UINT;  // returnTypeIndex
419         int parametersOffset = data.getInt(position);
420         if (parametersOffset == 0) {
421             return EMPTY_SHORT_ARRAY;
422         }
423         position = parametersOffset;
424         int size = data.getInt(position);
425         if (size <= 0) {
426             throw new AssertionError("Unexpected parameter type list size: " + size);
427         }
428         position += SizeOf.UINT;
429         short[] types = new short[size];
430         for (int i = 0; i < size; i++) {
431             types[i] = data.getShort(position);
432             position += SizeOf.USHORT;
433         }
434         return types;
435     }
436 
437     /**
438      * Look up a method id return type index from a method index. Cheaper than:
439      * {@code protoIds().get(methodIds().get(methodDexIndex).getProtoIndex()).getReturnTypeIndex();}
440      */
returnTypeIndexFromMethodIndex(int methodIndex)441     public int returnTypeIndexFromMethodIndex(int methodIndex) {
442         checkBounds(methodIndex, tableOfContents.methodIds.size);
443         int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
444         position += SizeOf.USHORT;  // declaringClassIndex
445         int protoIndex = data.getShort(position) & 0xFFFF;
446         checkBounds(protoIndex, tableOfContents.protoIds.size);
447         position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex);
448         position += SizeOf.UINT;  // shortyIndex
449         return data.getInt(position);  // returnTypeIndex
450     }
451 
452     /**
453      * Look up a descriptor index from a type index. Cheaper than:
454      * {@code open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();}
455      */
descriptorIndexFromTypeIndex(int typeIndex)456     public int descriptorIndexFromTypeIndex(int typeIndex) {
457        checkBounds(typeIndex, tableOfContents.typeIds.size);
458        int position = tableOfContents.typeIds.off + (SizeOf.TYPE_ID_ITEM * typeIndex);
459        return data.getInt(position);
460     }
461 
462     /**
463      * Look up a type index index from a class def index.
464      */
typeIndexFromClassDefIndex(int classDefIndex)465     public int typeIndexFromClassDefIndex(int classDefIndex) {
466         checkBounds(classDefIndex, tableOfContents.classDefs.size);
467         int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex);
468         return data.getInt(position);
469     }
470 
471     /**
472      * Look up an annotation directory offset from a class def index.
473      */
annotationDirectoryOffsetFromClassDefIndex(int classDefIndex)474     public int annotationDirectoryOffsetFromClassDefIndex(int classDefIndex) {
475         checkBounds(classDefIndex, tableOfContents.classDefs.size);
476         int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex);
477         position += SizeOf.UINT;  // type
478         position += SizeOf.UINT;  // accessFlags
479         position += SizeOf.UINT;  // superType
480         position += SizeOf.UINT;  // interfacesOffset
481         position += SizeOf.UINT;  // sourceFileIndex
482         return data.getInt(position);
483     }
484 
485     /**
486      * Look up interface types indices from a  return type index from a method index. Cheaper than:
487      * {@code ...getClassDef(classDefIndex).getInterfaces();}
488      */
interfaceTypeIndicesFromClassDefIndex(int classDefIndex)489     public short[] interfaceTypeIndicesFromClassDefIndex(int classDefIndex) {
490         checkBounds(classDefIndex, tableOfContents.classDefs.size);
491         int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex);
492         position += SizeOf.UINT;  // type
493         position += SizeOf.UINT;  // accessFlags
494         position += SizeOf.UINT;  // superType
495         int interfacesOffset = data.getInt(position);
496         if (interfacesOffset == 0) {
497             return EMPTY_SHORT_ARRAY;
498         }
499         position = interfacesOffset;
500         int size = data.getInt(position);
501         if (size <= 0) {
502             throw new AssertionError("Unexpected interfaces list size: " + size);
503         }
504         position += SizeOf.UINT;
505         short[] types = new short[size];
506         for (int i = 0; i < size; i++) {
507             types[i] = data.getShort(position);
508             position += SizeOf.USHORT;
509         }
510         return types;
511     }
512 
513     public final class Section implements ByteInput, ByteOutput {
514         private final String name;
515         private final ByteBuffer data;
516         private final int initialPosition;
517 
Section(String name, ByteBuffer data)518         private Section(String name, ByteBuffer data) {
519             this.name = name;
520             this.data = data;
521             this.initialPosition = data.position();
522         }
523 
getPosition()524         public int getPosition() {
525             return data.position();
526         }
527 
readInt()528         public int readInt() {
529             return data.getInt();
530         }
531 
readShort()532         public short readShort() {
533             return data.getShort();
534         }
535 
readUnsignedShort()536         public int readUnsignedShort() {
537             return readShort() & 0xffff;
538         }
539 
readByte()540         public byte readByte() {
541             return data.get();
542         }
543 
readByteArray(int length)544         public byte[] readByteArray(int length) {
545             byte[] result = new byte[length];
546             data.get(result);
547             return result;
548         }
549 
readShortArray(int length)550         public short[] readShortArray(int length) {
551             if (length == 0) {
552                 return EMPTY_SHORT_ARRAY;
553             }
554             short[] result = new short[length];
555             for (int i = 0; i < length; i++) {
556                 result[i] = readShort();
557             }
558             return result;
559         }
560 
readUleb128()561         public int readUleb128() {
562             return Leb128.readUnsignedLeb128(this);
563         }
564 
readUleb128p1()565         public int readUleb128p1() {
566             return Leb128.readUnsignedLeb128(this) - 1;
567         }
568 
readSleb128()569         public int readSleb128() {
570             return Leb128.readSignedLeb128(this);
571         }
572 
writeUleb128p1(int i)573         public void writeUleb128p1(int i) {
574             writeUleb128(i + 1);
575         }
576 
readTypeList()577         public TypeList readTypeList() {
578             int size = readInt();
579             short[] types = readShortArray(size);
580             alignToFourBytes();
581             return new TypeList(Dex.this, types);
582         }
583 
readString()584         public String readString() {
585             int offset = readInt();
586             int savedPosition = data.position();
587             int savedLimit = data.limit();
588             data.position(offset);
589             data.limit(data.capacity());
590             try {
591                 int expectedLength = readUleb128();
592                 String result = Mutf8.decode(this, new char[expectedLength]);
593                 if (result.length() != expectedLength) {
594                     throw new DexException("Declared length " + expectedLength
595                             + " doesn't match decoded length of " + result.length());
596                 }
597                 return result;
598             } catch (UTFDataFormatException e) {
599                 throw new DexException(e);
600             } finally {
601                 data.position(savedPosition);
602                 data.limit(savedLimit);
603             }
604         }
605 
readFieldId()606         public FieldId readFieldId() {
607             int declaringClassIndex = readUnsignedShort();
608             int typeIndex = readUnsignedShort();
609             int nameIndex = readInt();
610             return new FieldId(Dex.this, declaringClassIndex, typeIndex, nameIndex);
611         }
612 
readMethodId()613         public MethodId readMethodId() {
614             int declaringClassIndex = readUnsignedShort();
615             int protoIndex = readUnsignedShort();
616             int nameIndex = readInt();
617             return new MethodId(Dex.this, declaringClassIndex, protoIndex, nameIndex);
618         }
619 
readProtoId()620         public ProtoId readProtoId() {
621             int shortyIndex = readInt();
622             int returnTypeIndex = readInt();
623             int parametersOffset = readInt();
624             return new ProtoId(Dex.this, shortyIndex, returnTypeIndex, parametersOffset);
625         }
626 
readClassDef()627         public ClassDef readClassDef() {
628             int offset = getPosition();
629             int type = readInt();
630             int accessFlags = readInt();
631             int supertype = readInt();
632             int interfacesOffset = readInt();
633             int sourceFileIndex = readInt();
634             int annotationsOffset = readInt();
635             int classDataOffset = readInt();
636             int staticValuesOffset = readInt();
637             return new ClassDef(Dex.this, offset, type, accessFlags, supertype,
638                     interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset,
639                     staticValuesOffset);
640         }
641 
readCode()642         private Code readCode() {
643             int registersSize = readUnsignedShort();
644             int insSize = readUnsignedShort();
645             int outsSize = readUnsignedShort();
646             int triesSize = readUnsignedShort();
647             int debugInfoOffset = readInt();
648             int instructionsSize = readInt();
649             short[] instructions = readShortArray(instructionsSize);
650             Try[] tries;
651             CatchHandler[] catchHandlers;
652             if (triesSize > 0) {
653                 if (instructions.length % 2 == 1) {
654                     readShort(); // padding
655                 }
656 
657                 /*
658                  * We can't read the tries until we've read the catch handlers.
659                  * Unfortunately they're in the opposite order in the dex file
660                  * so we need to read them out-of-order.
661                  */
662                 Section triesSection = open(data.position());
663                 skip(triesSize * SizeOf.TRY_ITEM);
664                 catchHandlers = readCatchHandlers();
665                 tries = triesSection.readTries(triesSize, catchHandlers);
666             } else {
667                 tries = new Try[0];
668                 catchHandlers = new CatchHandler[0];
669             }
670             return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
671                             tries, catchHandlers);
672         }
673 
readCatchHandlers()674         private CatchHandler[] readCatchHandlers() {
675             int baseOffset = data.position();
676             int catchHandlersSize = readUleb128();
677             CatchHandler[] result = new CatchHandler[catchHandlersSize];
678             for (int i = 0; i < catchHandlersSize; i++) {
679                 int offset = data.position() - baseOffset;
680                 result[i] = readCatchHandler(offset);
681             }
682             return result;
683         }
684 
readTries(int triesSize, CatchHandler[] catchHandlers)685         private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) {
686             Try[] result = new Try[triesSize];
687             for (int i = 0; i < triesSize; i++) {
688                 int startAddress = readInt();
689                 int instructionCount = readUnsignedShort();
690                 int handlerOffset = readUnsignedShort();
691                 int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset);
692                 result[i] = new Try(startAddress, instructionCount, catchHandlerIndex);
693             }
694             return result;
695         }
696 
findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset)697         private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) {
698             for (int i = 0; i < catchHandlers.length; i++) {
699                 CatchHandler catchHandler = catchHandlers[i];
700                 if (catchHandler.getOffset() == offset) {
701                     return i;
702                 }
703             }
704             throw new IllegalArgumentException();
705         }
706 
readCatchHandler(int offset)707         private CatchHandler readCatchHandler(int offset) {
708             int size = readSleb128();
709             int handlersCount = Math.abs(size);
710             int[] typeIndexes = new int[handlersCount];
711             int[] addresses = new int[handlersCount];
712             for (int i = 0; i < handlersCount; i++) {
713                 typeIndexes[i] = readUleb128();
714                 addresses[i] = readUleb128();
715             }
716             int catchAllAddress = size <= 0 ? readUleb128() : -1;
717             return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset);
718         }
719 
readClassData()720         private ClassData readClassData() {
721             int staticFieldsSize = readUleb128();
722             int instanceFieldsSize = readUleb128();
723             int directMethodsSize = readUleb128();
724             int virtualMethodsSize = readUleb128();
725             ClassData.Field[] staticFields = readFields(staticFieldsSize);
726             ClassData.Field[] instanceFields = readFields(instanceFieldsSize);
727             ClassData.Method[] directMethods = readMethods(directMethodsSize);
728             ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize);
729             return new ClassData(staticFields, instanceFields, directMethods, virtualMethods);
730         }
731 
readFields(int count)732         private ClassData.Field[] readFields(int count) {
733             ClassData.Field[] result = new ClassData.Field[count];
734             int fieldIndex = 0;
735             for (int i = 0; i < count; i++) {
736                 fieldIndex += readUleb128(); // field index diff
737                 int accessFlags = readUleb128();
738                 result[i] = new ClassData.Field(fieldIndex, accessFlags);
739             }
740             return result;
741         }
742 
readMethods(int count)743         private ClassData.Method[] readMethods(int count) {
744             ClassData.Method[] result = new ClassData.Method[count];
745             int methodIndex = 0;
746             for (int i = 0; i < count; i++) {
747                 methodIndex += readUleb128(); // method index diff
748                 int accessFlags = readUleb128();
749                 int codeOff = readUleb128();
750                 result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff);
751             }
752             return result;
753         }
754 
755         /**
756          * Returns a byte array containing the bytes from {@code start} to this
757          * section's current position.
758          */
getBytesFrom(int start)759         private byte[] getBytesFrom(int start) {
760             int end = data.position();
761             byte[] result = new byte[end - start];
762             data.position(start);
763             data.get(result);
764             return result;
765         }
766 
readAnnotation()767         public Annotation readAnnotation() {
768             byte visibility = readByte();
769             int start = data.position();
770             new EncodedValueReader(this, EncodedValueReader.ENCODED_ANNOTATION).skipValue();
771             return new Annotation(Dex.this, visibility, new EncodedValue(getBytesFrom(start)));
772         }
773 
readEncodedArray()774         public EncodedValue readEncodedArray() {
775             int start = data.position();
776             new EncodedValueReader(this, EncodedValueReader.ENCODED_ARRAY).skipValue();
777             return new EncodedValue(getBytesFrom(start));
778         }
779 
skip(int count)780         public void skip(int count) {
781             if (count < 0) {
782                 throw new IllegalArgumentException();
783             }
784             data.position(data.position() + count);
785         }
786 
787         /**
788          * Skips bytes until the position is aligned to a multiple of 4.
789          */
alignToFourBytes()790         public void alignToFourBytes() {
791             data.position((data.position() + 3) & ~3);
792         }
793 
794         /**
795          * Writes 0x00 until the position is aligned to a multiple of 4.
796          */
alignToFourBytesWithZeroFill()797         public void alignToFourBytesWithZeroFill() {
798             while ((data.position() & 3) != 0) {
799                 data.put((byte) 0);
800             }
801         }
802 
assertFourByteAligned()803         public void assertFourByteAligned() {
804             if ((data.position() & 3) != 0) {
805                 throw new IllegalStateException("Not four byte aligned!");
806             }
807         }
808 
write(byte[] bytes)809         public void write(byte[] bytes) {
810             this.data.put(bytes);
811         }
812 
writeByte(int b)813         public void writeByte(int b) {
814             data.put((byte) b);
815         }
816 
writeShort(short i)817         public void writeShort(short i) {
818             data.putShort(i);
819         }
820 
writeUnsignedShort(int i)821         public void writeUnsignedShort(int i) {
822             short s = (short) i;
823             if (i != (s & 0xffff)) {
824                 throw new IllegalArgumentException("Expected an unsigned short: " + i);
825             }
826             writeShort(s);
827         }
828 
write(short[] shorts)829         public void write(short[] shorts) {
830             for (short s : shorts) {
831                 writeShort(s);
832             }
833         }
834 
writeInt(int i)835         public void writeInt(int i) {
836             data.putInt(i);
837         }
838 
writeUleb128(int i)839         public void writeUleb128(int i) {
840             try {
841                 Leb128.writeUnsignedLeb128(this, i);
842             } catch (ArrayIndexOutOfBoundsException e) {
843                 throw new DexException("Section limit " + data.limit() + " exceeded by " + name);
844             }
845         }
846 
writeSleb128(int i)847         public void writeSleb128(int i) {
848             try {
849                 Leb128.writeSignedLeb128(this, i);
850             } catch (ArrayIndexOutOfBoundsException e) {
851                 throw new DexException("Section limit " + data.limit() + " exceeded by " + name);
852             }
853         }
854 
writeStringData(String value)855         public void writeStringData(String value) {
856             try {
857                 int length = value.length();
858                 writeUleb128(length);
859                 write(Mutf8.encode(value));
860                 writeByte(0);
861             } catch (UTFDataFormatException e) {
862                 throw new AssertionError();
863             }
864         }
865 
writeTypeList(TypeList typeList)866         public void writeTypeList(TypeList typeList) {
867             short[] types = typeList.getTypes();
868             writeInt(types.length);
869             for (short type : types) {
870                 writeShort(type);
871             }
872             alignToFourBytesWithZeroFill();
873         }
874 
875         /**
876          * Returns the number of bytes remaining in this section.
877          */
remaining()878         public int remaining() {
879             return data.remaining();
880         }
881 
882         /**
883          * Returns the number of bytes used by this section.
884          */
used()885         public int used() {
886             return data.position() - initialPosition;
887         }
888     }
889 
890     private final class StringTable extends AbstractList<String> implements RandomAccess {
get(int index)891         @Override public String get(int index) {
892             checkBounds(index, tableOfContents.stringIds.size);
893             return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
894                     .readString();
895         }
size()896         @Override public int size() {
897             return tableOfContents.stringIds.size;
898         }
899     }
900 
901     private final class TypeIndexToDescriptorIndexTable extends AbstractList<Integer>
902             implements RandomAccess {
get(int index)903         @Override public Integer get(int index) {
904             return descriptorIndexFromTypeIndex(index);
905         }
size()906         @Override public int size() {
907             return tableOfContents.typeIds.size;
908         }
909     }
910 
911     private final class TypeIndexToDescriptorTable extends AbstractList<String>
912             implements RandomAccess {
get(int index)913         @Override public String get(int index) {
914             return strings.get(descriptorIndexFromTypeIndex(index));
915         }
size()916         @Override public int size() {
917             return tableOfContents.typeIds.size;
918         }
919     }
920 
921     private final class ProtoIdTable extends AbstractList<ProtoId> implements RandomAccess {
get(int index)922         @Override public ProtoId get(int index) {
923             checkBounds(index, tableOfContents.protoIds.size);
924             return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
925                     .readProtoId();
926         }
size()927         @Override public int size() {
928             return tableOfContents.protoIds.size;
929         }
930     }
931 
932     private final class FieldIdTable extends AbstractList<FieldId> implements RandomAccess {
get(int index)933         @Override public FieldId get(int index) {
934             checkBounds(index, tableOfContents.fieldIds.size);
935             return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
936                     .readFieldId();
937         }
size()938         @Override public int size() {
939             return tableOfContents.fieldIds.size;
940         }
941     }
942 
943     private final class MethodIdTable extends AbstractList<MethodId> implements RandomAccess {
get(int index)944         @Override public MethodId get(int index) {
945             checkBounds(index, tableOfContents.methodIds.size);
946             return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
947                     .readMethodId();
948         }
size()949         @Override public int size() {
950             return tableOfContents.methodIds.size;
951         }
952     }
953 
954     private final class ClassDefIterator implements Iterator<ClassDef> {
955         private final Dex.Section in = open(tableOfContents.classDefs.off);
956         private int count = 0;
957 
958         @Override
hasNext()959         public boolean hasNext() {
960             return count < tableOfContents.classDefs.size;
961         }
962         @Override
next()963         public ClassDef next() {
964             if (!hasNext()) {
965                 throw new NoSuchElementException();
966             }
967             count++;
968             return in.readClassDef();
969         }
970         @Override
remove()971             public void remove() {
972             throw new UnsupportedOperationException();
973         }
974     }
975 
976     private final class ClassDefIterable implements Iterable<ClassDef> {
iterator()977         public Iterator<ClassDef> iterator() {
978             return !tableOfContents.classDefs.exists()
979                ? Collections.<ClassDef>emptySet().iterator()
980                : new ClassDefIterator();
981         }
982     }
983 }
984