Home | History | Annotate | Download | only in dex
      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      */
     74     public Dex(byte[] data) throws IOException {
     75         this(ByteBuffer.wrap(data));
     76     }
     77 
     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      */
     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      */
     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      */
    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      */
    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 
    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 
    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 
    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 
    176     public void writeTo(File dexOut) throws IOException {
    177         OutputStream out = new FileOutputStream(dexOut);
    178         writeTo(out);
    179         out.close();
    180     }
    181 
    182     public TableOfContents getTableOfContents() {
    183         return tableOfContents;
    184     }
    185 
    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 
    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 
    212     public int getLength() {
    213         return data.capacity();
    214     }
    215 
    216     public int getNextSectionStart() {
    217         return nextSectionStart;
    218     }
    219 
    220     /**
    221      * Returns a copy of the the bytes of this dex.
    222      */
    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 
    231     public List<String> strings() {
    232         return strings;
    233     }
    234 
    235     public List<Integer> typeIds() {
    236         return typeIds;
    237     }
    238 
    239     public List<String> typeNames() {
    240         return typeNames;
    241     }
    242 
    243     public List<ProtoId> protoIds() {
    244         return protoIds;
    245     }
    246 
    247     public List<FieldId> fieldIds() {
    248         return fieldIds;
    249     }
    250 
    251     public List<MethodId> methodIds() {
    252         return methodIds;
    253     }
    254 
    255     public Iterable<ClassDef> classDefs() {
    256         return new ClassDefIterable();
    257     }
    258 
    259     public TypeList readTypeList(int offset) {
    260         if (offset == 0) {
    261             return TypeList.EMPTY;
    262         }
    263         return open(offset).readTypeList();
    264     }
    265 
    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 
    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      */
    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      */
    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      */
    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      */
    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 
    344     public int findStringIndex(String s) {
    345         return Collections.binarySearch(strings, s);
    346     }
    347 
    348     public int findTypeIndex(String descriptor) {
    349         return Collections.binarySearch(typeNames, descriptor);
    350     }
    351 
    352     public int findFieldIndex(FieldId fieldId) {
    353         return Collections.binarySearch(fieldIds, fieldId);
    354     }
    355 
    356     public int findMethodIndex(MethodId methodId) {
    357         return Collections.binarySearch(methodIds, methodId);
    358     }
    359 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 a type index index from a class def index.
    473      */
    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      */
    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 
    518         private Section(String name, ByteBuffer data) {
    519             this.name = name;
    520             this.data = data;
    521             this.initialPosition = data.position();
    522         }
    523 
    524         public int getPosition() {
    525             return data.position();
    526         }
    527 
    528         public int readInt() {
    529             return data.getInt();
    530         }
    531 
    532         public short readShort() {
    533             return data.getShort();
    534         }
    535 
    536         public int readUnsignedShort() {
    537             return readShort() & 0xffff;
    538         }
    539 
    540         public byte readByte() {
    541             return data.get();
    542         }
    543 
    544         public byte[] readByteArray(int length) {
    545             byte[] result = new byte[length];
    546             data.get(result);
    547             return result;
    548         }
    549 
    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 
    561         public int readUleb128() {
    562             return Leb128.readUnsignedLeb128(this);
    563         }
    564 
    565         public int readUleb128p1() {
    566             return Leb128.readUnsignedLeb128(this) - 1;
    567         }
    568 
    569         public int readSleb128() {
    570             return Leb128.readSignedLeb128(this);
    571         }
    572 
    573         public void writeUleb128p1(int i) {
    574             writeUleb128(i + 1);
    575         }
    576 
    577         public TypeList readTypeList() {
    578             int size = readInt();
    579             short[] types = readShortArray(size);
    580             alignToFourBytes();
    581             return new TypeList(Dex.this, types);
    582         }
    583 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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          */
    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 
    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 
    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 
    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          */
    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          */
    797         public void alignToFourBytesWithZeroFill() {
    798             while ((data.position() & 3) != 0) {
    799                 data.put((byte) 0);
    800             }
    801         }
    802 
    803         public void assertFourByteAligned() {
    804             if ((data.position() & 3) != 0) {
    805                 throw new IllegalStateException("Not four byte aligned!");
    806             }
    807         }
    808 
    809         public void write(byte[] bytes) {
    810             this.data.put(bytes);
    811         }
    812 
    813         public void writeByte(int b) {
    814             data.put((byte) b);
    815         }
    816 
    817         public void writeShort(short i) {
    818             data.putShort(i);
    819         }
    820 
    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 
    829         public void write(short[] shorts) {
    830             for (short s : shorts) {
    831                 writeShort(s);
    832             }
    833         }
    834 
    835         public void writeInt(int i) {
    836             data.putInt(i);
    837         }
    838 
    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 
    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 
    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 
    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          */
    878         public int remaining() {
    879             return data.remaining();
    880         }
    881 
    882         /**
    883          * Returns the number of bytes used by this section.
    884          */
    885         public int used () {
    886             return data.position() - initialPosition;
    887         }
    888     }
    889 
    890     private final class StringTable extends AbstractList<String> implements RandomAccess {
    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         }
    896         @Override public int size() {
    897             return tableOfContents.stringIds.size;
    898         }
    899     };
    900 
    901     private final class TypeIndexToDescriptorIndexTable extends AbstractList<Integer>
    902             implements RandomAccess {
    903         @Override public Integer get(int index) {
    904             return descriptorIndexFromTypeIndex(index);
    905         }
    906         @Override public int size() {
    907             return tableOfContents.typeIds.size;
    908         }
    909     };
    910 
    911     private final class TypeIndexToDescriptorTable extends AbstractList<String>
    912             implements RandomAccess {
    913         @Override public String get(int index) {
    914             return strings.get(descriptorIndexFromTypeIndex(index));
    915         }
    916         @Override public int size() {
    917             return tableOfContents.typeIds.size;
    918         }
    919     };
    920 
    921     private final class ProtoIdTable extends AbstractList<ProtoId> implements RandomAccess {
    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         }
    927         @Override public int size() {
    928             return tableOfContents.protoIds.size;
    929         }
    930     };
    931 
    932     private final class FieldIdTable extends AbstractList<FieldId> implements RandomAccess {
    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         }
    938         @Override public int size() {
    939             return tableOfContents.fieldIds.size;
    940         }
    941     };
    942 
    943     private final class MethodIdTable extends AbstractList<MethodId> implements RandomAccess {
    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         }
    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
    959         public boolean hasNext() {
    960             return count < tableOfContents.classDefs.size;
    961         }
    962         @Override
    963         public ClassDef next() {
    964             if (!hasNext()) {
    965                 throw new NoSuchElementException();
    966             }
    967             count++;
    968             return in.readClassDef();
    969         }
    970         @Override
    971             public void remove() {
    972             throw new UnsupportedOperationException();
    973         }
    974     };
    975 
    976     private final class ClassDefIterable implements Iterable<ClassDef> {
    977         public Iterator<ClassDef> iterator() {
    978             return !tableOfContents.classDefs.exists()
    979                ? Collections.<ClassDef>emptySet().iterator()
    980                : new ClassDefIterator();
    981         }
    982     };
    983 }
    984