ClassDefItem.java revision a59fe7e5232eea406a6f7b6055eeb5884683f8b2
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 javax.annotation.Nonnull;
39import javax.annotation.Nullable;
40import java.util.*;
41
42public 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