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