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