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