ClassDefinition.java revision 0b2f7d6a57e90424b3ee455c041aab3996c05f2c
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.Code.Analysis.ValidationException;
33import org.jf.dexlib.EncodedValue.EncodedValue;
34import org.jf.dexlib.*;
35import org.jf.dexlib.Code.Instruction;
36import org.jf.dexlib.Code.Format.Instruction21c;
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
109                for (Instruction instruction: directMethod.codeItem.getInstructions()) {
110                    switch (instruction.opcode) {
111                        case SPUT:
112                        case SPUT_BOOLEAN:
113                        case SPUT_BYTE:
114                        case SPUT_CHAR:
115                        case SPUT_OBJECT:
116                        case SPUT_SHORT:
117                        case SPUT_WIDE:
118                            Instruction21c ins = (Instruction21c)instruction;
119                            FieldIdItem fieldIdItem = (FieldIdItem)ins.getReferencedItem();
120                            fieldsSetInStaticConstructor.put(fieldIdItem.getIndex(), fieldIdItem);
121                    }
122                }
123            }
124        }
125    }
126
127    public void writeTo(IndentingWriter writer) throws IOException {
128        writeClass(writer);
129        writeSuper(writer);
130        writeSourceFile(writer);
131        writeInterfaces(writer);
132        writeAnnotations(writer);
133        writeStaticFields(writer);
134        writeInstanceFields(writer);
135        writeDirectMethods(writer);
136        writeVirtualMethods(writer);
137        return ;
138    }
139
140    private void writeClass(IndentingWriter writer) throws IOException {
141        writer.write(".class ");
142        writeAccessFlags(writer);
143        writer.write(classDefItem.getClassType().getTypeDescriptor());
144        writer.write('\n');
145    }
146
147    private void writeAccessFlags(IndentingWriter writer) throws IOException {
148        for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDefItem.getAccessFlags())) {
149            writer.write(accessFlag.toString());
150            writer.write(' ');
151        }
152    }
153
154    private void writeSuper(IndentingWriter writer) throws IOException {
155        TypeIdItem superClass = classDefItem.getSuperclass();
156        if (superClass != null) {
157            writer.write(".super ");
158            writer.write(superClass.getTypeDescriptor());
159            writer.write('\n');
160        }
161    }
162
163    private void writeSourceFile(IndentingWriter writer) throws IOException {
164        StringIdItem sourceFile = classDefItem.getSourceFile();
165        if (sourceFile != null) {
166            writer.write(".source \"");
167            writer.write(sourceFile.getStringValue());
168            writer.write("\"\n");
169        }
170    }
171
172    private void writeInterfaces(IndentingWriter writer) throws IOException {
173        TypeListItem interfaceList = classDefItem.getInterfaces();
174        if (interfaceList == null) {
175            return;
176        }
177
178        List<TypeIdItem> interfaces = interfaceList.getTypes();
179        if (interfaces == null || interfaces.size() == 0) {
180            return;
181        }
182
183        writer.write('\n');
184        writer.write("# interfaces\n");
185        for (TypeIdItem typeIdItem: interfaceList.getTypes()) {
186            writer.write(".implements ");
187            writer.write(typeIdItem.getTypeDescriptor());
188            writer.write('\n');
189        }
190    }
191
192    private void writeAnnotations(IndentingWriter writer) throws IOException {
193        AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
194        if (annotationDirectory == null) {
195            return;
196        }
197
198        AnnotationSetItem annotationSet = annotationDirectory.getClassAnnotations();
199        if (annotationSet == null) {
200            return;
201        }
202
203        writer.write("\n\n");
204        writer.write("# annotations\n");
205        AnnotationFormatter.writeTo(writer, annotationSet);
206    }
207
208    private void writeStaticFields(IndentingWriter writer) throws IOException {
209        if (classDataItem == null) {
210            return;
211        }
212        //if classDataItem is not null, then classDefItem won't be null either
213        assert(classDefItem != null);
214
215        EncodedArrayItem encodedStaticInitializers = classDefItem.getStaticFieldInitializers();
216
217        EncodedValue[] staticInitializers;
218        if (encodedStaticInitializers != null) {
219            staticInitializers = encodedStaticInitializers.getEncodedArray().values;
220        } else {
221            staticInitializers = new EncodedValue[0];
222        }
223
224        ClassDataItem.EncodedField[] encodedFields = classDataItem.getStaticFields();
225        if (encodedFields == null || encodedFields.length == 0) {
226            return;
227        }
228
229        writer.write("\n\n");
230        writer.write("# static fields\n");
231
232        boolean first = true;
233        for (int i=0; i<encodedFields.length; i++) {
234            if (!first) {
235                writer.write('\n');
236            }
237            first = false;
238
239            ClassDataItem.EncodedField field = encodedFields[i];
240            EncodedValue encodedValue = null;
241            if (i < staticInitializers.length) {
242                encodedValue = staticInitializers[i];
243            }
244            AnnotationSetItem annotationSet = fieldAnnotationsMap.get(field.field.getIndex());
245
246            boolean setInStaticConstructor =
247                    fieldsSetInStaticConstructor.get(field.field.getIndex()) != null;
248
249            FieldDefinition.writeTo(writer, field, encodedValue, annotationSet, setInStaticConstructor);
250        }
251    }
252
253    private void writeInstanceFields(IndentingWriter writer) throws IOException {
254        if (classDataItem == null) {
255            return;
256        }
257
258        ClassDataItem.EncodedField[] encodedFields = classDataItem.getInstanceFields();
259        if (encodedFields == null || encodedFields.length == 0) {
260            return;
261        }
262
263        writer.write("\n\n");
264        writer.write("# instance fields\n");
265        boolean first = true;
266        for (ClassDataItem.EncodedField field: classDataItem.getInstanceFields()) {
267            if (!first) {
268                writer.write('\n');
269            }
270            first = false;
271
272            AnnotationSetItem annotationSet = fieldAnnotationsMap.get(field.field.getIndex());
273
274            FieldDefinition.writeTo(writer, field, null, annotationSet, false);
275        }
276    }
277
278    private void writeDirectMethods(IndentingWriter writer) throws IOException {
279        if (classDataItem == null) {
280            return;
281        }
282
283        ClassDataItem.EncodedMethod[] directMethods = classDataItem.getDirectMethods();
284
285        if (directMethods == null || directMethods.length == 0) {
286            return;
287        }
288
289        writer.write("\n\n");
290        writer.write("# direct methods\n");
291        writeMethods(writer, directMethods);
292    }
293
294    private void writeVirtualMethods(IndentingWriter writer) throws IOException {
295        if (classDataItem == null) {
296            return;
297        }
298
299        ClassDataItem.EncodedMethod[] virtualMethods = classDataItem.getVirtualMethods();
300
301        if (virtualMethods == null || virtualMethods.length == 0) {
302            return;
303        }
304
305        writer.write("\n\n");
306        writer.write("# virtual methods\n");
307        writeMethods(writer, virtualMethods);
308    }
309
310    private void writeMethods(IndentingWriter writer, ClassDataItem.EncodedMethod[] methods) throws IOException {
311        boolean first = true;
312        for (ClassDataItem.EncodedMethod method: methods) {
313            if (!first) {
314                writer.write('\n');
315            }
316            first = false;
317
318            AnnotationSetItem annotationSet = methodAnnotationsMap.get(method.method.getIndex());
319            AnnotationSetRefList parameterAnnotationList = parameterAnnotationsMap.get(method.method.getIndex());
320
321            MethodDefinition methodDefinition = new MethodDefinition(method);
322            methodDefinition.writeTo(writer, annotationSet, parameterAnnotationList);
323
324
325
326            ValidationException validationException = methodDefinition.getValidationException();
327            if (validationException != null) {
328                validationException.printStackTrace(System.err);
329                this.validationErrors = true;
330            }
331        }
332    }
333}
334