ClassDefinition.java revision c6e54994a7be1bdbdd751ede7c96e07e7fb1c84f
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.dexlib2.iface.instruction.Instruction; 34import org.jf.dexlib2.iface.instruction.formats.Instruction21c; 35import org.jf.dexlib2.iface.reference.FieldReference; 36import org.jf.dexlib2.util.ReferenceUtil; 37import org.jf.util.IndentingWriter; 38import org.jf.util.StringUtils; 39 40import javax.annotation.Nonnull; 41import java.io.IOException; 42import java.util.Collection; 43import java.util.HashSet; 44import java.util.List; 45 46public class ClassDefinition { 47 @Nonnull public final ClassDef classDef; 48 @Nonnull private final HashSet<String> fieldsSetInStaticConstructor; 49 50 protected boolean validationErrors; 51 52 public ClassDefinition(ClassDef classDef) { 53 this.classDef = classDef; 54 fieldsSetInStaticConstructor = findFieldsSetInStaticConstructor(); 55 } 56 57 public boolean hadValidationErrors() { 58 return validationErrors; 59 } 60 61 @Nonnull 62 private HashSet<String> findFieldsSetInStaticConstructor() { 63 HashSet<String> fieldsSetInStaticConstructor = new HashSet<String>(); 64 65 for (Method method: classDef.getMethods()) { 66 if (method.getName().equals("<clinit>")) { 67 MethodImplementation impl = method.getImplementation(); 68 if (impl != null) { 69 for (Instruction instruction: impl.getInstructions()) { 70 switch (instruction.getOpcode()) { 71 case SPUT: 72 case SPUT_BOOLEAN: 73 case SPUT_BYTE: 74 case SPUT_CHAR: 75 case SPUT_OBJECT: 76 case SPUT_SHORT: 77 case SPUT_WIDE: { 78 Instruction21c ins = (Instruction21c)instruction; 79 FieldReference fieldRef = (FieldReference)ins.getReference(); 80 if (fieldRef.getContainingClass().equals((classDef.getType()))) { 81 fieldsSetInStaticConstructor.add(ReferenceUtil.getShortFieldDescriptor(fieldRef)); 82 } 83 break; 84 } 85 } 86 } 87 } 88 } 89 } 90 return fieldsSetInStaticConstructor; 91 } 92 93 public void writeTo(IndentingWriter writer) throws IOException { 94 writeClass(writer); 95 writeSuper(writer); 96 writeSourceFile(writer); 97 writeInterfaces(writer); 98 writeAnnotations(writer); 99 writeStaticFields(writer); 100 writeInstanceFields(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.getType()); 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 Collection<? 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 writeStaticFields(IndentingWriter writer) throws IOException { 160 boolean wroteHeader = false; 161 for (Field field: classDef.getFields()) { 162 if (AccessFlags.STATIC.isSet(field.getAccessFlags())) { 163 if (!wroteHeader) { 164 writer.write("\n\n"); 165 writer.write("# static fields"); 166 wroteHeader = true; 167 } 168 writer.write('\n'); 169 // TODO: detect duplicate fields. 170 171 boolean setInStaticConstructor = 172 fieldsSetInStaticConstructor.contains(ReferenceUtil.getShortFieldDescriptor(field)); 173 174 FieldDefinition.writeTo(writer, field, setInStaticConstructor); 175 } 176 } 177 } 178 179 private void writeInstanceFields(IndentingWriter writer) throws IOException { 180 boolean wroteHeader = false; 181 for (Field field: classDef.getFields()) { 182 if (!AccessFlags.STATIC.isSet(field.getAccessFlags())) { 183 if (!wroteHeader) { 184 writer.write("\n\n"); 185 writer.write("# instance fields"); 186 wroteHeader = true; 187 } 188 writer.write('\n'); 189 // TODO: detect duplicate fields. 190 191 FieldDefinition.writeTo(writer, field, false); 192 } 193 } 194 } 195 196 private void writeDirectMethods(IndentingWriter writer) throws IOException { 197 boolean wroteHeader = false; 198 for (Method method: classDef.getMethods()) { 199 int accessFlags = method.getAccessFlags(); 200 201 if (AccessFlags.STATIC.isSet(accessFlags) || 202 AccessFlags.PRIVATE.isSet(accessFlags) || 203 AccessFlags.CONSTRUCTOR.isSet(accessFlags)) { 204 if (!wroteHeader) { 205 writer.write("\n\n"); 206 writer.write("# direct methods"); 207 wroteHeader = true; 208 } 209 writer.write('\n'); 210 // TODO: detect duplicate methods. 211 // TODO: check for method validation errors 212 213 MethodImplementation methodImpl = method.getImplementation(); 214 if (methodImpl == null) { 215 MethodDefinition.writeEmptyMethodTo(writer, method); 216 } else { 217 MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl); 218 methodDefinition.writeTo(writer); 219 } 220 } 221 } 222 } 223 224 private void writeVirtualMethods(IndentingWriter writer) throws IOException { 225 boolean wroteHeader = false; 226 for (Method method: classDef.getMethods()) { 227 int accessFlags = method.getAccessFlags(); 228 229 if (!AccessFlags.STATIC.isSet(accessFlags) && 230 !AccessFlags.PRIVATE.isSet(accessFlags) && 231 !AccessFlags.CONSTRUCTOR.isSet(accessFlags)) { 232 if (!wroteHeader) { 233 writer.write("\n\n"); 234 writer.write("# virtual methods"); 235 wroteHeader = true; 236 } 237 writer.write('\n'); 238 // TODO: detect duplicate methods. 239 // TODO: check for method validation errors 240 241 MethodImplementation methodImpl = method.getImplementation(); 242 if (methodImpl == null) { 243 MethodDefinition.writeEmptyMethodTo(writer, method); 244 } else { 245 MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl); 246 methodDefinition.writeTo(writer); 247 } 248 } 249 } 250 } 251} 252