ClassDefinition.java revision db389aa3a1d898d3a452f3f0b2220b334b23cb4c
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.dexlib2.AccessFlags; 32import org.jf.dexlib2.iface.*; 33import org.jf.util.StringUtils; 34import org.jf.util.IndentingWriter; 35 36import javax.annotation.Nonnull; 37import java.io.IOException; 38import java.util.List; 39 40public class ClassDefinition { 41 @Nonnull public final ClassDef classDef; 42 //@Nonnull private final HashSet<String> fieldsSetInStaticConstructor; 43 44 protected boolean validationErrors; 45 46 public ClassDefinition(ClassDef classDef) { 47 this.classDef = classDef; 48 //fieldsSetInStaticConstructor = findFieldsSetInStaticConstructor(); 49 } 50 51 public boolean hadValidationErrors() { 52 return validationErrors; 53 } 54 55 //TODO: uncomment 56 /*@Nonnull 57 private HashSet<String> findFieldsSetInStaticConstructor() { 58 HashSet<String> fieldsSetInStaticConstructor = new HashSet<String>(); 59 60 for (Method method: classDef.getMethods()) { 61 if (method.getName().equals("<clinit>")) { 62 MethodImplementation impl = method.getImplementation(); 63 if (impl != null) { 64 for (Instruction instruction: impl.getInstructions()) { 65 switch (instruction.getOpcode()) { 66 case SPUT: 67 case SPUT_BOOLEAN: 68 case SPUT_BYTE: 69 case SPUT_CHAR: 70 case SPUT_OBJECT: 71 case SPUT_SHORT: 72 case SPUT_WIDE: { 73 Instruction21c ins = (Instruction21c)instruction; 74 String field = ins.getReference(); 75 if (field.startsWith(classDef.getName())) { 76 fieldsSetInStaticConstructor.add(field); 77 } 78 break; 79 } 80 } 81 } 82 } 83 } 84 } 85 return fieldsSetInStaticConstructor; 86 }*/ 87 88 public void writeTo(IndentingWriter writer) throws IOException { 89 writeClass(writer); 90 writeSuper(writer); 91 writeSourceFile(writer); 92 writeInterfaces(writer); 93 writeAnnotations(writer); 94 writeStaticFields(writer); 95 writeInstanceFields(writer); 96 writeDirectMethods(writer); 97 writeVirtualMethods(writer); 98 } 99 100 private void writeClass(IndentingWriter writer) throws IOException { 101 writer.write(".class "); 102 writeAccessFlags(writer); 103 writer.write(classDef.getType()); 104 writer.write('\n'); 105 } 106 107 private void writeAccessFlags(IndentingWriter writer) throws IOException { 108 for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDef.getAccessFlags())) { 109 writer.write(accessFlag.toString()); 110 writer.write(' '); 111 } 112 } 113 114 private void writeSuper(IndentingWriter writer) throws IOException { 115 String superClass = classDef.getSuperclass(); 116 if (superClass != null) { 117 writer.write(".super "); 118 writer.write(superClass); 119 writer.write('\n'); 120 } 121 } 122 123 private void writeSourceFile(IndentingWriter writer) throws IOException { 124 String sourceFile = classDef.getSourceFile(); 125 if (sourceFile != null) { 126 writer.write(".source \""); 127 StringUtils.writeEscapedString(writer, sourceFile); 128 writer.write("\"\n"); 129 } 130 } 131 132 private void writeInterfaces(IndentingWriter writer) throws IOException { 133 List<String> interfaces = classDef.getInterfaces(); 134 if (interfaces.size() != 0) { 135 writer.write('\n'); 136 writer.write("# interfaces\n"); 137 for (String interfaceName: interfaces) { 138 writer.write(".implements "); 139 writer.write(interfaceName); 140 writer.write('\n'); 141 } 142 } 143 } 144 145 private void writeAnnotations(IndentingWriter writer) throws IOException { 146 List<? extends Annotation> classAnnotations = classDef.getAnnotations(); 147 if (classAnnotations.size() != 0) { 148 writer.write("\n\n"); 149 writer.write("# annotations\n"); 150 AnnotationFormatter.writeTo(writer, classAnnotations); 151 } 152 } 153 154 private void writeStaticFields(IndentingWriter writer) throws IOException { 155 boolean wroteHeader = false; 156 for (Field field: classDef.getFields()) { 157 if (AccessFlags.STATIC.isSet(field.getAccessFlags())) { 158 if (!wroteHeader) { 159 writer.write("\n\n"); 160 writer.write("# static fields"); 161 wroteHeader = true; 162 } 163 writer.write('\n'); 164 // TODO: detect duplicate fields. 165 // TODO: check if field is set in static constructor 166 167 FieldDefinition.writeTo(writer, field, false); 168 } 169 } 170 } 171 172 private void writeInstanceFields(IndentingWriter writer) throws IOException { 173 boolean wroteHeader = false; 174 for (Field field: classDef.getFields()) { 175 if (!AccessFlags.STATIC.isSet(field.getAccessFlags())) { 176 if (!wroteHeader) { 177 writer.write("\n\n"); 178 writer.write("# instance fields"); 179 wroteHeader = true; 180 } 181 writer.write('\n'); 182 // TODO: detect duplicate fields. 183 // TODO: check if field is set in static constructor 184 185 FieldDefinition.writeTo(writer, field, false); 186 } 187 } 188 } 189 190 private void writeDirectMethods(IndentingWriter writer) throws IOException { 191 boolean wroteHeader = false; 192 for (Method method: classDef.getMethods()) { 193 int accessFlags = method.getAccessFlags(); 194 195 if (AccessFlags.STATIC.isSet(accessFlags) || 196 AccessFlags.PRIVATE.isSet(accessFlags) || 197 AccessFlags.CONSTRUCTOR.isSet(accessFlags)) { 198 if (!wroteHeader) { 199 writer.write("\n\n"); 200 writer.write("# direct methods"); 201 wroteHeader = true; 202 } 203 writer.write('\n'); 204 // TODO: detect duplicate methods. 205 // TODO: check for method validation errors 206 207 MethodImplementation methodImpl = method.getImplementation(); 208 if (methodImpl == null) { 209 MethodDefinition.writeEmptyMethodTo(writer, method); 210 } else { 211 MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl); 212 methodDefinition.writeTo(writer); 213 } 214 } 215 } 216 } 217 218 private void writeVirtualMethods(IndentingWriter writer) throws IOException { 219 boolean wroteHeader = false; 220 for (Method method: classDef.getMethods()) { 221 int accessFlags = method.getAccessFlags(); 222 223 if (!AccessFlags.STATIC.isSet(accessFlags) && 224 !AccessFlags.PRIVATE.isSet(accessFlags) && 225 !AccessFlags.CONSTRUCTOR.isSet(accessFlags)) { 226 if (!wroteHeader) { 227 writer.write("\n\n"); 228 writer.write("# virtual methods"); 229 wroteHeader = true; 230 } 231 writer.write('\n'); 232 // TODO: detect duplicate methods. 233 // TODO: check for method validation errors 234 235 MethodImplementation methodImpl = method.getImplementation(); 236 if (methodImpl == null) { 237 MethodDefinition.writeEmptyMethodTo(writer, method); 238 } else { 239 MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl); 240 methodDefinition.writeTo(writer); 241 } 242 } 243 } 244 } 245 246 //TODO: uncomment 247 /*private void writeDirectMethods(IndentingWriter writer) throws IOException { 248 if (classDataItem == null) { 249 return; 250 } 251 252 List<ClassDataItem.EncodedMethod> directMethods = classDataItem.getDirectMethods(); 253 if (directMethods.size() == 0) { 254 return; 255 } 256 257 writer.write("\n\n"); 258 writer.write("# direct methods\n"); 259 writeMethods(writer, directMethods); 260 } 261 262 private void writeVirtualMethods(IndentingWriter writer) throws IOException { 263 if (classDataItem == null) { 264 return; 265 } 266 267 List<ClassDataItem.EncodedMethod> virtualMethods = classDataItem.getVirtualMethods(); 268 269 if (virtualMethods.size() == 0) { 270 return; 271 } 272 273 writer.write("\n\n"); 274 writer.write("# virtual methods\n"); 275 writeMethods(writer, virtualMethods); 276 } 277 278 private void writeMethods(IndentingWriter writer, List<ClassDataItem.EncodedMethod> methods) throws IOException { 279 for (int i=0; i<methods.size(); i++) { 280 ClassDataItem.EncodedMethod method = methods.get(i); 281 if (i > 0) { 282 writer.write('\n'); 283 } 284 285 AnnotationSetItem methodAnnotations = null; 286 AnnotationSetRefList parameterAnnotations = null; 287 AnnotationDirectoryItem annotations = classDefItem.getAnnotations(); 288 if (annotations != null) { 289 methodAnnotations = annotations.getMethodAnnotations(method.method); 290 parameterAnnotations = annotations.getParameterAnnotations(method.method); 291 } 292 293 IndentingWriter methodWriter = writer; 294 // the encoded methods are sorted, so we just have to compare with the previous one to detect duplicates 295 if (i > 0 && method.equals(methods.get(i-1))) { 296 methodWriter = new CommentingIndentingWriter(writer, "#"); 297 methodWriter.write("Ignoring method with duplicate signature\n"); 298 System.err.println(String.format("Warning: class %s has duplicate method %s, Ignoring.", 299 classDefItem.getClassType().getTypeDescriptor(), method.method.getShortMethodString())); 300 } 301 302 MethodDefinition methodDefinition = new MethodDefinition(method); 303 methodDefinition.writeTo(methodWriter, methodAnnotations, parameterAnnotations); 304 305 ValidationException validationException = methodDefinition.getValidationException(); 306 if (validationException != null) { 307 System.err.println(String.format("Error while disassembling method %s. Continuing.", 308 method.method.getMethodString())); 309 validationException.printStackTrace(System.err); 310 this.validationErrors = true; 311 } 312 } 313 }*/ 314} 315