1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21package proguard.optimize.peephole; 22 23import proguard.classfile.*; 24import proguard.classfile.editor.*; 25import proguard.classfile.attribute.*; 26import proguard.classfile.attribute.annotation.*; 27import proguard.classfile.attribute.annotation.visitor.*; 28import proguard.classfile.attribute.visitor.*; 29import proguard.classfile.constant.*; 30import proguard.classfile.constant.visitor.ConstantVisitor; 31import proguard.classfile.util.*; 32import proguard.classfile.visitor.*; 33 34/** 35 * This ClassVisitor replaces references to classes and class members if the 36 * classes have targets that are intended to replace them. 37 * 38 * @see VerticalClassMerger 39 * @see ClassReferenceFixer 40 * @see MemberReferenceFixer 41 * @author Eric Lafortune 42 */ 43public class TargetClassChanger 44extends SimplifiedVisitor 45implements ClassVisitor, 46 ConstantVisitor, 47 MemberVisitor, 48 AttributeVisitor, 49 LocalVariableInfoVisitor, 50 LocalVariableTypeInfoVisitor, 51 AnnotationVisitor, 52 ElementValueVisitor 53{ 54 private static final boolean DEBUG = false; 55 56 57 // Implementations for ClassVisitor. 58 59 public void visitProgramClass(ProgramClass programClass) 60 { 61 // Change the references of the constant pool. 62 programClass.constantPoolEntriesAccept(this); 63 64 // Change the references of the class members. 65 programClass.fieldsAccept(this); 66 programClass.methodsAccept(this); 67 68 // Change the references of the attributes. 69 programClass.attributesAccept(this); 70 71 // Is the class itself being retargeted? 72 Clazz targetClass = ClassMerger.getTargetClass(programClass); 73 if (targetClass != null) 74 { 75 // Restore the class name. We have to add a new class entry 76 // to avoid an existing entry with the same name being reused. The 77 // names have to be fixed later, based on their referenced classes. 78 programClass.u2thisClass = 79 addNewClassConstant(programClass, 80 programClass.getName(), 81 programClass); 82 83 // This class will loose all its interfaces. 84 programClass.u2interfacesCount = 0; 85 86 // This class will loose all its subclasses. 87 programClass.subClasses = null; 88 } 89 else 90 { 91 // Remove interface classes that are pointing to this class. 92 int newInterfacesCount = 0; 93 for (int index = 0; index < programClass.u2interfacesCount; index++) 94 { 95 Clazz interfaceClass = programClass.getInterface(index); 96 if (!programClass.equals(interfaceClass)) 97 { 98 programClass.u2interfaces[newInterfacesCount++] = 99 programClass.u2interfaces[index]; 100 } 101 } 102 programClass.u2interfacesCount = newInterfacesCount; 103 104 // Update the subclasses of the superclass and interfaces of the 105 // target class. 106 ConstantVisitor subclassAdder = 107 new ReferencedClassVisitor( 108 new SubclassFilter(programClass, 109 new SubclassAdder(programClass))); 110 111 programClass.superClassConstantAccept(subclassAdder); 112 programClass.interfaceConstantsAccept(subclassAdder); 113 114 // TODO: Maybe restore private method references. 115 } 116 } 117 118 119 public void visitLibraryClass(LibraryClass libraryClass) 120 { 121 // Change the references of the class members. 122 libraryClass.fieldsAccept(this); 123 libraryClass.methodsAccept(this); 124 } 125 126 127 // Implementations for MemberVisitor. 128 129 public void visitProgramField(ProgramClass programClass, ProgramField programField) 130 { 131 // Change the referenced class. 132 programField.referencedClass = 133 updateReferencedClass(programField.referencedClass); 134 135 // Change the references of the attributes. 136 programField.attributesAccept(programClass, this); 137 } 138 139 140 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 141 { 142 // Change the referenced classes. 143 updateReferencedClasses(programMethod.referencedClasses); 144 145 // Change the references of the attributes. 146 programMethod.attributesAccept(programClass, this); 147 } 148 149 150 public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) 151 { 152 // Change the referenced class. 153 libraryField.referencedClass = 154 updateReferencedClass(libraryField.referencedClass); 155 } 156 157 158 public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) 159 { 160 // Change the referenced classes. 161 updateReferencedClasses(libraryMethod.referencedClasses); 162 } 163 164 165 // Implementations for ConstantVisitor. 166 167 public void visitAnyConstant(Clazz clazz, Constant constant) {} 168 169 170 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 171 { 172 // Does the string refer to a class, due to a Class.forName construct? 173 Clazz referencedClass = stringConstant.referencedClass; 174 Clazz newReferencedClass = updateReferencedClass(referencedClass); 175 if (referencedClass != newReferencedClass) 176 { 177 // Change the referenced class. 178 stringConstant.referencedClass = newReferencedClass; 179 180 // Change the referenced class member, if applicable. 181 stringConstant.referencedMember = 182 updateReferencedMember(stringConstant.referencedMember, 183 stringConstant.getString(clazz), 184 null, 185 newReferencedClass); 186 } 187 } 188 189 190 public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) 191 { 192 Clazz referencedClass = refConstant.referencedClass; 193 Clazz newReferencedClass = updateReferencedClass(referencedClass); 194 if (referencedClass != newReferencedClass) 195 { 196 if (DEBUG) 197 { 198 System.out.println("TargetClassChanger:"); 199 System.out.println(" ["+clazz.getName()+"] changing reference from ["+refConstant.referencedClass+"."+refConstant.referencedMember.getName(refConstant.referencedClass)+refConstant.referencedMember.getDescriptor(refConstant.referencedClass)+"]"); 200 } 201 202 // Change the referenced class. 203 refConstant.referencedClass = newReferencedClass; 204 205 // Change the referenced class member. 206 refConstant.referencedMember = 207 updateReferencedMember(refConstant.referencedMember, 208 refConstant.getName(clazz), 209 refConstant.getType(clazz), 210 newReferencedClass); 211 212 if (DEBUG) 213 { 214 System.out.println(" ["+clazz.getName()+"] to ["+refConstant.referencedClass+"."+refConstant.referencedMember.getName(refConstant.referencedClass)+refConstant.referencedMember.getDescriptor(refConstant.referencedClass)+"]"); 215 } 216 } 217 } 218 219 220 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 221 { 222 // Change the referenced class. 223 classConstant.referencedClass = 224 updateReferencedClass(classConstant.referencedClass); 225 } 226 227 228 // Implementations for AttributeVisitor. 229 230 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 231 232 233 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 234 { 235 // Change the references of the attributes. 236 codeAttribute.attributesAccept(clazz, method, this); 237 } 238 239 240 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 241 { 242 // Change the references of the local variables. 243 localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 244 } 245 246 247 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 248 { 249 // Change the references of the local variables. 250 localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 251 } 252 253 254 public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) 255 { 256 // Change the referenced classes. 257 updateReferencedClasses(signatureAttribute.referencedClasses); 258 } 259 260 261 public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) 262 { 263 // Change the references of the annotations. 264 annotationsAttribute.annotationsAccept(clazz, this); 265 } 266 267 268 public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) 269 { 270 // Change the references of the annotations. 271 parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); 272 } 273 274 275 public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) 276 { 277 // Change the references of the annotation. 278 annotationDefaultAttribute.defaultValueAccept(clazz, this); 279 } 280 281 282 283 // Implementations for LocalVariableInfoVisitor. 284 285 public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) 286 { 287 // Change the referenced class. 288 localVariableInfo.referencedClass = 289 updateReferencedClass(localVariableInfo.referencedClass); 290 } 291 292 // Implementations for LocalVariableTypeInfoVisitor. 293 294 public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) 295 { 296 // Change the referenced classes. 297 updateReferencedClasses(localVariableTypeInfo.referencedClasses); 298 } 299 300 // Implementations for AnnotationVisitor. 301 302 public void visitAnnotation(Clazz clazz, Annotation annotation) 303 { 304 // Change the referenced classes. 305 updateReferencedClasses(annotation.referencedClasses); 306 307 // Change the references of the element values. 308 annotation.elementValuesAccept(clazz, this); 309 } 310 311 312 // Implementations for ElementValueVisitor. 313 314 public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) 315 { 316 Clazz referencedClass = elementValue.referencedClass; 317 Clazz newReferencedClass = updateReferencedClass(referencedClass); 318 if (referencedClass != newReferencedClass) 319 { 320 // Change the referenced annotation class. 321 elementValue.referencedClass = newReferencedClass; 322 323 // Change the referenced method. 324 elementValue.referencedMethod = 325 (Method)updateReferencedMember(elementValue.referencedMethod, 326 elementValue.getMethodName(clazz), 327 null, 328 newReferencedClass); 329 } 330 } 331 332 333 public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) 334 { 335 // Change the referenced annotation class and method. 336 visitAnyElementValue(clazz, annotation, constantElementValue); 337 } 338 339 340 public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) 341 { 342 // Change the referenced annotation class and method. 343 visitAnyElementValue(clazz, annotation, enumConstantElementValue); 344 345 // Change the referenced classes. 346 updateReferencedClasses(enumConstantElementValue.referencedClasses); 347 } 348 349 350 public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) 351 { 352 // Change the referenced annotation class and method. 353 visitAnyElementValue(clazz, annotation, classElementValue); 354 355 // Change the referenced classes. 356 updateReferencedClasses(classElementValue.referencedClasses); 357 } 358 359 360 public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) 361 { 362 // Change the referenced annotation class and method. 363 visitAnyElementValue(clazz, annotation, annotationElementValue); 364 365 // Change the references of the annotation. 366 annotationElementValue.annotationAccept(clazz, this); 367 } 368 369 370 public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) 371 { 372 // Change the referenced annotation class and method. 373 visitAnyElementValue(clazz, annotation, arrayElementValue); 374 375 // Change the references of the element values. 376 arrayElementValue.elementValuesAccept(clazz, annotation, this); 377 } 378 379 380 // Small utility methods. 381 382 /** 383 * Updates the retargeted classes in the given array of classes. 384 */ 385 private void updateReferencedClasses(Clazz[] referencedClasses) 386 { 387 if (referencedClasses == null) 388 { 389 return; 390 } 391 392 for (int index = 0; index < referencedClasses.length; index++) 393 { 394 referencedClasses[index] = 395 updateReferencedClass(referencedClasses[index]); 396 } 397 } 398 399 400 /** 401 * Returns the retargeted class of the given class. 402 */ 403 private Clazz updateReferencedClass(Clazz referencedClass) 404 { 405 if (referencedClass == null) 406 { 407 return null; 408 } 409 410 Clazz targetClazz = ClassMerger.getTargetClass(referencedClass); 411 return targetClazz != null ? 412 targetClazz : 413 referencedClass; 414 } 415 416 417 /** 418 * Returns the retargeted class member of the given class member. 419 */ 420 private Member updateReferencedMember(Member referencedMember, 421 String name, 422 String type, 423 Clazz newReferencedClass) 424 { 425 if (referencedMember == null) 426 { 427 return null; 428 } 429 430 return referencedMember instanceof Field ? 431 (Member)newReferencedClass.findField(name, type) : 432 (Member)newReferencedClass.findMethod(name, type); 433 } 434 435 436 /** 437 * Explicitly adds a new class constant for the given class in the given 438 * program class. 439 */ 440 private int addNewClassConstant(ProgramClass programClass, 441 String className, 442 Clazz referencedClass) 443 { 444 ConstantPoolEditor constantPoolEditor = 445 new ConstantPoolEditor(programClass); 446 447 int nameIndex = 448 constantPoolEditor.addUtf8Constant(className); 449 450 int classConstantIndex = 451 constantPoolEditor.addConstant(new ClassConstant(nameIndex, 452 referencedClass)); 453 return classConstantIndex; 454 } 455}