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