• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * [The "BSD licence"]
3  * Copyright (c) 2010 Ben Gruver (JesusFreke)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 package com.android.tools.smali.baksmali.Adaptors;
29 
30 import com.android.tools.smali.dexlib2.iface.Annotation;
31 import com.android.tools.smali.dexlib2.iface.ClassDef;
32 import com.android.tools.smali.dexlib2.iface.Field;
33 import com.android.tools.smali.dexlib2.iface.Method;
34 import com.android.tools.smali.dexlib2.iface.MethodImplementation;
35 import com.android.tools.smali.baksmali.BaksmaliOptions;
36 import com.android.tools.smali.baksmali.formatter.BaksmaliFormatter;
37 import com.android.tools.smali.baksmali.formatter.BaksmaliWriter;
38 import com.android.tools.smali.dexlib2.AccessFlags;
39 import com.android.tools.smali.dexlib2.dexbacked.DexBackedClassDef;
40 import com.android.tools.smali.dexlib2.iface.instruction.Instruction;
41 import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c;
42 import com.android.tools.smali.dexlib2.iface.reference.FieldReference;
43 import com.android.tools.smali.dexlib2.iface.reference.Reference;
44 
45 import javax.annotation.Nonnull;
46 import java.io.IOException;
47 import java.util.Collection;
48 import java.util.HashSet;
49 import java.util.List;
50 import java.util.Set;
51 
52 public class ClassDefinition {
53     @Nonnull public final BaksmaliOptions options;
54     @Nonnull public final ClassDef classDef;
55     @Nonnull private final HashSet<String> fieldsSetInStaticConstructor;
56     @Nonnull private final BaksmaliFormatter formatter;
57 
58     protected boolean validationErrors;
59 
ClassDefinition(@onnull BaksmaliOptions options, @Nonnull ClassDef classDef)60     public ClassDefinition(@Nonnull BaksmaliOptions options, @Nonnull ClassDef classDef) {
61         this.options = options;
62         this.classDef = classDef;
63         formatter = new BaksmaliFormatter(options.implicitReferences ? classDef.getType() : null);
64         fieldsSetInStaticConstructor = findFieldsSetInStaticConstructor(classDef);
65     }
66 
hadValidationErrors()67     public boolean hadValidationErrors() {
68         return validationErrors;
69     }
70 
71     @Nonnull
findFieldsSetInStaticConstructor(@onnull ClassDef classDef)72     private HashSet<String> findFieldsSetInStaticConstructor(@Nonnull ClassDef classDef) {
73         HashSet<String> fieldsSetInStaticConstructor = new HashSet<String>();
74 
75         for (Method method: classDef.getDirectMethods()) {
76             if (method.getName().equals("<clinit>")) {
77                 MethodImplementation impl = method.getImplementation();
78                 if (impl != null) {
79                     for (Instruction instruction: impl.getInstructions()) {
80                         switch (instruction.getOpcode()) {
81                             case SPUT:
82                             case SPUT_BOOLEAN:
83                             case SPUT_BYTE:
84                             case SPUT_CHAR:
85                             case SPUT_OBJECT:
86                             case SPUT_SHORT:
87                             case SPUT_WIDE: {
88                                 Instruction21c ins = (Instruction21c)instruction;
89                                 FieldReference fieldRef = (FieldReference)ins.getReference();
90                                 try {
91                                     fieldRef.validateReference();
92                                     if (fieldRef.getDefiningClass().equals((classDef.getType()))) {
93                                         fieldsSetInStaticConstructor.add(
94                                                 formatter.getShortFieldDescriptor(fieldRef));
95                                     }
96                                 } catch (Reference.InvalidReferenceException ex) {
97                                     // Just ignore for now. We'll deal with it when processing the instruction
98                                 }
99                                 break;
100                             }
101                         }
102                     }
103                 }
104             }
105         }
106         return fieldsSetInStaticConstructor;
107     }
108 
writeTo(BaksmaliWriter writer)109     public void writeTo(BaksmaliWriter writer) throws IOException {
110         writeClass(writer);
111         writeSuper(writer);
112         writeSourceFile(writer);
113         writeInterfaces(writer);
114         writeAnnotations(writer);
115         Set<String> staticFields = writeStaticFields(writer);
116         writeInstanceFields(writer, staticFields);
117         Set<String> directMethods = writeDirectMethods(writer);
118         writeVirtualMethods(writer, directMethods);
119     }
120 
writeClass(BaksmaliWriter writer)121     private void writeClass(BaksmaliWriter writer) throws IOException {
122         writer.write(".class ");
123         writeAccessFlags(writer);
124         writer.writeType(classDef.getType());
125         writer.write('\n');
126     }
127 
writeAccessFlags(BaksmaliWriter writer)128     private void writeAccessFlags(BaksmaliWriter writer) throws IOException {
129         for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDef.getAccessFlags())) {
130             writer.write(accessFlag.toString());
131             writer.write(' ');
132         }
133     }
134 
writeSuper(BaksmaliWriter writer)135     private void writeSuper(BaksmaliWriter writer) throws IOException {
136         String superClass = classDef.getSuperclass();
137         if (superClass != null) {
138             writer.write(".super ");
139             writer.writeType(superClass);
140             writer.write('\n');
141         }
142     }
143 
writeSourceFile(BaksmaliWriter writer)144     private void writeSourceFile(BaksmaliWriter writer) throws IOException {
145         String sourceFile = classDef.getSourceFile();
146         if (sourceFile != null) {
147             writer.write(".source ");
148             writer.writeQuotedString(sourceFile);
149             writer.write("\n");
150         }
151     }
152 
writeInterfaces(BaksmaliWriter writer)153     private void writeInterfaces(BaksmaliWriter writer) throws IOException {
154         List<String> interfaces = classDef.getInterfaces();
155 
156         if (interfaces.size() != 0) {
157             writer.write('\n');
158             writer.write("# interfaces\n");
159             for (String interfaceName: interfaces) {
160                 writer.write(".implements ");
161                 writer.writeType(interfaceName);
162                 writer.write('\n');
163             }
164         }
165     }
166 
writeAnnotations(BaksmaliWriter writer)167     private void writeAnnotations(BaksmaliWriter writer) throws IOException {
168         Collection<? extends Annotation> classAnnotations = classDef.getAnnotations();
169         if (classAnnotations.size() != 0) {
170             writer.write("\n\n");
171             writer.write("# annotations\n");
172 
173             AnnotationFormatter.writeTo(writer, classAnnotations);
174         }
175     }
176 
writeStaticFields(BaksmaliWriter writer)177     private Set<String> writeStaticFields(BaksmaliWriter writer) throws IOException {
178         boolean wroteHeader = false;
179         Set<String> writtenFields = new HashSet<String>();
180 
181         Iterable<? extends Field> staticFields;
182         if (classDef instanceof DexBackedClassDef) {
183             staticFields = ((DexBackedClassDef)classDef).getStaticFields(false);
184         } else {
185             staticFields = classDef.getStaticFields();
186         }
187 
188         for (Field field: staticFields) {
189             if (!wroteHeader) {
190                 writer.write("\n\n");
191                 writer.write("# static fields");
192                 wroteHeader = true;
193             }
194             writer.write('\n');
195 
196             boolean setInStaticConstructor;
197             BaksmaliWriter fieldWriter = writer;
198             String fieldString = formatter.getShortFieldDescriptor(field);
199             if (!writtenFields.add(fieldString)) {
200                 writer.write("# duplicate field ignored\n");
201                 fieldWriter = getCommentingWriter(writer);
202                 System.err.println(String.format("Ignoring duplicate field: %s->%s", classDef.getType(), fieldString));
203                 setInStaticConstructor = false;
204             } else {
205                 setInStaticConstructor = fieldsSetInStaticConstructor.contains(fieldString);
206             }
207             FieldDefinition.writeTo(fieldWriter, field, setInStaticConstructor);
208         }
209         return writtenFields;
210     }
211 
writeInstanceFields(BaksmaliWriter writer, Set<String> staticFields)212     private void writeInstanceFields(BaksmaliWriter writer, Set<String> staticFields) throws IOException {
213         boolean wroteHeader = false;
214         Set<String> writtenFields = new HashSet<String>();
215 
216         Iterable<? extends Field> instanceFields;
217         if (classDef instanceof DexBackedClassDef) {
218             instanceFields = ((DexBackedClassDef)classDef).getInstanceFields(false);
219         } else {
220             instanceFields = classDef.getInstanceFields();
221         }
222 
223         for (Field field: instanceFields) {
224             if (!wroteHeader) {
225                 writer.write("\n\n");
226                 writer.write("# instance fields");
227                 wroteHeader = true;
228             }
229             writer.write('\n');
230 
231             BaksmaliWriter fieldWriter = writer;
232             String fieldString = formatter.getShortFieldDescriptor(field);
233             if (!writtenFields.add(fieldString)) {
234                 writer.write("# duplicate field ignored\n");
235                 fieldWriter = getCommentingWriter(writer);
236                 System.err.println(String.format("Ignoring duplicate field: %s->%s", classDef.getType(), fieldString));
237             } else if (staticFields.contains(fieldString)) {
238                 System.err.println(String.format("Duplicate static+instance field found: %s->%s",
239                         classDef.getType(), fieldString));
240                 System.err.println("You will need to rename one of these fields, including all references.");
241 
242                 writer.write("# There is both a static and instance field with this signature.\n" +
243                              "# You will need to rename one of these fields, including all references.\n");
244             }
245             FieldDefinition.writeTo(fieldWriter, field, false);
246         }
247     }
248 
writeDirectMethods(BaksmaliWriter writer)249     private Set<String> writeDirectMethods(BaksmaliWriter writer) throws IOException {
250         boolean wroteHeader = false;
251         Set<String> writtenMethods = new HashSet<String>();
252 
253         Iterable<? extends Method> directMethods;
254         if (classDef instanceof DexBackedClassDef) {
255             directMethods = ((DexBackedClassDef)classDef).getDirectMethods(false);
256         } else {
257             directMethods = classDef.getDirectMethods();
258         }
259 
260         for (Method method: directMethods) {
261             if (!wroteHeader) {
262                 writer.write("\n\n");
263                 writer.write("# direct methods");
264                 wroteHeader = true;
265             }
266             writer.write('\n');
267 
268             // TODO: check for method validation errors
269             String methodString = formatter.getShortMethodDescriptor(method);
270 
271             BaksmaliWriter methodWriter = writer;
272             if (!writtenMethods.add(methodString)) {
273                 writer.write("# duplicate method ignored\n");
274                 methodWriter = getCommentingWriter(writer);
275             }
276 
277             MethodImplementation methodImpl = method.getImplementation();
278             if (methodImpl == null) {
279                 MethodDefinition.writeEmptyMethodTo(methodWriter, method, this);
280             } else {
281                 MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
282                 methodDefinition.writeTo(methodWriter);
283             }
284         }
285         return writtenMethods;
286     }
287 
writeVirtualMethods(BaksmaliWriter writer, Set<String> directMethods)288     private void writeVirtualMethods(BaksmaliWriter writer, Set<String> directMethods)
289             throws IOException {
290         boolean wroteHeader = false;
291         Set<String> writtenMethods = new HashSet<String>();
292 
293         Iterable<? extends Method> virtualMethods;
294         if (classDef instanceof DexBackedClassDef) {
295             virtualMethods = ((DexBackedClassDef)classDef).getVirtualMethods(false);
296         } else {
297             virtualMethods = classDef.getVirtualMethods();
298         }
299 
300         for (Method method: virtualMethods) {
301             if (!wroteHeader) {
302                 writer.write("\n\n");
303                 writer.write("# virtual methods");
304                 wroteHeader = true;
305             }
306             writer.write('\n');
307 
308             // TODO: check for method validation errors
309             String methodString = formatter.getShortMethodDescriptor(method);
310 
311             BaksmaliWriter methodWriter = writer;
312             if (!writtenMethods.add(methodString)) {
313                 writer.write("# duplicate method ignored\n");
314                 methodWriter = getCommentingWriter(writer);
315             } else if (directMethods.contains(methodString)) {
316                 writer.write("# There is both a direct and virtual method with this signature.\n" +
317                              "# You will need to rename one of these methods, including all references.\n");
318                 System.err.println(String.format("Duplicate direct+virtual method found: %s->%s",
319                         classDef.getType(), methodString));
320                 System.err.println("You will need to rename one of these methods, including all references.");
321             }
322 
323             MethodImplementation methodImpl = method.getImplementation();
324             if (methodImpl == null) {
325                 MethodDefinition.writeEmptyMethodTo(methodWriter, method, this);
326             } else {
327                 MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
328                 methodDefinition.writeTo(methodWriter);
329             }
330         }
331     }
332 
getCommentingWriter(BaksmaliWriter writer)333     public BaksmaliWriter getCommentingWriter(BaksmaliWriter writer) {
334         return formatter.getWriter(new CommentingIndentingWriter(writer.indentingWriter()));
335     }
336 
getFormatter()337     public BaksmaliFormatter getFormatter() {
338         return formatter;
339     }
340 }
341