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.MethodHandle.MethodHandleType;
22 import com.android.dex.util.ByteInput;
23 import com.android.dex.util.ByteOutput;
24 import com.android.dex.util.FileUtils;
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         try {
97             loadFrom(in);
98         } finally {
99             in.close();
100         }
101     }
102 
103     /**
104      * Creates a new dex buffer from the dex file {@code file}.
105      */
Dex(File file)106     public Dex(File file) throws IOException {
107         if (FileUtils.hasArchiveSuffix(file.getName())) {
108             ZipFile zipFile = new ZipFile(file);
109             ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME);
110             if (entry != null) {
111                 try (InputStream inputStream = zipFile.getInputStream(entry)) {
112                     loadFrom(inputStream);
113                 }
114                 zipFile.close();
115             } else {
116                 throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file);
117             }
118         } else if (file.getName().endsWith(".dex")) {
119             try (InputStream inputStream = new FileInputStream(file)) {
120                 loadFrom(inputStream);
121             }
122         } else {
123             throw new DexException("unknown output extension: " + file);
124         }
125     }
126 
127     /**
128      * It is the caller's responsibility to close {@code in}.
129      */
loadFrom(InputStream in)130     private void loadFrom(InputStream in) throws IOException {
131         ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
132         byte[] buffer = new byte[8192];
133 
134         int count;
135         while ((count = in.read(buffer)) != -1) {
136             bytesOut.write(buffer, 0, count);
137         }
138 
139         this.data = ByteBuffer.wrap(bytesOut.toByteArray());
140         this.data.order(ByteOrder.LITTLE_ENDIAN);
141         this.tableOfContents.readFrom(this);
142     }
143 
checkBounds(int index, int length)144     private static void checkBounds(int index, int length) {
145         if (index < 0 || index >= length) {
146             throw new IndexOutOfBoundsException("index:" + index + ", length=" + length);
147         }
148     }
149 
writeTo(OutputStream out)150     public void writeTo(OutputStream out) throws IOException {
151         byte[] buffer = new byte[8192];
152         ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
153         data.clear();
154         while (data.hasRemaining()) {
155             int count = Math.min(buffer.length, data.remaining());
156             data.get(buffer, 0, count);
157             out.write(buffer, 0, count);
158         }
159     }
160 
writeTo(File dexOut)161     public void writeTo(File dexOut) throws IOException {
162         try (OutputStream out = new FileOutputStream(dexOut)) {
163             writeTo(out);
164         }
165     }
166 
getTableOfContents()167     public TableOfContents getTableOfContents() {
168         return tableOfContents;
169     }
170 
open(int position)171     public Section open(int position) {
172         if (position < 0 || position >= data.capacity()) {
173             throw new IllegalArgumentException("position=" + position
174                     + " length=" + data.capacity());
175         }
176         ByteBuffer sectionData = data.duplicate();
177         sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary?
178         sectionData.position(position);
179         sectionData.limit(data.capacity());
180         return new Section("section", sectionData);
181     }
182 
appendSection(int maxByteCount, String name)183     public Section appendSection(int maxByteCount, String name) {
184         if ((maxByteCount & 3) != 0) {
185             throw new IllegalStateException("Not four byte aligned!");
186         }
187         int limit = nextSectionStart + maxByteCount;
188         ByteBuffer sectionData = data.duplicate();
189         sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary?
190         sectionData.position(nextSectionStart);
191         sectionData.limit(limit);
192         Section result = new Section(name, sectionData);
193         nextSectionStart = limit;
194         return result;
195     }
196 
getLength()197     public int getLength() {
198         return data.capacity();
199     }
200 
getNextSectionStart()201     public int getNextSectionStart() {
202         return nextSectionStart;
203     }
204 
205     /**
206      * Returns a copy of the the bytes of this dex.
207      */
getBytes()208     public byte[] getBytes() {
209         ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
210         byte[] result = new byte[data.capacity()];
211         data.position(0);
212         data.get(result);
213         return result;
214     }
215 
strings()216     public List<String> strings() {
217         return strings;
218     }
219 
typeIds()220     public List<Integer> typeIds() {
221         return typeIds;
222     }
223 
typeNames()224     public List<String> typeNames() {
225         return typeNames;
226     }
227 
protoIds()228     public List<ProtoId> protoIds() {
229         return protoIds;
230     }
231 
fieldIds()232     public List<FieldId> fieldIds() {
233         return fieldIds;
234     }
235 
methodIds()236     public List<MethodId> methodIds() {
237         return methodIds;
238     }
239 
classDefs()240     public Iterable<ClassDef> classDefs() {
241         return new ClassDefIterable();
242     }
243 
readTypeList(int offset)244     public TypeList readTypeList(int offset) {
245         if (offset == 0) {
246             return TypeList.EMPTY;
247         }
248         return open(offset).readTypeList();
249     }
250 
readClassData(ClassDef classDef)251     public ClassData readClassData(ClassDef classDef) {
252         int offset = classDef.getClassDataOffset();
253         if (offset == 0) {
254             throw new IllegalArgumentException("offset == 0");
255         }
256         return open(offset).readClassData();
257     }
258 
readCode(ClassData.Method method)259     public Code readCode(ClassData.Method method) {
260         int offset = method.getCodeOffset();
261         if (offset == 0) {
262             throw new IllegalArgumentException("offset == 0");
263         }
264         return open(offset).readCode();
265     }
266 
267     /**
268      * Returns the signature of all but the first 32 bytes of this dex. The
269      * first 32 bytes of dex files are not specified to be included in the
270      * signature.
271      */
computeSignature()272     public byte[] computeSignature() throws IOException {
273         MessageDigest digest;
274         try {
275             digest = MessageDigest.getInstance("SHA-1");
276         } catch (NoSuchAlgorithmException e) {
277             throw new AssertionError();
278         }
279         byte[] buffer = new byte[8192];
280         ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
281         data.limit(data.capacity());
282         data.position(SIGNATURE_OFFSET + SIGNATURE_SIZE);
283         while (data.hasRemaining()) {
284             int count = Math.min(buffer.length, data.remaining());
285             data.get(buffer, 0, count);
286             digest.update(buffer, 0, count);
287         }
288         return digest.digest();
289     }
290 
291     /**
292      * Returns the checksum of all but the first 12 bytes of {@code dex}.
293      */
computeChecksum()294     public int computeChecksum() throws IOException {
295         Adler32 adler32 = new Adler32();
296         byte[] buffer = new byte[8192];
297         ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
298         data.limit(data.capacity());
299         data.position(CHECKSUM_OFFSET + CHECKSUM_SIZE);
300         while (data.hasRemaining()) {
301             int count = Math.min(buffer.length, data.remaining());
302             data.get(buffer, 0, count);
303             adler32.update(buffer, 0, count);
304         }
305         return (int) adler32.getValue();
306     }
307 
308     /**
309      * Generates the signature and checksum of the dex file {@code out} and
310      * writes them to the file.
311      */
writeHashes()312     public void writeHashes() throws IOException {
313         open(SIGNATURE_OFFSET).write(computeSignature());
314         open(CHECKSUM_OFFSET).writeInt(computeChecksum());
315     }
316 
317     /**
318      * Look up a descriptor index from a type index. Cheaper than:
319      * {@code open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();}
320      */
descriptorIndexFromTypeIndex(int typeIndex)321     public int descriptorIndexFromTypeIndex(int typeIndex) {
322        checkBounds(typeIndex, tableOfContents.typeIds.size);
323        int position = tableOfContents.typeIds.off + (SizeOf.TYPE_ID_ITEM * typeIndex);
324        return data.getInt(position);
325     }
326 
327 
328     public final class Section implements ByteInput, ByteOutput {
329         private final String name;
330         private final ByteBuffer data;
331         private final int initialPosition;
332 
Section(String name, ByteBuffer data)333         private Section(String name, ByteBuffer data) {
334             this.name = name;
335             this.data = data;
336             this.initialPosition = data.position();
337         }
338 
getPosition()339         public int getPosition() {
340             return data.position();
341         }
342 
readInt()343         public int readInt() {
344             return data.getInt();
345         }
346 
readShort()347         public short readShort() {
348             return data.getShort();
349         }
350 
readUnsignedShort()351         public int readUnsignedShort() {
352             return readShort() & 0xffff;
353         }
354 
355         @Override
readByte()356         public byte readByte() {
357             return data.get();
358         }
359 
readByteArray(int length)360         public byte[] readByteArray(int length) {
361             byte[] result = new byte[length];
362             data.get(result);
363             return result;
364         }
365 
readShortArray(int length)366         public short[] readShortArray(int length) {
367             if (length == 0) {
368                 return EMPTY_SHORT_ARRAY;
369             }
370             short[] result = new short[length];
371             for (int i = 0; i < length; i++) {
372                 result[i] = readShort();
373             }
374             return result;
375         }
376 
readUleb128()377         public int readUleb128() {
378             return Leb128.readUnsignedLeb128(this);
379         }
380 
readUleb128p1()381         public int readUleb128p1() {
382             return Leb128.readUnsignedLeb128(this) - 1;
383         }
384 
readSleb128()385         public int readSleb128() {
386             return Leb128.readSignedLeb128(this);
387         }
388 
writeUleb128p1(int i)389         public void writeUleb128p1(int i) {
390             writeUleb128(i + 1);
391         }
392 
readTypeList()393         public TypeList readTypeList() {
394             int size = readInt();
395             short[] types = readShortArray(size);
396             alignToFourBytes();
397             return new TypeList(Dex.this, types);
398         }
399 
readString()400         public String readString() {
401             int offset = readInt();
402             int savedPosition = data.position();
403             int savedLimit = data.limit();
404             data.position(offset);
405             data.limit(data.capacity());
406             try {
407                 int expectedLength = readUleb128();
408                 String result = Mutf8.decode(this, new char[expectedLength]);
409                 if (result.length() != expectedLength) {
410                     throw new DexException("Declared length " + expectedLength
411                             + " doesn't match decoded length of " + result.length());
412                 }
413                 return result;
414             } catch (UTFDataFormatException e) {
415                 throw new DexException(e);
416             } finally {
417                 data.position(savedPosition);
418                 data.limit(savedLimit);
419             }
420         }
421 
readFieldId()422         public FieldId readFieldId() {
423             int declaringClassIndex = readUnsignedShort();
424             int typeIndex = readUnsignedShort();
425             int nameIndex = readInt();
426             return new FieldId(Dex.this, declaringClassIndex, typeIndex, nameIndex);
427         }
428 
readMethodId()429         public MethodId readMethodId() {
430             int declaringClassIndex = readUnsignedShort();
431             int protoIndex = readUnsignedShort();
432             int nameIndex = readInt();
433             return new MethodId(Dex.this, declaringClassIndex, protoIndex, nameIndex);
434         }
435 
readProtoId()436         public ProtoId readProtoId() {
437             int shortyIndex = readInt();
438             int returnTypeIndex = readInt();
439             int parametersOffset = readInt();
440             return new ProtoId(Dex.this, shortyIndex, returnTypeIndex, parametersOffset);
441         }
442 
readCallSiteId()443         public CallSiteId readCallSiteId() {
444             int offset = readInt();
445             return new CallSiteId(Dex.this, offset);
446         }
447 
readMethodHandle()448         public MethodHandle readMethodHandle() {
449             MethodHandleType methodHandleType = MethodHandleType.fromValue(readUnsignedShort());
450             int unused1 = readUnsignedShort();
451             int fieldOrMethodId = readUnsignedShort();
452             int unused2 = readUnsignedShort();
453             return new MethodHandle(Dex.this, methodHandleType, unused1, fieldOrMethodId, unused2);
454         }
455 
readClassDef()456         public ClassDef readClassDef() {
457             int offset = getPosition();
458             int type = readInt();
459             int accessFlags = readInt();
460             int supertype = readInt();
461             int interfacesOffset = readInt();
462             int sourceFileIndex = readInt();
463             int annotationsOffset = readInt();
464             int classDataOffset = readInt();
465             int staticValuesOffset = readInt();
466             return new ClassDef(Dex.this, offset, type, accessFlags, supertype,
467                     interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset,
468                     staticValuesOffset);
469         }
470 
readCode()471         private Code readCode() {
472             int registersSize = readUnsignedShort();
473             int insSize = readUnsignedShort();
474             int outsSize = readUnsignedShort();
475             int triesSize = readUnsignedShort();
476             int debugInfoOffset = readInt();
477             int instructionsSize = readInt();
478             short[] instructions = readShortArray(instructionsSize);
479             Try[] tries;
480             CatchHandler[] catchHandlers;
481             if (triesSize > 0) {
482                 if (instructions.length % 2 == 1) {
483                     readShort(); // padding
484                 }
485 
486                 /*
487                  * We can't read the tries until we've read the catch handlers.
488                  * Unfortunately they're in the opposite order in the dex file
489                  * so we need to read them out-of-order.
490                  */
491                 Section triesSection = open(data.position());
492                 skip(triesSize * SizeOf.TRY_ITEM);
493                 catchHandlers = readCatchHandlers();
494                 tries = triesSection.readTries(triesSize, catchHandlers);
495             } else {
496                 tries = new Try[0];
497                 catchHandlers = new CatchHandler[0];
498             }
499             return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
500                             tries, catchHandlers);
501         }
502 
readCatchHandlers()503         private CatchHandler[] readCatchHandlers() {
504             int baseOffset = data.position();
505             int catchHandlersSize = readUleb128();
506             CatchHandler[] result = new CatchHandler[catchHandlersSize];
507             for (int i = 0; i < catchHandlersSize; i++) {
508                 int offset = data.position() - baseOffset;
509                 result[i] = readCatchHandler(offset);
510             }
511             return result;
512         }
513 
readTries(int triesSize, CatchHandler[] catchHandlers)514         private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) {
515             Try[] result = new Try[triesSize];
516             for (int i = 0; i < triesSize; i++) {
517                 int startAddress = readInt();
518                 int instructionCount = readUnsignedShort();
519                 int handlerOffset = readUnsignedShort();
520                 int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset);
521                 result[i] = new Try(startAddress, instructionCount, catchHandlerIndex);
522             }
523             return result;
524         }
525 
findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset)526         private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) {
527             for (int i = 0; i < catchHandlers.length; i++) {
528                 CatchHandler catchHandler = catchHandlers[i];
529                 if (catchHandler.getOffset() == offset) {
530                     return i;
531                 }
532             }
533             throw new IllegalArgumentException();
534         }
535 
readCatchHandler(int offset)536         private CatchHandler readCatchHandler(int offset) {
537             int size = readSleb128();
538             int handlersCount = Math.abs(size);
539             int[] typeIndexes = new int[handlersCount];
540             int[] addresses = new int[handlersCount];
541             for (int i = 0; i < handlersCount; i++) {
542                 typeIndexes[i] = readUleb128();
543                 addresses[i] = readUleb128();
544             }
545             int catchAllAddress = size <= 0 ? readUleb128() : -1;
546             return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset);
547         }
548 
readClassData()549         private ClassData readClassData() {
550             int staticFieldsSize = readUleb128();
551             int instanceFieldsSize = readUleb128();
552             int directMethodsSize = readUleb128();
553             int virtualMethodsSize = readUleb128();
554             ClassData.Field[] staticFields = readFields(staticFieldsSize);
555             ClassData.Field[] instanceFields = readFields(instanceFieldsSize);
556             ClassData.Method[] directMethods = readMethods(directMethodsSize);
557             ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize);
558             return new ClassData(staticFields, instanceFields, directMethods, virtualMethods);
559         }
560 
readFields(int count)561         private ClassData.Field[] readFields(int count) {
562             ClassData.Field[] result = new ClassData.Field[count];
563             int fieldIndex = 0;
564             for (int i = 0; i < count; i++) {
565                 fieldIndex += readUleb128(); // field index diff
566                 int accessFlags = readUleb128();
567                 result[i] = new ClassData.Field(fieldIndex, accessFlags);
568             }
569             return result;
570         }
571 
readMethods(int count)572         private ClassData.Method[] readMethods(int count) {
573             ClassData.Method[] result = new ClassData.Method[count];
574             int methodIndex = 0;
575             for (int i = 0; i < count; i++) {
576                 methodIndex += readUleb128(); // method index diff
577                 int accessFlags = readUleb128();
578                 int codeOff = readUleb128();
579                 result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff);
580             }
581             return result;
582         }
583 
584         /**
585          * Returns a byte array containing the bytes from {@code start} to this
586          * section's current position.
587          */
getBytesFrom(int start)588         private byte[] getBytesFrom(int start) {
589             int end = data.position();
590             byte[] result = new byte[end - start];
591             data.position(start);
592             data.get(result);
593             return result;
594         }
595 
readAnnotation()596         public Annotation readAnnotation() {
597             byte visibility = readByte();
598             int start = data.position();
599             new EncodedValueReader(this, EncodedValueReader.ENCODED_ANNOTATION).skipValue();
600             return new Annotation(Dex.this, visibility, new EncodedValue(getBytesFrom(start)));
601         }
602 
readEncodedArray()603         public EncodedValue readEncodedArray() {
604             int start = data.position();
605             new EncodedValueReader(this, EncodedValueReader.ENCODED_ARRAY).skipValue();
606             return new EncodedValue(getBytesFrom(start));
607         }
608 
skip(int count)609         public void skip(int count) {
610             if (count < 0) {
611                 throw new IllegalArgumentException();
612             }
613             data.position(data.position() + count);
614         }
615 
616         /**
617          * Skips bytes until the position is aligned to a multiple of 4.
618          */
alignToFourBytes()619         public void alignToFourBytes() {
620             data.position((data.position() + 3) & ~3);
621         }
622 
623         /**
624          * Writes 0x00 until the position is aligned to a multiple of 4.
625          */
alignToFourBytesWithZeroFill()626         public void alignToFourBytesWithZeroFill() {
627             while ((data.position() & 3) != 0) {
628                 data.put((byte) 0);
629             }
630         }
631 
assertFourByteAligned()632         public void assertFourByteAligned() {
633             if ((data.position() & 3) != 0) {
634                 throw new IllegalStateException("Not four byte aligned!");
635             }
636         }
637 
write(byte[] bytes)638         public void write(byte[] bytes) {
639             this.data.put(bytes);
640         }
641 
642         @Override
writeByte(int b)643         public void writeByte(int b) {
644             data.put((byte) b);
645         }
646 
writeShort(short i)647         public void writeShort(short i) {
648             data.putShort(i);
649         }
650 
writeUnsignedShort(int i)651         public void writeUnsignedShort(int i) {
652             short s = (short) i;
653             if (i != (s & 0xffff)) {
654                 throw new IllegalArgumentException("Expected an unsigned short: " + i);
655             }
656             writeShort(s);
657         }
658 
write(short[] shorts)659         public void write(short[] shorts) {
660             for (short s : shorts) {
661                 writeShort(s);
662             }
663         }
664 
writeInt(int i)665         public void writeInt(int i) {
666             data.putInt(i);
667         }
668 
writeUleb128(int i)669         public void writeUleb128(int i) {
670             try {
671                 Leb128.writeUnsignedLeb128(this, i);
672             } catch (ArrayIndexOutOfBoundsException e) {
673                 throw new DexException("Section limit " + data.limit() + " exceeded by " + name);
674             }
675         }
676 
writeSleb128(int i)677         public void writeSleb128(int i) {
678             try {
679                 Leb128.writeSignedLeb128(this, i);
680             } catch (ArrayIndexOutOfBoundsException e) {
681                 throw new DexException("Section limit " + data.limit() + " exceeded by " + name);
682             }
683         }
684 
writeStringData(String value)685         public void writeStringData(String value) {
686             try {
687                 int length = value.length();
688                 writeUleb128(length);
689                 write(Mutf8.encode(value));
690                 writeByte(0);
691             } catch (UTFDataFormatException e) {
692                 throw new AssertionError();
693             }
694         }
695 
writeTypeList(TypeList typeList)696         public void writeTypeList(TypeList typeList) {
697             short[] types = typeList.getTypes();
698             writeInt(types.length);
699             for (short type : types) {
700                 writeShort(type);
701             }
702             alignToFourBytesWithZeroFill();
703         }
704 
705         /**
706          * Returns the number of bytes used by this section.
707          */
used()708         public int used() {
709             return data.position() - initialPosition;
710         }
711     }
712 
713     private final class StringTable extends AbstractList<String> implements RandomAccess {
714         @Override
get(int index)715         public String get(int index) {
716             checkBounds(index, tableOfContents.stringIds.size);
717             return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
718                     .readString();
719         }
720         @Override
size()721         public int size() {
722             return tableOfContents.stringIds.size;
723         }
724     }
725 
726     private final class TypeIndexToDescriptorIndexTable extends AbstractList<Integer>
727             implements RandomAccess {
728         @Override
get(int index)729         public Integer get(int index) {
730             return descriptorIndexFromTypeIndex(index);
731         }
732         @Override
size()733         public int size() {
734             return tableOfContents.typeIds.size;
735         }
736     }
737 
738     private final class TypeIndexToDescriptorTable extends AbstractList<String>
739             implements RandomAccess {
740         @Override
get(int index)741         public String get(int index) {
742             return strings.get(descriptorIndexFromTypeIndex(index));
743         }
744         @Override
size()745         public int size() {
746             return tableOfContents.typeIds.size;
747         }
748     }
749 
750     private final class ProtoIdTable extends AbstractList<ProtoId> implements RandomAccess {
751         @Override
get(int index)752         public ProtoId get(int index) {
753             checkBounds(index, tableOfContents.protoIds.size);
754             return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
755                     .readProtoId();
756         }
757         @Override
size()758         public int size() {
759             return tableOfContents.protoIds.size;
760         }
761     }
762 
763     private final class FieldIdTable extends AbstractList<FieldId> implements RandomAccess {
764         @Override
get(int index)765         public FieldId get(int index) {
766             checkBounds(index, tableOfContents.fieldIds.size);
767             return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
768                     .readFieldId();
769         }
770         @Override
size()771         public int size() {
772             return tableOfContents.fieldIds.size;
773         }
774     }
775 
776     private final class MethodIdTable extends AbstractList<MethodId> implements RandomAccess {
777         @Override
get(int index)778         public MethodId get(int index) {
779             checkBounds(index, tableOfContents.methodIds.size);
780             return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
781                     .readMethodId();
782         }
783         @Override
size()784         public int size() {
785             return tableOfContents.methodIds.size;
786         }
787     }
788 
789     private final class ClassDefIterator implements Iterator<ClassDef> {
790         private final Dex.Section in = open(tableOfContents.classDefs.off);
791         private int count = 0;
792 
793         @Override
hasNext()794         public boolean hasNext() {
795             return count < tableOfContents.classDefs.size;
796         }
797         @Override
next()798         public ClassDef next() {
799             if (!hasNext()) {
800                 throw new NoSuchElementException();
801             }
802             count++;
803             return in.readClassDef();
804         }
805         @Override
remove()806             public void remove() {
807             throw new UnsupportedOperationException();
808         }
809     }
810 
811     private final class ClassDefIterable implements Iterable<ClassDef> {
812         @Override
iterator()813         public Iterator<ClassDef> iterator() {
814             return !tableOfContents.classDefs.exists()
815                ? Collections.<ClassDef>emptySet().iterator()
816                : new ClassDefIterator();
817         }
818     }
819 }
820