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