1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 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.attribute.*; 25import proguard.classfile.attribute.annotation.*; 26import proguard.classfile.attribute.annotation.visitor.*; 27import proguard.classfile.attribute.visitor.*; 28import proguard.classfile.constant.*; 29import proguard.classfile.constant.visitor.ConstantVisitor; 30import proguard.classfile.editor.*; 31import proguard.classfile.util.SimplifiedVisitor; 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 // We're only making changes locally in the class. 62 // Not all other classes may have been retargeted yet. 63 64 // Change the references of the constant pool. 65 programClass.constantPoolEntriesAccept(this); 66 67 // Change the references of the class members. 68 programClass.fieldsAccept(this); 69 programClass.methodsAccept(this); 70 71 // Change the references of the attributes. 72 programClass.attributesAccept(this); 73 74 // Remove duplicate interfaces and interface classes that have ended 75 // up pointing to the class itself. 76 boolean[] delete = null; 77 for (int index = 0; index < programClass.u2interfacesCount; index++) 78 { 79 Clazz interfaceClass = programClass.getInterface(index); 80 if (interfaceClass != null && 81 (programClass.equals(interfaceClass) || 82 containsInterfaceClass(programClass, 83 index, 84 interfaceClass))) 85 { 86 // Lazily create the array. 87 if (delete == null) 88 { 89 delete = new boolean[programClass.u2interfacesCount]; 90 } 91 92 delete[index] = true; 93 } 94 } 95 96 if (delete != null) 97 { 98 new InterfaceDeleter(delete).visitProgramClass(programClass); 99 } 100 101 // Is the class being retargeted? 102 Clazz targetClass = ClassMerger.getTargetClass(programClass); 103 if (targetClass != null) 104 { 105 // We're not changing anything special in the superclass and 106 // interface hierarchy of the retargeted class. The shrinking 107 // step will remove the class for us. 108 109 // Restore the class name. We have to add a new class entry 110 // to avoid an existing entry with the same name being reused. The 111 // names have to be fixed later, based on their referenced classes. 112 programClass.u2thisClass = 113 addNewClassConstant(programClass, 114 programClass.getName(), 115 programClass); 116 117 // This class will no longer have any subclasses, because their 118 // subclasses and interfaces will be retargeted. 119 programClass.subClasses = null; 120 } 121 else 122 { 123 // This class has become the subclass of its possibly new 124 // superclass and of any new interfaces. 125 ConstantVisitor subclassAdder = 126 new ReferencedClassVisitor( 127 new SubclassFilter(programClass, 128 new SubclassAdder(programClass))); 129 130 programClass.superClassConstantAccept(subclassAdder); 131 programClass.interfaceConstantsAccept(subclassAdder); 132 133 // TODO: Maybe restore private method references. 134 } 135 } 136 137 138 public void visitLibraryClass(LibraryClass libraryClass) 139 { 140 // Change the references of the class members. 141 libraryClass.fieldsAccept(this); 142 libraryClass.methodsAccept(this); 143 } 144 145 146 // Implementations for MemberVisitor. 147 148 public void visitProgramField(ProgramClass programClass, ProgramField programField) 149 { 150 // Change the referenced class. 151 programField.referencedClass = 152 updateReferencedClass(programField.referencedClass); 153 154 // Change the references of the attributes. 155 programField.attributesAccept(programClass, this); 156 } 157 158 159 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 160 { 161 // Change the referenced classes. 162 updateReferencedClasses(programMethod.referencedClasses); 163 164 // Change the references of the attributes. 165 programMethod.attributesAccept(programClass, this); 166 } 167 168 169 public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) 170 { 171 // Change the referenced class. 172 libraryField.referencedClass = 173 updateReferencedClass(libraryField.referencedClass); 174 } 175 176 177 public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) 178 { 179 // Change the referenced classes. 180 updateReferencedClasses(libraryMethod.referencedClasses); 181 } 182 183 184 // Implementations for ConstantVisitor. 185 186 public void visitAnyConstant(Clazz clazz, Constant constant) {} 187 188 189 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 190 { 191 // Does the string refer to a class, due to a Class.forName construct? 192 Clazz referencedClass = stringConstant.referencedClass; 193 Clazz newReferencedClass = updateReferencedClass(referencedClass); 194 if (referencedClass != newReferencedClass) 195 { 196 // Change the referenced class. 197 stringConstant.referencedClass = newReferencedClass; 198 199 // Change the referenced class member, if applicable. 200 stringConstant.referencedMember = 201 updateReferencedMember(stringConstant.referencedMember, 202 stringConstant.getString(clazz), 203 null, 204 newReferencedClass); 205 } 206 } 207 208 209 public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) 210 { 211 Clazz referencedClass = refConstant.referencedClass; 212 Clazz newReferencedClass = updateReferencedClass(referencedClass); 213 if (referencedClass != newReferencedClass) 214 { 215 if (DEBUG) 216 { 217 System.out.println("TargetClassChanger:"); 218 System.out.println(" ["+clazz.getName()+"] changing reference from ["+refConstant.referencedClass+"."+refConstant.referencedMember.getName(refConstant.referencedClass)+refConstant.referencedMember.getDescriptor(refConstant.referencedClass)+"]"); 219 } 220 221 // Change the referenced class. 222 refConstant.referencedClass = newReferencedClass; 223 224 // Change the referenced class member. 225 refConstant.referencedMember = 226 updateReferencedMember(refConstant.referencedMember, 227 refConstant.getName(clazz), 228 refConstant.getType(clazz), 229 newReferencedClass); 230 231 if (DEBUG) 232 { 233 System.out.println(" ["+clazz.getName()+"] to ["+refConstant.referencedClass+"."+refConstant.referencedMember.getName(refConstant.referencedClass)+refConstant.referencedMember.getDescriptor(refConstant.referencedClass)+"]"); 234 } 235 } 236 } 237 238 239 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 240 { 241 // Change the referenced class. 242 classConstant.referencedClass = 243 updateReferencedClass(classConstant.referencedClass); 244 } 245 246 247 // Implementations for AttributeVisitor. 248 249 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 250 251 252 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 253 { 254 // Change the references of the attributes. 255 codeAttribute.attributesAccept(clazz, method, this); 256 } 257 258 259 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 260 { 261 // Change the references of the local variables. 262 localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 263 } 264 265 266 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 267 { 268 // Change the references of the local variables. 269 localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 270 } 271 272 273 public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) 274 { 275 // Change the referenced classes. 276 updateReferencedClasses(signatureAttribute.referencedClasses); 277 } 278 279 280 public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) 281 { 282 // Change the references of the annotations. 283 annotationsAttribute.annotationsAccept(clazz, this); 284 } 285 286 287 public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) 288 { 289 // Change the references of the annotations. 290 parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); 291 } 292 293 294 public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) 295 { 296 // Change the references of the annotation. 297 annotationDefaultAttribute.defaultValueAccept(clazz, this); 298 } 299 300 301 // Implementations for LocalVariableInfoVisitor. 302 303 public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) 304 { 305 // Change the referenced class. 306 localVariableInfo.referencedClass = 307 updateReferencedClass(localVariableInfo.referencedClass); 308 } 309 310 311 // Implementations for LocalVariableTypeInfoVisitor. 312 313 public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) 314 { 315 // Change the referenced classes. 316 updateReferencedClasses(localVariableTypeInfo.referencedClasses); 317 } 318 319 320 // Implementations for AnnotationVisitor. 321 322 public void visitAnnotation(Clazz clazz, Annotation annotation) 323 { 324 // Change the referenced classes. 325 updateReferencedClasses(annotation.referencedClasses); 326 327 // Change the references of the element values. 328 annotation.elementValuesAccept(clazz, this); 329 } 330 331 332 // Implementations for ElementValueVisitor. 333 334 public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) 335 { 336 Clazz referencedClass = elementValue.referencedClass; 337 Clazz newReferencedClass = updateReferencedClass(referencedClass); 338 if (referencedClass != newReferencedClass) 339 { 340 // Change the referenced annotation class. 341 elementValue.referencedClass = newReferencedClass; 342 343 // Change the referenced method. 344 elementValue.referencedMethod = 345 (Method)updateReferencedMember(elementValue.referencedMethod, 346 elementValue.getMethodName(clazz), 347 null, 348 newReferencedClass); 349 } 350 } 351 352 353 public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) 354 { 355 // Change the referenced annotation class and method. 356 visitAnyElementValue(clazz, annotation, constantElementValue); 357 } 358 359 360 public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) 361 { 362 // Change the referenced annotation class and method. 363 visitAnyElementValue(clazz, annotation, enumConstantElementValue); 364 365 // Change the referenced classes. 366 updateReferencedClasses(enumConstantElementValue.referencedClasses); 367 } 368 369 370 public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) 371 { 372 // Change the referenced annotation class and method. 373 visitAnyElementValue(clazz, annotation, classElementValue); 374 375 // Change the referenced classes. 376 updateReferencedClasses(classElementValue.referencedClasses); 377 } 378 379 380 public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) 381 { 382 // Change the referenced annotation class and method. 383 visitAnyElementValue(clazz, annotation, annotationElementValue); 384 385 // Change the references of the annotation. 386 annotationElementValue.annotationAccept(clazz, this); 387 } 388 389 390 public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) 391 { 392 // Change the referenced annotation class and method. 393 visitAnyElementValue(clazz, annotation, arrayElementValue); 394 395 // Change the references of the element values. 396 arrayElementValue.elementValuesAccept(clazz, annotation, this); 397 } 398 399 400 // Small utility methods. 401 402 /** 403 * Returns whether the given class contains the given interface 404 * class in its first given number of interfaces. 405 */ 406 private boolean containsInterfaceClass(Clazz clazz, 407 int interfaceCount, 408 Clazz interfaceClass) 409 { 410 for (int index = 0; index < interfaceCount; index++) 411 { 412 if (interfaceClass.equals(clazz.getInterface(index))) 413 { 414 return true; 415 } 416 } 417 418 return false; 419 } 420 421 422 /** 423 * Updates the retargeted classes in the given array of classes. 424 */ 425 private void updateReferencedClasses(Clazz[] referencedClasses) 426 { 427 if (referencedClasses == null) 428 { 429 return; 430 } 431 432 for (int index = 0; index < referencedClasses.length; index++) 433 { 434 referencedClasses[index] = 435 updateReferencedClass(referencedClasses[index]); 436 } 437 } 438 439 440 /** 441 * Returns the retargeted class of the given class. 442 */ 443 private Clazz updateReferencedClass(Clazz referencedClass) 444 { 445 if (referencedClass == null) 446 { 447 return null; 448 } 449 450 Clazz targetClazz = ClassMerger.getTargetClass(referencedClass); 451 return targetClazz != null ? 452 targetClazz : 453 referencedClass; 454 } 455 456 457 /** 458 * Returns the retargeted class member of the given class member. 459 */ 460 private Member updateReferencedMember(Member referencedMember, 461 String name, 462 String type, 463 Clazz newReferencedClass) 464 { 465 if (referencedMember == null) 466 { 467 return null; 468 } 469 470 return referencedMember instanceof Field ? 471 (Member)newReferencedClass.findField(name, type) : 472 (Member)newReferencedClass.findMethod(name, type); 473 } 474 475 476 /** 477 * Explicitly adds a new class constant for the given class in the given 478 * program class. 479 */ 480 private int addNewClassConstant(ProgramClass programClass, 481 String className, 482 Clazz referencedClass) 483 { 484 ConstantPoolEditor constantPoolEditor = 485 new ConstantPoolEditor(programClass); 486 487 int nameIndex = 488 constantPoolEditor.addUtf8Constant(className); 489 490 int classConstantIndex = 491 constantPoolEditor.addConstant(new ClassConstant(nameIndex, 492 referencedClass)); 493 return classConstantIndex; 494 } 495}