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 */
21package proguard.optimize.peephole;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.visitor.AttributeNameFilter;
25import proguard.classfile.constant.visitor.*;
26import proguard.classfile.editor.*;
27import proguard.classfile.util.*;
28import proguard.classfile.visitor.*;
29import proguard.optimize.KeepMarker;
30import proguard.optimize.info.*;
31import proguard.util.*;
32
33import 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 */
46public class ClassMerger
47extends      SimplifiedVisitor
48implements   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     */
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     */
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
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
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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
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    {
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
713        public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
714        {
715            // Linked methods share their optimization info.
716        }
717    }
718}
719