Home | History | Annotate | Download | only in dexbacked
      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 
     32 package org.jf.dexlib2.dexbacked;
     33 
     34 import com.google.common.io.ByteStreams;
     35 import org.jf.dexlib2.Opcodes;
     36 import org.jf.dexlib2.ReferenceType;
     37 import org.jf.dexlib2.dexbacked.raw.*;
     38 import org.jf.dexlib2.dexbacked.reference.DexBackedFieldReference;
     39 import org.jf.dexlib2.dexbacked.reference.DexBackedMethodReference;
     40 import org.jf.dexlib2.dexbacked.reference.DexBackedStringReference;
     41 import org.jf.dexlib2.dexbacked.reference.DexBackedTypeReference;
     42 import org.jf.dexlib2.dexbacked.util.FixedSizeSet;
     43 import org.jf.dexlib2.iface.DexFile;
     44 import org.jf.dexlib2.iface.reference.Reference;
     45 import org.jf.dexlib2.util.DexUtil;
     46 import org.jf.util.ExceptionWithContext;
     47 
     48 import javax.annotation.Nonnull;
     49 import javax.annotation.Nullable;
     50 import java.io.IOException;
     51 import java.io.InputStream;
     52 import java.util.AbstractList;
     53 import java.util.List;
     54 import java.util.Set;
     55 
     56 public 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