ClassDefinition.java revision db389aa3a1d898d3a452f3f0b2220b334b23cb4c
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.dexlib2.AccessFlags;
32import org.jf.dexlib2.iface.*;
33import org.jf.util.StringUtils;
34import org.jf.util.IndentingWriter;
35
36import javax.annotation.Nonnull;
37import java.io.IOException;
38import java.util.List;
39
40public class ClassDefinition {
41    @Nonnull public final ClassDef classDef;
42    //@Nonnull private final HashSet<String> fieldsSetInStaticConstructor;
43
44    protected boolean validationErrors;
45
46    public ClassDefinition(ClassDef classDef) {
47        this.classDef = classDef;
48        //fieldsSetInStaticConstructor = findFieldsSetInStaticConstructor();
49    }
50
51    public boolean hadValidationErrors() {
52        return validationErrors;
53    }
54
55    //TODO: uncomment
56    /*@Nonnull
57    private HashSet<String> findFieldsSetInStaticConstructor() {
58        HashSet<String> fieldsSetInStaticConstructor = new HashSet<String>();
59
60        for (Method method: classDef.getMethods()) {
61            if (method.getName().equals("<clinit>")) {
62                MethodImplementation impl = method.getImplementation();
63                if (impl != null) {
64                    for (Instruction instruction: impl.getInstructions()) {
65                        switch (instruction.getOpcode()) {
66                            case SPUT:
67                            case SPUT_BOOLEAN:
68                            case SPUT_BYTE:
69                            case SPUT_CHAR:
70                            case SPUT_OBJECT:
71                            case SPUT_SHORT:
72                            case SPUT_WIDE: {
73                                Instruction21c ins = (Instruction21c)instruction;
74                                String field = ins.getReference();
75                                if (field.startsWith(classDef.getName())) {
76                                    fieldsSetInStaticConstructor.add(field);
77                                }
78                                break;
79                            }
80                        }
81                    }
82                }
83            }
84        }
85        return fieldsSetInStaticConstructor;
86    }*/
87
88    public void writeTo(IndentingWriter writer) throws IOException {
89        writeClass(writer);
90        writeSuper(writer);
91        writeSourceFile(writer);
92        writeInterfaces(writer);
93        writeAnnotations(writer);
94        writeStaticFields(writer);
95        writeInstanceFields(writer);
96        writeDirectMethods(writer);
97        writeVirtualMethods(writer);
98    }
99
100    private void writeClass(IndentingWriter writer) throws IOException {
101        writer.write(".class ");
102        writeAccessFlags(writer);
103        writer.write(classDef.getType());
104        writer.write('\n');
105    }
106
107    private void writeAccessFlags(IndentingWriter writer) throws IOException {
108        for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDef.getAccessFlags())) {
109            writer.write(accessFlag.toString());
110            writer.write(' ');
111        }
112    }
113
114    private void writeSuper(IndentingWriter writer) throws IOException {
115        String superClass = classDef.getSuperclass();
116        if (superClass != null) {
117            writer.write(".super ");
118            writer.write(superClass);
119            writer.write('\n');
120        }
121    }
122
123    private void writeSourceFile(IndentingWriter writer) throws IOException {
124        String sourceFile = classDef.getSourceFile();
125        if (sourceFile != null) {
126            writer.write(".source \"");
127            StringUtils.writeEscapedString(writer, sourceFile);
128            writer.write("\"\n");
129        }
130    }
131
132    private void writeInterfaces(IndentingWriter writer) throws IOException {
133        List<String> interfaces = classDef.getInterfaces();
134        if (interfaces.size() != 0) {
135            writer.write('\n');
136            writer.write("# interfaces\n");
137            for (String interfaceName: interfaces) {
138                writer.write(".implements ");
139                writer.write(interfaceName);
140                writer.write('\n');
141            }
142        }
143    }
144
145    private void writeAnnotations(IndentingWriter writer) throws IOException {
146        List<? extends Annotation> classAnnotations = classDef.getAnnotations();
147        if (classAnnotations.size() != 0) {
148            writer.write("\n\n");
149            writer.write("# annotations\n");
150            AnnotationFormatter.writeTo(writer, classAnnotations);
151        }
152    }
153
154    private void writeStaticFields(IndentingWriter writer) throws IOException {
155        boolean wroteHeader = false;
156        for (Field field: classDef.getFields()) {
157            if (AccessFlags.STATIC.isSet(field.getAccessFlags())) {
158                if (!wroteHeader) {
159                    writer.write("\n\n");
160                    writer.write("# static fields");
161                    wroteHeader = true;
162                }
163                writer.write('\n');
164                // TODO: detect duplicate fields.
165                // TODO: check if field is set in static constructor
166
167                FieldDefinition.writeTo(writer, field, false);
168            }
169        }
170    }
171
172    private void writeInstanceFields(IndentingWriter writer) throws IOException {
173        boolean wroteHeader = false;
174        for (Field field: classDef.getFields()) {
175            if (!AccessFlags.STATIC.isSet(field.getAccessFlags())) {
176                if (!wroteHeader) {
177                    writer.write("\n\n");
178                    writer.write("# instance fields");
179                    wroteHeader = true;
180                }
181                writer.write('\n');
182                // TODO: detect duplicate fields.
183                // TODO: check if field is set in static constructor
184
185                FieldDefinition.writeTo(writer, field, false);
186            }
187        }
188    }
189
190    private void writeDirectMethods(IndentingWriter writer) throws IOException {
191        boolean wroteHeader = false;
192        for (Method method: classDef.getMethods()) {
193            int accessFlags = method.getAccessFlags();
194
195            if (AccessFlags.STATIC.isSet(accessFlags) ||
196                    AccessFlags.PRIVATE.isSet(accessFlags) ||
197                    AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
198                if (!wroteHeader) {
199                    writer.write("\n\n");
200                    writer.write("# direct methods");
201                    wroteHeader = true;
202                }
203                writer.write('\n');
204                // TODO: detect duplicate methods.
205                // TODO: check for method validation errors
206
207                MethodImplementation methodImpl = method.getImplementation();
208                if (methodImpl == null) {
209                    MethodDefinition.writeEmptyMethodTo(writer, method);
210                } else {
211                    MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
212                    methodDefinition.writeTo(writer);
213                }
214            }
215        }
216    }
217
218    private void writeVirtualMethods(IndentingWriter writer) throws IOException {
219        boolean wroteHeader = false;
220        for (Method method: classDef.getMethods()) {
221            int accessFlags = method.getAccessFlags();
222
223            if (!AccessFlags.STATIC.isSet(accessFlags) &&
224                    !AccessFlags.PRIVATE.isSet(accessFlags) &&
225                    !AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
226                if (!wroteHeader) {
227                    writer.write("\n\n");
228                    writer.write("# virtual methods");
229                    wroteHeader = true;
230                }
231                writer.write('\n');
232                // TODO: detect duplicate methods.
233                // TODO: check for method validation errors
234
235                MethodImplementation methodImpl = method.getImplementation();
236                if (methodImpl == null) {
237                    MethodDefinition.writeEmptyMethodTo(writer, method);
238                } else {
239                    MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
240                    methodDefinition.writeTo(writer);
241                }
242            }
243        }
244    }
245
246    //TODO: uncomment
247    /*private void writeDirectMethods(IndentingWriter writer) throws IOException {
248        if (classDataItem == null) {
249            return;
250        }
251
252        List<ClassDataItem.EncodedMethod> directMethods = classDataItem.getDirectMethods();
253        if (directMethods.size() == 0) {
254            return;
255        }
256
257        writer.write("\n\n");
258        writer.write("# direct methods\n");
259        writeMethods(writer, directMethods);
260    }
261
262    private void writeVirtualMethods(IndentingWriter writer) throws IOException {
263        if (classDataItem == null) {
264            return;
265        }
266
267        List<ClassDataItem.EncodedMethod> virtualMethods = classDataItem.getVirtualMethods();
268
269        if (virtualMethods.size() == 0) {
270            return;
271        }
272
273        writer.write("\n\n");
274        writer.write("# virtual methods\n");
275        writeMethods(writer, virtualMethods);
276    }
277
278    private void writeMethods(IndentingWriter writer, List<ClassDataItem.EncodedMethod> methods) throws IOException {
279        for (int i=0; i<methods.size(); i++) {
280            ClassDataItem.EncodedMethod method = methods.get(i);
281            if (i > 0) {
282                writer.write('\n');
283            }
284
285            AnnotationSetItem methodAnnotations = null;
286            AnnotationSetRefList parameterAnnotations = null;
287            AnnotationDirectoryItem annotations = classDefItem.getAnnotations();
288            if (annotations != null) {
289                methodAnnotations = annotations.getMethodAnnotations(method.method);
290                parameterAnnotations = annotations.getParameterAnnotations(method.method);
291            }
292
293            IndentingWriter methodWriter = writer;
294            // the encoded methods are sorted, so we just have to compare with the previous one to detect duplicates
295            if (i > 0 && method.equals(methods.get(i-1))) {
296                methodWriter = new CommentingIndentingWriter(writer, "#");
297                methodWriter.write("Ignoring method with duplicate signature\n");
298                System.err.println(String.format("Warning: class %s has duplicate method %s, Ignoring.",
299                        classDefItem.getClassType().getTypeDescriptor(), method.method.getShortMethodString()));
300            }
301
302            MethodDefinition methodDefinition = new MethodDefinition(method);
303            methodDefinition.writeTo(methodWriter, methodAnnotations, parameterAnnotations);
304
305            ValidationException validationException = methodDefinition.getValidationException();
306            if (validationException != null) {
307                System.err.println(String.format("Error while disassembling method %s. Continuing.",
308                        method.method.getMethodString()));
309                validationException.printStackTrace(System.err);
310                this.validationErrors = true;
311            }
312        }
313    }*/
314}
315