ClassDefinition.java revision e77b5a7354f99f417f8d80df629d528e02ef5f9c
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 com.google.common.collect.Lists;
32import org.jf.dexlib2.AccessFlags;
33import org.jf.dexlib2.iface.*;
34import org.jf.dexlib2.iface.instruction.Instruction;
35import org.jf.dexlib2.iface.instruction.formats.Instruction21c;
36import org.jf.dexlib2.iface.reference.FieldReference;
37import org.jf.dexlib2.util.ReferenceUtil;
38import org.jf.util.IndentingWriter;
39import org.jf.util.StringUtils;
40
41import javax.annotation.Nonnull;
42import java.io.IOException;
43import java.util.Collection;
44import java.util.Collections;
45import java.util.HashSet;
46import java.util.List;
47
48public class ClassDefinition {
49    @Nonnull public final ClassDef classDef;
50    @Nonnull private final HashSet<String> fieldsSetInStaticConstructor;
51
52    protected boolean validationErrors;
53
54    public ClassDefinition(ClassDef classDef) {
55        this.classDef = classDef;
56        fieldsSetInStaticConstructor = findFieldsSetInStaticConstructor();
57    }
58
59    public boolean hadValidationErrors() {
60        return validationErrors;
61    }
62
63    @Nonnull
64    private HashSet<String> findFieldsSetInStaticConstructor() {
65        HashSet<String> fieldsSetInStaticConstructor = new HashSet<String>();
66
67        for (Method method: classDef.getMethods()) {
68            if (method.getName().equals("<clinit>")) {
69                MethodImplementation impl = method.getImplementation();
70                if (impl != null) {
71                    for (Instruction instruction: impl.getInstructions()) {
72                        switch (instruction.getOpcode()) {
73                            case SPUT:
74                            case SPUT_BOOLEAN:
75                            case SPUT_BYTE:
76                            case SPUT_CHAR:
77                            case SPUT_OBJECT:
78                            case SPUT_SHORT:
79                            case SPUT_WIDE: {
80                                Instruction21c ins = (Instruction21c)instruction;
81                                FieldReference fieldRef = (FieldReference)ins.getReference();
82                                if (fieldRef.getDefiningClass().equals((classDef.getType()))) {
83                                    fieldsSetInStaticConstructor.add(ReferenceUtil.getShortFieldDescriptor(fieldRef));
84                                }
85                                break;
86                            }
87                        }
88                    }
89                }
90            }
91        }
92        return fieldsSetInStaticConstructor;
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(classDef.getType());
111        writer.write('\n');
112    }
113
114    private void writeAccessFlags(IndentingWriter writer) throws IOException {
115        for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDef.getAccessFlags())) {
116            writer.write(accessFlag.toString());
117            writer.write(' ');
118        }
119    }
120
121    private void writeSuper(IndentingWriter writer) throws IOException {
122        String superClass = classDef.getSuperclass();
123        if (superClass != null) {
124            writer.write(".super ");
125            writer.write(superClass);
126            writer.write('\n');
127        }
128    }
129
130    private void writeSourceFile(IndentingWriter writer) throws IOException {
131        String sourceFile = classDef.getSourceFile();
132        if (sourceFile != null) {
133            writer.write(".source \"");
134            StringUtils.writeEscapedString(writer, sourceFile);
135            writer.write("\"\n");
136        }
137    }
138
139    private void writeInterfaces(IndentingWriter writer) throws IOException {
140        List<String> interfaces = Lists.newArrayList(classDef.getInterfaces());
141        Collections.sort(interfaces);
142
143        if (interfaces.size() != 0) {
144            writer.write('\n');
145            writer.write("# interfaces\n");
146            for (String interfaceName: interfaces) {
147                writer.write(".implements ");
148                writer.write(interfaceName);
149                writer.write('\n');
150            }
151        }
152    }
153
154    private void writeAnnotations(IndentingWriter writer) throws IOException {
155        Collection<? extends Annotation> classAnnotations = classDef.getAnnotations();
156        if (classAnnotations.size() != 0) {
157            writer.write("\n\n");
158            writer.write("# annotations\n");
159            AnnotationFormatter.writeTo(writer, classAnnotations);
160        }
161    }
162
163    private void writeStaticFields(IndentingWriter writer) throws IOException {
164        boolean wroteHeader = false;
165        for (Field field: classDef.getFields()) {
166            if (AccessFlags.STATIC.isSet(field.getAccessFlags())) {
167                if (!wroteHeader) {
168                    writer.write("\n\n");
169                    writer.write("# static fields");
170                    wroteHeader = true;
171                }
172                writer.write('\n');
173                // TODO: detect duplicate fields.
174
175                boolean setInStaticConstructor =
176                        fieldsSetInStaticConstructor.contains(ReferenceUtil.getShortFieldDescriptor(field));
177
178                FieldDefinition.writeTo(writer, field, setInStaticConstructor);
179            }
180        }
181    }
182
183    private void writeInstanceFields(IndentingWriter writer) throws IOException {
184        boolean wroteHeader = false;
185        for (Field field: classDef.getFields()) {
186            if (!AccessFlags.STATIC.isSet(field.getAccessFlags())) {
187                if (!wroteHeader) {
188                    writer.write("\n\n");
189                    writer.write("# instance fields");
190                    wroteHeader = true;
191                }
192                writer.write('\n');
193                // TODO: detect duplicate fields.
194
195                FieldDefinition.writeTo(writer, field, false);
196            }
197        }
198    }
199
200    private void writeDirectMethods(IndentingWriter writer) throws IOException {
201        boolean wroteHeader = false;
202        for (Method method: classDef.getMethods()) {
203            int accessFlags = method.getAccessFlags();
204
205            if (AccessFlags.STATIC.isSet(accessFlags) ||
206                    AccessFlags.PRIVATE.isSet(accessFlags) ||
207                    AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
208                if (!wroteHeader) {
209                    writer.write("\n\n");
210                    writer.write("# direct methods");
211                    wroteHeader = true;
212                }
213                writer.write('\n');
214                // TODO: detect duplicate methods.
215                // TODO: check for method validation errors
216
217                MethodImplementation methodImpl = method.getImplementation();
218                if (methodImpl == null) {
219                    MethodDefinition.writeEmptyMethodTo(writer, method);
220                } else {
221                    MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
222                    methodDefinition.writeTo(writer);
223                }
224            }
225        }
226    }
227
228    private void writeVirtualMethods(IndentingWriter writer) throws IOException {
229        boolean wroteHeader = false;
230        for (Method method: classDef.getMethods()) {
231            int accessFlags = method.getAccessFlags();
232
233            if (!AccessFlags.STATIC.isSet(accessFlags) &&
234                    !AccessFlags.PRIVATE.isSet(accessFlags) &&
235                    !AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
236                if (!wroteHeader) {
237                    writer.write("\n\n");
238                    writer.write("# virtual methods");
239                    wroteHeader = true;
240                }
241                writer.write('\n');
242                // TODO: detect duplicate methods.
243                // TODO: check for method validation errors
244
245                MethodImplementation methodImpl = method.getImplementation();
246                if (methodImpl == null) {
247                    MethodDefinition.writeEmptyMethodTo(writer, method);
248                } else {
249                    MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
250                    methodDefinition.writeTo(writer);
251                }
252            }
253        }
254    }
255}
256