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.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.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 name or type? 302 String newName = referencedMember.getName(referencedClass); 303 String newType = referencedMember.getDescriptor(referencedClass); 304 305 if (!enclosingMethodAttribute.getName(clazz).equals(newName) || 306 !enclosingMethodAttribute.getType(clazz).equals(newType)) 307 { 308 // Update the name and type index. 309 enclosingMethodAttribute.u2nameAndTypeIndex = 310 new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, 311 newType); 312 } 313 } 314 } 315 316 317 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 318 { 319 // Recompute the maximum stack size if necessary. 320 if (stackSizesMayHaveChanged) 321 { 322 stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); 323 } 324 325 // Fix the nested attributes. 326 codeAttribute.attributesAccept(clazz, method, this); 327 } 328 329 330 public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) 331 { 332 // Fix the annotations. 333 annotationsAttribute.annotationsAccept(clazz, this); 334 } 335 336 337 public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) 338 { 339 // Fix the annotations. 340 parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); 341 } 342 343 344 public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) 345 { 346 // Fix the annotation. 347 annotationDefaultAttribute.defaultValueAccept(clazz, this); 348 } 349 350 351 // Implementations for AnnotationVisitor. 352 353 public void visitAnnotation(Clazz clazz, Annotation annotation) 354 { 355 // Fix the element values. 356 annotation.elementValuesAccept(clazz, this); 357 } 358 359 360 // Implementations for ElementValueVisitor. 361 362 public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) 363 { 364 fixElementValue(clazz, annotation, constantElementValue); 365 } 366 367 368 public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) 369 { 370 fixElementValue(clazz, annotation, enumConstantElementValue); 371 } 372 373 374 public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) 375 { 376 fixElementValue(clazz, annotation, classElementValue); 377 } 378 379 380 public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) 381 { 382 fixElementValue(clazz, annotation, annotationElementValue); 383 384 // Fix the annotation. 385 annotationElementValue.annotationAccept(clazz, this); 386 } 387 388 389 public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) 390 { 391 fixElementValue(clazz, annotation, arrayElementValue); 392 393 // Fix the element values. 394 arrayElementValue.elementValuesAccept(clazz, annotation, this); 395 } 396 397 398 // Small utility methods. 399 400 /** 401 * Fixes the method reference of the element value, if any. 402 */ 403 private void fixElementValue(Clazz clazz, 404 Annotation annotation, 405 ElementValue elementValue) 406 { 407 // Do we know the referenced method? 408 Member referencedMember = elementValue.referencedMethod; 409 if (referencedMember != null) 410 { 411 // Does it have a new name or type? 412 String methodName = elementValue.getMethodName(clazz); 413 String newMethodName = referencedMember.getName(elementValue.referencedClass); 414 415 if (!methodName.equals(newMethodName)) 416 { 417 // Update the element name index. 418 elementValue.u2elementNameIndex = 419 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newMethodName); 420 } 421 } 422 } 423 424 425 private void debug(Clazz clazz, 426 StringConstant stringConstant, 427 Clazz referencedClass, 428 Member referencedMember) 429 { 430 System.out.println("MemberReferenceFixer:"); 431 System.out.println(" ["+clazz.getName()+"]: String ["+ 432 stringConstant.getString(clazz)+"] -> ["+ 433 referencedClass.getName()+"."+referencedMember.getName(referencedClass)+" "+referencedMember.getDescriptor(referencedClass)+"]"); 434 } 435 436 437 private void debug(Clazz clazz, 438 RefConstant refConstant, 439 Clazz referencedClass, 440 Member referencedMember) 441 { 442 System.out.println("MemberReferenceFixer:"); 443 System.out.println(" ["+clazz.getName()+"]: ["+ 444 refConstant.getClassName(clazz)+"."+refConstant.getName(clazz)+" "+refConstant.getType(clazz)+"] -> ["+ 445 referencedClass.getName()+"."+referencedMember.getName(referencedClass)+" "+referencedMember.getDescriptor(referencedClass)+"]"); 446 } 447} 448