1/*
2 * Copyright 2012, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32package org.jf.dexlib2.dexbacked;
33
34import com.google.common.io.ByteStreams;
35import org.jf.dexlib2.Opcodes;
36import org.jf.dexlib2.ReferenceType;
37import org.jf.dexlib2.dexbacked.raw.*;
38import org.jf.dexlib2.dexbacked.reference.DexBackedFieldReference;
39import org.jf.dexlib2.dexbacked.reference.DexBackedMethodReference;
40import org.jf.dexlib2.dexbacked.reference.DexBackedStringReference;
41import org.jf.dexlib2.dexbacked.reference.DexBackedTypeReference;
42import org.jf.dexlib2.dexbacked.util.FixedSizeSet;
43import org.jf.dexlib2.iface.DexFile;
44import org.jf.dexlib2.iface.reference.Reference;
45import org.jf.dexlib2.util.DexUtil;
46import org.jf.util.ExceptionWithContext;
47
48import javax.annotation.Nonnull;
49import javax.annotation.Nullable;
50import java.io.IOException;
51import java.io.InputStream;
52import java.util.AbstractList;
53import java.util.List;
54import java.util.Set;
55
56public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
57    @Nonnull private final Opcodes opcodes;
58
59    private final int stringCount;
60    private final int stringStartOffset;
61    private final int typeCount;
62    private final int typeStartOffset;
63    private final int protoCount;
64    private final int protoStartOffset;
65    private final int fieldCount;
66    private final int fieldStartOffset;
67    private final int methodCount;
68    private final int methodStartOffset;
69    private final int classCount;
70    private final int classStartOffset;
71
72    protected DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) {
73        super(buf, offset);
74
75        this.opcodes = opcodes;
76
77        if (verifyMagic) {
78            DexUtil.verifyDexHeader(buf, offset);
79        }
80
81        stringCount = readSmallUint(HeaderItem.STRING_COUNT_OFFSET);
82        stringStartOffset = readSmallUint(HeaderItem.STRING_START_OFFSET);
83        typeCount = readSmallUint(HeaderItem.TYPE_COUNT_OFFSET);
84        typeStartOffset = readSmallUint(HeaderItem.TYPE_START_OFFSET);
85        protoCount = readSmallUint(HeaderItem.PROTO_COUNT_OFFSET);
86        protoStartOffset = readSmallUint(HeaderItem.PROTO_START_OFFSET);
87        fieldCount = readSmallUint(HeaderItem.FIELD_COUNT_OFFSET);
88        fieldStartOffset = readSmallUint(HeaderItem.FIELD_START_OFFSET);
89        methodCount = readSmallUint(HeaderItem.METHOD_COUNT_OFFSET);
90        methodStartOffset = readSmallUint(HeaderItem.METHOD_START_OFFSET);
91        classCount = readSmallUint(HeaderItem.CLASS_COUNT_OFFSET);
92        classStartOffset = readSmallUint(HeaderItem.CLASS_START_OFFSET);
93    }
94
95    public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull BaseDexBuffer buf) {
96        this(opcodes, buf.buf, buf.baseOffset);
97    }
98
99    public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset) {
100        this(opcodes, buf, offset, false);
101    }
102
103    public DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf) {
104        this(opcodes, buf, 0, true);
105    }
106
107    @Nonnull
108    public static DexBackedDexFile fromInputStream(@Nonnull Opcodes opcodes, @Nonnull InputStream is)
109            throws IOException {
110        DexUtil.verifyDexHeader(is);
111
112        byte[] buf = ByteStreams.toByteArray(is);
113        return new DexBackedDexFile(opcodes, buf, 0, false);
114    }
115
116    @Override @Nonnull public Opcodes getOpcodes() {
117        return opcodes;
118    }
119
120    // Will only be true for a dalvik-style odex file
121    public boolean isOdexFile() {
122        return false;
123    }
124
125    // Will be true for both a dalvik-style odex file, and an art-style odex file embedded in an oat file
126    public boolean hasOdexOpcodes() {
127        return false;
128    }
129
130    @Nonnull
131    @Override
132    public Set<? extends DexBackedClassDef> getClasses() {
133        return new FixedSizeSet<DexBackedClassDef>() {
134            @Nonnull
135            @Override
136            public DexBackedClassDef readItem(int index) {
137                return new DexBackedClassDef(DexBackedDexFile.this, getClassDefItemOffset(index));
138            }
139
140            @Override
141            public int size() {
142                return classCount;
143            }
144        };
145    }
146
147    public int getStringIdItemOffset(int stringIndex) {
148        if (stringIndex < 0 || stringIndex >= stringCount) {
149            throw new InvalidItemIndex(stringIndex, "String index out of bounds: %d", stringIndex);
150        }
151        return stringStartOffset + stringIndex*StringIdItem.ITEM_SIZE;
152    }
153
154    public int getTypeIdItemOffset(int typeIndex) {
155        if (typeIndex < 0 || typeIndex >= typeCount) {
156            throw new InvalidItemIndex(typeIndex, "Type index out of bounds: %d", typeIndex);
157        }
158        return typeStartOffset + typeIndex*TypeIdItem.ITEM_SIZE;
159    }
160
161    public int getFieldIdItemOffset(int fieldIndex) {
162        if (fieldIndex < 0 || fieldIndex >= fieldCount) {
163            throw new InvalidItemIndex(fieldIndex, "Field index out of bounds: %d", fieldIndex);
164        }
165        return fieldStartOffset + fieldIndex*FieldIdItem.ITEM_SIZE;
166    }
167
168    public int getMethodIdItemOffset(int methodIndex) {
169        if (methodIndex < 0 || methodIndex >= methodCount) {
170            throw new InvalidItemIndex(methodIndex, "Method index out of bounds: %d", methodIndex);
171        }
172        return methodStartOffset + methodIndex*MethodIdItem.ITEM_SIZE;
173    }
174
175    public int getProtoIdItemOffset(int protoIndex) {
176        if (protoIndex < 0 || protoIndex >= protoCount) {
177            throw new InvalidItemIndex(protoIndex, "Proto index out of bounds: %d", protoIndex);
178        }
179        return protoStartOffset + protoIndex*ProtoIdItem.ITEM_SIZE;
180    }
181
182    public int getClassDefItemOffset(int classIndex) {
183        if (classIndex < 0 || classIndex >= classCount) {
184            throw new InvalidItemIndex(classIndex, "Class index out of bounds: %d", classIndex);
185        }
186        return classStartOffset + classIndex*ClassDefItem.ITEM_SIZE;
187    }
188
189    public int getClassCount() {
190        return classCount;
191    }
192
193    public int getStringCount() {
194        return stringCount;
195    }
196
197    public int getTypeCount() {
198        return typeCount;
199    }
200
201    public int getProtoCount() {
202        return protoCount;
203    }
204
205    public int getFieldCount() {
206        return fieldCount;
207    }
208
209    public int getMethodCount() {
210        return methodCount;
211    }
212
213    @Nonnull
214    public String getString(int stringIndex) {
215        int stringOffset = getStringIdItemOffset(stringIndex);
216        int stringDataOffset = readSmallUint(stringOffset);
217        DexReader reader = readerAt(stringDataOffset);
218        int utf16Length = reader.readSmallUleb128();
219        return reader.readString(utf16Length);
220    }
221
222    @Nullable
223    public String getOptionalString(int stringIndex) {
224        if (stringIndex == -1) {
225            return null;
226        }
227        return getString(stringIndex);
228    }
229
230    @Nonnull
231    public String getType(int typeIndex) {
232        int typeOffset = getTypeIdItemOffset(typeIndex);
233        int stringIndex = readSmallUint(typeOffset);
234        return getString(stringIndex);
235    }
236
237    @Nullable
238    public String getOptionalType(int typeIndex) {
239        if (typeIndex == -1) {
240            return null;
241        }
242        return getType(typeIndex);
243    }
244
245    public List<DexBackedStringReference> getStrings() {
246        return new AbstractList<DexBackedStringReference>() {
247            @Override public DexBackedStringReference get(int index) {
248                if (index < 0 || index >= getStringCount()) {
249                    throw new IndexOutOfBoundsException();
250                }
251                return new DexBackedStringReference(DexBackedDexFile.this, index);
252            }
253
254            @Override public int size() {
255                return getStringCount();
256            }
257        };
258    }
259
260    public List<DexBackedTypeReference> getTypes() {
261        return new AbstractList<DexBackedTypeReference>() {
262            @Override public DexBackedTypeReference get(int index) {
263                if (index < 0 || index >= getTypeCount()) {
264                    throw new IndexOutOfBoundsException();
265                }
266                return new DexBackedTypeReference(DexBackedDexFile.this, index);
267            }
268
269            @Override public int size() {
270                return getTypeCount();
271            }
272        };
273    }
274
275    public List<DexBackedMethodReference> getMethods() {
276        return new AbstractList<DexBackedMethodReference>() {
277            @Override public DexBackedMethodReference get(int index) {
278                if (index < 0 || index >= getMethodCount()) {
279                    throw new IndexOutOfBoundsException();
280                }
281                return new DexBackedMethodReference(DexBackedDexFile.this, index);
282            }
283
284            @Override public int size() {
285                return getMethodCount();
286            }
287        };
288    }
289
290    public List<DexBackedFieldReference> getFields() {
291        return new AbstractList<DexBackedFieldReference>() {
292            @Override public DexBackedFieldReference get(int index) {
293                if (index < 0 || index >= getFieldCount()) {
294                    throw new IndexOutOfBoundsException();
295                }
296                return new DexBackedFieldReference(DexBackedDexFile.this, index);
297            }
298
299            @Override public int size() {
300                return getFieldCount();
301            }
302        };
303    }
304
305    public List<? extends Reference> getReferences(int referenceType) {
306        switch (referenceType) {
307            case ReferenceType.STRING:
308                return getStrings();
309            case ReferenceType.TYPE:
310                return getTypes();
311            case ReferenceType.METHOD:
312                return getMethods();
313            case ReferenceType.FIELD:
314                return getFields();
315            default:
316                throw new IllegalArgumentException(String.format("Invalid reference type: %d", referenceType));
317        }
318    }
319
320    @Override
321    @Nonnull
322    public DexReader readerAt(int offset) {
323        return new DexReader(this, offset);
324    }
325
326    public static class NotADexFile extends RuntimeException {
327        public NotADexFile() {
328        }
329
330        public NotADexFile(Throwable cause) {
331            super(cause);
332        }
333
334        public NotADexFile(String message) {
335            super(message);
336        }
337
338        public NotADexFile(String message, Throwable cause) {
339            super(message, cause);
340        }
341    }
342
343    public static class InvalidItemIndex extends ExceptionWithContext {
344        private final int itemIndex;
345
346        public InvalidItemIndex(int itemIndex) {
347            super("");
348            this.itemIndex = itemIndex;
349        }
350
351        public InvalidItemIndex(int itemIndex, String message, Object... formatArgs) {
352            super(message, formatArgs);
353            this.itemIndex = itemIndex;
354        }
355
356        public int getInvalidIndex() {
357            return itemIndex;
358        }
359    }
360}
361