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 */ 21 package proguard.optimize.peephole; 22 23 import proguard.classfile.*; 24 import proguard.classfile.attribute.visitor.AttributeNameFilter; 25 import proguard.classfile.constant.visitor.*; 26 import proguard.classfile.editor.*; 27 import proguard.classfile.util.*; 28 import proguard.classfile.visitor.*; 29 import proguard.optimize.KeepMarker; 30 import proguard.optimize.info.*; 31 import proguard.util.*; 32 33 import java.util.*; 34 35 /** 36 * This ClassVisitor inlines the classes that it visits in a given target class, 37 * whenever possible. 38 * 39 * @see RetargetedInnerClassAttributeRemover 40 * @see TargetClassChanger 41 * @see ClassReferenceFixer 42 * @see MemberReferenceFixer 43 * @see AccessFixer 44 * @author Eric Lafortune 45 */ 46 public class ClassMerger 47 extends SimplifiedVisitor 48 implements ClassVisitor, 49 ConstantVisitor 50 { 51 //* 52 private static final boolean DEBUG = false; 53 private static final boolean DETAILS = false; 54 /*/ 55 private static boolean DEBUG = System.getProperty("cm") != null; 56 private static boolean DETAILS = System.getProperty("cmd") != null; 57 //*/ 58 59 60 private final ProgramClass targetClass; 61 private final boolean allowAccessModification; 62 private final boolean mergeInterfacesAggressively; 63 private final ClassVisitor extraClassVisitor; 64 65 private final MemberVisitor fieldOptimizationInfoCopier = new FieldOptimizationInfoCopier(); 66 67 68 /** 69 * Creates a new ClassMerger that will merge classes into the given target 70 * class. 71 * @param targetClass the class into which all visited 72 * classes will be merged. 73 * @param allowAccessModification specifies whether the access modifiers 74 * of classes can be changed in order to 75 * merge them. 76 * @param mergeInterfacesAggressively specifies whether interfaces may 77 * be merged aggressively. 78 */ ClassMerger(ProgramClass targetClass, boolean allowAccessModification, boolean mergeInterfacesAggressively)79 public ClassMerger(ProgramClass targetClass, 80 boolean allowAccessModification, 81 boolean mergeInterfacesAggressively) 82 { 83 this(targetClass, allowAccessModification, mergeInterfacesAggressively, null); 84 } 85 86 87 /** 88 * Creates a new ClassMerger that will merge classes into the given target 89 * class. 90 * @param targetClass the class into which all visited 91 * classes will be merged. 92 * @param allowAccessModification specifies whether the access modifiers 93 * of classes can be changed in order to 94 * merge them. 95 * @param mergeInterfacesAggressively specifies whether interfaces may 96 * be merged aggressively. 97 * @param extraClassVisitor an optional extra visitor for all 98 * merged classes. 99 */ ClassMerger(ProgramClass targetClass, boolean allowAccessModification, boolean mergeInterfacesAggressively, ClassVisitor extraClassVisitor)100 public ClassMerger(ProgramClass targetClass, 101 boolean allowAccessModification, 102 boolean mergeInterfacesAggressively, 103 ClassVisitor extraClassVisitor) 104 { 105 this.targetClass = targetClass; 106 this.allowAccessModification = allowAccessModification; 107 this.mergeInterfacesAggressively = mergeInterfacesAggressively; 108 this.extraClassVisitor = extraClassVisitor; 109 } 110 111 112 // Implementations for ClassVisitor. 113 visitProgramClass(ProgramClass programClass)114 public void visitProgramClass(ProgramClass programClass) 115 { 116 //final String CLASS_NAME = "abc/Def"; 117 //DEBUG = programClass.getName().equals(CLASS_NAME) || 118 // targetClass.getName().equals(CLASS_NAME); 119 120 // TODO: Remove this when the class merger has stabilized. 121 // Catch any unexpected exceptions from the actual visiting method. 122 try 123 { 124 visitProgramClass0(programClass); 125 } 126 catch (RuntimeException ex) 127 { 128 System.err.println("Unexpected error while merging classes:"); 129 System.err.println(" Class = ["+programClass.getName()+"]"); 130 System.err.println(" Target class = ["+targetClass.getName()+"]"); 131 System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); 132 133 if (DEBUG) 134 { 135 programClass.accept(new ClassPrinter()); 136 targetClass.accept(new ClassPrinter()); 137 } 138 139 throw ex; 140 } 141 } 142 visitProgramClass0(ProgramClass programClass)143 public void visitProgramClass0(ProgramClass programClass) 144 { 145 if (!programClass.equals(targetClass) && 146 147 // Don't merge classes that must be preserved. 148 !KeepMarker.isKept(programClass) && 149 !KeepMarker.isKept(targetClass) && 150 151 // Only merge classes that haven't been retargeted yet. 152 getTargetClass(programClass) == null && 153 getTargetClass(targetClass) == null && 154 155 // Don't merge annotation classes, with all their introspection and 156 // infinite recursion. 157 (programClass.getAccessFlags() & ClassConstants.ACC_ANNOTATTION) == 0 && 158 159 (!DETAILS || print(programClass, "Package visibility?")) && 160 161 // Only merge classes if we can change the access permissions, or 162 // if they are in the same package, or 163 // if they are public and don't contain or invoke package visible 164 // class members. 165 (allowAccessModification || 166 ((programClass.getAccessFlags() & 167 targetClass.getAccessFlags() & 168 ClassConstants.ACC_PUBLIC) != 0 && 169 !PackageVisibleMemberContainingClassMarker.containsPackageVisibleMembers(programClass) && 170 !PackageVisibleMemberInvokingClassMarker.invokesPackageVisibleMembers(programClass)) || 171 ClassUtil.internalPackageName(programClass.getName()).equals( 172 ClassUtil.internalPackageName(targetClass.getName()))) && 173 174 (!DETAILS || print(programClass, "Interface/abstract/single?")) && 175 176 // Only merge two classes or two interfaces or two abstract classes, 177 // or a single implementation into its interface. 178 ((programClass.getAccessFlags() & 179 (ClassConstants.ACC_INTERFACE | 180 ClassConstants.ACC_ABSTRACT)) == 181 (targetClass.getAccessFlags() & 182 (ClassConstants.ACC_INTERFACE | 183 ClassConstants.ACC_ABSTRACT)) || 184 (isOnlySubClass(programClass, targetClass) && 185 programClass.getSuperClass() != null && 186 (programClass.getSuperClass().equals(targetClass) || 187 programClass.getSuperClass().equals(targetClass.getSuperClass())))) && 188 189 (!DETAILS || print(programClass, "Indirect implementation?")) && 190 191 // One class must not implement the other class indirectly. 192 !indirectlyImplementedInterfaces(programClass).contains(targetClass) && 193 !targetClass.extendsOrImplements(programClass) && 194 195 (!DETAILS || print(programClass, "Interfaces same subinterfaces?")) && 196 197 // Interfaces must have exactly the same subinterfaces, not 198 // counting themselves, to avoid any loops in the interface 199 // hierarchy. 200 ((programClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) == 0 || 201 (targetClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) == 0 || 202 subInterfaces(programClass, targetClass).equals(subInterfaces(targetClass, programClass))) && 203 204 (!DETAILS || print(programClass, "Same initialized superclasses?")) && 205 206 // The two classes must have the same superclasses and interfaces 207 // with static initializers. 208 initializedSuperClasses(programClass).equals(initializedSuperClasses(targetClass)) && 209 210 (!DETAILS || print(programClass, "Same instanceofed superclasses?")) && 211 212 // The two classes must have the same superclasses and interfaces 213 // that are tested with 'instanceof'. 214 instanceofedSuperClasses(programClass).equals(instanceofedSuperClasses(targetClass)) && 215 216 (!DETAILS || print(programClass, "Same caught superclasses?")) && 217 218 // The two classes must have the same superclasses that are caught 219 // as exceptions. 220 caughtSuperClasses(programClass).equals(caughtSuperClasses(targetClass)) && 221 222 (!DETAILS || print(programClass, "Not .classed?")) && 223 224 // The two classes must not both be part of a .class construct. 225 !(DotClassMarker.isDotClassed(programClass) && 226 DotClassMarker.isDotClassed(targetClass)) && 227 228 (!DETAILS || print(programClass, "No clashing fields?")) && 229 230 // The classes must not have clashing fields. 231 !haveAnyIdenticalFields(programClass, targetClass) && 232 233 (!DETAILS || print(programClass, "No unwanted fields?")) && 234 235 // The two classes must not introduce any unwanted fields. 236 !introducesUnwantedFields(programClass, targetClass) && 237 !introducesUnwantedFields(targetClass, programClass) && 238 239 (!DETAILS || print(programClass, "No shadowed fields?")) && 240 241 // The two classes must not shadow each others fields. 242 !shadowsAnyFields(programClass, targetClass) && 243 !shadowsAnyFields(targetClass, programClass) && 244 245 (!DETAILS || print(programClass, "No clashing methods?")) && 246 247 // The classes must not have clashing methods. 248 !haveAnyIdenticalMethods(programClass, targetClass) && 249 250 (!DETAILS || print(programClass, "No abstract methods?")) && 251 252 // The classes must not introduce abstract methods, unless 253 // explicitly allowed. 254 (mergeInterfacesAggressively || 255 (!introducesUnwantedAbstractMethods(programClass, targetClass) && 256 !introducesUnwantedAbstractMethods(targetClass, programClass))) && 257 258 (!DETAILS || print(programClass, "No overridden methods?")) && 259 260 // The classes must not override each others concrete methods. 261 !overridesAnyMethods(programClass, targetClass) && 262 !overridesAnyMethods(targetClass, programClass) && 263 264 (!DETAILS || print(programClass, "No shadowed methods?")) && 265 266 // The classes must not shadow each others non-private methods. 267 !shadowsAnyMethods(programClass, targetClass) && 268 !shadowsAnyMethods(targetClass, programClass)) 269 { 270 // We're not actually merging the classes, but only copying the 271 // contents from the source class to the target class. We'll 272 // then let all other classes point to it. The shrinking step 273 // will finally remove the source class. 274 if (DEBUG) 275 { 276 System.out.println("ClassMerger ["+programClass.getName()+"] -> ["+targetClass.getName()+"]"); 277 System.out.println(" Source interface? ["+((programClass.getAccessFlags() & ClassConstants.ACC_INTERFACE)!=0)+"]"); 278 System.out.println(" Target interface? ["+((targetClass.getAccessFlags() & ClassConstants.ACC_INTERFACE)!=0)+"]"); 279 System.out.println(" Source subclasses ["+programClass.subClasses+"]"); 280 System.out.println(" Target subclasses ["+targetClass.subClasses+"]"); 281 System.out.println(" Source superclass ["+programClass.getSuperClass().getName()+"]"); 282 System.out.println(" Target superclass ["+targetClass.getSuperClass().getName()+"]"); 283 284 //System.out.println("=== Before ==="); 285 //programClass.accept(new ClassPrinter()); 286 //targetClass.accept(new ClassPrinter()); 287 } 288 289 // Combine the access flags. 290 int targetAccessFlags = targetClass.getAccessFlags(); 291 int sourceAccessFlags = programClass.getAccessFlags(); 292 293 targetClass.u2accessFlags = 294 ((targetAccessFlags & 295 sourceAccessFlags) & 296 (ClassConstants.ACC_INTERFACE | 297 ClassConstants.ACC_ABSTRACT)) | 298 ((targetAccessFlags | 299 sourceAccessFlags) & 300 (ClassConstants.ACC_PUBLIC | 301 ClassConstants.ACC_SUPER | 302 ClassConstants.ACC_ANNOTATTION | 303 ClassConstants.ACC_ENUM)); 304 305 // Copy over the superclass, if it's a non-interface class being 306 // merged into an interface class. 307 // However, we're currently never merging in a way that changes the 308 // superclass. 309 //if ((programClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) == 0 && 310 // (targetClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) != 0) 311 //{ 312 // targetClass.u2superClass = 313 // new ConstantAdder(targetClass).addConstant(programClass, programClass.u2superClass); 314 //} 315 316 // Copy over the interfaces that aren't present yet and that 317 // wouldn't cause loops in the class hierarchy. 318 // Note that the code shouldn't be iterating over the original 319 // list at this point. This is why we only add subclasses in 320 // a separate step. 321 programClass.interfaceConstantsAccept( 322 new ExceptClassConstantFilter(targetClass.getName(), 323 new ImplementedClassConstantFilter(targetClass, 324 new ImplementingClassConstantFilter(targetClass, 325 new InterfaceAdder(targetClass))))); 326 327 // Copy over the class members. 328 MemberAdder memberAdder = 329 new MemberAdder(targetClass, fieldOptimizationInfoCopier); 330 331 programClass.fieldsAccept(memberAdder); 332 programClass.methodsAccept(memberAdder); 333 334 // Copy over the other attributes. 335 programClass.attributesAccept( 336 new AttributeNameFilter(new NotMatcher( 337 new OrMatcher(new FixedStringMatcher(ClassConstants.ATTR_BootstrapMethods), 338 new OrMatcher(new FixedStringMatcher(ClassConstants.ATTR_SourceFile), 339 new OrMatcher(new FixedStringMatcher(ClassConstants.ATTR_InnerClasses), 340 new FixedStringMatcher(ClassConstants.ATTR_EnclosingMethod))))), 341 new AttributeAdder(targetClass, true))); 342 343 // Update the optimization information of the target class. 344 ClassOptimizationInfo info = 345 ClassOptimizationInfo.getClassOptimizationInfo(targetClass); 346 if (info != null) 347 { 348 info.merge(ClassOptimizationInfo.getClassOptimizationInfo(programClass)); 349 } 350 351 // Remember to replace the inlined class by the target class. 352 setTargetClass(programClass, targetClass); 353 354 //if (DEBUG) 355 //{ 356 // System.out.println("=== After ===="); 357 // targetClass.accept(new ClassPrinter()); 358 //} 359 360 // Visit the merged class, if required. 361 if (extraClassVisitor != null) 362 { 363 extraClassVisitor.visitProgramClass(programClass); 364 } 365 } 366 } 367 368 print(ProgramClass programClass, String message)369 private boolean print(ProgramClass programClass, String message) 370 { 371 System.out.println("Merge ["+targetClass.getName()+"] <- ["+programClass.getName()+"] "+message); 372 373 return true; 374 } 375 376 377 // Small utility methods. 378 379 /** 380 * Returns whether a given class is the only subclass of another given class. 381 */ isOnlySubClass(Clazz subClass, ProgramClass clazz)382 private boolean isOnlySubClass(Clazz subClass, 383 ProgramClass clazz) 384 { 385 // TODO: The list of subclasses is not up to date. 386 return clazz.subClasses != null && 387 clazz.subClasses.length == 1 && 388 clazz.subClasses[0].equals(subClass); 389 } 390 391 392 /** 393 * Returns the set of indirectly implemented interfaces. 394 */ indirectlyImplementedInterfaces(Clazz clazz)395 private Set indirectlyImplementedInterfaces(Clazz clazz) 396 { 397 Set set = new HashSet(); 398 399 ReferencedClassVisitor referencedInterfaceCollector = 400 new ReferencedClassVisitor( 401 new ClassHierarchyTraveler(false, false, true, false, 402 new ClassCollector(set))); 403 404 // Visit all superclasses and collect their interfaces. 405 clazz.superClassConstantAccept(referencedInterfaceCollector); 406 407 // Visit all interfaces and collect their interfaces. 408 clazz.interfaceConstantsAccept(referencedInterfaceCollector); 409 410 return set; 411 } 412 413 414 /** 415 * Returns the set of interface subclasses, not including the given class. 416 */ subInterfaces(Clazz clazz, Clazz exceptClass)417 private Set subInterfaces(Clazz clazz, Clazz exceptClass) 418 { 419 Set set = new HashSet(); 420 421 // Visit all subclasses, collecting the interface classes. 422 clazz.hierarchyAccept(false, false, false, true, 423 new ClassAccessFilter(ClassConstants.ACC_INTERFACE, 0, 424 new ExceptClassesFilter(new Clazz[] { exceptClass }, 425 new ClassCollector(set)))); 426 427 return set; 428 } 429 430 431 /** 432 * Returns the set of superclasses and interfaces that are initialized. 433 */ initializedSuperClasses(Clazz clazz)434 private Set initializedSuperClasses(Clazz clazz) 435 { 436 Set set = new HashSet(); 437 438 // Visit all superclasses and interfaces, collecting the ones that have 439 // static initializers. 440 clazz.hierarchyAccept(true, true, true, false, 441 new StaticInitializerContainingClassFilter( 442 new ClassCollector(set))); 443 444 return set; 445 } 446 447 448 /** 449 * Returns the set of superclasses and interfaces that are used in 450 * 'instanceof' tests. 451 */ instanceofedSuperClasses(Clazz clazz)452 private Set instanceofedSuperClasses(Clazz clazz) 453 { 454 Set set = new HashSet(); 455 456 // Visit all superclasses and interfaces, collecting the ones that are 457 // used in an 'instanceof' test. 458 clazz.hierarchyAccept(true, true, true, false, 459 new InstanceofClassFilter( 460 new ClassCollector(set))); 461 462 return set; 463 } 464 465 466 /** 467 * Returns the set of superclasses that are caught as exceptions. 468 */ caughtSuperClasses(Clazz clazz)469 private Set caughtSuperClasses(Clazz clazz) 470 { 471 // Don't bother if this isn't an exception at all. 472 if (!clazz.extends_(ClassConstants.NAME_JAVA_LANG_THROWABLE)) 473 { 474 return Collections.EMPTY_SET; 475 } 476 477 // Visit all superclasses, collecting the ones that are caught 478 // (plus java.lang.Object, in the current implementation). 479 Set set = new HashSet(); 480 481 clazz.hierarchyAccept(true, true, false, false, 482 new CaughtClassFilter( 483 new ClassCollector(set))); 484 485 return set; 486 } 487 488 489 /** 490 * Returns whether the two given classes have fields with the same 491 * names and descriptors. 492 */ haveAnyIdenticalFields(Clazz clazz, Clazz targetClass)493 private boolean haveAnyIdenticalFields(Clazz clazz, Clazz targetClass) 494 { 495 MemberCounter counter = new MemberCounter(); 496 497 // Visit all fields, counting the with the same name and descriptor in 498 // the target class. 499 clazz.fieldsAccept(new SimilarMemberVisitor(targetClass, true, false, false, false, 500 counter)); 501 502 return counter.getCount() > 0; 503 } 504 505 506 /** 507 * Returns whether the given class would introduce any unwanted fields 508 * in the target class. 509 */ introducesUnwantedFields(ProgramClass programClass, ProgramClass targetClass)510 private boolean introducesUnwantedFields(ProgramClass programClass, 511 ProgramClass targetClass) 512 { 513 // It's ok if the target class is never instantiated, without any other 514 // subclasses except for maybe the source class. 515 if (!InstantiationClassMarker.isInstantiated(targetClass) && 516 (targetClass.subClasses == null || 517 isOnlySubClass(programClass, targetClass))) 518 { 519 return false; 520 } 521 522 MemberCounter counter = new MemberCounter(); 523 524 // Count all non-static fields in the the source class. 525 programClass.fieldsAccept(new MemberAccessFilter(0, ClassConstants.ACC_STATIC, 526 counter)); 527 528 return counter.getCount() > 0; 529 } 530 531 532 /** 533 * Returns whether the given class or its subclasses shadow any fields in 534 * the given target class. 535 */ shadowsAnyFields(Clazz clazz, Clazz targetClass)536 private boolean shadowsAnyFields(Clazz clazz, Clazz targetClass) 537 { 538 MemberCounter counter = new MemberCounter(); 539 540 // Visit all fields, counting the ones that are shadowing non-private 541 // fields in the class hierarchy of the target class. 542 clazz.hierarchyAccept(true, false, false, true, 543 new AllFieldVisitor( 544 new SimilarMemberVisitor(targetClass, true, true, true, false, 545 new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, 546 counter)))); 547 548 return counter.getCount() > 0; 549 } 550 551 552 /** 553 * Returns whether the two given classes have class members with the same 554 * name and descriptor. 555 */ haveAnyIdenticalMethods(Clazz clazz, Clazz targetClass)556 private boolean haveAnyIdenticalMethods(Clazz clazz, Clazz targetClass) 557 { 558 MemberCounter counter = new MemberCounter(); 559 560 // Visit all non-abstract methods, counting the ones that are also 561 // present in the target class. 562 clazz.methodsAccept(new MemberAccessFilter(0, ClassConstants.ACC_ABSTRACT, 563 new SimilarMemberVisitor(targetClass, true, false, false, false, 564 new MemberAccessFilter(0, ClassConstants.ACC_ABSTRACT, 565 counter)))); 566 567 return counter.getCount() > 0; 568 } 569 570 571 /** 572 * Returns whether the given class would introduce any abstract methods 573 * in the target class. 574 */ introducesUnwantedAbstractMethods(Clazz clazz, ProgramClass targetClass)575 private boolean introducesUnwantedAbstractMethods(Clazz clazz, 576 ProgramClass targetClass) 577 { 578 // It's ok if the target class is already abstract and it has at most 579 // the class as a subclass. 580 if ((targetClass.getAccessFlags() & 581 (ClassConstants.ACC_ABSTRACT | 582 ClassConstants.ACC_INTERFACE)) != 0 && 583 (targetClass.subClasses == null || 584 isOnlySubClass(clazz, targetClass))) 585 { 586 return false; 587 } 588 589 MemberCounter counter = new MemberCounter(); 590 Set targetSet = new HashSet(); 591 592 // Collect all abstract methods, and similar abstract methods in the 593 // class hierarchy of the target class. 594 clazz.methodsAccept(new MemberAccessFilter(ClassConstants.ACC_ABSTRACT, 0, 595 new MultiMemberVisitor(new MemberVisitor[] 596 { 597 counter, 598 new SimilarMemberVisitor(targetClass, true, true, true, false, 599 new MemberAccessFilter(ClassConstants.ACC_ABSTRACT, 0, 600 new MemberCollector(targetSet))) 601 }))); 602 603 return targetSet.size() < counter.getCount(); 604 } 605 606 607 /** 608 * Returns whether the given class overrides any methods in the given 609 * target class. 610 */ overridesAnyMethods(Clazz clazz, Clazz targetClass)611 private boolean overridesAnyMethods(Clazz clazz, Clazz targetClass) 612 { 613 MemberCounter counter = new MemberCounter(); 614 615 // Visit all non-private non-static methods, counting the ones that are 616 // being overridden in the class hierarchy of the target class. 617 clazz.methodsAccept(new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC | ClassConstants.ACC_ABSTRACT, 618 new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_CLINIT)), 619 new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_INIT)), 620 new SimilarMemberVisitor(targetClass, true, true, false, false, 621 new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC | ClassConstants.ACC_ABSTRACT, 622 counter)))))); 623 624 return counter.getCount() > 0; 625 } 626 627 628 /** 629 * Returns whether the given class or its subclasses shadow any methods in 630 * the given target class. 631 */ shadowsAnyMethods(Clazz clazz, Clazz targetClass)632 private boolean shadowsAnyMethods(Clazz clazz, Clazz targetClass) 633 { 634 MemberCounter counter = new MemberCounter(); 635 636 // Visit all private methods, counting the ones that are shadowing 637 // non-private methods in the class hierarchy of the target class. 638 clazz.hierarchyAccept(true, false, false, true, 639 new AllMethodVisitor( 640 new MemberAccessFilter(ClassConstants.ACC_PRIVATE, 0, 641 new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_INIT)), 642 new SimilarMemberVisitor(targetClass, true, true, true, false, 643 new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, 644 counter)))))); 645 646 // Visit all static methods, counting the ones that are shadowing 647 // non-private methods in the class hierarchy of the target class. 648 clazz.hierarchyAccept(true, false, false, true, 649 new AllMethodVisitor( 650 new MemberAccessFilter(ClassConstants.ACC_STATIC, 0, 651 new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_CLINIT)), 652 new SimilarMemberVisitor(targetClass, true, true, true, false, 653 new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, 654 counter)))))); 655 656 return counter.getCount() > 0; 657 } 658 659 setTargetClass(Clazz clazz, Clazz targetClass)660 public static void setTargetClass(Clazz clazz, Clazz targetClass) 661 { 662 ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); 663 if (info != null) 664 { 665 info.setTargetClass(targetClass); 666 } 667 } 668 669 getTargetClass(Clazz clazz)670 public static Clazz getTargetClass(Clazz clazz) 671 { 672 Clazz targetClass = null; 673 674 // Return the last target class, if any. 675 while (true) 676 { 677 ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); 678 if (info == null) 679 { 680 return targetClass; 681 } 682 683 clazz = info.getTargetClass(); 684 if (clazz == null) 685 { 686 return targetClass; 687 } 688 689 targetClass = clazz; 690 } 691 } 692 693 694 /** 695 * This MemberVisitor copies field optimization info from copied fields. 696 */ 697 private static class FieldOptimizationInfoCopier 698 extends SimplifiedVisitor 699 implements MemberVisitor 700 { visitProgramField(ProgramClass programClass, ProgramField programField)701 public void visitProgramField(ProgramClass programClass, ProgramField programField) 702 { 703 // Copy the optimization info from the field that was just copied. 704 ProgramField copiedField = (ProgramField)programField.getVisitorInfo(); 705 Object info = copiedField.getVisitorInfo(); 706 707 programField.setVisitorInfo(info instanceof FieldOptimizationInfo ? 708 new FieldOptimizationInfo((FieldOptimizationInfo)info) : 709 info); 710 } 711 712 visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)713 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 714 { 715 // Linked methods share their optimization info. 716 } 717 } 718 } 719