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