ClassDefinition.java revision e77b5a7354f99f417f8d80df629d528e02ef5f9c
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 com.google.common.collect.Lists; 32import org.jf.dexlib2.AccessFlags; 33import org.jf.dexlib2.iface.*; 34import org.jf.dexlib2.iface.instruction.Instruction; 35import org.jf.dexlib2.iface.instruction.formats.Instruction21c; 36import org.jf.dexlib2.iface.reference.FieldReference; 37import org.jf.dexlib2.util.ReferenceUtil; 38import org.jf.util.IndentingWriter; 39import org.jf.util.StringUtils; 40 41import javax.annotation.Nonnull; 42import java.io.IOException; 43import java.util.Collection; 44import java.util.Collections; 45import java.util.HashSet; 46import java.util.List; 47 48public class ClassDefinition { 49 @Nonnull public final ClassDef classDef; 50 @Nonnull private final HashSet<String> fieldsSetInStaticConstructor; 51 52 protected boolean validationErrors; 53 54 public ClassDefinition(ClassDef classDef) { 55 this.classDef = classDef; 56 fieldsSetInStaticConstructor = findFieldsSetInStaticConstructor(); 57 } 58 59 public boolean hadValidationErrors() { 60 return validationErrors; 61 } 62 63 @Nonnull 64 private HashSet<String> findFieldsSetInStaticConstructor() { 65 HashSet<String> fieldsSetInStaticConstructor = new HashSet<String>(); 66 67 for (Method method: classDef.getMethods()) { 68 if (method.getName().equals("<clinit>")) { 69 MethodImplementation impl = method.getImplementation(); 70 if (impl != null) { 71 for (Instruction instruction: impl.getInstructions()) { 72 switch (instruction.getOpcode()) { 73 case SPUT: 74 case SPUT_BOOLEAN: 75 case SPUT_BYTE: 76 case SPUT_CHAR: 77 case SPUT_OBJECT: 78 case SPUT_SHORT: 79 case SPUT_WIDE: { 80 Instruction21c ins = (Instruction21c)instruction; 81 FieldReference fieldRef = (FieldReference)ins.getReference(); 82 if (fieldRef.getDefiningClass().equals((classDef.getType()))) { 83 fieldsSetInStaticConstructor.add(ReferenceUtil.getShortFieldDescriptor(fieldRef)); 84 } 85 break; 86 } 87 } 88 } 89 } 90 } 91 } 92 return fieldsSetInStaticConstructor; 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(classDef.getType()); 111 writer.write('\n'); 112 } 113 114 private void writeAccessFlags(IndentingWriter writer) throws IOException { 115 for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDef.getAccessFlags())) { 116 writer.write(accessFlag.toString()); 117 writer.write(' '); 118 } 119 } 120 121 private void writeSuper(IndentingWriter writer) throws IOException { 122 String superClass = classDef.getSuperclass(); 123 if (superClass != null) { 124 writer.write(".super "); 125 writer.write(superClass); 126 writer.write('\n'); 127 } 128 } 129 130 private void writeSourceFile(IndentingWriter writer) throws IOException { 131 String sourceFile = classDef.getSourceFile(); 132 if (sourceFile != null) { 133 writer.write(".source \""); 134 StringUtils.writeEscapedString(writer, sourceFile); 135 writer.write("\"\n"); 136 } 137 } 138 139 private void writeInterfaces(IndentingWriter writer) throws IOException { 140 List<String> interfaces = Lists.newArrayList(classDef.getInterfaces()); 141 Collections.sort(interfaces); 142 143 if (interfaces.size() != 0) { 144 writer.write('\n'); 145 writer.write("# interfaces\n"); 146 for (String interfaceName: interfaces) { 147 writer.write(".implements "); 148 writer.write(interfaceName); 149 writer.write('\n'); 150 } 151 } 152 } 153 154 private void writeAnnotations(IndentingWriter writer) throws IOException { 155 Collection<? extends Annotation> classAnnotations = classDef.getAnnotations(); 156 if (classAnnotations.size() != 0) { 157 writer.write("\n\n"); 158 writer.write("# annotations\n"); 159 AnnotationFormatter.writeTo(writer, classAnnotations); 160 } 161 } 162 163 private void writeStaticFields(IndentingWriter writer) throws IOException { 164 boolean wroteHeader = false; 165 for (Field field: classDef.getFields()) { 166 if (AccessFlags.STATIC.isSet(field.getAccessFlags())) { 167 if (!wroteHeader) { 168 writer.write("\n\n"); 169 writer.write("# static fields"); 170 wroteHeader = true; 171 } 172 writer.write('\n'); 173 // TODO: detect duplicate fields. 174 175 boolean setInStaticConstructor = 176 fieldsSetInStaticConstructor.contains(ReferenceUtil.getShortFieldDescriptor(field)); 177 178 FieldDefinition.writeTo(writer, field, setInStaticConstructor); 179 } 180 } 181 } 182 183 private void writeInstanceFields(IndentingWriter writer) throws IOException { 184 boolean wroteHeader = false; 185 for (Field field: classDef.getFields()) { 186 if (!AccessFlags.STATIC.isSet(field.getAccessFlags())) { 187 if (!wroteHeader) { 188 writer.write("\n\n"); 189 writer.write("# instance fields"); 190 wroteHeader = true; 191 } 192 writer.write('\n'); 193 // TODO: detect duplicate fields. 194 195 FieldDefinition.writeTo(writer, field, false); 196 } 197 } 198 } 199 200 private void writeDirectMethods(IndentingWriter writer) throws IOException { 201 boolean wroteHeader = false; 202 for (Method method: classDef.getMethods()) { 203 int accessFlags = method.getAccessFlags(); 204 205 if (AccessFlags.STATIC.isSet(accessFlags) || 206 AccessFlags.PRIVATE.isSet(accessFlags) || 207 AccessFlags.CONSTRUCTOR.isSet(accessFlags)) { 208 if (!wroteHeader) { 209 writer.write("\n\n"); 210 writer.write("# direct methods"); 211 wroteHeader = true; 212 } 213 writer.write('\n'); 214 // TODO: detect duplicate methods. 215 // TODO: check for method validation errors 216 217 MethodImplementation methodImpl = method.getImplementation(); 218 if (methodImpl == null) { 219 MethodDefinition.writeEmptyMethodTo(writer, method); 220 } else { 221 MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl); 222 methodDefinition.writeTo(writer); 223 } 224 } 225 } 226 } 227 228 private void writeVirtualMethods(IndentingWriter writer) throws IOException { 229 boolean wroteHeader = false; 230 for (Method method: classDef.getMethods()) { 231 int accessFlags = method.getAccessFlags(); 232 233 if (!AccessFlags.STATIC.isSet(accessFlags) && 234 !AccessFlags.PRIVATE.isSet(accessFlags) && 235 !AccessFlags.CONSTRUCTOR.isSet(accessFlags)) { 236 if (!wroteHeader) { 237 writer.write("\n\n"); 238 writer.write("# virtual methods"); 239 wroteHeader = true; 240 } 241 writer.write('\n'); 242 // TODO: detect duplicate methods. 243 // TODO: check for method validation errors 244 245 MethodImplementation methodImpl = method.getImplementation(); 246 if (methodImpl == null) { 247 MethodDefinition.writeEmptyMethodTo(writer, method); 248 } else { 249 MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl); 250 methodDefinition.writeTo(writer); 251 } 252 } 253 } 254 } 255} 256