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