ClassDefinition.java revision c6e54994a7be1bdbdd751ede7c96e07e7fb1c84f
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.IndentingWriter;
38import org.jf.util.StringUtils;
39
40import javax.annotation.Nonnull;
41import java.io.IOException;
42import java.util.Collection;
43import java.util.HashSet;
44import java.util.List;
45
46public class ClassDefinition {
47    @Nonnull public final ClassDef classDef;
48    @Nonnull private final HashSet<String> fieldsSetInStaticConstructor;
49
50    protected boolean validationErrors;
51
52    public ClassDefinition(ClassDef classDef) {
53        this.classDef = classDef;
54        fieldsSetInStaticConstructor = findFieldsSetInStaticConstructor();
55    }
56
57    public boolean hadValidationErrors() {
58        return validationErrors;
59    }
60
61    @Nonnull
62    private HashSet<String> findFieldsSetInStaticConstructor() {
63        HashSet<String> fieldsSetInStaticConstructor = new HashSet<String>();
64
65        for (Method method: classDef.getMethods()) {
66            if (method.getName().equals("<clinit>")) {
67                MethodImplementation impl = method.getImplementation();
68                if (impl != null) {
69                    for (Instruction instruction: impl.getInstructions()) {
70                        switch (instruction.getOpcode()) {
71                            case SPUT:
72                            case SPUT_BOOLEAN:
73                            case SPUT_BYTE:
74                            case SPUT_CHAR:
75                            case SPUT_OBJECT:
76                            case SPUT_SHORT:
77                            case SPUT_WIDE: {
78                                Instruction21c ins = (Instruction21c)instruction;
79                                FieldReference fieldRef = (FieldReference)ins.getReference();
80                                if (fieldRef.getContainingClass().equals((classDef.getType()))) {
81                                    fieldsSetInStaticConstructor.add(ReferenceUtil.getShortFieldDescriptor(fieldRef));
82                                }
83                                break;
84                            }
85                        }
86                    }
87                }
88            }
89        }
90        return fieldsSetInStaticConstructor;
91    }
92
93    public void writeTo(IndentingWriter writer) throws IOException {
94        writeClass(writer);
95        writeSuper(writer);
96        writeSourceFile(writer);
97        writeInterfaces(writer);
98        writeAnnotations(writer);
99        writeStaticFields(writer);
100        writeInstanceFields(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.getType());
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        Collection<? 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 writeStaticFields(IndentingWriter writer) throws IOException {
160        boolean wroteHeader = false;
161        for (Field field: classDef.getFields()) {
162            if (AccessFlags.STATIC.isSet(field.getAccessFlags())) {
163                if (!wroteHeader) {
164                    writer.write("\n\n");
165                    writer.write("# static fields");
166                    wroteHeader = true;
167                }
168                writer.write('\n');
169                // TODO: detect duplicate fields.
170
171                boolean setInStaticConstructor =
172                        fieldsSetInStaticConstructor.contains(ReferenceUtil.getShortFieldDescriptor(field));
173
174                FieldDefinition.writeTo(writer, field, setInStaticConstructor);
175            }
176        }
177    }
178
179    private void writeInstanceFields(IndentingWriter writer) throws IOException {
180        boolean wroteHeader = false;
181        for (Field field: classDef.getFields()) {
182            if (!AccessFlags.STATIC.isSet(field.getAccessFlags())) {
183                if (!wroteHeader) {
184                    writer.write("\n\n");
185                    writer.write("# instance fields");
186                    wroteHeader = true;
187                }
188                writer.write('\n');
189                // TODO: detect duplicate fields.
190
191                FieldDefinition.writeTo(writer, field, false);
192            }
193        }
194    }
195
196    private void writeDirectMethods(IndentingWriter writer) throws IOException {
197        boolean wroteHeader = false;
198        for (Method method: classDef.getMethods()) {
199            int accessFlags = method.getAccessFlags();
200
201            if (AccessFlags.STATIC.isSet(accessFlags) ||
202                    AccessFlags.PRIVATE.isSet(accessFlags) ||
203                    AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
204                if (!wroteHeader) {
205                    writer.write("\n\n");
206                    writer.write("# direct methods");
207                    wroteHeader = true;
208                }
209                writer.write('\n');
210                // TODO: detect duplicate methods.
211                // TODO: check for method validation errors
212
213                MethodImplementation methodImpl = method.getImplementation();
214                if (methodImpl == null) {
215                    MethodDefinition.writeEmptyMethodTo(writer, method);
216                } else {
217                    MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
218                    methodDefinition.writeTo(writer);
219                }
220            }
221        }
222    }
223
224    private void writeVirtualMethods(IndentingWriter writer) throws IOException {
225        boolean wroteHeader = false;
226        for (Method method: classDef.getMethods()) {
227            int accessFlags = method.getAccessFlags();
228
229            if (!AccessFlags.STATIC.isSet(accessFlags) &&
230                    !AccessFlags.PRIVATE.isSet(accessFlags) &&
231                    !AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
232                if (!wroteHeader) {
233                    writer.write("\n\n");
234                    writer.write("# virtual methods");
235                    wroteHeader = true;
236                }
237                writer.write('\n');
238                // TODO: detect duplicate methods.
239                // TODO: check for method validation errors
240
241                MethodImplementation methodImpl = method.getImplementation();
242                if (methodImpl == null) {
243                    MethodDefinition.writeEmptyMethodTo(writer, method);
244                } else {
245                    MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
246                    methodDefinition.writeTo(writer);
247                }
248            }
249        }
250    }
251}
252