ClassReferenceInitializer.java revision 9f606f95f03a75961498803e24bee6799a7c0885
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.util; 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.visitor.*; 31 32 33/** 34 * This ClassVisitor initializes the references of all classes that 35 * it visits. 36 * <p> 37 * All class constant pool entries get direct references to the corresponding 38 * classes. These references make it more convenient to travel up and across 39 * the class hierarchy. 40 * <p> 41 * All field and method reference constant pool entries get direct references 42 * to the corresponding classes, fields, and methods. 43 * <p> 44 * All name and type constant pool entries get a list of direct references to 45 * the classes listed in the type. 46 * <p> 47 * This visitor optionally prints warnings if some items can't be found. 48 * <p> 49 * The class hierarchy must be initialized before using this visitor. 50 * 51 * @author Eric Lafortune 52 */ 53public class ClassReferenceInitializer 54extends SimplifiedVisitor 55implements ClassVisitor, 56 MemberVisitor, 57 ConstantVisitor, 58 AttributeVisitor, 59 LocalVariableInfoVisitor, 60 LocalVariableTypeInfoVisitor, 61 AnnotationVisitor, 62 ElementValueVisitor 63{ 64 private final ClassPool programClassPool; 65 private final ClassPool libraryClassPool; 66 private final WarningPrinter missingClassWarningPrinter; 67 private final WarningPrinter missingMemberWarningPrinter; 68 private final WarningPrinter dependencyWarningPrinter; 69 70 private final MemberFinder memberFinder = new MemberFinder(); 71 72 73 /** 74 * Creates a new ClassReferenceInitializer that initializes the references 75 * of all visited class files, optionally printing warnings if some classes 76 * or class members can't be found or if they are in the program class pool. 77 */ 78 public ClassReferenceInitializer(ClassPool programClassPool, 79 ClassPool libraryClassPool, 80 WarningPrinter missingClassWarningPrinter, 81 WarningPrinter missingMemberWarningPrinter, 82 WarningPrinter dependencyWarningPrinter) 83 { 84 this.programClassPool = programClassPool; 85 this.libraryClassPool = libraryClassPool; 86 this.missingClassWarningPrinter = missingClassWarningPrinter; 87 this.missingMemberWarningPrinter = missingMemberWarningPrinter; 88 this.dependencyWarningPrinter = dependencyWarningPrinter; 89 } 90 91 92 // Implementations for ClassVisitor. 93 94 public void visitProgramClass(ProgramClass programClass) 95 { 96 // Initialize the constant pool entries. 97 programClass.constantPoolEntriesAccept(this); 98 99 // Initialize all fields and methods. 100 programClass.fieldsAccept(this); 101 programClass.methodsAccept(this); 102 103 // Initialize the attributes. 104 programClass.attributesAccept(this); 105 } 106 107 108 public void visitLibraryClass(LibraryClass libraryClass) 109 { 110 // Initialize all fields and methods. 111 libraryClass.fieldsAccept(this); 112 libraryClass.methodsAccept(this); 113 } 114 115 116 // Implementations for MemberVisitor. 117 118 public void visitProgramField(ProgramClass programClass, ProgramField programField) 119 { 120 programField.referencedClass = 121 findReferencedClass(programClass.getName(), 122 programField.getDescriptor(programClass)); 123 124 // Initialize the attributes. 125 programField.attributesAccept(programClass, this); 126 } 127 128 129 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 130 { 131 programMethod.referencedClasses = 132 findReferencedClasses(programClass.getName(), 133 programMethod.getDescriptor(programClass)); 134 135 // Initialize the attributes. 136 programMethod.attributesAccept(programClass, this); 137 } 138 139 140 public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) 141 { 142 libraryField.referencedClass = 143 findReferencedClass(libraryClass.getName(), 144 libraryField.getDescriptor(libraryClass)); 145 } 146 147 148 public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) 149 { 150 libraryMethod.referencedClasses = 151 findReferencedClasses(libraryClass.getName(), 152 libraryMethod.getDescriptor(libraryClass)); 153 } 154 155 156 // Implementations for ConstantVisitor. 157 158 public void visitAnyConstant(Clazz clazz, Constant constant) {} 159 160 161 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 162 { 163 // Fill out the String class. 164 stringConstant.javaLangStringClass = 165 findClass(clazz.getName(), ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING); 166 } 167 168 169 public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) 170 { 171 String className = refConstant.getClassName(clazz); 172 173 // See if we can find the referenced class. 174 // Unresolved references are assumed to refer to library classes 175 // that will not change anyway. 176 Clazz referencedClass = findClass(clazz.getName(), className); 177 178 if (referencedClass != null && 179 !ClassUtil.isInternalArrayType(className)) 180 { 181 String name = refConstant.getName(clazz); 182 String type = refConstant.getType(clazz); 183 184 boolean isFieldRef = refConstant.getTag() == ClassConstants.CONSTANT_Fieldref; 185 186 // See if we can find the referenced class member somewhere in the 187 // hierarchy. 188 refConstant.referencedMember = memberFinder.findMember(clazz, 189 referencedClass, 190 name, 191 type, 192 isFieldRef); 193 refConstant.referencedClass = memberFinder.correspondingClass(); 194 195 if (refConstant.referencedMember == null) 196 { 197 // We've haven't found the class member anywhere in the hierarchy. 198 missingMemberWarningPrinter.print(clazz.getName(), 199 className, 200 "Warning: " + 201 ClassUtil.externalClassName(clazz.getName()) + 202 ": can't find referenced " + 203 (isFieldRef ? 204 "field '" + ClassUtil.externalFullFieldDescription(0, name, type) : 205 "method '" + ClassUtil.externalFullMethodDescription(className, 0, name, type)) + 206 "' in class " + 207 ClassUtil.externalClassName(className)); 208 } 209 } 210 } 211 212 213 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 214 { 215 String className = clazz.getName(); 216 217 // Fill out the referenced class. 218 classConstant.referencedClass = 219 findClass(className, classConstant.getName(clazz)); 220 221 // Fill out the Class class. 222 classConstant.javaLangClassClass = 223 findClass(className, ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS); 224 } 225 226 227 // Implementations for AttributeVisitor. 228 229 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 230 231 232 public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) 233 { 234 String className = clazz.getName(); 235 String enclosingClassName = enclosingMethodAttribute.getClassName(clazz); 236 237 // See if we can find the referenced class. 238 Clazz referencedClass = findClass(className, enclosingClassName); 239 240 if (referencedClass == null) 241 { 242 // We couldn't find the enclosing class. 243 missingClassWarningPrinter.print(className, 244 enclosingClassName, 245 "Warning: " + 246 ClassUtil.externalClassName(className) + 247 ": can't find enclosing class " + 248 ClassUtil.externalClassName(enclosingClassName)); 249 return; 250 } 251 252 // Make sure there is actually an enclosed method. 253 if (enclosingMethodAttribute.u2nameAndTypeIndex == 0) 254 { 255 return; 256 } 257 258 String name = enclosingMethodAttribute.getName(clazz); 259 String type = enclosingMethodAttribute.getType(clazz); 260 261 // See if we can find the method in the referenced class. 262 Method referencedMethod = referencedClass.findMethod(name, type); 263 264 if (referencedMethod == null) 265 { 266 // We couldn't find the enclosing method. 267 missingMemberWarningPrinter.print(className, 268 enclosingClassName, 269 "Warning: " + 270 ClassUtil.externalClassName(className) + 271 ": can't find enclosing method '" + 272 ClassUtil.externalFullMethodDescription(enclosingClassName, 0, name, type) + 273 "' in class " + 274 ClassUtil.externalClassName(enclosingClassName)); 275 return; 276 } 277 278 // Save the references. 279 enclosingMethodAttribute.referencedClass = referencedClass; 280 enclosingMethodAttribute.referencedMethod = referencedMethod; 281 } 282 283 284 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 285 { 286 // Initialize the nested attributes. 287 codeAttribute.attributesAccept(clazz, method, this); 288 } 289 290 291 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 292 { 293 // Initialize the local variables. 294 localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 295 } 296 297 298 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 299 { 300 // Initialize the local variable types. 301 localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 302 } 303 304 305 public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) 306 { 307 signatureAttribute.referencedClasses = 308 findReferencedClasses(clazz.getName(), 309 clazz.getString(signatureAttribute.u2signatureIndex)); 310 } 311 312 313 public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) 314 { 315 // Initialize the annotations. 316 annotationsAttribute.annotationsAccept(clazz, this); 317 } 318 319 320 public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) 321 { 322 // Initialize the annotations. 323 parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); 324 } 325 326 327 public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) 328 { 329 // Initialize the annotation. 330 annotationDefaultAttribute.defaultValueAccept(clazz, this); 331 } 332 333 334 // Implementations for LocalVariableInfoVisitor. 335 336 public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) 337 { 338 localVariableInfo.referencedClass = 339 findReferencedClass(clazz.getName(), 340 clazz.getString(localVariableInfo.u2descriptorIndex)); 341 } 342 343 344 // Implementations for LocalVariableTypeInfoVisitor. 345 346 public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) 347 { 348 localVariableTypeInfo.referencedClasses = 349 findReferencedClasses(clazz.getName(), 350 clazz.getString(localVariableTypeInfo.u2signatureIndex)); 351 } 352 353 354 // Implementations for AnnotationVisitor. 355 356 public void visitAnnotation(Clazz clazz, Annotation annotation) 357 { 358 annotation.referencedClasses = 359 findReferencedClasses(clazz.getName(), 360 clazz.getString(annotation.u2typeIndex)); 361 362 // Initialize the element values. 363 annotation.elementValuesAccept(clazz, this); 364 } 365 366 367 // Implementations for ElementValueVisitor. 368 369 public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) 370 { 371 initializeElementValue(clazz, annotation, constantElementValue); 372 } 373 374 375 public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) 376 { 377 initializeElementValue(clazz, annotation, enumConstantElementValue); 378 379 enumConstantElementValue.referencedClasses = 380 findReferencedClasses(clazz.getName(), 381 clazz.getString(enumConstantElementValue.u2typeNameIndex)); 382 } 383 384 385 public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) 386 { 387 initializeElementValue(clazz, annotation, classElementValue); 388 389 classElementValue.referencedClasses = 390 findReferencedClasses(clazz.getName(), 391 clazz.getString(classElementValue.u2classInfoIndex)); 392 } 393 394 395 public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) 396 { 397 initializeElementValue(clazz, annotation, annotationElementValue); 398 399 // Initialize the annotation. 400 annotationElementValue.annotationAccept(clazz, this); 401 } 402 403 404 public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) 405 { 406 initializeElementValue(clazz, annotation, arrayElementValue); 407 408 // Initialize the element values. 409 arrayElementValue.elementValuesAccept(clazz, annotation, this); 410 } 411 412 413 /** 414 * Initializes the referenced method of an element value, if any. 415 */ 416 private void initializeElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) 417 { 418 // See if we have a referenced class. 419 if (annotation != null && 420 annotation.referencedClasses != null && 421 elementValue.u2elementNameIndex != 0) 422 { 423 // See if we can find the method in the referenced class 424 // (ignoring the descriptor). 425 String name = clazz.getString(elementValue.u2elementNameIndex); 426 427 Clazz referencedClass = annotation.referencedClasses[0]; 428 elementValue.referencedClass = referencedClass; 429 elementValue.referencedMethod = referencedClass.findMethod(name, null); 430 } 431 } 432 433 434 // Small utility methods. 435 436 /** 437 * Returns the single class referenced by the given descriptor, or 438 * <code>null</code> if there isn't any useful reference. 439 */ 440 private Clazz findReferencedClass(String referencingClassName, 441 String descriptor) 442 { 443 DescriptorClassEnumeration enumeration = 444 new DescriptorClassEnumeration(descriptor); 445 446 enumeration.nextFluff(); 447 448 if (enumeration.hasMoreClassNames()) 449 { 450 return findClass(referencingClassName, enumeration.nextClassName()); 451 } 452 453 return null; 454 } 455 456 457 /** 458 * Returns an array of classes referenced by the given descriptor, or 459 * <code>null</code> if there aren't any useful references. 460 */ 461 private Clazz[] findReferencedClasses(String referencingClassName, 462 String descriptor) 463 { 464 DescriptorClassEnumeration enumeration = 465 new DescriptorClassEnumeration(descriptor); 466 467 int classCount = enumeration.classCount(); 468 if (classCount > 0) 469 { 470 Clazz[] referencedClasses = new Clazz[classCount]; 471 472 boolean foundReferencedClasses = false; 473 474 for (int index = 0; index < classCount; index++) 475 { 476 String fluff = enumeration.nextFluff(); 477 String name = enumeration.nextClassName(); 478 479 Clazz referencedClass = findClass(referencingClassName, name); 480 481 if (referencedClass != null) 482 { 483 referencedClasses[index] = referencedClass; 484 foundReferencedClasses = true; 485 } 486 } 487 488 if (foundReferencedClasses) 489 { 490 return referencedClasses; 491 } 492 } 493 494 return null; 495 } 496 497 498 /** 499 * Returns the class with the given name, either for the program class pool 500 * or from the library class pool, or <code>null</code> if it can't be found. 501 */ 502 private Clazz findClass(String referencingClassName, String name) 503 { 504 // Ignore any primitive array types. 505 if (ClassUtil.isInternalArrayType(name) && 506 !ClassUtil.isInternalClassType(name)) 507 { 508 return null; 509 } 510 511 // First look for the class in the program class pool. 512 Clazz clazz = programClassPool.getClass(name); 513 514 // Otherwise look for the class in the library class pool. 515 if (clazz == null) 516 { 517 clazz = libraryClassPool.getClass(name); 518 519 if (clazz == null && 520 missingClassWarningPrinter != null) 521 { 522 // We didn't find the superclass or interface. Print a warning. 523 missingClassWarningPrinter.print(referencingClassName, 524 name, 525 "Warning: " + 526 ClassUtil.externalClassName(referencingClassName) + 527 ": can't find referenced class " + 528 ClassUtil.externalClassName(name)); 529 } 530 } 531 else if (dependencyWarningPrinter != null) 532 { 533 // The superclass or interface was found in the program class pool. 534 // Print a warning. 535 dependencyWarningPrinter.print(referencingClassName, 536 name, 537 "Warning: library class " + 538 ClassUtil.externalClassName(referencingClassName) + 539 " depends on program class " + 540 ClassUtil.externalClassName(name)); 541 } 542 543 return clazz; 544 } 545} 546