ClassDefItem.java revision d320d6ce8c8b9ea0aa1c661fc4219e4679c1e196
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
29package org.jf.dexlib;
30
31import org.jf.dexlib.EncodedValue.ArrayEncodedSubValue;
32import org.jf.dexlib.EncodedValue.EncodedValue;
33import org.jf.dexlib.Util.AccessFlags;
34import org.jf.dexlib.Util.AnnotatedOutput;
35import org.jf.dexlib.Util.Input;
36import org.jf.dexlib.Util.TypeUtils;
37
38import java.util.Collections;
39import java.util.Comparator;
40import java.util.HashMap;
41import java.util.List;
42
43public class ClassDefItem extends Item<ClassDefItem> {
44    private TypeIdItem classType;
45    private int accessFlags;
46    private TypeIdItem superType;
47    private TypeListItem implementedInterfaces;
48    private StringIdItem sourceFile;
49    private AnnotationDirectoryItem annotations;
50    private ClassDataItem classData;
51    private EncodedArrayItem staticFieldInitializers;
52
53    /**
54     * Creates a new uninitialized <code>ClassDefItem</code>
55     * @param dexFile The <code>DexFile</code> that this item belongs to
56     */
57    protected ClassDefItem(DexFile dexFile) {
58        super(dexFile);
59    }
60
61    /**
62     * Creates a new <code>ClassDefItem</code> with the given values
63     * @param dexFile The <code>DexFile</code> that this item belongs to
64     * @param classType The type of this class
65     * @param accessFlags The access flags of this class
66     * @param superType The superclass of this class, or null if none (only valid for java.lang.Object)
67     * @param implementedInterfaces A list of the interfaces that this class implements, or null if none
68     * @param sourceFile The main source file that this class is defined in, or null if not available
69     * @param annotations The annotations for this class and its fields, methods and method parameters, or null if none
70     * @param classData The <code>ClassDataItem</code> containing the method and field definitions for this class
71     * @param staticFieldInitializers The initial values for this class's static fields, or null if none. The initial
72     * values should be in the same order as the static fields in the <code>ClassDataItem</code>. It can contain
73     * fewer items than static fields, in which case the remaining static fields will be initialized with a default
74     * value of null/0. The initial value for any fields that don't specifically have a value can be either the
75     * type-appropriate null/0 encoded value, or null.
76     */
77    private ClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, TypeIdItem superType,
78                         TypeListItem implementedInterfaces, StringIdItem sourceFile,
79                         AnnotationDirectoryItem annotations, ClassDataItem classData,
80                         EncodedArrayItem staticFieldInitializers) {
81        super(dexFile);
82        assert classType != null;
83        this.classType = classType;
84        this.accessFlags = accessFlags;
85        this.superType = superType;
86        this.implementedInterfaces = implementedInterfaces;
87        this.sourceFile = sourceFile;
88        this.annotations = annotations;
89        this.classData = classData;
90        this.staticFieldInitializers = staticFieldInitializers;
91
92        if (classData != null) {
93            classData.setParent(this);
94        }
95        if (annotations != null) {
96            annotations.setParent(this);
97        }
98    }
99
100    /**
101     * Returns a <code>ClassDefItem</code> for the given values, and that has been interned into the given
102     * <code>DexFile</code>
103     * @param dexFile The <code>DexFile</code> that this item belongs to
104     * @param classType The type of this class
105     * @param accessFlags The access flags of this class
106     * @param superType The superclass of this class, or null if none (only valid for java.lang.Object)
107     * @param implementedInterfaces A list of the interfaces that this class implements, or null if none
108     * @param sourceFile The main source file that this class is defined in, or null if not available
109     * @param annotations The annotations for this class and its fields, methods and method parameters, or null if none
110     * @param classData The <code>ClassDataItem</code> containing the method and field definitions for this class
111     * @param staticFieldInitializers The initial values for this class's static fields, or null if none. If it is not
112     * null, it must contain the same number of items as the number of static fields in this class. The value in the
113     * <code>StaticFieldInitializer</code> for any field that doesn't have an explicit initial value can either be null
114     * or be the type-appropriate null/0 value.
115     * @return a <code>ClassDefItem</code> for the given values, and that has been interned into the given
116     * <code>DexFile</code>
117     */
118    public static ClassDefItem internClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags,
119                         TypeIdItem superType, TypeListItem implementedInterfaces, StringIdItem sourceFile,
120                         AnnotationDirectoryItem annotations, ClassDataItem classData,
121                         List<StaticFieldInitializer> staticFieldInitializers) {
122        EncodedArrayItem encodedArrayItem = null;
123        if(!dexFile.getInplace() && staticFieldInitializers != null && staticFieldInitializers.size() > 0) {
124            assert classData != null;
125            assert staticFieldInitializers.size() == classData.getStaticFields().length;
126            encodedArrayItem = makeStaticFieldInitializersItem(dexFile, staticFieldInitializers);
127        }
128
129        ClassDefItem classDefItem = new ClassDefItem(dexFile, classType, accessFlags, superType, implementedInterfaces,
130                sourceFile, annotations, classData, encodedArrayItem);
131        return dexFile.ClassDefsSection.intern(classDefItem);
132    }
133
134    /**
135     * Looks up a <code>ClassDefItem</code> from the given <code>DexFile</code> for the given
136     * values
137     * @param dexFile The <code>DexFile</code> that the <code>ClassDefItem</code> belongs to
138     * @param classType The type of the class
139     * @param accessFlags The access flags of the class
140     * @param superType The superclass of the class, or null if none (only valid for java.lang.Object)
141     * @param implementedInterfaces A list of the interfaces that the class implements, or null if none
142     * @param sourceFile The main source file that the class is defined in, or null if not available
143     * @param annotations The annotations for the class and its fields, methods and method parameters, or null if none
144     * @param classData The <code>ClassDataItem</code> containing the method and field definitions for the class
145     * @param staticFieldInitializers The initial values for the class's static fields, or null if none. If it is not
146     * null, it must contain the same number of items as the number of static fields in the class. The value in the
147     * <code>StaticFieldInitializer</code> for any field that doesn't have an explicit initial value can either be null
148     * or be the type-appropriate null/0 value.
149     * @return a <code>ClassDefItem</code> from the given <code>DexFile</code> for the given
150     * values, or null if it doesn't exist
151     */
152    public static ClassDefItem lookupClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags,
153                         TypeIdItem superType, TypeListItem implementedInterfaces, StringIdItem sourceFile,
154                         AnnotationDirectoryItem annotations, ClassDataItem classData,
155                         List<StaticFieldInitializer> staticFieldInitializers) {
156        EncodedArrayItem encodedArrayItem = null;
157        if(!dexFile.getInplace() && staticFieldInitializers != null && staticFieldInitializers.size() > 0) {
158            assert classData != null;
159            assert staticFieldInitializers.size() == classData.getStaticFields().length;
160            encodedArrayItem = makeStaticFieldInitializersItem(dexFile, staticFieldInitializers);
161        }
162
163        ClassDefItem classDefItem = new ClassDefItem(dexFile, classType, accessFlags, superType, implementedInterfaces,
164                sourceFile, annotations, classData, encodedArrayItem);
165        return dexFile.ClassDefsSection.getInternedItem(classDefItem);
166    }
167
168    /** {@inheritDoc} */
169    protected void readItem(Input in, ReadContext readContext) {
170        classType = dexFile.TypeIdsSection.getItemByIndex(in.readInt());
171        accessFlags = in.readInt();
172        superType = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readInt());
173        implementedInterfaces = (TypeListItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_TYPE_LIST,
174                in.readInt());
175        sourceFile = dexFile.StringIdsSection.getOptionalItemByIndex(in.readInt());
176        annotations = (AnnotationDirectoryItem)readContext.getOptionalOffsettedItemByOffset(
177                ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM, in.readInt());
178        classData = (ClassDataItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CLASS_DATA_ITEM, in.readInt());
179        staticFieldInitializers = (EncodedArrayItem)readContext.getOptionalOffsettedItemByOffset(
180                ItemType.TYPE_ENCODED_ARRAY_ITEM, in.readInt());
181
182        if (classData != null) {
183            classData.setParent(this);
184        }
185        if (annotations != null) {
186            annotations.setParent(this);
187        }
188    }
189
190    /** {@inheritDoc} */
191    protected int placeItem(int offset) {
192        return offset + 32;
193    }
194
195    /** {@inheritDoc} */
196    protected void writeItem(AnnotatedOutput out) {
197        if (out.annotates()) {
198            out.annotate(4, "class_type: " + classType.getTypeDescriptor());
199            out.annotate(4, "access_flags: " + AccessFlags.formatAccessFlagsForClass(accessFlags));
200            out.annotate(4, "superclass_type: " + (superType==null?"":superType.getTypeDescriptor()));
201            out.annotate(4, "interfaces: " +
202                    (implementedInterfaces==null?"":implementedInterfaces.getTypeListString(" ")));
203            out.annotate(4, "source_file: " + (sourceFile==null?"":sourceFile.getStringValue()));
204            out.annotate(4, "annotations_off: " +
205                    (annotations==null?"":"0x"+Integer.toHexString(annotations.getOffset())));
206            out.annotate(4, "class_data_off:" +
207                    (classData==null?"":"0x"+Integer.toHexString(classData.getOffset())));
208            out.annotate(4, "static_values_off: " +
209                    (staticFieldInitializers==null?"":"0x"+Integer.toHexString(staticFieldInitializers.getOffset())));
210        }
211        out.writeInt(classType.getIndex());
212        out.writeInt(accessFlags);
213        out.writeInt(superType==null?-1:superType.getIndex());
214        out.writeInt(implementedInterfaces==null?0:implementedInterfaces.getOffset());
215        out.writeInt(sourceFile==null?-1:sourceFile.getIndex());
216        out.writeInt(annotations==null?0:annotations.getOffset());
217        out.writeInt(classData==null?0:classData.getOffset());
218        out.writeInt(staticFieldInitializers==null?0:staticFieldInitializers.getOffset());
219    }
220
221    /** {@inheritDoc} */
222    public ItemType getItemType() {
223        return ItemType.TYPE_CLASS_DEF_ITEM;
224    }
225
226    /** {@inheritDoc} */
227    public String getConciseIdentity() {
228        return "class_def_item: " + classType.getTypeDescriptor();
229    }
230
231    /** {@inheritDoc} */
232    public int compareTo(ClassDefItem o) {
233        //The actual sorting for this class is implemented in SortClassDefItemSection.
234        //This method is just used for sorting the associated ClassDataItem items, so
235        //we can just do the comparison based on the offsets of the items
236        return this.getOffset() - o.getOffset();
237    }
238
239    public TypeIdItem getClassType() {
240        return classType;
241    }
242
243    public int getAccessFlags() {
244        return accessFlags;
245    }
246
247    public TypeIdItem getSuperclass() {
248        return superType;
249    }
250
251    public TypeListItem getInterfaces() {
252        return implementedInterfaces;
253    }
254
255    public StringIdItem getSourceFile() {
256        return sourceFile;
257    }
258
259    public AnnotationDirectoryItem getAnnotations() {
260        return annotations;
261    }
262
263    public ClassDataItem getClassData() {
264        return classData;
265    }
266
267    public EncodedArrayItem getStaticFieldInitializers() {
268        return staticFieldInitializers;
269    }
270
271    public static int placeClassDefItems(IndexedSection<ClassDefItem> section, int offset) {
272        ClassDefPlacer cdp = new ClassDefPlacer(section);
273        return cdp.placeSection(offset);
274    }
275
276    /**
277     * This class places the items within a ClassDefItem section, such that superclasses and interfaces are
278     * placed before sub/implementing classes
279     */
280    private static class ClassDefPlacer {
281        private final IndexedSection<ClassDefItem> section;
282        private final HashMap<TypeIdItem, ClassDefItem> unplacedClassDefsByType =
283                new HashMap<TypeIdItem, ClassDefItem>();
284
285        private int currentIndex = 0;
286        private int currentOffset;
287
288        public ClassDefPlacer(IndexedSection<ClassDefItem> section) {
289            this.section = section;
290
291            for (ClassDefItem classDefItem: section.items) {
292                TypeIdItem typeIdItem = classDefItem.classType;
293                unplacedClassDefsByType.put(typeIdItem, classDefItem);
294            }
295        }
296
297        public int placeSection(int offset) {
298            currentOffset = offset;
299
300            if (section.DexFile.getSortAllItems()) {
301                //presort the list, to guarantee a unique ordering
302                Collections.sort(section.items, new Comparator<ClassDefItem>() {
303                    public int compare(ClassDefItem a, ClassDefItem b) {
304                        return a.getClassType().compareTo(b.getClassType());
305                    }
306                });
307            }
308
309            //we need to initialize the offset for all the classes to -1, so we can tell which ones
310            //have been placed
311            for (ClassDefItem classDefItem: section.items) {
312                classDefItem.offset = -1;
313            }
314
315            for (ClassDefItem classDefItem: section.items) {
316                placeClass(classDefItem);
317            }
318
319            for (ClassDefItem classDefItem: unplacedClassDefsByType.values()) {
320                section.items.set(classDefItem.getIndex(), classDefItem);
321            }
322
323            return currentOffset;
324        }
325
326        private void placeClass(ClassDefItem classDefItem) {
327            if (!classDefItem.isPlaced()) {
328                TypeIdItem superType = classDefItem.superType;
329                ClassDefItem superClassDefItem = unplacedClassDefsByType.get(superType);
330
331                if (superClassDefItem != null) {
332                    placeClass(superClassDefItem);
333                }
334
335                TypeListItem interfaces = classDefItem.implementedInterfaces;
336
337                if (interfaces != null) {
338                    for (TypeIdItem interfaceType: interfaces.getTypes()) {
339                        ClassDefItem interfaceClass = unplacedClassDefsByType.get(interfaceType);
340                        if (interfaceClass != null) {
341                            placeClass(interfaceClass);
342                        }
343                    }
344                }
345
346                currentOffset = classDefItem.placeAt(currentOffset, currentIndex++);
347                unplacedClassDefsByType.remove(classDefItem.classType);
348            }
349        }
350    }
351
352    public static class StaticFieldInitializer implements Comparable<StaticFieldInitializer> {
353        public final EncodedValue value;
354        public final ClassDataItem.EncodedField field;
355        public StaticFieldInitializer(EncodedValue value, ClassDataItem.EncodedField field) {
356            this.value = value;
357            this.field = field;
358        }
359
360        public int compareTo(StaticFieldInitializer other) {
361            return field.compareTo(other.field);
362        }
363    }
364
365
366    /**
367     * A helper method to sort the static field initializers and populate the default values as needed
368     * @param dexFile the <code>DexFile</code>
369     * @param staticFieldInitializers the initial values
370     * @return an interned EncodedArrayItem containing the static field initializers
371     */
372    private static EncodedArrayItem makeStaticFieldInitializersItem(DexFile dexFile,
373                                                               List<StaticFieldInitializer> staticFieldInitializers) {
374        if (staticFieldInitializers == null || staticFieldInitializers.size() == 0) {
375            return null;
376        }
377
378        int len = staticFieldInitializers.size();
379
380        Collections.sort(staticFieldInitializers);
381
382        int lastIndex = -1;
383        for (int i=len-1; i>=0; i--) {
384            StaticFieldInitializer staticFieldInitializer = staticFieldInitializers.get(i);
385
386            if (staticFieldInitializer.value != null &&
387                    (staticFieldInitializer.value.compareTo(TypeUtils.makeDefaultValueForType(dexFile,
388                    staticFieldInitializer.field.field.getFieldType().getTypeDescriptor())) != 0)) {
389                lastIndex = i;
390                break;
391            }
392        }
393
394        //we don't have any non-null/non-default values, so we don't need to create an EncodedArrayItem
395        if (lastIndex == -1) {
396            return null;
397        }
398
399        EncodedValue[] values = new EncodedValue[lastIndex+1];
400
401        for (int i=0; i<=lastIndex; i++) {
402            StaticFieldInitializer staticFieldInitializer = staticFieldInitializers.get(i);
403            EncodedValue encodedValue = staticFieldInitializer.value;
404            if (encodedValue == null) {
405                encodedValue = TypeUtils.makeDefaultValueForType(dexFile,
406                        staticFieldInitializer.field.field.getFieldType().getTypeDescriptor());
407            }
408
409            values[i] = encodedValue;
410        }
411
412        ArrayEncodedSubValue encodedArrayValue = new ArrayEncodedSubValue(values);
413        return EncodedArrayItem.internEncodedArrayItem(dexFile, encodedArrayValue);
414    }
415}
416