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