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