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