DexBuffer.java revision fa3b293a068fd521a6ba6019a051ad502dfaca55
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
308        private Section(String name, int position, int limit) {
309            this.name = name;
310            this.position = position;
311            this.limit = limit;
312        }
313
314        private Section(int position) {
315            this("section", position, data.length);
316        }
317
318        public int getPosition() {
319            return position;
320        }
321
322        public int readInt() {
323            int result = (data[position] & 0xff)
324                    | (data[position + 1] & 0xff) << 8
325                    | (data[position + 2] & 0xff) << 16
326                    | (data[position + 3] & 0xff) << 24;
327            position += 4;
328            return result;
329        }
330
331        public short readShort() {
332            int result = (data[position] & 0xff)
333                    | (data[position + 1] & 0xff) << 8;
334            position += 2;
335            return (short) result;
336        }
337
338        public int readUnsignedShort() {
339            return readShort() & 0xffff;
340        }
341
342        public byte readByte() {
343            return (byte) (data[position++] & 0xff);
344        }
345
346        public byte[] readByteArray(int length) {
347            byte[] result = Arrays.copyOfRange(data, position, position + length);
348            position += length;
349            return result;
350        }
351
352        public short[] readShortArray(int length) {
353            short[] result = new short[length];
354            for (int i = 0; i < length; i++) {
355                result[i] = readShort();
356            }
357            return result;
358        }
359
360        public int readUleb128() {
361            return Leb128Utils.readUnsignedLeb128(this);
362        }
363
364        public int readSleb128() {
365            return Leb128Utils.readSignedLeb128(this);
366        }
367
368        public TypeList readTypeList() {
369            int size = readInt();
370            short[] types = new short[size];
371            for (int i = 0; i < size; i++) {
372                types[i] = readShort();
373            }
374            alignToFourBytes();
375            return new TypeList(DexBuffer.this, types);
376        }
377
378        public String readString() {
379            int offset = readInt();
380            int savedPosition = position;
381            position = offset;
382            try {
383                int expectedLength = readUleb128();
384                String result = Mutf8.decode(this, new char[expectedLength]);
385                if (result.length() != expectedLength) {
386                    throw new DexException("Declared length " + expectedLength
387                            + " doesn't match decoded length of " + result.length());
388                }
389                return result;
390            } catch (UTFDataFormatException e) {
391                throw new DexException(e);
392            } finally {
393                position = savedPosition;
394            }
395        }
396
397        public FieldId readFieldId() {
398            int declaringClassIndex = readUnsignedShort();
399            int typeIndex = readUnsignedShort();
400            int nameIndex = readInt();
401            return new FieldId(DexBuffer.this, declaringClassIndex, typeIndex, nameIndex);
402        }
403
404        public MethodId readMethodId() {
405            int declaringClassIndex = readUnsignedShort();
406            int protoIndex = readUnsignedShort();
407            int nameIndex = readInt();
408            return new MethodId(DexBuffer.this, declaringClassIndex, protoIndex, nameIndex);
409        }
410
411        public ProtoId readProtoId() {
412            int shortyIndex = readInt();
413            int returnTypeIndex = readInt();
414            int parametersOffset = readInt();
415            return new ProtoId(DexBuffer.this, shortyIndex, returnTypeIndex, parametersOffset);
416        }
417
418        public ClassDef readClassDef() {
419            int offset = getPosition();
420            int type = readInt();
421            int accessFlags = readInt();
422            int supertype = readInt();
423            int interfacesOffset = readInt();
424            int sourceFileIndex = readInt();
425            int annotationsOffset = readInt();
426            int classDataOffset = readInt();
427            int staticValuesOffset = readInt();
428            return new ClassDef(DexBuffer.this, offset, type, accessFlags, supertype,
429                    interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset,
430                    staticValuesOffset);
431        }
432
433        private Code readCode() {
434            int registersSize = readUnsignedShort();
435            int insSize = readUnsignedShort();
436            int outsSize = readUnsignedShort();
437            int triesSize = readUnsignedShort();
438            int debugInfoOffset = readInt();
439            int instructionsSize = readInt();
440            short[] instructions = readShortArray(instructionsSize);
441            Code.Try[] tries = new Code.Try[triesSize];
442            Code.CatchHandler[] catchHandlers = new Code.CatchHandler[0];
443            if (triesSize > 0) {
444                if (instructions.length % 2 == 1) {
445                    readShort(); // padding
446                }
447
448                for (int i = 0; i < triesSize; i++) {
449                    int startAddress = readInt();
450                    int instructionCount = readUnsignedShort();
451                    int handlerOffset = readUnsignedShort();
452                    tries[i] = new Code.Try(startAddress, instructionCount, handlerOffset);
453                }
454
455                int catchHandlersSize = readUleb128();
456                catchHandlers = new Code.CatchHandler[catchHandlersSize];
457                for (int i = 0; i < catchHandlersSize; i++) {
458                    catchHandlers[i] = readCatchHandler();
459                }
460            }
461            return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
462                    tries, catchHandlers);
463        }
464
465        private Code.CatchHandler readCatchHandler() {
466            int size = readSleb128();
467            int handlersCount = Math.abs(size);
468            int[] typeIndexes = new int[handlersCount];
469            int[] addresses = new int[handlersCount];
470            for (int i = 0; i < handlersCount; i++) {
471                typeIndexes[i] = readUleb128();
472                addresses[i] = readUleb128();
473            }
474            int catchAllAddress = size <= 0 ? readUleb128() : -1;
475            return new Code.CatchHandler(typeIndexes, addresses, catchAllAddress);
476        }
477
478        private ClassData readClassData() {
479            int staticFieldsSize = readUleb128();
480            int instanceFieldsSize = readUleb128();
481            int directMethodsSize = readUleb128();
482            int virtualMethodsSize = readUleb128();
483            ClassData.Field[] staticFields = readFields(staticFieldsSize);
484            ClassData.Field[] instanceFields = readFields(instanceFieldsSize);
485            ClassData.Method[] directMethods = readMethods(directMethodsSize);
486            ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize);
487            return new ClassData(staticFields, instanceFields, directMethods, virtualMethods);
488        }
489
490        private ClassData.Field[] readFields(int count) {
491            ClassData.Field[] result = new ClassData.Field[count];
492            int fieldIndex = 0;
493            for (int i = 0; i < count; i++) {
494                fieldIndex += readUleb128(); // field index diff
495                int accessFlags = readUleb128();
496                result[i] = new ClassData.Field(fieldIndex, accessFlags);
497            }
498            return result;
499        }
500
501        private ClassData.Method[] readMethods(int count) {
502            ClassData.Method[] result = new ClassData.Method[count];
503            int methodIndex = 0;
504            for (int i = 0; i < count; i++) {
505                methodIndex += readUleb128(); // method index diff
506                int accessFlags = readUleb128();
507                int codeOff = readUleb128();
508                result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff);
509            }
510            return result;
511        }
512
513        public Annotation readAnnotation() {
514            byte visibility = readByte();
515            int typeIndex = readUleb128();
516            int size = readUleb128();
517            int[] names = new int[size];
518            EncodedValue[] values = new EncodedValue[size];
519            for (int i = 0; i < size; i++) {
520                names[i] = readUleb128();
521                values[i] = readEncodedValue();
522            }
523            return new Annotation(DexBuffer.this, visibility, typeIndex, names, values);
524        }
525
526        public EncodedValue readEncodedValue() {
527            int start = position;
528            new EncodedValueReader(this).readValue();
529            int end = position;
530            return new EncodedValue(Arrays.copyOfRange(data, start, end));
531        }
532
533        public EncodedValue readEncodedArray() {
534            int start = position;
535            new EncodedValueReader(this).readArray();
536            int end = position;
537            return new EncodedValue(Arrays.copyOfRange(data, start, end));
538        }
539
540        private void ensureCapacity(int size) {
541            if (position + size > limit) {
542                throw new DexException("Section limit " + limit + " exceeded by " + name);
543            }
544        }
545
546        /**
547         * Writes 0x00 until the position is aligned to a multiple of 4.
548         */
549        public void alignToFourBytes() {
550            int unalignedCount = position;
551            position = DexBuffer.fourByteAlign(position);
552            for (int i = unalignedCount; i < position; i++) {
553                data[i] = 0;
554            }
555        }
556
557        public void assertFourByteAligned() {
558            if ((position & 3) != 0) {
559                throw new IllegalStateException("Not four byte aligned!");
560            }
561        }
562
563        public void write(byte[] bytes) {
564            ensureCapacity(bytes.length);
565            System.arraycopy(bytes, 0, data, position, bytes.length);
566            position += bytes.length;
567        }
568
569        public void writeByte(int b) {
570            ensureCapacity(1);
571            data[position++] = (byte) b;
572        }
573
574        public void writeShort(short i) {
575            ensureCapacity(2);
576            data[position    ] = (byte) i;
577            data[position + 1] = (byte) (i >>> 8);
578            position += 2;
579        }
580
581        public void writeUnsignedShort(int i) {
582            short s = (short) i;
583            if (i != (s & 0xffff)) {
584                throw new IllegalArgumentException("Expected an unsigned short: " + i);
585            }
586            writeShort(s);
587        }
588
589        public void write(short[] shorts) {
590            for (short s : shorts) {
591                writeShort(s);
592            }
593        }
594
595        public void writeInt(int i) {
596            ensureCapacity(4);
597            data[position    ] = (byte) i;
598            data[position + 1] = (byte) (i >>>  8);
599            data[position + 2] = (byte) (i >>> 16);
600            data[position + 3] = (byte) (i >>> 24);
601            position += 4;
602        }
603
604        public void writeUleb128(int i) {
605            try {
606                Leb128Utils.writeUnsignedLeb128(this, i);
607                ensureCapacity(0);
608            } catch (ArrayIndexOutOfBoundsException e) {
609                throw new DexException("Section limit " + limit + " exceeded by " + name);
610            }
611        }
612
613        public void writeSleb128(int i) {
614            try {
615                Leb128Utils.writeSignedLeb128(this, i);
616                ensureCapacity(0);
617            } catch (ArrayIndexOutOfBoundsException e) {
618                throw new DexException("Section limit " + limit + " exceeded by " + name);
619            }
620        }
621
622        public void writeStringData(String value) {
623            try {
624                int length = value.length();
625                writeUleb128(length);
626                write(Mutf8.encode(value));
627                writeByte(0);
628            } catch (UTFDataFormatException e) {
629                throw new AssertionError();
630            }
631        }
632
633        public void writeTypeList(TypeList typeList) {
634            short[] types = typeList.getTypes();
635            writeInt(types.length);
636            for (short type : types) {
637                writeShort(type);
638            }
639            alignToFourBytes();
640        }
641
642        /**
643         * Returns the number of bytes remaining in this section.
644         */
645        public int remaining() {
646            return limit - position;
647        }
648    }
649}
650