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