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