• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
29 package org.jf.baksmali.Adaptors;
30 
31 import org.jf.dexlib.Util.Utf8Utils;
32 import org.jf.util.CommentingIndentingWriter;
33 import org.jf.util.IndentingWriter;
34 import org.jf.dexlib.*;
35 import org.jf.dexlib.Code.Analysis.ValidationException;
36 import org.jf.dexlib.Code.Format.Instruction21c;
37 import org.jf.dexlib.Code.Format.Instruction41c;
38 import org.jf.dexlib.Code.Instruction;
39 import org.jf.dexlib.EncodedValue.EncodedValue;
40 import org.jf.dexlib.Util.AccessFlags;
41 import org.jf.dexlib.Util.SparseArray;
42 
43 import javax.annotation.Nullable;
44 import java.io.IOException;
45 import java.util.List;
46 
47 public class ClassDefinition {
48     private ClassDefItem classDefItem;
49     @Nullable
50     private ClassDataItem classDataItem;
51 
52     private SparseArray<FieldIdItem> fieldsSetInStaticConstructor;
53 
54     protected boolean validationErrors;
55 
ClassDefinition(ClassDefItem classDefItem)56     public ClassDefinition(ClassDefItem classDefItem) {
57         this.classDefItem = classDefItem;
58         this.classDataItem = classDefItem.getClassData();
59         findFieldsSetInStaticConstructor();
60     }
61 
hadValidationErrors()62     public boolean hadValidationErrors() {
63         return validationErrors;
64     }
65 
findFieldsSetInStaticConstructor()66     private void findFieldsSetInStaticConstructor() {
67         fieldsSetInStaticConstructor = new SparseArray<FieldIdItem>();
68 
69         if (classDataItem == null) {
70             return;
71         }
72 
73         for (ClassDataItem.EncodedMethod directMethod: classDataItem.getDirectMethods()) {
74             if (directMethod.method.getMethodName().getStringValue().equals("<clinit>") &&
75                     directMethod.codeItem != null) {
76                 for (Instruction instruction: directMethod.codeItem.getInstructions()) {
77                     switch (instruction.opcode) {
78                         case SPUT:
79                         case SPUT_BOOLEAN:
80                         case SPUT_BYTE:
81                         case SPUT_CHAR:
82                         case SPUT_OBJECT:
83                         case SPUT_SHORT:
84                         case SPUT_WIDE: {
85                             Instruction21c ins = (Instruction21c)instruction;
86                             FieldIdItem fieldIdItem = (FieldIdItem)ins.getReferencedItem();
87                             fieldsSetInStaticConstructor.put(fieldIdItem.getIndex(), fieldIdItem);
88                             break;
89                         }
90                         case SPUT_JUMBO:
91                         case SPUT_BOOLEAN_JUMBO:
92                         case SPUT_BYTE_JUMBO:
93                         case SPUT_CHAR_JUMBO:
94                         case SPUT_OBJECT_JUMBO:
95                         case SPUT_SHORT_JUMBO:
96                         case SPUT_WIDE_JUMBO: {
97                             Instruction41c ins = (Instruction41c)instruction;
98                             FieldIdItem fieldIdItem = (FieldIdItem)ins.getReferencedItem();
99                             fieldsSetInStaticConstructor.put(fieldIdItem.getIndex(), fieldIdItem);
100                             break;
101                         }
102                     }
103                 }
104             }
105         }
106     }
107 
writeTo(IndentingWriter writer)108     public void writeTo(IndentingWriter writer) throws IOException {
109         writeClass(writer);
110         writeSuper(writer);
111         writeSourceFile(writer);
112         writeInterfaces(writer);
113         writeAnnotations(writer);
114         writeStaticFields(writer);
115         writeInstanceFields(writer);
116         writeDirectMethods(writer);
117         writeVirtualMethods(writer);
118     }
119 
writeClass(IndentingWriter writer)120     private void writeClass(IndentingWriter writer) throws IOException {
121         writer.write(".class ");
122         writeAccessFlags(writer);
123         writer.write(classDefItem.getClassType().getTypeDescriptor());
124         writer.write('\n');
125     }
126 
writeAccessFlags(IndentingWriter writer)127     private void writeAccessFlags(IndentingWriter writer) throws IOException {
128         for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDefItem.getAccessFlags())) {
129             writer.write(accessFlag.toString());
130             writer.write(' ');
131         }
132     }
133 
writeSuper(IndentingWriter writer)134     private void writeSuper(IndentingWriter writer) throws IOException {
135         TypeIdItem superClass = classDefItem.getSuperclass();
136         if (superClass != null) {
137             writer.write(".super ");
138             writer.write(superClass.getTypeDescriptor());
139             writer.write('\n');
140         }
141     }
142 
writeSourceFile(IndentingWriter writer)143     private void writeSourceFile(IndentingWriter writer) throws IOException {
144         StringIdItem sourceFile = classDefItem.getSourceFile();
145         if (sourceFile != null) {
146             writer.write(".source \"");
147             Utf8Utils.writeEscapedString(writer, sourceFile.getStringValue());
148             writer.write("\"\n");
149         }
150     }
151 
writeInterfaces(IndentingWriter writer)152     private void writeInterfaces(IndentingWriter writer) throws IOException {
153         TypeListItem interfaceList = classDefItem.getInterfaces();
154         if (interfaceList == null) {
155             return;
156         }
157 
158         List<TypeIdItem> interfaces = interfaceList.getTypes();
159         if (interfaces == null || interfaces.size() == 0) {
160             return;
161         }
162 
163         writer.write('\n');
164         writer.write("# interfaces\n");
165         for (TypeIdItem typeIdItem: interfaceList.getTypes()) {
166             writer.write(".implements ");
167             writer.write(typeIdItem.getTypeDescriptor());
168             writer.write('\n');
169         }
170     }
171 
writeAnnotations(IndentingWriter writer)172     private void writeAnnotations(IndentingWriter writer) throws IOException {
173         AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
174         if (annotationDirectory == null) {
175             return;
176         }
177 
178         AnnotationSetItem annotationSet = annotationDirectory.getClassAnnotations();
179         if (annotationSet == null) {
180             return;
181         }
182 
183         writer.write("\n\n");
184         writer.write("# annotations\n");
185         AnnotationFormatter.writeTo(writer, annotationSet);
186     }
187 
writeStaticFields(IndentingWriter writer)188     private void writeStaticFields(IndentingWriter writer) throws IOException {
189         if (classDataItem == null) {
190             return;
191         }
192         //if classDataItem is not null, then classDefItem won't be null either
193         assert(classDefItem != null);
194 
195         EncodedArrayItem encodedStaticInitializers = classDefItem.getStaticFieldInitializers();
196 
197         EncodedValue[] staticInitializers;
198         if (encodedStaticInitializers != null) {
199             staticInitializers = encodedStaticInitializers.getEncodedArray().values;
200         } else {
201             staticInitializers = new EncodedValue[0];
202         }
203 
204         List<ClassDataItem.EncodedField> encodedFields = classDataItem.getStaticFields();
205         if (encodedFields.size() == 0) {
206             return;
207         }
208 
209         writer.write("\n\n");
210         writer.write("# static fields\n");
211 
212         for (int i=0; i<encodedFields.size(); i++) {
213             if (i > 0) {
214                 writer.write('\n');
215             }
216 
217             ClassDataItem.EncodedField field = encodedFields.get(i);
218             EncodedValue encodedValue = null;
219             if (i < staticInitializers.length) {
220                 encodedValue = staticInitializers[i];
221             }
222             AnnotationSetItem fieldAnnotations = null;
223             AnnotationDirectoryItem annotations = classDefItem.getAnnotations();
224             if (annotations != null) {
225                 fieldAnnotations = annotations.getFieldAnnotations(field.field);
226             }
227 
228             IndentingWriter fieldWriter = writer;
229             // the encoded fields are sorted, so we just have to compare with the previous one to detect duplicates
230             if (i > 0 && field.equals(encodedFields.get(i-1))) {
231                 fieldWriter = new CommentingIndentingWriter(writer, "#");
232                 fieldWriter.write("Ignoring field with duplicate signature\n");
233                 System.err.println(String.format("Warning: class %s has duplicate static field %s, Ignoring.",
234                         classDefItem.getClassType().getTypeDescriptor(), field.field.getShortFieldString()));
235             }
236 
237             boolean setInStaticConstructor =
238                     fieldsSetInStaticConstructor.get(field.field.getIndex()) != null;
239 
240             FieldDefinition.writeTo(fieldWriter, field, encodedValue, fieldAnnotations, setInStaticConstructor);
241         }
242     }
243 
writeInstanceFields(IndentingWriter writer)244     private void writeInstanceFields(IndentingWriter writer) throws IOException {
245         if (classDataItem == null) {
246             return;
247         }
248 
249         List<ClassDataItem.EncodedField> encodedFields = classDataItem.getInstanceFields();
250         if (encodedFields.size() == 0) {
251             return;
252         }
253 
254         writer.write("\n\n");
255         writer.write("# instance fields\n");
256         for (int i=0; i<encodedFields.size(); i++) {
257             ClassDataItem.EncodedField field = encodedFields.get(i);
258 
259             if (i > 0) {
260                 writer.write('\n');
261             }
262 
263             AnnotationSetItem fieldAnnotations = null;
264             AnnotationDirectoryItem annotations = classDefItem.getAnnotations();
265             if (annotations != null) {
266                 fieldAnnotations = annotations.getFieldAnnotations(field.field);
267             }
268 
269             IndentingWriter fieldWriter = writer;
270             // the encoded fields are sorted, so we just have to compare with the previous one to detect duplicates
271             if (i > 0 && field.equals(encodedFields.get(i-1))) {
272                 fieldWriter = new CommentingIndentingWriter(writer, "#");
273                 fieldWriter.write("Ignoring field with duplicate signature\n");
274                 System.err.println(String.format("Warning: class %s has duplicate instance field %s, Ignoring.",
275                         classDefItem.getClassType().getTypeDescriptor(), field.field.getShortFieldString()));
276             }
277 
278             FieldDefinition.writeTo(fieldWriter, field, null, fieldAnnotations, false);
279         }
280     }
281 
writeDirectMethods(IndentingWriter writer)282     private void writeDirectMethods(IndentingWriter writer) throws IOException {
283         if (classDataItem == null) {
284             return;
285         }
286 
287         List<ClassDataItem.EncodedMethod> directMethods = classDataItem.getDirectMethods();
288         if (directMethods.size() == 0) {
289             return;
290         }
291 
292         writer.write("\n\n");
293         writer.write("# direct methods\n");
294         writeMethods(writer, directMethods);
295     }
296 
writeVirtualMethods(IndentingWriter writer)297     private void writeVirtualMethods(IndentingWriter writer) throws IOException {
298         if (classDataItem == null) {
299             return;
300         }
301 
302         List<ClassDataItem.EncodedMethod> virtualMethods = classDataItem.getVirtualMethods();
303 
304         if (virtualMethods.size() == 0) {
305             return;
306         }
307 
308         writer.write("\n\n");
309         writer.write("# virtual methods\n");
310         writeMethods(writer, virtualMethods);
311     }
312 
writeMethods(IndentingWriter writer, List<ClassDataItem.EncodedMethod> methods)313     private void writeMethods(IndentingWriter writer, List<ClassDataItem.EncodedMethod> methods) throws IOException {
314         for (int i=0; i<methods.size(); i++) {
315             ClassDataItem.EncodedMethod method = methods.get(i);
316             if (i > 0) {
317                 writer.write('\n');
318             }
319 
320             AnnotationSetItem methodAnnotations = null;
321             AnnotationSetRefList parameterAnnotations = null;
322             AnnotationDirectoryItem annotations = classDefItem.getAnnotations();
323             if (annotations != null) {
324                 methodAnnotations = annotations.getMethodAnnotations(method.method);
325                 parameterAnnotations = annotations.getParameterAnnotations(method.method);
326             }
327 
328             IndentingWriter methodWriter = writer;
329             // the encoded methods are sorted, so we just have to compare with the previous one to detect duplicates
330             if (i > 0 && method.equals(methods.get(i-1))) {
331                 methodWriter = new CommentingIndentingWriter(writer, "#");
332                 methodWriter.write("Ignoring method with duplicate signature\n");
333                 System.err.println(String.format("Warning: class %s has duplicate method %s, Ignoring.",
334                         classDefItem.getClassType().getTypeDescriptor(), method.method.getShortMethodString()));
335             }
336 
337             MethodDefinition methodDefinition = new MethodDefinition(method);
338             methodDefinition.writeTo(methodWriter, methodAnnotations, parameterAnnotations);
339 
340             ValidationException validationException = methodDefinition.getValidationException();
341             if (validationException != null) {
342                 System.err.println(String.format("Error while disassembling method %s. Continuing.",
343                         method.method.getMethodString()));
344                 validationException.printStackTrace(System.err);
345                 this.validationErrors = true;
346             }
347         }
348     }
349 }
350