ClassDefinition.java revision ecc73ab3f5d1d323f640a3283768ed007d315d81
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.baksmali.Adaptors;
30
31import org.jf.baksmali.IndentingWriter;
32import org.jf.dexlib.*;
33import org.jf.dexlib.Code.Analysis.ValidationException;
34import org.jf.dexlib.Code.Format.Instruction21c;
35import org.jf.dexlib.Code.Instruction;
36import org.jf.dexlib.EncodedValue.EncodedValue;
37import org.jf.dexlib.Util.AccessFlags;
38import org.jf.dexlib.Util.SparseArray;
39
40import java.io.IOException;
41import java.util.List;
42
43public class ClassDefinition {
44    private ClassDefItem classDefItem;
45    private ClassDataItem classDataItem;
46
47    private SparseArray<AnnotationSetItem> methodAnnotationsMap;
48    private SparseArray<AnnotationSetItem> fieldAnnotationsMap;
49    private SparseArray<AnnotationSetRefList> parameterAnnotationsMap;
50
51    private SparseArray<FieldIdItem> fieldsSetInStaticConstructor;
52
53    protected boolean validationErrors;
54
55    public ClassDefinition(ClassDefItem classDefItem) {
56        this.classDefItem = classDefItem;
57        this.classDataItem = classDefItem.getClassData();
58        buildAnnotationMaps();
59        findFieldsSetInStaticConstructor();
60    }
61
62    public boolean hadValidationErrors() {
63        return validationErrors;
64    }
65
66    private void buildAnnotationMaps() {
67        AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
68        if (annotationDirectory == null) {
69            methodAnnotationsMap = new SparseArray<AnnotationSetItem>(0);
70            fieldAnnotationsMap = new SparseArray<AnnotationSetItem>(0);
71            parameterAnnotationsMap = new SparseArray<AnnotationSetRefList>(0);
72            return;
73        }
74
75        methodAnnotationsMap = new SparseArray<AnnotationSetItem>(annotationDirectory.getMethodAnnotationCount());
76        annotationDirectory.iterateMethodAnnotations(new AnnotationDirectoryItem.MethodAnnotationIteratorDelegate() {
77            public void processMethodAnnotations(MethodIdItem method, AnnotationSetItem methodAnnotations) {
78                methodAnnotationsMap.put(method.getIndex(), methodAnnotations);
79            }
80        });
81
82        fieldAnnotationsMap = new SparseArray<AnnotationSetItem>(annotationDirectory.getFieldAnnotationCount());
83        annotationDirectory.iterateFieldAnnotations(new AnnotationDirectoryItem.FieldAnnotationIteratorDelegate() {
84            public void processFieldAnnotations(FieldIdItem field, AnnotationSetItem fieldAnnotations) {
85                fieldAnnotationsMap.put(field.getIndex(), fieldAnnotations);
86            }
87        });
88
89        parameterAnnotationsMap = new SparseArray<AnnotationSetRefList>(
90                annotationDirectory.getParameterAnnotationCount());
91        annotationDirectory.iterateParameterAnnotations(
92          new AnnotationDirectoryItem.ParameterAnnotationIteratorDelegate() {
93            public void processParameterAnnotations(MethodIdItem method, AnnotationSetRefList parameterAnnotations) {
94                parameterAnnotationsMap.put(method.getIndex(), parameterAnnotations);
95            }
96        });
97    }
98
99    private void findFieldsSetInStaticConstructor() {
100        fieldsSetInStaticConstructor = new SparseArray<FieldIdItem>();
101
102        if (classDataItem == null) {
103            return;
104        }
105
106        for (ClassDataItem.EncodedMethod directMethod: classDataItem.getDirectMethods()) {
107            if (directMethod.method.getMethodName().getStringValue().equals("<clinit>")) {
108                if (directMethod.codeItem == null) {
109                    break;
110                }
111
112                for (Instruction instruction: directMethod.codeItem.getInstructions()) {
113                    switch (instruction.opcode) {
114                        case SPUT:
115                        case SPUT_BOOLEAN:
116                        case SPUT_BYTE:
117                        case SPUT_CHAR:
118                        case SPUT_OBJECT:
119                        case SPUT_SHORT:
120                        case SPUT_WIDE:
121                            Instruction21c ins = (Instruction21c)instruction;
122                            FieldIdItem fieldIdItem = (FieldIdItem)ins.getReferencedItem();
123                            fieldsSetInStaticConstructor.put(fieldIdItem.getIndex(), fieldIdItem);
124                    }
125                }
126            }
127        }
128    }
129
130    public void writeTo(IndentingWriter writer) throws IOException {
131        writeClass(writer);
132        writeSuper(writer);
133        writeSourceFile(writer);
134        writeInterfaces(writer);
135        writeAnnotations(writer);
136        writeStaticFields(writer);
137        writeInstanceFields(writer);
138        writeDirectMethods(writer);
139        writeVirtualMethods(writer);
140        return ;
141    }
142
143    private void writeClass(IndentingWriter writer) throws IOException {
144        writer.write(".class ");
145        writeAccessFlags(writer);
146        writer.write(classDefItem.getClassType().getTypeDescriptor());
147        writer.write('\n');
148    }
149
150    private void writeAccessFlags(IndentingWriter writer) throws IOException {
151        for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDefItem.getAccessFlags())) {
152            writer.write(accessFlag.toString());
153            writer.write(' ');
154        }
155    }
156
157    private void writeSuper(IndentingWriter writer) throws IOException {
158        TypeIdItem superClass = classDefItem.getSuperclass();
159        if (superClass != null) {
160            writer.write(".super ");
161            writer.write(superClass.getTypeDescriptor());
162            writer.write('\n');
163        }
164    }
165
166    private void writeSourceFile(IndentingWriter writer) throws IOException {
167        StringIdItem sourceFile = classDefItem.getSourceFile();
168        if (sourceFile != null) {
169            writer.write(".source \"");
170            writer.write(sourceFile.getStringValue());
171            writer.write("\"\n");
172        }
173    }
174
175    private void writeInterfaces(IndentingWriter writer) throws IOException {
176        TypeListItem interfaceList = classDefItem.getInterfaces();
177        if (interfaceList == null) {
178            return;
179        }
180
181        List<TypeIdItem> interfaces = interfaceList.getTypes();
182        if (interfaces == null || interfaces.size() == 0) {
183            return;
184        }
185
186        writer.write('\n');
187        writer.write("# interfaces\n");
188        for (TypeIdItem typeIdItem: interfaceList.getTypes()) {
189            writer.write(".implements ");
190            writer.write(typeIdItem.getTypeDescriptor());
191            writer.write('\n');
192        }
193    }
194
195    private void writeAnnotations(IndentingWriter writer) throws IOException {
196        AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
197        if (annotationDirectory == null) {
198            return;
199        }
200
201        AnnotationSetItem annotationSet = annotationDirectory.getClassAnnotations();
202        if (annotationSet == null) {
203            return;
204        }
205
206        writer.write("\n\n");
207        writer.write("# annotations\n");
208        AnnotationFormatter.writeTo(writer, annotationSet);
209    }
210
211    private void writeStaticFields(IndentingWriter writer) throws IOException {
212        if (classDataItem == null) {
213            return;
214        }
215        //if classDataItem is not null, then classDefItem won't be null either
216        assert(classDefItem != null);
217
218        EncodedArrayItem encodedStaticInitializers = classDefItem.getStaticFieldInitializers();
219
220        EncodedValue[] staticInitializers;
221        if (encodedStaticInitializers != null) {
222            staticInitializers = encodedStaticInitializers.getEncodedArray().values;
223        } else {
224            staticInitializers = new EncodedValue[0];
225        }
226
227        ClassDataItem.EncodedField[] encodedFields = classDataItem.getStaticFields();
228        if (encodedFields == null || encodedFields.length == 0) {
229            return;
230        }
231
232        writer.write("\n\n");
233        writer.write("# static fields\n");
234
235        boolean first = true;
236        for (int i=0; i<encodedFields.length; i++) {
237            if (!first) {
238                writer.write('\n');
239            }
240            first = false;
241
242            ClassDataItem.EncodedField field = encodedFields[i];
243            EncodedValue encodedValue = null;
244            if (i < staticInitializers.length) {
245                encodedValue = staticInitializers[i];
246            }
247            AnnotationSetItem annotationSet = fieldAnnotationsMap.get(field.field.getIndex());
248
249            boolean setInStaticConstructor =
250                    fieldsSetInStaticConstructor.get(field.field.getIndex()) != null;
251
252            FieldDefinition.writeTo(writer, field, encodedValue, annotationSet, setInStaticConstructor);
253        }
254    }
255
256    private void writeInstanceFields(IndentingWriter writer) throws IOException {
257        if (classDataItem == null) {
258            return;
259        }
260
261        ClassDataItem.EncodedField[] encodedFields = classDataItem.getInstanceFields();
262        if (encodedFields == null || encodedFields.length == 0) {
263            return;
264        }
265
266        writer.write("\n\n");
267        writer.write("# instance fields\n");
268        boolean first = true;
269        for (ClassDataItem.EncodedField field: classDataItem.getInstanceFields()) {
270            if (!first) {
271                writer.write('\n');
272            }
273            first = false;
274
275            AnnotationSetItem annotationSet = fieldAnnotationsMap.get(field.field.getIndex());
276
277            FieldDefinition.writeTo(writer, field, null, annotationSet, false);
278        }
279    }
280
281    private void writeDirectMethods(IndentingWriter writer) throws IOException {
282        if (classDataItem == null) {
283            return;
284        }
285
286        ClassDataItem.EncodedMethod[] directMethods = classDataItem.getDirectMethods();
287
288        if (directMethods == null || directMethods.length == 0) {
289            return;
290        }
291
292        writer.write("\n\n");
293        writer.write("# direct methods\n");
294        writeMethods(writer, directMethods);
295    }
296
297    private void writeVirtualMethods(IndentingWriter writer) throws IOException {
298        if (classDataItem == null) {
299            return;
300        }
301
302        ClassDataItem.EncodedMethod[] virtualMethods = classDataItem.getVirtualMethods();
303
304        if (virtualMethods == null || virtualMethods.length == 0) {
305            return;
306        }
307
308        writer.write("\n\n");
309        writer.write("# virtual methods\n");
310        writeMethods(writer, virtualMethods);
311    }
312
313    private void writeMethods(IndentingWriter writer, ClassDataItem.EncodedMethod[] methods) throws IOException {
314        boolean first = true;
315        for (ClassDataItem.EncodedMethod method: methods) {
316            if (!first) {
317                writer.write('\n');
318            }
319            first = false;
320
321            AnnotationSetItem annotationSet = methodAnnotationsMap.get(method.method.getIndex());
322            AnnotationSetRefList parameterAnnotationList = parameterAnnotationsMap.get(method.method.getIndex());
323
324            MethodDefinition methodDefinition = new MethodDefinition(method);
325            methodDefinition.writeTo(writer, annotationSet, parameterAnnotationList);
326
327            ValidationException validationException = methodDefinition.getValidationException();
328            if (validationException != null) {
329                System.err.println(String.format("Error while disassembling method %s. Continuing.",
330                        method.method.getMethodString()));
331                validationException.printStackTrace(System.err);
332                this.validationErrors = true;
333            }
334        }
335    }
336}
337