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