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.*; 28import proguard.classfile.constant.*; 29import proguard.classfile.constant.visitor.ConstantVisitor; 30import proguard.classfile.util.*; 31import proguard.classfile.visitor.*; 32 33/** 34 * This ClassVisitor fixes references of constant pool entries, fields, 35 * methods, and attributes to classes whose names have changed. Descriptors 36 * of member references are not updated yet. 37 * 38 * @see MemberReferenceFixer 39 * @author Eric Lafortune 40 */ 41public class ClassReferenceFixer 42extends SimplifiedVisitor 43implements ClassVisitor, 44 ConstantVisitor, 45 MemberVisitor, 46 AttributeVisitor, 47 InnerClassesInfoVisitor, 48 LocalVariableInfoVisitor, 49 LocalVariableTypeInfoVisitor, 50 AnnotationVisitor, 51 ElementValueVisitor 52{ 53 private final boolean ensureUniqueMemberNames; 54 55 56 /** 57 * Creates a new ClassReferenceFixer. 58 * @param ensureUniqueMemberNames specifies whether class members whose 59 * descriptor changes should get new, unique 60 * names, in order to avoid naming conflicts 61 * with similar methods. 62 */ 63 public ClassReferenceFixer(boolean ensureUniqueMemberNames) 64 { 65 this.ensureUniqueMemberNames = ensureUniqueMemberNames; 66 } 67 68 69 // Implementations for ClassVisitor. 70 71 public void visitProgramClass(ProgramClass programClass) 72 { 73 // Fix the constant pool. 74 programClass.constantPoolEntriesAccept(this); 75 76 // Fix class members. 77 programClass.fieldsAccept(this); 78 programClass.methodsAccept(this); 79 80 // Fix the attributes. 81 programClass.attributesAccept(this); 82 } 83 84 85 public void visitLibraryClass(LibraryClass libraryClass) 86 { 87 // Fix class members. 88 libraryClass.fieldsAccept(this); 89 libraryClass.methodsAccept(this); 90 } 91 92 93 // Implementations for MemberVisitor. 94 95 public void visitProgramField(ProgramClass programClass, ProgramField programField) 96 { 97 // Has the descriptor changed? 98 String descriptor = programField.getDescriptor(programClass); 99 String newDescriptor = newDescriptor(descriptor, 100 programField.referencedClass); 101 102 if (!descriptor.equals(newDescriptor)) 103 { 104 ConstantPoolEditor constantPoolEditor = 105 new ConstantPoolEditor(programClass); 106 107 // Update the descriptor. 108 programField.u2descriptorIndex = 109 constantPoolEditor.addUtf8Constant(newDescriptor); 110 111 // Update the name, if requested. 112 if (ensureUniqueMemberNames) 113 { 114 String name = programField.getName(programClass); 115 String newName = newUniqueMemberName(name, descriptor); 116 programField.u2nameIndex = 117 constantPoolEditor.addUtf8Constant(newName); 118 } 119 } 120 121 // Fix the attributes. 122 programField.attributesAccept(programClass, this); 123 } 124 125 126 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 127 { 128 // Has the descriptor changed? 129 String descriptor = programMethod.getDescriptor(programClass); 130 String newDescriptor = newDescriptor(descriptor, 131 programMethod.referencedClasses); 132 133 if (!descriptor.equals(newDescriptor)) 134 { 135 ConstantPoolEditor constantPoolEditor = 136 new ConstantPoolEditor(programClass); 137 138 // Update the descriptor. 139 programMethod.u2descriptorIndex = 140 constantPoolEditor.addUtf8Constant(newDescriptor); 141 142 // Update the name, if requested. 143 if (ensureUniqueMemberNames) 144 { 145 String name = programMethod.getName(programClass); 146 String newName = newUniqueMemberName(name, descriptor); 147 programMethod.u2nameIndex = 148 constantPoolEditor.addUtf8Constant(newName); 149 } 150 } 151 152 // Fix the attributes. 153 programMethod.attributesAccept(programClass, this); 154 } 155 156 157 public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) 158 { 159 // Has the descriptor changed? 160 String descriptor = libraryField.getDescriptor(libraryClass); 161 String newDescriptor = newDescriptor(descriptor, 162 libraryField.referencedClass); 163 164 // Update the descriptor. 165 libraryField.descriptor = newDescriptor; 166 } 167 168 169 public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) 170 { 171 // Has the descriptor changed? 172 String descriptor = libraryMethod.getDescriptor(libraryClass); 173 String newDescriptor = newDescriptor(descriptor, 174 libraryMethod.referencedClasses); 175 176 // Update the descriptor. 177 libraryMethod.descriptor = newDescriptor; 178 } 179 180 181 // Implementations for ConstantVisitor. 182 183 public void visitAnyConstant(Clazz clazz, Constant constant) {} 184 185 186 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 187 { 188 // Does the string refer to a class, due to a Class.forName construct? 189 Clazz referencedClass = stringConstant.referencedClass; 190 Member referencedMember = stringConstant.referencedMember; 191 if (referencedClass != null && 192 referencedMember == null) 193 { 194 // Reconstruct the new class name. 195 String externalClassName = stringConstant.getString(clazz); 196 String internalClassName = ClassUtil.internalClassName(externalClassName); 197 String newInternalClassName = newClassName(internalClassName, 198 referencedClass); 199 200 // Update the String entry if required. 201 if (!newInternalClassName.equals(internalClassName)) 202 { 203 String newExternalClassName = ClassUtil.externalClassName(newInternalClassName); 204 205 // Refer to a new Utf8 entry. 206 stringConstant.u2stringIndex = 207 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newExternalClassName); 208 } 209 } 210 } 211 212 213 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 214 { 215 // Do we know the referenced class? 216 Clazz referencedClass = classConstant.referencedClass; 217 if (referencedClass != null) 218 { 219 // Has the class name changed? 220 String className = classConstant.getName(clazz); 221 String newClassName = newClassName(className, referencedClass); 222 if (!className.equals(newClassName)) 223 { 224 // Refer to a new Utf8 entry. 225 classConstant.u2nameIndex = 226 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newClassName); 227 } 228 } 229 } 230 231 // Implementations for AttributeVisitor. 232 233 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 234 235 236 public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) 237 { 238 // Fix the inner class names. 239 innerClassesAttribute.innerClassEntriesAccept(clazz, this); 240 } 241 242 243 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 244 { 245 // Fix the attributes. 246 codeAttribute.attributesAccept(clazz, method, this); 247 } 248 249 250 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 251 { 252 // Fix the types of the local variables. 253 localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 254 } 255 256 257 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 258 { 259 // Fix the signatures of the local variables. 260 localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 261 } 262 263 264 public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) 265 { 266 // Compute the new signature. 267 String signature = clazz.getString(signatureAttribute.u2signatureIndex); 268 String newSignature = newDescriptor(signature, 269 signatureAttribute.referencedClasses); 270 271 if (!signature.equals(newSignature)) 272 { 273 signatureAttribute.u2signatureIndex = 274 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); 275 } 276 } 277 278 279 public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) 280 { 281 // Fix the annotations. 282 annotationsAttribute.annotationsAccept(clazz, this); 283 } 284 285 286 public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) 287 { 288 // Fix the annotations. 289 parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); 290 } 291 292 293 public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) 294 { 295 // Fix the annotation. 296 annotationDefaultAttribute.defaultValueAccept(clazz, this); 297 } 298 299 300 // Implementations for InnerClassesInfoVisitor. 301 302 public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) 303 { 304 // Fix the inner class name. 305 int innerClassIndex = innerClassesInfo.u2innerClassIndex; 306 int innerNameIndex = innerClassesInfo.u2innerNameIndex; 307 if (innerClassIndex != 0 && 308 innerNameIndex != 0) 309 { 310 String newInnerName = clazz.getClassName(innerClassIndex); 311 int index = newInnerName.lastIndexOf(ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR); 312 if (index >= 0) 313 { 314 innerClassesInfo.u2innerNameIndex = 315 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newInnerName.substring(index + 1)); 316 } 317 } 318 } 319 320 321 // Implementations for LocalVariableInfoVisitor. 322 323 public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) 324 { 325 // Has the descriptor changed? 326 String descriptor = clazz.getString(localVariableInfo.u2descriptorIndex); 327 String newDescriptor = newDescriptor(descriptor, 328 localVariableInfo.referencedClass); 329 330 if (!descriptor.equals(newDescriptor)) 331 { 332 // Refer to a new Utf8 entry. 333 localVariableInfo.u2descriptorIndex = 334 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newDescriptor); 335 } 336 } 337 338 // Implementations for LocalVariableTypeInfoVisitor. 339 340 public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) 341 { 342 // Has the signature changed? 343 String signature = clazz.getString(localVariableTypeInfo.u2signatureIndex); 344 String newSignature = newDescriptor(signature, 345 localVariableTypeInfo.referencedClasses); 346 347 if (!signature.equals(newSignature)) 348 { 349 localVariableTypeInfo.u2signatureIndex = 350 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); 351 } 352 } 353 354 // Implementations for AnnotationVisitor. 355 356 public void visitAnnotation(Clazz clazz, Annotation annotation) 357 { 358 // Compute the new type name. 359 String typeName = clazz.getString(annotation.u2typeIndex); 360 String newTypeName = newDescriptor(typeName, 361 annotation.referencedClasses); 362 363 if (!typeName.equals(newTypeName)) 364 { 365 // Refer to a new Utf8 entry. 366 annotation.u2typeIndex = 367 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newTypeName); 368 } 369 370 // Fix the element values. 371 annotation.elementValuesAccept(clazz, this); 372 } 373 374 375 // Implementations for ElementValueVisitor. 376 377 public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) 378 { 379 } 380 381 382 public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) 383 { 384 // Compute the new type name. 385 String typeName = clazz.getString(enumConstantElementValue.u2typeNameIndex); 386 String newTypeName = newDescriptor(typeName, 387 enumConstantElementValue.referencedClasses); 388 389 if (!typeName.equals(newTypeName)) 390 { 391 // Refer to a new Utf8 entry. 392 enumConstantElementValue.u2typeNameIndex = 393 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newTypeName); 394 } 395 } 396 397 398 public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) 399 { 400 // Compute the new class name. 401 String className = clazz.getString(classElementValue.u2classInfoIndex); 402 String newClassName = newDescriptor(className, 403 classElementValue.referencedClasses); 404 405 if (!className.equals(newClassName)) 406 { 407 // Refer to a new Utf8 entry. 408 classElementValue.u2classInfoIndex = 409 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newClassName); 410 } 411 } 412 413 414 public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) 415 { 416 // Fix the annotation. 417 annotationElementValue.annotationAccept(clazz, this); 418 } 419 420 421 public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) 422 { 423 // Fix the element values. 424 arrayElementValue.elementValuesAccept(clazz, annotation, this); 425 } 426 427 428 // Small utility methods. 429 430 private static String newDescriptor(String descriptor, 431 Clazz referencedClass) 432 { 433 // If there is no referenced class, the descriptor won't change. 434 if (referencedClass == null) 435 { 436 return descriptor; 437 } 438 439 // Unravel and reconstruct the class element of the descriptor. 440 DescriptorClassEnumeration descriptorClassEnumeration = 441 new DescriptorClassEnumeration(descriptor); 442 443 StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length()); 444 newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff()); 445 446 // Only if the descriptor contains a class name (e.g. with an array of 447 // primitive types), the descriptor can change. 448 if (descriptorClassEnumeration.hasMoreClassNames()) 449 { 450 String className = descriptorClassEnumeration.nextClassName(); 451 String fluff = descriptorClassEnumeration.nextFluff(); 452 453 String newClassName = newClassName(className, 454 referencedClass); 455 456 newDescriptorBuffer.append(newClassName); 457 newDescriptorBuffer.append(fluff); 458 } 459 460 return newDescriptorBuffer.toString(); 461 } 462 463 464 private static String newDescriptor(String descriptor, 465 Clazz[] referencedClasses) 466 { 467 // If there are no referenced classes, the descriptor won't change. 468 if (referencedClasses == null || 469 referencedClasses.length == 0) 470 { 471 return descriptor; 472 } 473 474 // Unravel and reconstruct the class elements of the descriptor. 475 DescriptorClassEnumeration descriptorClassEnumeration = 476 new DescriptorClassEnumeration(descriptor); 477 478 StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length()); 479 newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff()); 480 481 int index = 0; 482 while (descriptorClassEnumeration.hasMoreClassNames()) 483 { 484 String className = descriptorClassEnumeration.nextClassName(); 485 boolean isInnerClassName = descriptorClassEnumeration.isInnerClassName(); 486 String fluff = descriptorClassEnumeration.nextFluff(); 487 488 String newClassName = newClassName(className, 489 referencedClasses[index++]); 490 491 // Strip the outer class name again, if it's an inner class. 492 if (isInnerClassName) 493 { 494 newClassName = 495 newClassName.substring(newClassName.lastIndexOf(ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR)+1); 496 } 497 498 newDescriptorBuffer.append(newClassName); 499 newDescriptorBuffer.append(fluff); 500 } 501 502 return newDescriptorBuffer.toString(); 503 } 504 505 506 /** 507 * Returns a unique class member name, based on the given name and descriptor. 508 */ 509 private String newUniqueMemberName(String name, String descriptor) 510 { 511 return name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? 512 ClassConstants.INTERNAL_METHOD_NAME_INIT : 513 name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); 514 } 515 516 517 /** 518 * Returns the new class name based on the given class name and the new 519 * name of the given referenced class. Class names of array types 520 * are handled properly. 521 */ 522 private static String newClassName(String className, 523 Clazz referencedClass) 524 { 525 // If there is no referenced class, the class name won't change. 526 if (referencedClass == null) 527 { 528 return className; 529 } 530 531 // Reconstruct the class name. 532 String newClassName = referencedClass.getName(); 533 534 // Is it an array type? 535 if (className.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY) 536 { 537 // Add the array prefixes and suffix "[L...;". 538 newClassName = 539 className.substring(0, className.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1) + 540 newClassName + 541 ClassConstants.INTERNAL_TYPE_CLASS_END; 542 } 543 544 return newClassName; 545 } 546} 547