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