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