ClassDefItem.java revision fdef6422d2c5c87c0a6599bd568943d493436820
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.EncodedValue.EncodedValue;
32import org.jf.dexlib.EncodedValue.EncodedValueSubField;
33import org.jf.dexlib.Util.TypeUtils;
34import org.jf.dexlib.Util.Input;
35
36import java.util.*;
37
38public class ClassDefItem extends IndexedItem<ClassDefItem> {
39    private final IndexedItemReference<TypeIdItem> classTypeReferenceField;
40    private final IntegerField accessFlagsField;
41    private final IndexedItemReference<TypeIdItem> superclassTypeReferenceField;
42    private final OffsettedItemReference<TypeListItem> classInterfacesListReferenceField;
43    private final IndexedItemReference<StringIdItem> sourceFileReferenceField;
44    private final OffsettedItemReference<AnnotationDirectoryItem> classAnnotationsReferenceField;
45    private final OffsettedItemReference<ClassDataItem> classDataReferenceField;
46    private final OffsettedItemReference<EncodedArrayItem> staticFieldInitialValuesReferenceField;
47
48    private ArrayList<EncodedValue> staticFieldInitialValuesList;
49
50    private final DexFile dexFile;
51
52    public ClassDefItem(DexFile dexFile, int index) {
53        super(dexFile, index);
54
55        this.dexFile = dexFile;
56
57        fields = new Field[] {
58                classTypeReferenceField = new IndexedItemReference<TypeIdItem>(dexFile.TypeIdsSection,
59                        new IntegerField(null), "class_idx"),
60                //TODO: add annotated output showing the flags
61                accessFlagsField = new IntegerField("access_flags:"),
62                superclassTypeReferenceField = new IndexedItemReference<TypeIdItem>(dexFile.TypeIdsSection,
63                        new IntegerField(null), "superclass_idx"),
64                classInterfacesListReferenceField = new OffsettedItemReference<TypeListItem>(dexFile.TypeListsSection,
65                        new IntegerField(null), "interfaces_off"),
66                sourceFileReferenceField = new IndexedItemReference<StringIdItem>(dexFile.StringIdsSection,
67                        new IntegerField(null), "source_file_off"),
68                classAnnotationsReferenceField = new OffsettedItemReference<AnnotationDirectoryItem>(
69                    dexFile.AnnotationDirectoriesSection, new IntegerField(null), "annotations_off"),
70                classDataReferenceField = new OffsettedItemReference<ClassDataItem>(dexFile.ClassDataSection,
71                        new IntegerField(null), "class_data_off"),
72                staticFieldInitialValuesReferenceField = new OffsettedItemReference<EncodedArrayItem>(
73                        dexFile.EncodedArraysSection, new IntegerField(null), "static_values_off")
74        };
75    }
76
77    public ClassDefItem(DexFile dexFile,
78                        TypeIdItem classType,
79                        int accessFlags,
80                        TypeIdItem superType,
81                        TypeListItem implementsList,
82                        StringIdItem source,
83                        ClassDataItem classDataItem) {
84        this(dexFile, -1);
85
86        classTypeReferenceField.setReference(classType);
87        accessFlagsField.cacheValue(accessFlags);
88        superclassTypeReferenceField.setReference(superType);
89        classInterfacesListReferenceField.setReference(implementsList);
90        sourceFileReferenceField.setReference(source);
91        classDataReferenceField.setReference(classDataItem);
92
93        if (classDataItem != null) {
94            classDataItem.setParent(this);
95        }
96    }
97
98    public TypeIdItem getSuperclass() {
99        return superclassTypeReferenceField.getReference();
100    }
101
102    public TypeIdItem getClassType() {
103        return classTypeReferenceField.getReference();
104    }
105
106    protected int getAlignment() {
107        return 4;
108    }
109
110    public ItemType getItemType() {
111        return ItemType.TYPE_CLASS_DEF_ITEM;
112    }
113
114    public String getClassName() {
115        return classTypeReferenceField.getReference().getTypeDescriptor();
116    }
117
118    public String getSourceFile() {
119        StringIdItem stringIdItem = sourceFileReferenceField.getReference();
120        if (stringIdItem == null) {
121            return null;
122        }
123        return stringIdItem.getStringValue();
124    }
125
126    public int getAccessFlags() {
127        return accessFlagsField.getCachedValue();
128    }
129
130    public List<TypeIdItem> getInterfaces() {
131        TypeListItem interfaceList = classInterfacesListReferenceField.getReference();
132        if (interfaceList == null) {
133            return null;
134        }
135
136        return interfaceList.getTypes();
137    }
138
139    public ClassDataItem getClassData() {
140        return classDataReferenceField.getReference();
141    }
142
143    public AnnotationDirectoryItem getAnnotationDirectory() {
144        return classAnnotationsReferenceField.getReference();
145    }
146
147    public String getConciseIdentity() {
148        return "class_def_item: " + getClassName();
149    }
150
151    public int hashCode() {
152        return classTypeReferenceField.getReference().hashCode();
153    }
154
155    public boolean equals(Object o) {
156        if (!(o instanceof ClassDefItem)) {
157            return false;
158        }
159        ClassDefItem other = (ClassDefItem)o;
160        return classTypeReferenceField.equals(other.classTypeReferenceField);
161    }
162
163    public EncodedArrayItem getStaticInitializers() {
164        return staticFieldInitialValuesReferenceField.getReference();
165    }
166
167    public int compareTo(ClassDefItem o) {
168        //The actual sorting for this class is implemented in SortClassDefItemSection.
169        //This method is just used for sorting the associated ClassDataItem items, so
170        //we can just do the comparison based on the offsets of the items
171        return ((Integer)this.offset).compareTo(o.offset);
172    }
173
174    public void addField(ClassDataItem.EncodedField encodedField, EncodedValue initialValue) {
175        //fields are added in ClassDefItem instead of ClassDataItem because we need to grab
176        //the static initializers for StaticFieldInitialValues
177        if (!encodedField.isStatic() && initialValue != null) {
178            throw new RuntimeException("Initial values are only allowed for static fields.");
179        }
180
181        ClassDataItem classDataItem = this.classDataReferenceField.getReference();
182
183        int fieldIndex = classDataItem.addField(encodedField);
184        if (initialValue != null) {
185            if (staticFieldInitialValuesList == null) {
186                staticFieldInitialValuesList = new ArrayList<EncodedValue>();
187
188                EncodedArrayItem encodedArrayItem = new EncodedArrayItem(dexFile, staticFieldInitialValuesList);
189                staticFieldInitialValuesReferenceField.setReference(encodedArrayItem);
190            }
191
192            //All static fields before this one must have an initial value. Add any default values as needed
193            for (int i=staticFieldInitialValuesList.size(); i < fieldIndex; i++) {
194                ClassDataItem.EncodedField staticField = classDataItem.getStaticFields().get(i);
195                EncodedValueSubField subField = TypeUtils.makeDefaultValueForType(dexFile,
196                        staticField.getField().getFieldType().getTypeDescriptor());
197                EncodedValue encodedValue = new EncodedValue(dexFile, subField);
198                staticFieldInitialValuesList.add(i, encodedValue);
199            }
200
201            staticFieldInitialValuesList.add(fieldIndex, initialValue);
202        } else if (staticFieldInitialValuesList != null && encodedField.isStatic() && fieldIndex < staticFieldInitialValuesList.size()) {
203            EncodedValueSubField subField = TypeUtils.makeDefaultValueForType(dexFile,
204                    encodedField.getField().getFieldType().getTypeDescriptor());
205            EncodedValue encodedValue = new EncodedValue(dexFile, subField);
206            staticFieldInitialValuesList.add(fieldIndex, encodedValue);
207        }
208    }
209
210    public void setAnnotations(AnnotationDirectoryItem annotations) {
211        this.classAnnotationsReferenceField.setReference(annotations);
212        if (annotations != null) {
213            annotations.setParent(this);
214        }
215    }
216
217    public void setClassDataItem(ClassDataItem classDataItem) {
218        this.classDataReferenceField.setReference(classDataItem);
219        if (classDataItem != null) {
220            classDataItem.setParent(this);
221        }
222    }
223
224    public void readFrom(Input in, int index) {
225        super.readFrom(in, index);
226
227        ClassDataItem classDataItem = classDataReferenceField.getReference();
228        if (classDataItem != null) {
229            classDataItem.setParent(this);
230        }
231
232        AnnotationDirectoryItem annotationDirectoryItem = classAnnotationsReferenceField.getReference();
233        if (annotationDirectoryItem != null) {
234            annotationDirectoryItem.setParent(this);
235        }
236    }
237
238    public static int placeClassDefItems(IndexedSection<ClassDefItem> section, int offset) {
239        ClassDefPlacer cdp = new ClassDefPlacer(section);
240        return cdp.placeSection(offset);
241    }
242
243    /**
244     * This class places the items within a ClassDefItem section, such that superclasses and interfaces are
245     * placed before sub/implementing classes
246     */
247    private static class ClassDefPlacer {
248        private final IndexedSection<ClassDefItem> section;
249        private final HashMap<TypeIdItem, ClassDefItem> classDefsByType = new HashMap<TypeIdItem, ClassDefItem>();
250
251        private int currentIndex = 0;
252        private int currentOffset;
253
254        public ClassDefPlacer(IndexedSection<ClassDefItem> section) {
255            this.section = section;
256
257            for (ClassDefItem classDefItem: section.items) {
258                TypeIdItem typeIdItem = classDefItem.classTypeReferenceField.getReference();
259                classDefsByType.put(typeIdItem, classDefItem);
260            }
261        }
262
263        public int placeSection(int offset) {
264            currentOffset = offset;
265
266            if (section.dexFile.getSortAllItems()) {
267                //presort the list, to guarantee a unique ordering
268                Collections.sort(section.items, new Comparator<ClassDefItem>() {
269                    public int compare(ClassDefItem classDefItem, ClassDefItem classDefItem1) {
270                        return classDefItem.getClassType().compareTo(classDefItem1.getClassType());
271                    }
272                });
273            }
274
275            for (ClassDefItem classDefItem: section.items) {
276                classDefItem.offset = -1;
277            }
278
279            for (ClassDefItem classDefItem: section.items) {
280                placeClass(classDefItem);
281            }
282
283            for (ClassDefItem classDefItem: classDefsByType.values()) {
284                section.items.set(classDefItem.getIndex(), classDefItem);
285            }
286
287            return currentOffset;
288        }
289
290        private void placeClass(ClassDefItem classDefItem) {
291            if (!classDefItem.isPlaced()) {
292                TypeIdItem superType = classDefItem.superclassTypeReferenceField.getReference();
293                ClassDefItem superClassDefItem = classDefsByType.get(superType);
294
295                if (superClassDefItem != null) {
296                    placeClass(superClassDefItem);
297                }
298
299                TypeListItem interfaces = classDefItem.classInterfacesListReferenceField.getReference();
300
301                if (interfaces != null) {
302                    for (TypeIdItem interfaceType: interfaces.getTypes()) {
303                        ClassDefItem interfaceClass = classDefsByType.get(interfaceType);
304                        if (interfaceClass != null) {
305                            placeClass(interfaceClass);
306                        }
307                    }
308                }
309
310                currentOffset = classDefItem.place(currentIndex++, currentOffset);
311            }
312        }
313
314    }
315}
316