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.classfile.editor; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.*; 25import proguard.classfile.attribute.annotation.*; 26import proguard.classfile.attribute.annotation.visitor.*; 27import proguard.classfile.attribute.visitor.AttributeVisitor; 28import proguard.classfile.constant.*; 29import proguard.classfile.constant.visitor.ConstantVisitor; 30import proguard.classfile.util.*; 31import proguard.classfile.visitor.*; 32 33/** 34 * This ClassVisitor fixes constant pool field and method references to fields 35 * and methods whose names or descriptors have changed. 36 * 37 * @author Eric Lafortune 38 */ 39public class MemberReferenceFixer 40extends SimplifiedVisitor 41implements ClassVisitor, 42 ConstantVisitor, 43 MemberVisitor, 44 AttributeVisitor, 45 AnnotationVisitor, 46 ElementValueVisitor 47{ 48 private static final boolean DEBUG = false; 49 50 51 private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); 52 53 // Parameter for the visitor methods. 54 private int constantIndex; 55 56 // Return values for the visitor methods. 57 private boolean isInterfaceMethod; 58 private boolean stackSizesMayHaveChanged; 59 60 61 // Implementations for ClassVisitor. 62 63 public void visitProgramClass(ProgramClass programClass) 64 { 65 stackSizesMayHaveChanged = false; 66 67 // Fix the constant pool entries. 68 for (int index = 1; index < programClass.u2constantPoolCount; index++) 69 { 70 Constant constant = programClass.constantPool[index]; 71 if (constant != null) 72 { 73 // Fix the entry, replacing it entirely if needed. 74 this.constantIndex = index; 75 76 constant.accept(programClass, this); 77 } 78 } 79 80 // Fix the class members. 81 programClass.fieldsAccept(this); 82 programClass.methodsAccept(this); 83 84 // Fix the attributes. 85 programClass.attributesAccept(this); 86 } 87 88 89 // Implementations for ConstantVisitor. 90 91 public void visitAnyConstant(Clazz clazz, Constant constant) {} 92 93 94 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 95 { 96 // Does the string refer to a class member, due to a 97 // Class.get[Declared]{Field,Method} construct? 98 Member referencedMember = stringConstant.referencedMember; 99 if (referencedMember != null) 100 { 101 Clazz referencedClass = stringConstant.referencedClass; 102 103 // Does it have a new name? 104 String newName = referencedMember.getName(referencedClass); 105 106 if (!stringConstant.getString(clazz).equals(newName)) 107 { 108 if (DEBUG) 109 { 110 debug(clazz, stringConstant, referencedClass, referencedMember); 111 } 112 113 // Update the name. 114 stringConstant.u2stringIndex = 115 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newName); 116 } 117 } 118 } 119 120 121 public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) 122 { 123 // Do we know the referenced field? 124 Member referencedMember = fieldrefConstant.referencedMember; 125 if (referencedMember != null) 126 { 127 Clazz referencedClass = fieldrefConstant.referencedClass; 128 129 // Does it have a new name or type? 130 String newName = referencedMember.getName(referencedClass); 131 String newType = referencedMember.getDescriptor(referencedClass); 132 133 if (!fieldrefConstant.getName(clazz).equals(newName) || 134 !fieldrefConstant.getType(clazz).equals(newType)) 135 { 136 if (DEBUG) 137 { 138 debug(clazz, fieldrefConstant, referencedClass, referencedMember); 139 } 140 141 // Update the name and type index. 142 fieldrefConstant.u2nameAndTypeIndex = 143 new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType); 144 } 145 } 146 } 147 148 149 public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) 150 { 151 // Do we know the referenced interface method? 152 Member referencedMember = interfaceMethodrefConstant.referencedMember; 153 if (referencedMember != null) 154 { 155 Clazz referencedClass = interfaceMethodrefConstant.referencedClass; 156 157 // Does it have a new name or type? 158 String newName = referencedMember.getName(referencedClass); 159 String newType = referencedMember.getDescriptor(referencedClass); 160 161 if (!interfaceMethodrefConstant.getName(clazz).equals(newName) || 162 !interfaceMethodrefConstant.getType(clazz).equals(newType)) 163 { 164 if (DEBUG) 165 { 166 debug(clazz, interfaceMethodrefConstant, referencedClass, referencedMember); 167 } 168 169 // Update the name and type index. 170 interfaceMethodrefConstant.u2nameAndTypeIndex = 171 new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType); 172 173 // Remember that the stack sizes of the methods in this class 174 // may have changed. 175 stackSizesMayHaveChanged = true; 176 } 177 178 // Check if this is an interface method. 179 isInterfaceMethod = true; 180 clazz.constantPoolEntryAccept(interfaceMethodrefConstant.u2classIndex, this); 181 182 // Has the method become a non-interface method? 183 if (!isInterfaceMethod) 184 { 185 if (DEBUG) 186 { 187 System.out.println("MemberReferenceFixer:"); 188 System.out.println(" Class file = "+clazz.getName()); 189 System.out.println(" Ref class = "+referencedClass.getName()); 190 System.out.println(" Ref method = "+interfaceMethodrefConstant.getName(clazz)+interfaceMethodrefConstant.getType(clazz)); 191 System.out.println(" -> ordinary method"); 192 } 193 194 // Replace the interface method reference by a method reference. 195 ((ProgramClass)clazz).constantPool[this.constantIndex] = 196 new MethodrefConstant(interfaceMethodrefConstant.u2classIndex, 197 interfaceMethodrefConstant.u2nameAndTypeIndex, 198 referencedClass, 199 referencedMember); 200 } 201 } 202 } 203 204 205 public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) 206 { 207 // Do we know the referenced method? 208 Member referencedMember = methodrefConstant.referencedMember; 209 if (referencedMember != null) 210 { 211 Clazz referencedClass = methodrefConstant.referencedClass; 212 213 // Does it have a new name or type? 214 String newName = referencedMember.getName(referencedClass); 215 String newType = referencedMember.getDescriptor(referencedClass); 216 217 if (!methodrefConstant.getName(clazz).equals(newName) || 218 !methodrefConstant.getType(clazz).equals(newType)) 219 { 220 if (DEBUG) 221 { 222 debug(clazz, methodrefConstant, referencedClass, referencedMember); 223 } 224 225 // Update the name and type index. 226 methodrefConstant.u2nameAndTypeIndex = 227 new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType); 228 229 // Remember that the stack sizes of the methods in this class 230 // may have changed. 231 stackSizesMayHaveChanged = true; 232 } 233 234 // Check if this is an interface method. 235 isInterfaceMethod = false; 236 clazz.constantPoolEntryAccept(methodrefConstant.u2classIndex, this); 237 238 // Has the method become an interface method? 239 if (isInterfaceMethod) 240 { 241 if (DEBUG) 242 { 243 System.out.println("MemberReferenceFixer:"); 244 System.out.println(" Class file = "+clazz.getName()); 245 System.out.println(" Ref class = "+referencedClass.getName()); 246 System.out.println(" Ref method = "+methodrefConstant.getName(clazz)+methodrefConstant.getType(clazz)); 247 System.out.println(" -> interface method"); 248 } 249 250 // Replace the method reference by an interface method reference. 251 ((ProgramClass)clazz).constantPool[this.constantIndex] = 252 new InterfaceMethodrefConstant(methodrefConstant.u2classIndex, 253 methodrefConstant.u2nameAndTypeIndex, 254 referencedClass, 255 referencedMember); 256 } 257 } 258 } 259 260 261 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 262 { 263 // Check if this class entry is an array type. 264 if (ClassUtil.isInternalArrayType(classConstant.getName(clazz))) 265 { 266 isInterfaceMethod = false; 267 } 268 else 269 { 270 // Check if this class entry refers to an interface class. 271 Clazz referencedClass = classConstant.referencedClass; 272 if (referencedClass != null) 273 { 274 isInterfaceMethod = (referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0; 275 } 276 } 277 } 278 279 280 // Implementations for MemberVisitor. 281 282 public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) 283 { 284 // Fix the attributes. 285 programMember.attributesAccept(programClass, this); 286 } 287 288 289 // Implementations for AttributeVisitor. 290 291 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 292 293 294 public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) 295 { 296 Member referencedMember = enclosingMethodAttribute.referencedMethod; 297 if (referencedMember != null) 298 { 299 Clazz referencedClass = enclosingMethodAttribute.referencedClass; 300 301 // Does it have a new class? 302 if (!enclosingMethodAttribute.getClassName(clazz).equals(referencedClass.getName())) 303 { 304 // Update the class index. 305 enclosingMethodAttribute.u2classIndex = 306 new ConstantPoolEditor((ProgramClass)clazz).addClassConstant(referencedClass); 307 } 308 309 // Does it have a new name or type? 310 if (!enclosingMethodAttribute.getName(clazz).equals(referencedMember.getName(referencedClass)) || 311 !enclosingMethodAttribute.getType(clazz).equals(referencedMember.getDescriptor(referencedClass))) 312 { 313 // Update the name and type index. 314 enclosingMethodAttribute.u2nameAndTypeIndex = 315 new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(referencedMember.getName(referencedClass), 316 referencedMember.getDescriptor(referencedClass)); 317 } 318 } 319 } 320 321 322 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 323 { 324 // Recompute the maximum stack size if necessary. 325 if (stackSizesMayHaveChanged) 326 { 327 stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); 328 } 329 330 // Fix the nested attributes. 331 codeAttribute.attributesAccept(clazz, method, this); 332 } 333 334 335 public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) 336 { 337 // Fix the annotations. 338 annotationsAttribute.annotationsAccept(clazz, this); 339 } 340 341 342 public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) 343 { 344 // Fix the annotations. 345 parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); 346 } 347 348 349 public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) 350 { 351 // Fix the annotation. 352 annotationDefaultAttribute.defaultValueAccept(clazz, this); 353 } 354 355 356 // Implementations for AnnotationVisitor. 357 358 public void visitAnnotation(Clazz clazz, Annotation annotation) 359 { 360 // Fix the element values. 361 annotation.elementValuesAccept(clazz, this); 362 } 363 364 365 // Implementations for ElementValueVisitor. 366 367 public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) 368 { 369 fixElementValue(clazz, annotation, constantElementValue); 370 } 371 372 373 public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) 374 { 375 fixElementValue(clazz, annotation, enumConstantElementValue); 376 } 377 378 379 public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) 380 { 381 fixElementValue(clazz, annotation, classElementValue); 382 } 383 384 385 public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) 386 { 387 fixElementValue(clazz, annotation, annotationElementValue); 388 389 // Fix the annotation. 390 annotationElementValue.annotationAccept(clazz, this); 391 } 392 393 394 public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) 395 { 396 fixElementValue(clazz, annotation, arrayElementValue); 397 398 // Fix the element values. 399 arrayElementValue.elementValuesAccept(clazz, annotation, this); 400 } 401 402 403 // Small utility methods. 404 405 /** 406 * Fixes the method reference of the element value, if any. 407 */ 408 private void fixElementValue(Clazz clazz, 409 Annotation annotation, 410 ElementValue elementValue) 411 { 412 // Do we know the referenced method? 413 Member referencedMember = elementValue.referencedMethod; 414 if (referencedMember != null) 415 { 416 // Does it have a new name or type? 417 String methodName = elementValue.getMethodName(clazz); 418 String newMethodName = referencedMember.getName(elementValue.referencedClass); 419 420 if (!methodName.equals(newMethodName)) 421 { 422 // Update the element name index. 423 elementValue.u2elementNameIndex = 424 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newMethodName); 425 } 426 } 427 } 428 429 430 private void debug(Clazz clazz, 431 StringConstant stringConstant, 432 Clazz referencedClass, 433 Member referencedMember) 434 { 435 System.out.println("MemberReferenceFixer:"); 436 System.out.println(" Class file = "+clazz.getName()); 437 System.out.println(" Ref class = "+referencedClass.getName()); 438 System.out.println(" Ref member name = "+stringConstant.getString(clazz)); 439 System.out.println(" -> "+referencedMember.getName(referencedClass)); 440 } 441 442 443 private void debug(Clazz clazz, 444 RefConstant refConstant, 445 Clazz referencedClass, 446 Member referencedMember) 447 { 448 System.out.println("MemberReferenceFixer:"); 449 System.out.println(" Class file = "+clazz.getName()); 450 System.out.println(" Ref class = "+referencedClass.getName()); 451 System.out.println(" Ref member name = "+refConstant.getName(clazz)); 452 System.out.println(" -> "+referencedMember.getName(referencedClass)); 453 System.out.println(" Ref descriptor = "+refConstant.getType(clazz)); 454 System.out.println(" -> "+referencedMember.getDescriptor(referencedClass)); 455 } 456} 457