1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2009 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 // Implementations for ClassVisitor. 55 56 public void visitProgramClass(ProgramClass programClass) 57 { 58 Clazz superClass = null; 59 Clazz[] interfaceClasses = null; 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 subclasses. 84 programClass.subClasses = null; 85 } 86 87 // Remove interface classes that are pointing to this class. 88 int newInterfacesCount = 0; 89 for (int index = 0; index < programClass.u2interfacesCount; index++) 90 { 91 Clazz interfaceClass = programClass.getInterface(index); 92 if (!programClass.equals(interfaceClass)) 93 { 94 programClass.u2interfaces[newInterfacesCount++] = 95 programClass.u2interfaces[index]; 96 } 97 } 98 programClass.u2interfacesCount = newInterfacesCount; 99 100 // Update the subclasses of the superclass and interfaces of the 101 // target class. 102 ConstantVisitor subclassAdder = 103 new ReferencedClassVisitor( 104 new SubclassFilter(programClass, 105 new SubclassAdder(programClass))); 106 107 programClass.superClassConstantAccept(subclassAdder); 108 programClass.interfaceConstantsAccept(subclassAdder); 109 110 // TODO: Maybe restore private method references. 111 } 112 113 114 public void visitLibraryClass(LibraryClass libraryClass) 115 { 116 // Change the references of the class members. 117 libraryClass.fieldsAccept(this); 118 libraryClass.methodsAccept(this); 119 } 120 121 122 // Implementations for MemberVisitor. 123 124 public void visitProgramField(ProgramClass programClass, ProgramField programField) 125 { 126 // Change the referenced class. 127 programField.referencedClass = 128 updateReferencedClass(programField.referencedClass); 129 130 // Change the references of the attributes. 131 programField.attributesAccept(programClass, this); 132 } 133 134 135 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 136 { 137 // Change the referenced classes. 138 updateReferencedClasses(programMethod.referencedClasses); 139 140 // Change the references of the attributes. 141 programMethod.attributesAccept(programClass, this); 142 } 143 144 145 public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) 146 { 147 // Change the referenced class. 148 libraryField.referencedClass = 149 updateReferencedClass(libraryField.referencedClass); 150 } 151 152 153 public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) 154 { 155 // Change the referenced classes. 156 updateReferencedClasses(libraryMethod.referencedClasses); 157 } 158 159 160 // Implementations for ConstantVisitor. 161 162 public void visitAnyConstant(Clazz clazz, Constant constant) {} 163 164 165 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 166 { 167 // Does the string refer to a class, due to a Class.forName construct? 168 Clazz referencedClass = stringConstant.referencedClass; 169 Clazz newReferencedClass = updateReferencedClass(referencedClass); 170 if (referencedClass != newReferencedClass) 171 { 172 // Change the referenced class. 173 stringConstant.referencedClass = newReferencedClass; 174 175 // Change the referenced class member, if applicable. 176 stringConstant.referencedMember = 177 updateReferencedMember(stringConstant.referencedMember, 178 stringConstant.getString(clazz), 179 null, 180 newReferencedClass); 181 } 182 } 183 184 185 public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) 186 { 187 Clazz referencedClass = refConstant.referencedClass; 188 Clazz newReferencedClass = updateReferencedClass(referencedClass); 189 if (referencedClass != newReferencedClass) 190 { 191 // Change the referenced class. 192 refConstant.referencedClass = newReferencedClass; 193 194 // Change the referenced class member. 195 refConstant.referencedMember = 196 updateReferencedMember(refConstant.referencedMember, 197 refConstant.getName(clazz), 198 refConstant.getType(clazz), 199 newReferencedClass); 200 } 201 } 202 203 204 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 205 { 206 // Change the referenced class. 207 classConstant.referencedClass = 208 updateReferencedClass(classConstant.referencedClass); 209 } 210 211 212 // Implementations for AttributeVisitor. 213 214 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 215 216 217 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 218 { 219 // Change the references of the attributes. 220 codeAttribute.attributesAccept(clazz, method, this); 221 } 222 223 224 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 225 { 226 // Change the references of the local variables. 227 localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 228 } 229 230 231 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 232 { 233 // Change the references of the local variables. 234 localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 235 } 236 237 238 public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) 239 { 240 // Change the referenced classes. 241 updateReferencedClasses(signatureAttribute.referencedClasses); 242 } 243 244 245 public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) 246 { 247 // Change the references of the annotations. 248 annotationsAttribute.annotationsAccept(clazz, this); 249 } 250 251 252 public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) 253 { 254 // Change the references of the annotations. 255 parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); 256 } 257 258 259 public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) 260 { 261 // Change the references of the annotation. 262 annotationDefaultAttribute.defaultValueAccept(clazz, this); 263 } 264 265 266 267 // Implementations for LocalVariableInfoVisitor. 268 269 public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) 270 { 271 // Change the referenced class. 272 localVariableInfo.referencedClass = 273 updateReferencedClass(localVariableInfo.referencedClass); 274 } 275 276 // Implementations for LocalVariableTypeInfoVisitor. 277 278 public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) 279 { 280 // Change the referenced classes. 281 updateReferencedClasses(localVariableTypeInfo.referencedClasses); 282 } 283 284 // Implementations for AnnotationVisitor. 285 286 public void visitAnnotation(Clazz clazz, Annotation annotation) 287 { 288 // Change the referenced classes. 289 updateReferencedClasses(annotation.referencedClasses); 290 291 // Change the references of the element values. 292 annotation.elementValuesAccept(clazz, this); 293 } 294 295 296 // Implementations for ElementValueVisitor. 297 298 public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) 299 { 300 Clazz referencedClass = elementValue.referencedClass; 301 Clazz newReferencedClass = updateReferencedClass(referencedClass); 302 if (referencedClass != newReferencedClass) 303 { 304 // Change the referenced annotation class. 305 elementValue.referencedClass = newReferencedClass; 306 307 // Change the referenced method. 308 elementValue.referencedMethod = 309 (Method)updateReferencedMember(elementValue.referencedMethod, 310 elementValue.getMethodName(clazz), 311 null, 312 newReferencedClass); 313 } 314 } 315 316 317 public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) 318 { 319 // Change the referenced annotation class and method. 320 visitAnyElementValue(clazz, annotation, constantElementValue); 321 } 322 323 324 public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) 325 { 326 // Change the referenced annotation class and method. 327 visitAnyElementValue(clazz, annotation, enumConstantElementValue); 328 329 // Change the referenced classes. 330 updateReferencedClasses(enumConstantElementValue.referencedClasses); 331 } 332 333 334 public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) 335 { 336 // Change the referenced annotation class and method. 337 visitAnyElementValue(clazz, annotation, classElementValue); 338 339 // Change the referenced classes. 340 updateReferencedClasses(classElementValue.referencedClasses); 341 } 342 343 344 public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) 345 { 346 // Change the referenced annotation class and method. 347 visitAnyElementValue(clazz, annotation, annotationElementValue); 348 349 // Change the references of the annotation. 350 annotationElementValue.annotationAccept(clazz, this); 351 } 352 353 354 public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) 355 { 356 // Change the referenced annotation class and method. 357 visitAnyElementValue(clazz, annotation, arrayElementValue); 358 359 // Change the references of the element values. 360 arrayElementValue.elementValuesAccept(clazz, annotation, this); 361 } 362 363 364 // Small utility methods. 365 366 /** 367 * Updates the retargeted classes in the given array of classes. 368 */ 369 private void updateReferencedClasses(Clazz[] referencedClasses) 370 { 371 if (referencedClasses == null) 372 { 373 return; 374 } 375 376 for (int index = 0; index < referencedClasses.length; index++) 377 { 378 referencedClasses[index] = 379 updateReferencedClass(referencedClasses[index]); 380 } 381 } 382 383 384 /** 385 * Returns the retargeted class of the given class. 386 */ 387 private Clazz updateReferencedClass(Clazz referencedClass) 388 { 389 if (referencedClass == null) 390 { 391 return null; 392 } 393 394 Clazz targetClazz = ClassMerger.getTargetClass(referencedClass); 395 return targetClazz != null ? 396 targetClazz : 397 referencedClass; 398 } 399 400 401 /** 402 * Returns the retargeted class member of the given class member. 403 */ 404 private Member updateReferencedMember(Member referencedMember, 405 String name, 406 String type, 407 Clazz newReferencedClass) 408 { 409 if (referencedMember == null) 410 { 411 return null; 412 } 413 414 return referencedMember instanceof Field ? 415 (Member)newReferencedClass.findField(name, type) : 416 (Member)newReferencedClass.findMethod(name, type); 417 } 418 419 420 /** 421 * Explicitly adds a new class constant for the given class in the given 422 * program class. 423 */ 424 private int addNewClassConstant(ProgramClass programClass, 425 String className, 426 Clazz referencedClass) 427 { 428 ConstantPoolEditor constantPoolEditor = 429 new ConstantPoolEditor(programClass); 430 431 int nameIndex = 432 constantPoolEditor.addUtf8Constant(className); 433 434 int classConstantIndex = 435 constantPoolEditor.addConstant(new ClassConstant(nameIndex, 436 referencedClass)); 437 return classConstantIndex; 438 } 439}