ClassDefinition.java revision fbea4e751fa6f1748ded4379a4b64601cb53ba7b
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 109 for (Instruction instruction: directMethod.codeItem.getInstructions()) { 110 switch (instruction.opcode) { 111 case SPUT: 112 case SPUT_BOOLEAN: 113 case SPUT_BYTE: 114 case SPUT_CHAR: 115 case SPUT_OBJECT: 116 case SPUT_SHORT: 117 case SPUT_WIDE: 118 Instruction21c ins = (Instruction21c)instruction; 119 FieldIdItem fieldIdItem = (FieldIdItem)ins.getReferencedItem(); 120 fieldsSetInStaticConstructor.put(fieldIdItem.getIndex(), fieldIdItem); 121 } 122 } 123 } 124 } 125 } 126 127 public void writeTo(IndentingWriter writer) throws IOException { 128 writeClass(writer); 129 writeSuper(writer); 130 writeSourceFile(writer); 131 writeInterfaces(writer); 132 writeAnnotations(writer); 133 writeStaticFields(writer); 134 writeInstanceFields(writer); 135 writeDirectMethods(writer); 136 writeVirtualMethods(writer); 137 return ; 138 } 139 140 private void writeClass(IndentingWriter writer) throws IOException { 141 writer.write(".class "); 142 writeAccessFlags(writer); 143 writer.write(classDefItem.getClassType().getTypeDescriptor()); 144 writer.write('\n'); 145 } 146 147 private void writeAccessFlags(IndentingWriter writer) throws IOException { 148 for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDefItem.getAccessFlags())) { 149 writer.write(accessFlag.toString()); 150 writer.write(' '); 151 } 152 } 153 154 private void writeSuper(IndentingWriter writer) throws IOException { 155 TypeIdItem superClass = classDefItem.getSuperclass(); 156 if (superClass != null) { 157 writer.write(".super "); 158 writer.write(superClass.getTypeDescriptor()); 159 writer.write('\n'); 160 } 161 } 162 163 private void writeSourceFile(IndentingWriter writer) throws IOException { 164 StringIdItem sourceFile = classDefItem.getSourceFile(); 165 if (sourceFile != null) { 166 writer.write(".source \""); 167 writer.write(sourceFile.getStringValue()); 168 writer.write("\"\n"); 169 } 170 } 171 172 private void writeInterfaces(IndentingWriter writer) throws IOException { 173 TypeListItem interfaceList = classDefItem.getInterfaces(); 174 if (interfaceList == null) { 175 return; 176 } 177 178 List<TypeIdItem> interfaces = interfaceList.getTypes(); 179 if (interfaces == null || interfaces.size() == 0) { 180 return; 181 } 182 183 writer.write('\n'); 184 writer.write("# interfaces\n"); 185 for (TypeIdItem typeIdItem: interfaceList.getTypes()) { 186 writer.write(".implements "); 187 writer.write(typeIdItem.getTypeDescriptor()); 188 writer.write('\n'); 189 } 190 } 191 192 private void writeAnnotations(IndentingWriter writer) throws IOException { 193 AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations(); 194 if (annotationDirectory == null) { 195 return; 196 } 197 198 AnnotationSetItem annotationSet = annotationDirectory.getClassAnnotations(); 199 if (annotationSet == null) { 200 return; 201 } 202 203 writer.write("\n\n"); 204 writer.write("# annotations\n"); 205 AnnotationFormatter.writeTo(writer, annotationSet); 206 } 207 208 private void writeStaticFields(IndentingWriter writer) throws IOException { 209 if (classDataItem == null) { 210 return; 211 } 212 //if classDataItem is not null, then classDefItem won't be null either 213 assert(classDefItem != null); 214 215 EncodedArrayItem encodedStaticInitializers = classDefItem.getStaticFieldInitializers(); 216 217 EncodedValue[] staticInitializers; 218 if (encodedStaticInitializers != null) { 219 staticInitializers = encodedStaticInitializers.getEncodedArray().values; 220 } else { 221 staticInitializers = new EncodedValue[0]; 222 } 223 224 ClassDataItem.EncodedField[] encodedFields = classDataItem.getStaticFields(); 225 if (encodedFields == null || encodedFields.length == 0) { 226 return; 227 } 228 229 writer.write("\n\n"); 230 writer.write("# static fields\n"); 231 232 boolean first = true; 233 for (int i=0; i<encodedFields.length; i++) { 234 if (!first) { 235 writer.write('\n'); 236 } 237 first = false; 238 239 ClassDataItem.EncodedField field = encodedFields[i]; 240 EncodedValue encodedValue = null; 241 if (i < staticInitializers.length) { 242 encodedValue = staticInitializers[i]; 243 } 244 AnnotationSetItem annotationSet = fieldAnnotationsMap.get(field.field.getIndex()); 245 246 boolean setInStaticConstructor = 247 fieldsSetInStaticConstructor.get(field.field.getIndex()) != null; 248 249 FieldDefinition.writeTo(writer, field, encodedValue, annotationSet, setInStaticConstructor); 250 } 251 } 252 253 private void writeInstanceFields(IndentingWriter writer) throws IOException { 254 if (classDataItem == null) { 255 return; 256 } 257 258 ClassDataItem.EncodedField[] encodedFields = classDataItem.getInstanceFields(); 259 if (encodedFields == null || encodedFields.length == 0) { 260 return; 261 } 262 263 writer.write("\n\n"); 264 writer.write("# instance fields\n"); 265 boolean first = true; 266 for (ClassDataItem.EncodedField field: classDataItem.getInstanceFields()) { 267 if (!first) { 268 writer.write('\n'); 269 } 270 first = false; 271 272 AnnotationSetItem annotationSet = fieldAnnotationsMap.get(field.field.getIndex()); 273 274 FieldDefinition.writeTo(writer, field, null, annotationSet, false); 275 } 276 } 277 278 private void writeDirectMethods(IndentingWriter writer) throws IOException { 279 if (classDataItem == null) { 280 return; 281 } 282 283 ClassDataItem.EncodedMethod[] directMethods = classDataItem.getDirectMethods(); 284 285 if (directMethods == null || directMethods.length == 0) { 286 return; 287 } 288 289 writer.write("\n\n"); 290 writer.write("# direct methods\n"); 291 writeMethods(writer, directMethods); 292 } 293 294 private void writeVirtualMethods(IndentingWriter writer) throws IOException { 295 if (classDataItem == null) { 296 return; 297 } 298 299 ClassDataItem.EncodedMethod[] virtualMethods = classDataItem.getVirtualMethods(); 300 301 if (virtualMethods == null || virtualMethods.length == 0) { 302 return; 303 } 304 305 writer.write("\n\n"); 306 writer.write("# virtual methods\n"); 307 writeMethods(writer, virtualMethods); 308 } 309 310 private void writeMethods(IndentingWriter writer, ClassDataItem.EncodedMethod[] methods) throws IOException { 311 boolean first = true; 312 for (ClassDataItem.EncodedMethod method: methods) { 313 if (!first) { 314 writer.write('\n'); 315 } 316 first = false; 317 318 AnnotationSetItem annotationSet = methodAnnotationsMap.get(method.method.getIndex()); 319 AnnotationSetRefList parameterAnnotationList = parameterAnnotationsMap.get(method.method.getIndex()); 320 321 MethodDefinition methodDefinition = new MethodDefinition(method); 322 methodDefinition.writeTo(writer, annotationSet, parameterAnnotationList); 323 324 ValidationException validationException = methodDefinition.getValidationException(); 325 if (validationException != null) { 326 System.err.println(String.format("Error while disassembling method %s. Continuing.", 327 method.method.getMethodString())); 328 validationException.printStackTrace(System.err); 329 this.validationErrors = true; 330 } 331 } 332 } 333} 334