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