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