DexBuffer.java revision bc23c4f3ebaefebb3f1be7732767631f91e165ea
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
17package com.android.dx.io;
18
19import com.android.dx.dex.DexFormat;
20import com.android.dx.dex.SizeOf;
21import com.android.dx.dex.TableOfContents;
22import com.android.dx.merge.TypeList;
23import com.android.dx.util.ByteInput;
24import com.android.dx.util.ByteOutput;
25import com.android.dx.util.DexException;
26import com.android.dx.util.FileUtils;
27import com.android.dx.util.Leb128Utils;
28import com.android.dx.util.Mutf8;
29import java.io.ByteArrayOutputStream;
30import java.io.File;
31import java.io.FileInputStream;
32import java.io.FileOutputStream;
33import java.io.IOException;
34import java.io.InputStream;
35import java.io.OutputStream;
36import java.io.UTFDataFormatException;
37import java.util.AbstractList;
38import java.util.Arrays;
39import java.util.Collections;
40import java.util.Iterator;
41import java.util.List;
42import java.util.NoSuchElementException;
43import java.util.zip.ZipEntry;
44import java.util.zip.ZipFile;
45
46/**
47 * The bytes of a dex file in memory for reading and writing. All int offsets
48 * are unsigned.
49 */
50public final class DexBuffer {
51    private byte[] data;
52    private final TableOfContents tableOfContents = new TableOfContents();
53    private int length = 0;
54
55    private final List<String> strings = new AbstractList<String>() {
56        @Override public String get(int index) {
57            checkBounds(index, tableOfContents.stringIds.size);
58            return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
59                    .readString();
60        }
61        @Override public int size() {
62            return tableOfContents.stringIds.size;
63        }
64    };
65
66    private final List<Integer> typeIds = new AbstractList<Integer>() {
67        @Override public Integer get(int index) {
68            checkBounds(index, tableOfContents.typeIds.size);
69            return open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();
70        }
71        @Override public int size() {
72            return tableOfContents.typeIds.size;
73        }
74    };
75
76    private final List<String> typeNames = new AbstractList<String>() {
77        @Override public String get(int index) {
78            checkBounds(index, tableOfContents.typeIds.size);
79            return strings.get(typeIds.get(index));
80        }
81        @Override public int size() {
82            return tableOfContents.typeIds.size;
83        }
84    };
85
86    private final List<ProtoId> protoIds = new AbstractList<ProtoId>() {
87        @Override public ProtoId get(int index) {
88            checkBounds(index, tableOfContents.protoIds.size);
89            return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
90                    .readProtoId();
91        }
92        @Override public int size() {
93            return tableOfContents.protoIds.size;
94        }
95    };
96
97    private final List<FieldId> fieldIds = new AbstractList<FieldId>() {
98        @Override public FieldId get(int index) {
99            checkBounds(index, tableOfContents.fieldIds.size);
100            return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
101                    .readFieldId();
102        }
103        @Override public int size() {
104            return tableOfContents.fieldIds.size;
105        }
106    };
107
108    private final List<MethodId> methodIds = new AbstractList<MethodId>() {
109        @Override public MethodId get(int index) {
110            checkBounds(index, tableOfContents.methodIds.size);
111            return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
112                    .readMethodId();
113        }
114        @Override public int size() {
115            return tableOfContents.methodIds.size;
116        }
117    };
118
119    /**
120     * Creates a new dex buffer defining no classes.
121     */
122    public DexBuffer() {
123        this.data = new byte[0];
124    }
125
126    /**
127     * Creates a new dex buffer that reads from {@code data}. It is an error to
128     * modify {@code data} after using it to create a dex buffer.
129     */
130    public DexBuffer(byte[] data) throws IOException {
131        this.data = data;
132        this.length = data.length;
133        this.tableOfContents.readFrom(this);
134    }
135
136    /**
137     * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}.
138     */
139    public DexBuffer(InputStream in) throws IOException {
140        loadFrom(in);
141    }
142
143    /**
144     * Creates a new dex buffer from the dex file {@code file}.
145     */
146    public DexBuffer(File file) throws IOException {
147        if (FileUtils.hasArchiveSuffix(file.getName())) {
148            ZipFile zipFile = new ZipFile(file);
149            ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME);
150            if (entry != null) {
151                loadFrom(zipFile.getInputStream(entry));
152                zipFile.close();
153            } else {
154                throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file);
155            }
156        } else if (file.getName().endsWith(".dex")) {
157            loadFrom(new FileInputStream(file));
158        } else {
159            throw new DexException("unknown output extension: " + file);
160        }
161    }
162
163    private void loadFrom(InputStream in) throws IOException {
164        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
165        byte[] buffer = new byte[8192];
166
167        int count;
168        while ((count = in.read(buffer)) != -1) {
169            bytesOut.write(buffer, 0, count);
170        }
171        in.close();
172
173        this.data = bytesOut.toByteArray();
174        this.length = data.length;
175        this.tableOfContents.readFrom(this);
176    }
177
178    private static void checkBounds(int index, int length) {
179        if (index < 0 || index >= length) {
180            throw new IndexOutOfBoundsException("index:" + index + ", length=" + length);
181        }
182    }
183
184    public void writeTo(OutputStream out) throws IOException {
185        out.write(data);
186    }
187
188    public void writeTo(File dexOut) throws IOException {
189        OutputStream out = new FileOutputStream(dexOut);
190        writeTo(out);
191        out.close();
192    }
193
194    public TableOfContents getTableOfContents() {
195        return tableOfContents;
196    }
197
198    public Section open(int position) {
199        if (position < 0 || position > length) {
200            throw new IllegalArgumentException("position=" + position + " length=" + length);
201        }
202        return new Section(position);
203    }
204
205    public Section appendSection(int maxByteCount, String name) {
206        int limit = fourByteAlign(length + maxByteCount);
207        Section result = new Section(name, length, limit);
208        length = limit;
209        return result;
210    }
211
212    public void noMoreSections() {
213        data = new byte[length];
214    }
215
216    public int getLength() {
217        return length;
218    }
219
220    public static int fourByteAlign(int position) {
221        return (position + 3) & ~3;
222    }
223
224    public byte[] getBytes() {
225        return data;
226    }
227
228    public List<String> strings() {
229        return strings;
230    }
231
232    public List<Integer> typeIds() {
233        return typeIds;
234    }
235
236    public List<String> typeNames() {
237        return typeNames;
238    }
239
240    public List<ProtoId> protoIds() {
241        return protoIds;
242    }
243
244    public List<FieldId> fieldIds() {
245        return fieldIds;
246    }
247
248    public List<MethodId> methodIds() {
249        return methodIds;
250    }
251
252    public Iterable<ClassDef> classDefs() {
253        return new Iterable<ClassDef>() {
254            public Iterator<ClassDef> iterator() {
255                if (!tableOfContents.classDefs.exists()) {
256                    return Collections.<ClassDef>emptySet().iterator();
257                }
258                return new Iterator<ClassDef>() {
259                    private DexBuffer.Section in = open(tableOfContents.classDefs.off);
260                    private int count = 0;
261
262                    public boolean hasNext() {
263                        return count < tableOfContents.classDefs.size;
264                    }
265                    public ClassDef next() {
266                        if (!hasNext()) {
267                            throw new NoSuchElementException();
268                        }
269                        count++;
270                        return in.readClassDef();
271                    }
272                    public void remove() {
273                        throw new UnsupportedOperationException();
274                    }
275                };
276            }
277        };
278    }
279
280    public TypeList readTypeList(int offset) {
281        if (offset == 0) {
282            return TypeList.EMPTY;
283        }
284        return open(offset).readTypeList();
285    }
286
287    public ClassData readClassData(ClassDef classDef) {
288        int offset = classDef.getClassDataOffset();
289        if (offset == 0) {
290            throw new IllegalArgumentException("offset == 0");
291        }
292        return open(offset).readClassData();
293    }
294
295    public Code readCode(ClassData.Method method) {
296        int offset = method.getCodeOffset();
297        if (offset == 0) {
298            throw new IllegalArgumentException("offset == 0");
299        }
300        return open(offset).readCode();
301    }
302
303    public final class Section implements ByteInput, ByteOutput {
304        private final String name;
305        private int position;
306        private final int limit;
307        private final int initialPosition;
308
309        private Section(String name, int position, int limit) {
310            this.name = name;
311            this.position = this.initialPosition = position;
312            this.limit = limit;
313        }
314
315        private Section(int position) {
316            this("section", position, data.length);
317        }
318
319        public int getPosition() {
320            return position;
321        }
322
323        public int readInt() {
324            int result = (data[position] & 0xff)
325                    | (data[position + 1] & 0xff) << 8
326                    | (data[position + 2] & 0xff) << 16
327                    | (data[position + 3] & 0xff) << 24;
328            position += 4;
329            return result;
330        }
331
332        public short readShort() {
333            int result = (data[position] & 0xff)
334                    | (data[position + 1] & 0xff) << 8;
335            position += 2;
336            return (short) result;
337        }
338
339        public int readUnsignedShort() {
340            return readShort() & 0xffff;
341        }
342
343        public byte readByte() {
344            return (byte) (data[position++] & 0xff);
345        }
346
347        public byte[] readByteArray(int length) {
348            byte[] result = Arrays.copyOfRange(data, position, position + length);
349            position += length;
350            return result;
351        }
352
353        public short[] readShortArray(int length) {
354            short[] result = new short[length];
355            for (int i = 0; i < length; i++) {
356                result[i] = readShort();
357            }
358            return result;
359        }
360
361        public int readUleb128() {
362            return Leb128Utils.readUnsignedLeb128(this);
363        }
364
365        public int readUleb128p1() {
366            return Leb128Utils.readUnsignedLeb128(this) - 1;
367        }
368
369        public int readSleb128() {
370            return Leb128Utils.readSignedLeb128(this);
371        }
372
373        public TypeList readTypeList() {
374            int size = readInt();
375            short[] types = new short[size];
376            for (int i = 0; i < size; i++) {
377                types[i] = readShort();
378            }
379            alignToFourBytes();
380            return new TypeList(DexBuffer.this, types);
381        }
382
383        public String readString() {
384            int offset = readInt();
385            int savedPosition = position;
386            position = offset;
387            try {
388                int expectedLength = readUleb128();
389                String result = Mutf8.decode(this, new char[expectedLength]);
390                if (result.length() != expectedLength) {
391                    throw new DexException("Declared length " + expectedLength
392                            + " doesn't match decoded length of " + result.length());
393                }
394                return result;
395            } catch (UTFDataFormatException e) {
396                throw new DexException(e);
397            } finally {
398                position = savedPosition;
399            }
400        }
401
402        public FieldId readFieldId() {
403            int declaringClassIndex = readUnsignedShort();
404            int typeIndex = readUnsignedShort();
405            int nameIndex = readInt();
406            return new FieldId(DexBuffer.this, declaringClassIndex, typeIndex, nameIndex);
407        }
408
409        public MethodId readMethodId() {
410            int declaringClassIndex = readUnsignedShort();
411            int protoIndex = readUnsignedShort();
412            int nameIndex = readInt();
413            return new MethodId(DexBuffer.this, declaringClassIndex, protoIndex, nameIndex);
414        }
415
416        public ProtoId readProtoId() {
417            int shortyIndex = readInt();
418            int returnTypeIndex = readInt();
419            int parametersOffset = readInt();
420            return new ProtoId(DexBuffer.this, shortyIndex, returnTypeIndex, parametersOffset);
421        }
422
423        public ClassDef readClassDef() {
424            int offset = getPosition();
425            int type = readInt();
426            int accessFlags = readInt();
427            int supertype = readInt();
428            int interfacesOffset = readInt();
429            int sourceFileIndex = readInt();
430            int annotationsOffset = readInt();
431            int classDataOffset = readInt();
432            int staticValuesOffset = readInt();
433            return new ClassDef(DexBuffer.this, offset, type, accessFlags, supertype,
434                    interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset,
435                    staticValuesOffset);
436        }
437
438        private Code readCode() {
439            int registersSize = readUnsignedShort();
440            int insSize = readUnsignedShort();
441            int outsSize = readUnsignedShort();
442            int triesSize = readUnsignedShort();
443            int debugInfoOffset = readInt();
444            int instructionsSize = readInt();
445            short[] instructions = readShortArray(instructionsSize);
446            Code.Try[] tries = new Code.Try[triesSize];
447            Code.CatchHandler[] catchHandlers = new Code.CatchHandler[0];
448            if (triesSize > 0) {
449                if (instructions.length % 2 == 1) {
450                    readShort(); // padding
451                }
452
453                for (int i = 0; i < triesSize; i++) {
454                    int startAddress = readInt();
455                    int instructionCount = readUnsignedShort();
456                    int handlerOffset = readUnsignedShort();
457                    tries[i] = new Code.Try(startAddress, instructionCount, handlerOffset);
458                }
459
460                int catchHandlersSize = readUleb128();
461                catchHandlers = new Code.CatchHandler[catchHandlersSize];
462                for (int i = 0; i < catchHandlersSize; i++) {
463                    catchHandlers[i] = readCatchHandler();
464                }
465            }
466            return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
467                    tries, catchHandlers);
468        }
469
470        private Code.CatchHandler readCatchHandler() {
471            int size = readSleb128();
472            int handlersCount = Math.abs(size);
473            int[] typeIndexes = new int[handlersCount];
474            int[] addresses = new int[handlersCount];
475            for (int i = 0; i < handlersCount; i++) {
476                typeIndexes[i] = readUleb128();
477                addresses[i] = readUleb128();
478            }
479            int catchAllAddress = size <= 0 ? readUleb128() : -1;
480            return new Code.CatchHandler(typeIndexes, addresses, catchAllAddress);
481        }
482
483        private ClassData readClassData() {
484            int staticFieldsSize = readUleb128();
485            int instanceFieldsSize = readUleb128();
486            int directMethodsSize = readUleb128();
487            int virtualMethodsSize = readUleb128();
488            ClassData.Field[] staticFields = readFields(staticFieldsSize);
489            ClassData.Field[] instanceFields = readFields(instanceFieldsSize);
490            ClassData.Method[] directMethods = readMethods(directMethodsSize);
491            ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize);
492            return new ClassData(staticFields, instanceFields, directMethods, virtualMethods);
493        }
494
495        private ClassData.Field[] readFields(int count) {
496            ClassData.Field[] result = new ClassData.Field[count];
497            int fieldIndex = 0;
498            for (int i = 0; i < count; i++) {
499                fieldIndex += readUleb128(); // field index diff
500                int accessFlags = readUleb128();
501                result[i] = new ClassData.Field(fieldIndex, accessFlags);
502            }
503            return result;
504        }
505
506        private ClassData.Method[] readMethods(int count) {
507            ClassData.Method[] result = new ClassData.Method[count];
508            int methodIndex = 0;
509            for (int i = 0; i < count; i++) {
510                methodIndex += readUleb128(); // method index diff
511                int accessFlags = readUleb128();
512                int codeOff = readUleb128();
513                result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff);
514            }
515            return result;
516        }
517
518        public Annotation readAnnotation() {
519            byte visibility = readByte();
520            int typeIndex = readUleb128();
521            int size = readUleb128();
522            int[] names = new int[size];
523            EncodedValue[] values = new EncodedValue[size];
524            for (int i = 0; i < size; i++) {
525                names[i] = readUleb128();
526                values[i] = readEncodedValue();
527            }
528            return new Annotation(DexBuffer.this, visibility, typeIndex, names, values);
529        }
530
531        public EncodedValue readEncodedValue() {
532            int start = position;
533            new EncodedValueReader(this).readValue();
534            int end = position;
535            return new EncodedValue(Arrays.copyOfRange(data, start, end));
536        }
537
538        public EncodedValue readEncodedArray() {
539            int start = position;
540            new EncodedValueReader(this).readArray();
541            int end = position;
542            return new EncodedValue(Arrays.copyOfRange(data, start, end));
543        }
544
545        private void ensureCapacity(int size) {
546            if (position + size > limit) {
547                throw new DexException("Section limit " + limit + " exceeded by " + name);
548            }
549        }
550
551        /**
552         * Writes 0x00 until the position is aligned to a multiple of 4.
553         */
554        public void alignToFourBytes() {
555            int unalignedCount = position;
556            position = DexBuffer.fourByteAlign(position);
557            for (int i = unalignedCount; i < position; i++) {
558                data[i] = 0;
559            }
560        }
561
562        public void assertFourByteAligned() {
563            if ((position & 3) != 0) {
564                throw new IllegalStateException("Not four byte aligned!");
565            }
566        }
567
568        public void write(byte[] bytes) {
569            ensureCapacity(bytes.length);
570            System.arraycopy(bytes, 0, data, position, bytes.length);
571            position += bytes.length;
572        }
573
574        public void writeByte(int b) {
575            ensureCapacity(1);
576            data[position++] = (byte) b;
577        }
578
579        public void writeShort(short i) {
580            ensureCapacity(2);
581            data[position    ] = (byte) i;
582            data[position + 1] = (byte) (i >>> 8);
583            position += 2;
584        }
585
586        public void writeUnsignedShort(int i) {
587            short s = (short) i;
588            if (i != (s & 0xffff)) {
589                throw new IllegalArgumentException("Expected an unsigned short: " + i);
590            }
591            writeShort(s);
592        }
593
594        public void write(short[] shorts) {
595            for (short s : shorts) {
596                writeShort(s);
597            }
598        }
599
600        public void writeInt(int i) {
601            ensureCapacity(4);
602            data[position    ] = (byte) i;
603            data[position + 1] = (byte) (i >>>  8);
604            data[position + 2] = (byte) (i >>> 16);
605            data[position + 3] = (byte) (i >>> 24);
606            position += 4;
607        }
608
609        public void writeUleb128(int i) {
610            try {
611                Leb128Utils.writeUnsignedLeb128(this, i);
612                ensureCapacity(0);
613            } catch (ArrayIndexOutOfBoundsException e) {
614                throw new DexException("Section limit " + limit + " exceeded by " + name);
615            }
616        }
617
618        public void writeUleb128p1(int i) {
619            writeUleb128(i + 1);
620        }
621
622        public void writeSleb128(int i) {
623            try {
624                Leb128Utils.writeSignedLeb128(this, i);
625                ensureCapacity(0);
626            } catch (ArrayIndexOutOfBoundsException e) {
627                throw new DexException("Section limit " + limit + " exceeded by " + name);
628            }
629        }
630
631        public void writeStringData(String value) {
632            try {
633                int length = value.length();
634                writeUleb128(length);
635                write(Mutf8.encode(value));
636                writeByte(0);
637            } catch (UTFDataFormatException e) {
638                throw new AssertionError();
639            }
640        }
641
642        public void writeTypeList(TypeList typeList) {
643            short[] types = typeList.getTypes();
644            writeInt(types.length);
645            for (short type : types) {
646                writeShort(type);
647            }
648            alignToFourBytes();
649        }
650
651        /**
652         * Returns the number of bytes remaining in this section.
653         */
654        public int remaining() {
655            return limit - position;
656        }
657
658        /**
659         * Returns the number of bytes used by this section.
660         */
661        public int used () {
662            return position - initialPosition;
663        }
664    }
665}
666