Home | History | Annotate | Download | only in dexlib
      1 /*
      2  * [The "BSD licence"]
      3  * Copyright (c) 2010 Ben Gruver (JesusFreke)
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 package org.jf.dexlib;
     30 
     31 import org.jf.dexlib.EncodedValue.ArrayEncodedSubValue;
     32 import org.jf.dexlib.EncodedValue.EncodedValue;
     33 import org.jf.dexlib.Util.AccessFlags;
     34 import org.jf.dexlib.Util.AnnotatedOutput;
     35 import org.jf.dexlib.Util.Input;
     36 import org.jf.dexlib.Util.TypeUtils;
     37 
     38 import javax.annotation.Nonnull;
     39 import javax.annotation.Nullable;
     40 import java.util.*;
     41 
     42 public class ClassDefItem extends Item<ClassDefItem> {
     43     private TypeIdItem classType;
     44     private int accessFlags;
     45     private @Nullable TypeIdItem superType;
     46     private @Nullable TypeListItem implementedInterfaces;
     47     private @Nullable StringIdItem sourceFile;
     48     private @Nullable AnnotationDirectoryItem annotations;
     49     private @Nullable ClassDataItem classData;
     50     private @Nullable EncodedArrayItem staticFieldInitializers;
     51 
     52     /**
     53      * Creates a new uninitialized <code>ClassDefItem</code>
     54      * @param dexFile The <code>DexFile</code> that this item belongs to
     55      */
     56     protected ClassDefItem(DexFile dexFile) {
     57         super(dexFile);
     58     }
     59 
     60     /**
     61      * Creates a new <code>ClassDefItem</code> with the given values
     62      * @param dexFile The <code>DexFile</code> that this item belongs to
     63      * @param classType The type of this class
     64      * @param accessFlags The access flags of this class
     65      * @param superType The superclass of this class, or null if none (only valid for java.lang.Object)
     66      * @param implementedInterfaces A list of the interfaces that this class implements, or null if none
     67      * @param sourceFile The main source file that this class is defined in, or null if not available
     68      * @param annotations The annotations for this class and its fields, methods and method parameters, or null if none
     69      * @param classData The <code>ClassDataItem</code> containing the method and field definitions for this class
     70      * @param staticFieldInitializers The initial values for this class's static fields, or null if none. The initial
     71      * values should be in the same order as the static fields in the <code>ClassDataItem</code>. It can contain
     72      * fewer items than static fields, in which case the remaining static fields will be initialized with a default
     73      * value of null/0. The initial value for any fields that don't specifically have a value can be either the
     74      * type-appropriate null/0 encoded value, or null.
     75      */
     76     private ClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, @Nullable TypeIdItem superType,
     77                          @Nullable TypeListItem implementedInterfaces, @Nullable StringIdItem sourceFile,
     78                          @Nullable AnnotationDirectoryItem annotations, @Nullable ClassDataItem classData,
     79                          @Nullable EncodedArrayItem staticFieldInitializers) {
     80         super(dexFile);
     81         assert classType != null;
     82         this.classType = classType;
     83         this.accessFlags = accessFlags;
     84         this.superType = superType;
     85         this.implementedInterfaces = implementedInterfaces;
     86         this.sourceFile = sourceFile;
     87         this.annotations = annotations;
     88         this.classData = classData;
     89         this.staticFieldInitializers = staticFieldInitializers;
     90     }
     91 
     92     /**
     93      * Returns a <code>ClassDefItem</code> for the given values, and that has been interned into the given
     94      * <code>DexFile</code>
     95      * @param dexFile The <code>DexFile</code> that this item belongs to
     96      * @param classType The type of this class
     97      * @param accessFlags The access flags of this class
     98      * @param superType The superclass of this class, or null if none (only valid for java.lang.Object)
     99      * @param implementedInterfaces A list of the interfaces that this class implements, or null if none
    100      * @param sourceFile The main source file that this class is defined in, or null if not available
    101      * @param annotations The annotations for this class and its fields, methods and method parameters, or null if none
    102      * @param classData The <code>ClassDataItem</code> containing the method and field definitions for this class
    103      * @param staticFieldInitializers The initial values for this class's static fields, or null if none. If it is not
    104      * null, it must contain the same number of items as the number of static fields in this class. The value in the
    105      * <code>StaticFieldInitializer</code> for any field that doesn't have an explicit initial value can either be null
    106      * or be the type-appropriate null/0 value.
    107      * @return a <code>ClassDefItem</code> for the given values, and that has been interned into the given
    108      * <code>DexFile</code>
    109      */
    110     public static ClassDefItem internClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags,
    111                          @Nullable TypeIdItem superType, @Nullable TypeListItem implementedInterfaces,
    112                          @Nullable StringIdItem sourceFile, @Nullable AnnotationDirectoryItem annotations,
    113                          @Nullable ClassDataItem classData,
    114                          @Nullable List<StaticFieldInitializer> staticFieldInitializers) {
    115         EncodedArrayItem encodedArrayItem = null;
    116         if(!dexFile.getInplace() && staticFieldInitializers != null && staticFieldInitializers.size() > 0) {
    117             assert classData != null;
    118             assert staticFieldInitializers.size() == classData.getStaticFieldCount();
    119             encodedArrayItem = makeStaticFieldInitializersItem(dexFile, staticFieldInitializers);
    120         }
    121 
    122         ClassDefItem classDefItem = new ClassDefItem(dexFile, classType, accessFlags, superType, implementedInterfaces,
    123                 sourceFile, annotations, classData, encodedArrayItem);
    124         return dexFile.ClassDefsSection.intern(classDefItem);
    125     }
    126 
    127     /** {@inheritDoc} */
    128     protected void readItem(Input in, ReadContext readContext) {
    129         classType = dexFile.TypeIdsSection.getItemByIndex(in.readInt());
    130         accessFlags = in.readInt();
    131         superType = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readInt());
    132         implementedInterfaces = (TypeListItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_TYPE_LIST,
    133                 in.readInt());
    134         sourceFile = dexFile.StringIdsSection.getOptionalItemByIndex(in.readInt());
    135         annotations = (AnnotationDirectoryItem)readContext.getOptionalOffsettedItemByOffset(
    136                 ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM, in.readInt());
    137         classData = (ClassDataItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CLASS_DATA_ITEM, in.readInt());
    138         staticFieldInitializers = (EncodedArrayItem)readContext.getOptionalOffsettedItemByOffset(
    139                 ItemType.TYPE_ENCODED_ARRAY_ITEM, in.readInt());
    140     }
    141 
    142     /** {@inheritDoc} */
    143     protected int placeItem(int offset) {
    144         return offset + 32;
    145     }
    146 
    147     /** {@inheritDoc} */
    148     protected void writeItem(AnnotatedOutput out) {
    149         if (out.annotates()) {
    150             out.annotate(4, "class_type: " + classType.getTypeDescriptor());
    151             out.annotate(4, "access_flags: " + AccessFlags.formatAccessFlagsForClass(accessFlags));
    152             out.annotate(4, "superclass_type: " + (superType==null?"":superType.getTypeDescriptor()));
    153             out.annotate(4, "interfaces: " +
    154                     (implementedInterfaces==null?"":implementedInterfaces.getTypeListString(" ")));
    155             out.annotate(4, "source_file: " + (sourceFile==null?"":sourceFile.getStringValue()));
    156             out.annotate(4, "annotations_off: " +
    157                     (annotations==null?"":"0x"+Integer.toHexString(annotations.getOffset())));
    158             out.annotate(4, "class_data_off:" +
    159                     (classData==null?"":"0x"+Integer.toHexString(classData.getOffset())));
    160             out.annotate(4, "static_values_off: " +
    161                     (staticFieldInitializers==null?"":"0x"+Integer.toHexString(staticFieldInitializers.getOffset())));
    162         }
    163         out.writeInt(classType.getIndex());
    164         out.writeInt(accessFlags);
    165         out.writeInt(superType==null?-1:superType.getIndex());
    166         out.writeInt(implementedInterfaces==null?0:implementedInterfaces.getOffset());
    167         out.writeInt(sourceFile==null?-1:sourceFile.getIndex());
    168         out.writeInt(annotations==null?0:annotations.getOffset());
    169         out.writeInt(classData==null?0:classData.getOffset());
    170         out.writeInt(staticFieldInitializers==null?0:staticFieldInitializers.getOffset());
    171     }
    172 
    173     /** {@inheritDoc} */
    174     public ItemType getItemType() {
    175         return ItemType.TYPE_CLASS_DEF_ITEM;
    176     }
    177 
    178     /** {@inheritDoc} */
    179     public String getConciseIdentity() {
    180         return "class_def_item: " + classType.getTypeDescriptor();
    181     }
    182 
    183     /** {@inheritDoc} */
    184     public int compareTo(ClassDefItem o) {
    185         //The actual sorting for this class is done during the placement phase, in ClassDefPlacer.
    186         //This method is just used for sorting the associated ClassDataItem items after the ClassDefItems have been
    187         //placed, so we can just do the comparison based on the offsets
    188         return this.getOffset() - o.getOffset();
    189     }
    190 
    191     public TypeIdItem getClassType() {
    192         return classType;
    193     }
    194 
    195     public int getAccessFlags() {
    196         return accessFlags;
    197     }
    198 
    199     @Nullable
    200     public TypeIdItem getSuperclass() {
    201         return superType;
    202     }
    203 
    204     @Nullable
    205     public TypeListItem getInterfaces() {
    206         return implementedInterfaces;
    207     }
    208 
    209     @Nullable
    210     public StringIdItem getSourceFile() {
    211         return sourceFile;
    212     }
    213 
    214     @Nullable
    215     public AnnotationDirectoryItem getAnnotations() {
    216         return annotations;
    217     }
    218 
    219     @Nullable
    220     public ClassDataItem getClassData() {
    221         return classData;
    222     }
    223 
    224     @Nullable
    225     public EncodedArrayItem getStaticFieldInitializers() {
    226         return staticFieldInitializers;
    227     }
    228 
    229     public static int placeClassDefItems(IndexedSection<ClassDefItem> section, int offset) {
    230         ClassDefPlacer cdp = new ClassDefPlacer(section);
    231         return cdp.placeSection(offset);
    232     }
    233 
    234     /**
    235      * This class places the items within a ClassDefItem section, such that superclasses and interfaces are
    236      * placed before sub/implementing classes
    237      */
    238     private static class ClassDefPlacer {
    239         private final IndexedSection<ClassDefItem> section;
    240         private final HashMap<TypeIdItem, ClassDefItem> unplacedClassDefsByType =
    241                 new HashMap<TypeIdItem, ClassDefItem>();
    242 
    243         private int currentIndex = 0;
    244         private int currentOffset;
    245 
    246         public ClassDefPlacer(IndexedSection<ClassDefItem> section) {
    247             this.section = section;
    248 
    249             for (ClassDefItem classDefItem: section.items) {
    250                 TypeIdItem typeIdItem = classDefItem.classType;
    251                 unplacedClassDefsByType.put(typeIdItem, classDefItem);
    252             }
    253         }
    254 
    255         public int placeSection(int offset) {
    256             currentOffset = offset;
    257 
    258             if (section.DexFile.getSortAllItems()) {
    259                 //presort the list, to guarantee a unique ordering
    260                 Collections.sort(section.items, new Comparator<ClassDefItem>() {
    261                     public int compare(ClassDefItem a, ClassDefItem b) {
    262                         return a.getClassType().compareTo(b.getClassType());
    263                     }
    264                 });
    265             }
    266 
    267             //we need to initialize the offset for all the classes to -1, so we can tell which ones
    268             //have been placed
    269             for (ClassDefItem classDefItem: section.items) {
    270                 classDefItem.offset = -1;
    271             }
    272 
    273             for (ClassDefItem classDefItem: section.items) {
    274                 placeClass(classDefItem);
    275             }
    276 
    277             for (ClassDefItem classDefItem: unplacedClassDefsByType.values()) {
    278                 section.items.set(classDefItem.getIndex(), classDefItem);
    279             }
    280 
    281             return currentOffset;
    282         }
    283 
    284         private void placeClass(ClassDefItem classDefItem) {
    285             if (!classDefItem.isPlaced()) {
    286                 TypeIdItem superType = classDefItem.superType;
    287                 ClassDefItem superClassDefItem = unplacedClassDefsByType.get(superType);
    288 
    289                 if (superClassDefItem != null) {
    290                     placeClass(superClassDefItem);
    291                 }
    292 
    293                 TypeListItem interfaces = classDefItem.implementedInterfaces;
    294 
    295                 if (interfaces != null) {
    296                     for (TypeIdItem interfaceType: interfaces.getTypes()) {
    297                         ClassDefItem interfaceClass = unplacedClassDefsByType.get(interfaceType);
    298                         if (interfaceClass != null) {
    299                             placeClass(interfaceClass);
    300                         }
    301                     }
    302                 }
    303 
    304                 currentOffset = classDefItem.placeAt(currentOffset, currentIndex++);
    305                 unplacedClassDefsByType.remove(classDefItem.classType);
    306             }
    307         }
    308     }
    309 
    310     public static class StaticFieldInitializer implements Comparable<StaticFieldInitializer> {
    311         public final EncodedValue value;
    312         public final ClassDataItem.EncodedField field;
    313         public StaticFieldInitializer(EncodedValue value, ClassDataItem.EncodedField field) {
    314             this.value = value;
    315             this.field = field;
    316         }
    317 
    318         public int compareTo(StaticFieldInitializer other) {
    319             return field.compareTo(other.field);
    320         }
    321     }
    322 
    323 
    324     /**
    325      * A helper method to sort the static field initializers and populate the default values as needed
    326      * @param dexFile the <code>DexFile</code>
    327      * @param staticFieldInitializers the initial values
    328      * @return an interned EncodedArrayItem containing the static field initializers
    329      */
    330     private static EncodedArrayItem makeStaticFieldInitializersItem(DexFile dexFile,
    331             @Nonnull List<StaticFieldInitializer> staticFieldInitializers) {
    332         if (staticFieldInitializers.size() == 0) {
    333             return null;
    334         }
    335 
    336         int len = staticFieldInitializers.size();
    337 
    338         // make a copy before sorting. we don't want to modify the list passed to us
    339         staticFieldInitializers = new ArrayList<StaticFieldInitializer>(staticFieldInitializers);
    340         Collections.sort(staticFieldInitializers);
    341 
    342         int lastIndex = -1;
    343         for (int i=len-1; i>=0; i--) {
    344             StaticFieldInitializer staticFieldInitializer = staticFieldInitializers.get(i);
    345 
    346             if (staticFieldInitializer.value != null &&
    347                     (staticFieldInitializer.value.compareTo(TypeUtils.makeDefaultValueForType(
    348                     staticFieldInitializer.field.field.getFieldType())) != 0)) {
    349                 lastIndex = i;
    350                 break;
    351             }
    352         }
    353 
    354         //we don't have any non-null/non-default values, so we don't need to create an EncodedArrayItem
    355         if (lastIndex == -1) {
    356             return null;
    357         }
    358 
    359         EncodedValue[] values = new EncodedValue[lastIndex+1];
    360 
    361         for (int i=0; i<=lastIndex; i++) {
    362             StaticFieldInitializer staticFieldInitializer = staticFieldInitializers.get(i);
    363             EncodedValue encodedValue = staticFieldInitializer.value;
    364             if (encodedValue == null) {
    365                 encodedValue = TypeUtils.makeDefaultValueForType(staticFieldInitializer.field.field.getFieldType());
    366             }
    367 
    368             values[i] = encodedValue;
    369         }
    370 
    371         ArrayEncodedSubValue encodedArrayValue = new ArrayEncodedSubValue(values);
    372         return EncodedArrayItem.internEncodedArrayItem(dexFile, encodedArrayValue);
    373     }
    374 }
    375