• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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