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;
22
23import proguard.*;
24import proguard.classfile.*;
25import proguard.classfile.attribute.visitor.*;
26import proguard.classfile.constant.visitor.*;
27import proguard.classfile.editor.*;
28import proguard.classfile.instruction.visitor.*;
29import proguard.classfile.util.MethodLinker;
30import proguard.classfile.visitor.*;
31import proguard.evaluation.InvocationUnit;
32import proguard.evaluation.value.*;
33import proguard.optimize.evaluation.*;
34import proguard.optimize.info.*;
35import proguard.optimize.peephole.*;
36import proguard.util.*;
37
38import java.io.IOException;
39import java.util.*;
40
41/**
42 * This class optimizes class pools according to a given configuration.
43 *
44 * @author Eric Lafortune
45 */
46public class Optimizer
47{
48    private static final String CLASS_MARKING_FINAL            = "class/marking/final";
49    private static final String CLASS_UNBOXING_ENUM            = "class/unboxing/enum";
50    private static final String CLASS_MERGING_VERTICAL         = "class/merging/vertical";
51    private static final String CLASS_MERGING_HORIZONTAL       = "class/merging/horizontal";
52    private static final String FIELD_REMOVAL_WRITEONLY        = "field/removal/writeonly";
53    private static final String FIELD_MARKING_PRIVATE          = "field/marking/private";
54    private static final String FIELD_PROPAGATION_VALUE        = "field/propagation/value";
55    private static final String METHOD_MARKING_PRIVATE         = "method/marking/private";
56    private static final String METHOD_MARKING_STATIC          = "method/marking/static";
57    private static final String METHOD_MARKING_FINAL           = "method/marking/final";
58    private static final String METHOD_REMOVAL_PARAMETER       = "method/removal/parameter";
59    private static final String METHOD_PROPAGATION_PARAMETER   = "method/propagation/parameter";
60    private static final String METHOD_PROPAGATION_RETURNVALUE = "method/propagation/returnvalue";
61    private static final String METHOD_INLINING_SHORT          = "method/inlining/short";
62    private static final String METHOD_INLINING_UNIQUE         = "method/inlining/unique";
63    private static final String METHOD_INLINING_TAILRECURSION  = "method/inlining/tailrecursion";
64    private static final String CODE_MERGING                   = "code/merging";
65    private static final String CODE_SIMPLIFICATION_VARIABLE   = "code/simplification/variable";
66    private static final String CODE_SIMPLIFICATION_ARITHMETIC = "code/simplification/arithmetic";
67    private static final String CODE_SIMPLIFICATION_CAST       = "code/simplification/cast";
68    private static final String CODE_SIMPLIFICATION_FIELD      = "code/simplification/field";
69    private static final String CODE_SIMPLIFICATION_BRANCH     = "code/simplification/branch";
70    private static final String CODE_SIMPLIFICATION_STRING     = "code/simplification/string";
71    private static final String CODE_SIMPLIFICATION_ADVANCED   = "code/simplification/advanced";
72    private static final String CODE_REMOVAL_ADVANCED          = "code/removal/advanced";
73    private static final String CODE_REMOVAL_SIMPLE            = "code/removal/simple";
74    private static final String CODE_REMOVAL_VARIABLE          = "code/removal/variable";
75    private static final String CODE_REMOVAL_EXCEPTION         = "code/removal/exception";
76    private static final String CODE_ALLOCATION_VARIABLE       = "code/allocation/variable";
77
78
79    public static final String[] OPTIMIZATION_NAMES = new String[]
80    {
81        CLASS_MARKING_FINAL,
82        CLASS_MERGING_VERTICAL,
83        CLASS_MERGING_HORIZONTAL,
84        FIELD_REMOVAL_WRITEONLY,
85        FIELD_MARKING_PRIVATE,
86        FIELD_PROPAGATION_VALUE,
87        METHOD_MARKING_PRIVATE,
88        METHOD_MARKING_STATIC,
89        METHOD_MARKING_FINAL,
90        METHOD_REMOVAL_PARAMETER,
91        METHOD_PROPAGATION_PARAMETER,
92        METHOD_PROPAGATION_RETURNVALUE,
93        METHOD_INLINING_SHORT,
94        METHOD_INLINING_UNIQUE,
95        METHOD_INLINING_TAILRECURSION,
96        CODE_MERGING,
97        CODE_SIMPLIFICATION_VARIABLE,
98        CODE_SIMPLIFICATION_ARITHMETIC,
99        CODE_SIMPLIFICATION_CAST,
100        CODE_SIMPLIFICATION_FIELD,
101        CODE_SIMPLIFICATION_BRANCH,
102        CODE_SIMPLIFICATION_STRING,
103        CODE_SIMPLIFICATION_ADVANCED,
104        CODE_REMOVAL_ADVANCED,
105        CODE_REMOVAL_SIMPLE,
106        CODE_REMOVAL_VARIABLE,
107        CODE_REMOVAL_EXCEPTION,
108        CODE_ALLOCATION_VARIABLE,
109    };
110
111
112    private final Configuration configuration;
113
114
115    /**
116     * Creates a new Optimizer.
117     */
118    public Optimizer(Configuration configuration)
119    {
120        this.configuration = configuration;
121    }
122
123
124    /**
125     * Performs optimization of the given program class pool.
126     */
127    public boolean execute(ClassPool programClassPool,
128                           ClassPool libraryClassPool) throws IOException
129    {
130        // Check if we have at least some keep commands.
131        if (configuration.keep         == null &&
132            configuration.applyMapping == null &&
133            configuration.printMapping == null)
134        {
135            throw new IOException("You have to specify '-keep' options for the optimization step.");
136        }
137
138        // Create a matcher for filtering optimizations.
139        StringMatcher filter = configuration.optimizations != null ?
140            new ListParser(new NameParser()).parse(configuration.optimizations) :
141            new ConstantMatcher(true);
142
143        boolean classMarkingFinal            = filter.matches(CLASS_MARKING_FINAL);
144        boolean classUnboxingEnum            = filter.matches(CLASS_UNBOXING_ENUM);
145        boolean classMergingVertical         = filter.matches(CLASS_MERGING_VERTICAL);
146        boolean classMergingHorizontal       = filter.matches(CLASS_MERGING_HORIZONTAL);
147        boolean fieldRemovalWriteonly        = filter.matches(FIELD_REMOVAL_WRITEONLY);
148        boolean fieldMarkingPrivate          = filter.matches(FIELD_MARKING_PRIVATE);
149        boolean fieldPropagationValue        = filter.matches(FIELD_PROPAGATION_VALUE);
150        boolean methodMarkingPrivate         = filter.matches(METHOD_MARKING_PRIVATE);
151        boolean methodMarkingStatic          = filter.matches(METHOD_MARKING_STATIC);
152        boolean methodMarkingFinal           = filter.matches(METHOD_MARKING_FINAL);
153        boolean methodRemovalParameter       = filter.matches(METHOD_REMOVAL_PARAMETER);
154        boolean methodPropagationParameter   = filter.matches(METHOD_PROPAGATION_PARAMETER);
155        boolean methodPropagationReturnvalue = filter.matches(METHOD_PROPAGATION_RETURNVALUE);
156        boolean methodInliningShort          = filter.matches(METHOD_INLINING_SHORT);
157        boolean methodInliningUnique         = filter.matches(METHOD_INLINING_UNIQUE);
158        boolean methodInliningTailrecursion  = filter.matches(METHOD_INLINING_TAILRECURSION);
159        boolean codeMerging                  = filter.matches(CODE_MERGING);
160        boolean codeSimplificationVariable   = filter.matches(CODE_SIMPLIFICATION_VARIABLE);
161        boolean codeSimplificationArithmetic = filter.matches(CODE_SIMPLIFICATION_ARITHMETIC);
162        boolean codeSimplificationCast       = filter.matches(CODE_SIMPLIFICATION_CAST);
163        boolean codeSimplificationField      = filter.matches(CODE_SIMPLIFICATION_FIELD);
164        boolean codeSimplificationBranch     = filter.matches(CODE_SIMPLIFICATION_BRANCH);
165        boolean codeSimplificationString     = filter.matches(CODE_SIMPLIFICATION_STRING);
166        boolean codeSimplificationAdvanced   = filter.matches(CODE_SIMPLIFICATION_ADVANCED);
167        boolean codeRemovalAdvanced          = filter.matches(CODE_REMOVAL_ADVANCED);
168        boolean codeRemovalSimple            = filter.matches(CODE_REMOVAL_SIMPLE);
169        boolean codeRemovalVariable          = filter.matches(CODE_REMOVAL_VARIABLE);
170        boolean codeRemovalException         = filter.matches(CODE_REMOVAL_EXCEPTION);
171        boolean codeAllocationVariable       = filter.matches(CODE_ALLOCATION_VARIABLE);
172
173        // Create counters to count the numbers of optimizations.
174        ClassCounter       classMarkingFinalCounter            = new ClassCounter();
175        ClassCounter       classUnboxingEnumCounter            = new ClassCounter();
176        ClassCounter       classMergingVerticalCounter         = new ClassCounter();
177        ClassCounter       classMergingHorizontalCounter       = new ClassCounter();
178        MemberCounter      fieldRemovalWriteonlyCounter        = new MemberCounter();
179        MemberCounter      fieldMarkingPrivateCounter          = new MemberCounter();
180        MemberCounter      fieldPropagationValueCounter        = new MemberCounter();
181        MemberCounter      methodMarkingPrivateCounter         = new MemberCounter();
182        MemberCounter      methodMarkingStaticCounter          = new MemberCounter();
183        MemberCounter      methodMarkingFinalCounter           = new MemberCounter();
184        MemberCounter      methodRemovalParameterCounter       = new MemberCounter();
185        MemberCounter      methodPropagationParameterCounter   = new MemberCounter();
186        MemberCounter      methodPropagationReturnvalueCounter = new MemberCounter();
187        InstructionCounter methodInliningShortCounter          = new InstructionCounter();
188        InstructionCounter methodInliningUniqueCounter         = new InstructionCounter();
189        InstructionCounter methodInliningTailrecursionCounter  = new InstructionCounter();
190        InstructionCounter codeMergingCounter                  = new InstructionCounter();
191        InstructionCounter codeSimplificationVariableCounter   = new InstructionCounter();
192        InstructionCounter codeSimplificationArithmeticCounter = new InstructionCounter();
193        InstructionCounter codeSimplificationCastCounter       = new InstructionCounter();
194        InstructionCounter codeSimplificationFieldCounter      = new InstructionCounter();
195        InstructionCounter codeSimplificationBranchCounter     = new InstructionCounter();
196        InstructionCounter codeSimplificationStringCounter     = new InstructionCounter();
197        InstructionCounter codeSimplificationAdvancedCounter   = new InstructionCounter();
198        InstructionCounter deletedCounter                      = new InstructionCounter();
199        InstructionCounter addedCounter                        = new InstructionCounter();
200        MemberCounter      codeRemovalVariableCounter          = new MemberCounter();
201        ExceptionCounter   codeRemovalExceptionCounter         = new ExceptionCounter();
202        MemberCounter      codeAllocationVariableCounter       = new MemberCounter();
203        MemberCounter      initializerFixCounter1              = new MemberCounter();
204        MemberCounter      initializerFixCounter2              = new MemberCounter();
205
206        // Some optimizations are required by other optimizations.
207        codeSimplificationAdvanced =
208            codeSimplificationAdvanced ||
209            fieldPropagationValue      ||
210            methodPropagationParameter ||
211            methodPropagationReturnvalue;
212
213        codeRemovalAdvanced =
214            codeRemovalAdvanced   ||
215            fieldRemovalWriteonly ||
216            methodMarkingStatic   ||
217            methodRemovalParameter;
218
219        codeRemovalSimple =
220            codeRemovalSimple ||
221            codeSimplificationBranch;
222
223        codeRemovalException =
224            codeRemovalException ||
225            codeRemovalAdvanced ||
226            codeRemovalSimple;
227
228        // Clean up any old visitor info.
229        programClassPool.classesAccept(new ClassCleaner());
230        libraryClassPool.classesAccept(new ClassCleaner());
231
232        // Link all methods that should get the same optimization info.
233        programClassPool.classesAccept(new BottomClassFilter(
234                                       new MethodLinker()));
235        libraryClassPool.classesAccept(new BottomClassFilter(
236                                       new MethodLinker()));
237
238        // Create a visitor for marking the seeds.
239        KeepMarker keepMarker = new KeepMarker();
240        ClassPoolVisitor classPoolvisitor =
241            ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
242                                                                    keepMarker,
243                                                                    keepMarker,
244                                                                    false,
245                                                                    true,
246                                                                    false);
247        // Mark the seeds.
248        programClassPool.accept(classPoolvisitor);
249        libraryClassPool.accept(classPoolvisitor);
250
251        // All library classes and library class members remain unchanged.
252        libraryClassPool.classesAccept(keepMarker);
253        libraryClassPool.classesAccept(new AllMemberVisitor(keepMarker));
254
255        // We also keep all classes that are involved in .class constructs.
256        // We're not looking at enum classes though, so they can be simplified.
257        programClassPool.classesAccept(
258            new ClassAccessFilter(0, ClassConstants.ACC_ENUM,
259            new AllMethodVisitor(
260            new AllAttributeVisitor(
261            new AllInstructionVisitor(
262            new DotClassClassVisitor(keepMarker))))));
263
264        // We also keep all classes that are accessed dynamically.
265        programClassPool.classesAccept(
266            new AllConstantVisitor(
267            new ConstantTagFilter(ClassConstants.CONSTANT_String,
268            new ReferencedClassVisitor(keepMarker))));
269
270        // We also keep all class members that are accessed dynamically.
271        programClassPool.classesAccept(
272            new AllConstantVisitor(
273            new ConstantTagFilter(ClassConstants.CONSTANT_String,
274            new ReferencedMemberVisitor(keepMarker))));
275
276        // We also keep all bootstrap method signatures.
277        programClassPool.classesAccept(
278            new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_7,
279            new AllAttributeVisitor(
280            new AttributeNameFilter(ClassConstants.ATTR_BootstrapMethods,
281            new AllBootstrapMethodInfoVisitor(
282            new BootstrapMethodHandleTraveler(
283            new MethodrefTraveler(
284            new ReferencedMemberVisitor(keepMarker))))))));
285
286        // We also keep all bootstrap method arguments that point to methods.
287        // These arguments are typically the method handles for
288        // java.lang.invoke.LambdaMetafactory#metafactory, which provides the
289        // implementations for closures.
290        programClassPool.classesAccept(
291            new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_7,
292            new AllAttributeVisitor(
293            new AttributeNameFilter(ClassConstants.ATTR_BootstrapMethods,
294            new AllBootstrapMethodInfoVisitor(
295            new BootstrapMethodArgumentVisitor(
296            new MethodrefTraveler(
297            new ReferencedMemberVisitor(keepMarker))))))));
298
299        // We also keep all classes (and their methods) returned by dynamic
300        // method invocations. They may return dynamic implementations of
301        // interfaces that otherwise appear unused.
302        programClassPool.classesAccept(
303            new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_7,
304            new AllConstantVisitor(
305            new DynamicReturnedClassVisitor(
306            new MultiClassVisitor(new ClassVisitor[]
307            {
308                keepMarker,
309                new AllMemberVisitor(keepMarker)
310            })))));
311
312        // Attach some optimization info to all classes and class members, so
313        // it can be filled out later.
314        programClassPool.classesAccept(new ClassOptimizationInfoSetter());
315
316        programClassPool.classesAccept(new AllMemberVisitor(
317                                       new MemberOptimizationInfoSetter()));
318
319        if (configuration.assumeNoSideEffects != null)
320        {
321            // Create a visitor for marking methods that don't have any side effects.
322            NoSideEffectMethodMarker noSideEffectMethodMarker = new NoSideEffectMethodMarker();
323            ClassPoolVisitor noClassPoolvisitor =
324                ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.assumeNoSideEffects,
325                                                                        null,
326                                                                        noSideEffectMethodMarker);
327
328            // Mark the seeds.
329            programClassPool.accept(noClassPoolvisitor);
330            libraryClassPool.accept(noClassPoolvisitor);
331        }
332
333        if (classMarkingFinal)
334        {
335            // Make classes final, whereever possible.
336            programClassPool.classesAccept(
337                new ClassFinalizer(classMarkingFinalCounter));
338        }
339
340        if (methodMarkingFinal)
341        {
342            // Make methods final, whereever possible.
343            programClassPool.classesAccept(
344                new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE,
345                new AllMethodVisitor(
346                new MethodFinalizer(methodMarkingFinalCounter))));
347        }
348
349        if (fieldRemovalWriteonly)
350        {
351            // Mark all fields that are write-only.
352            programClassPool.classesAccept(
353                new AllMethodVisitor(
354                new AllAttributeVisitor(
355                new AllInstructionVisitor(
356                new ReadWriteFieldMarker()))));
357
358            // Count the write-only fields.
359            programClassPool.classesAccept(
360                new AllFieldVisitor(
361                new WriteOnlyFieldFilter(fieldRemovalWriteonlyCounter)));
362        }
363        else
364        {
365            // Mark all fields as read/write.
366            programClassPool.classesAccept(
367                new AllFieldVisitor(
368                new ReadWriteFieldMarker()));
369        }
370
371        if (classUnboxingEnum)
372        {
373             ClassCounter counter = new ClassCounter();
374
375            // Mark all final enums that qualify as simple enums.
376            programClassPool.classesAccept(
377                new ClassAccessFilter(ClassConstants.ACC_FINAL |
378                                      ClassConstants.ACC_ENUM, 0,
379                new SimpleEnumClassChecker()));
380
381            // Count the preliminary number of simple enums.
382            programClassPool.classesAccept(
383                new SimpleEnumFilter(counter));
384
385            // Only continue checking simple enums if there are any candidates.
386            if (counter.getCount() > 0)
387            {
388                // Unmark all simple enums that are explicitly used as objects.
389                programClassPool.classesAccept(
390                    new SimpleEnumUseChecker());
391
392                // Count the definitive number of simple enums.
393                programClassPool.classesAccept(
394                    new SimpleEnumFilter(classUnboxingEnumCounter));
395
396                // Only start handling simple enums if there are any.
397                if (classUnboxingEnumCounter.getCount() > 0)
398                {
399                    // Simplify the use of the enum classes in code.
400                    programClassPool.classesAccept(
401                        new AllMethodVisitor(
402                        new AllAttributeVisitor(
403                        new SimpleEnumUseSimplifier())));
404
405                    // Simplify the static initializers of simple enum classes.
406                    programClassPool.classesAccept(
407                        new SimpleEnumFilter(
408                        new SimpleEnumClassSimplifier()));
409
410                    // Simplify the use of the enum classes in descriptors.
411                    programClassPool.classesAccept(
412                        new SimpleEnumDescriptorSimplifier());
413
414                    // Update references to class members with simple enum classes.
415                    programClassPool.classesAccept(new MemberReferenceFixer());
416                }
417            }
418        }
419
420        // Mark all used parameters, including the 'this' parameters.
421        programClassPool.classesAccept(
422            new AllMethodVisitor(
423            new OptimizationInfoMemberFilter(
424            new ParameterUsageMarker(!methodMarkingStatic,
425                                     !methodRemovalParameter))));
426
427        // Mark all classes that have static initializers.
428        programClassPool.classesAccept(new StaticInitializerContainingClassMarker());
429
430        // Mark all methods that have side effects.
431        programClassPool.accept(new SideEffectMethodMarker());
432
433//        System.out.println("Optimizer.execute: before evaluation simplification");
434//        programClassPool.classAccept("abc/Def", new NamedMethodVisitor("abc", null, new ClassPrinter()));
435
436        // Perform partial evaluation for filling out fields, method parameters,
437        // and method return values, so they can be propagated.
438        if (fieldPropagationValue      ||
439            methodPropagationParameter ||
440            methodPropagationReturnvalue)
441        {
442            // We'll create values to be stored with fields, method parameters,
443            // and return values.
444            ValueFactory valueFactory         = new ParticularValueFactory();
445            ValueFactory detailedValueFactory = new DetailedValueFactory();
446
447            InvocationUnit storingInvocationUnit =
448                new StoringInvocationUnit(valueFactory,
449                                          fieldPropagationValue,
450                                          methodPropagationParameter,
451                                          methodPropagationReturnvalue);
452
453            // Evaluate synthetic classes in more detail, notably to propagate
454            // the arrays of the classes generated for enum switch statements.
455            programClassPool.classesAccept(
456                new ClassAccessFilter(ClassConstants.ACC_SYNTHETIC, 0,
457                new AllMethodVisitor(
458                new AllAttributeVisitor(
459                new PartialEvaluator(detailedValueFactory, storingInvocationUnit, false)))));
460
461            // Evaluate non-synthetic classes.
462            programClassPool.classesAccept(
463                new ClassAccessFilter(0, ClassConstants.ACC_SYNTHETIC,
464                new AllMethodVisitor(
465                new AllAttributeVisitor(
466                new PartialEvaluator(valueFactory, storingInvocationUnit, false)))));
467
468            if (fieldPropagationValue)
469            {
470                // Count the constant fields.
471                programClassPool.classesAccept(
472                    new AllFieldVisitor(
473                    new ConstantMemberFilter(fieldPropagationValueCounter)));
474            }
475
476            if (methodPropagationParameter)
477            {
478                // Count the constant method parameters.
479                programClassPool.classesAccept(
480                    new AllMethodVisitor(
481                    new ConstantParameterFilter(methodPropagationParameterCounter)));
482            }
483
484            if (methodPropagationReturnvalue)
485            {
486                // Count the constant method return values.
487                programClassPool.classesAccept(
488                    new AllMethodVisitor(
489                    new ConstantMemberFilter(methodPropagationReturnvalueCounter)));
490            }
491
492            if (classUnboxingEnumCounter.getCount() > 0)
493            {
494                // Propagate the simple enum constant counts.
495                programClassPool.classesAccept(
496                    new SimpleEnumFilter(
497                    new SimpleEnumArrayPropagator()));
498            }
499
500            if (codeSimplificationAdvanced)
501            {
502                // Fill out constants into the arrays of synthetic classes,
503                // notably the arrays of the classes generated for enum switch
504                // statements.
505                InvocationUnit loadingInvocationUnit =
506                    new LoadingInvocationUnit(valueFactory,
507                                              fieldPropagationValue,
508                                              methodPropagationParameter,
509                                              methodPropagationReturnvalue);
510
511                programClassPool.classesAccept(
512                    new ClassAccessFilter(ClassConstants.ACC_SYNTHETIC, 0,
513                    new AllMethodVisitor(
514                    new AllAttributeVisitor(
515                    new PartialEvaluator(valueFactory, loadingInvocationUnit, false)))));
516            }
517        }
518
519        // Perform partial evaluation again, now loading any previously stored
520        // values for fields, method parameters, and method return values.
521        ValueFactory valueFactory = new IdentifiedValueFactory();
522
523        InvocationUnit loadingInvocationUnit =
524            new LoadingInvocationUnit(valueFactory,
525                                      fieldPropagationValue,
526                                      methodPropagationParameter,
527                                      methodPropagationReturnvalue);
528
529        if (codeSimplificationAdvanced)
530        {
531            // Simplify based on partial evaluation, propagating constant
532            // field values, method parameter values, and return values.
533            programClassPool.classesAccept(
534                new AllMethodVisitor(
535                new AllAttributeVisitor(
536                new EvaluationSimplifier(
537                new PartialEvaluator(valueFactory, loadingInvocationUnit, false),
538                codeSimplificationAdvancedCounter))));
539        }
540
541        if (codeRemovalAdvanced)
542        {
543            // Remove code based on partial evaluation, also removing unused
544            // parameters from method invocations, and making methods static
545            // if possible.
546            programClassPool.classesAccept(
547                new AllMethodVisitor(
548                new AllAttributeVisitor(
549                new EvaluationShrinker(
550                new PartialEvaluator(valueFactory, loadingInvocationUnit, !codeSimplificationAdvanced),
551                deletedCounter, addedCounter))));
552        }
553
554        if (methodRemovalParameter)
555        {
556            // Shrink the parameters in the method descriptors.
557            programClassPool.classesAccept(
558                new AllMethodVisitor(
559                new OptimizationInfoMemberFilter(
560                new MethodDescriptorShrinker())));
561        }
562
563        if (methodMarkingStatic)
564        {
565            // Make all non-static methods that don't require the 'this'
566            // parameter static.
567            programClassPool.classesAccept(
568                new AllMethodVisitor(
569                new OptimizationInfoMemberFilter(
570                new MemberAccessFilter(0, ClassConstants.ACC_STATIC,
571                new MethodStaticizer(methodMarkingStaticCounter)))));
572        }
573
574        if (methodRemovalParameter)
575        {
576            // Fix all references to class members.
577            // This operation also updates the stack sizes.
578            programClassPool.classesAccept(
579                new MemberReferenceFixer());
580
581            // Remove unused bootstrap method arguments.
582            programClassPool.classesAccept(
583                new AllAttributeVisitor(
584                new AllBootstrapMethodInfoVisitor(
585                new BootstrapMethodArgumentShrinker())));
586        }
587
588        if (methodRemovalParameter ||
589            methodMarkingPrivate   ||
590            methodMarkingStatic)
591        {
592            // Remove all unused parameters from the byte code, shifting all
593            // remaining variables.
594            // This operation also updates the local variable frame sizes.
595            programClassPool.classesAccept(
596                new AllMethodVisitor(
597                new AllAttributeVisitor(
598                new ParameterShrinker(methodRemovalParameterCounter))));
599        }
600        else if (codeRemovalAdvanced)
601        {
602            // Just update the local variable frame sizes.
603            programClassPool.classesAccept(
604                new AllMethodVisitor(
605                new AllAttributeVisitor(
606                new StackSizeUpdater())));
607        }
608
609        if (methodRemovalParameter &&
610            methodRemovalParameterCounter.getCount() > 0)
611        {
612            // Tweak the descriptors of duplicate initializers, due to removed
613            // method parameters.
614            programClassPool.classesAccept(
615                new AllMethodVisitor(
616                new DuplicateInitializerFixer(initializerFixCounter1)));
617
618            if (initializerFixCounter1.getCount() > 0)
619            {
620                // Fix all invocations of tweaked initializers.
621                programClassPool.classesAccept(
622                    new AllMethodVisitor(
623                    new AllAttributeVisitor(
624                    new DuplicateInitializerInvocationFixer(addedCounter))));
625
626                // Fix all references to tweaked initializers.
627                programClassPool.classesAccept(new MemberReferenceFixer());
628            }
629        }
630
631        //// Specializing the class member descriptors seems to increase the
632        //// class file size, on average.
633        //// Specialize all class member descriptors.
634        //programClassPool.classesAccept(new AllMemberVisitor(
635        //                               new OptimizationInfoMemberFilter(
636        //                               new MemberDescriptorSpecializer())));
637        //
638        //// Fix all references to classes, for MemberDescriptorSpecializer.
639        //programClassPool.classesAccept(new AllMemberVisitor(
640        //                               new OptimizationInfoMemberFilter(
641        //                               new ClassReferenceFixer(true))));
642
643        // Mark all classes with package visible members.
644        // Mark all exception catches of methods.
645        // Count all method invocations.
646        // Mark super invocations and other access of methods.
647        programClassPool.classesAccept(
648            new MultiClassVisitor(
649            new ClassVisitor[]
650            {
651                new PackageVisibleMemberContainingClassMarker(),
652                new AllConstantVisitor(
653                new PackageVisibleMemberInvokingClassMarker()),
654                new AllMethodVisitor(
655                new MultiMemberVisitor(
656                new MemberVisitor[]
657                {
658                    new AllAttributeVisitor(
659                    new MultiAttributeVisitor(
660                    new AttributeVisitor[]
661                    {
662                        new CatchExceptionMarker(),
663                        new AllInstructionVisitor(
664                        new MultiInstructionVisitor(
665                        new InstructionVisitor[]
666                        {
667                            new InstantiationClassMarker(),
668                            new InstanceofClassMarker(),
669                            new DotClassMarker(),
670                            new MethodInvocationMarker(),
671                            new SuperInvocationMarker(),
672                            new DynamicInvocationMarker(),
673                            new BackwardBranchMarker(),
674                            new AccessMethodMarker(),
675                        })),
676                        new AllExceptionInfoVisitor(
677                        new ExceptionHandlerConstantVisitor(
678                        new ReferencedClassVisitor(
679                        new CaughtClassMarker()))),
680                    })),
681                })),
682            }));
683
684        if (classMergingVertical)
685        {
686            // Merge subclasses up into their superclasses or
687            // merge interfaces down into their implementing classes.
688            programClassPool.classesAccept(
689                new VerticalClassMerger(configuration.allowAccessModification,
690                                        configuration.mergeInterfacesAggressively,
691                                        classMergingVerticalCounter));
692        }
693
694        if (classMergingHorizontal)
695        {
696            // Merge classes into their sibling classes.
697            programClassPool.classesAccept(
698                new HorizontalClassMerger(configuration.allowAccessModification,
699                                          configuration.mergeInterfacesAggressively,
700                                          classMergingHorizontalCounter));
701        }
702
703        if (classMergingVerticalCounter  .getCount() > 0 ||
704            classMergingHorizontalCounter.getCount() > 0)
705        {
706            // Clean up inner class attributes to avoid loops.
707            programClassPool.classesAccept(new RetargetedInnerClassAttributeRemover());
708
709            // Update references to merged classes.
710            programClassPool.classesAccept(new TargetClassChanger());
711            programClassPool.classesAccept(new ClassReferenceFixer(true));
712            programClassPool.classesAccept(new MemberReferenceFixer());
713
714            if (configuration.allowAccessModification)
715            {
716                // Fix the access flags of referenced merged classes and their
717                // class members.
718                programClassPool.classesAccept(
719                    new AccessFixer());
720            }
721
722            // Fix the access flags of the inner classes information.
723            programClassPool.classesAccept(
724                new AllAttributeVisitor(
725                new AllInnerClassesInfoVisitor(
726                new InnerClassesAccessFixer())));
727
728            // Tweak the descriptors of duplicate initializers, due to merged
729            // parameter classes.
730            programClassPool.classesAccept(
731                new AllMethodVisitor(
732                new DuplicateInitializerFixer(initializerFixCounter2)));
733
734            if (initializerFixCounter2.getCount() > 0)
735            {
736                // Fix all invocations of tweaked initializers.
737                programClassPool.classesAccept(
738                    new AllMethodVisitor(
739                    new AllAttributeVisitor(
740                    new DuplicateInitializerInvocationFixer(addedCounter))));
741
742                // Fix all references to tweaked initializers.
743                programClassPool.classesAccept(new MemberReferenceFixer());
744            }
745        }
746
747        if (methodInliningUnique)
748        {
749            // Inline methods that are only invoked once.
750            programClassPool.classesAccept(
751                new AllMethodVisitor(
752                new AllAttributeVisitor(
753                new MethodInliner(configuration.microEdition,
754                                  configuration.allowAccessModification,
755                                  true,
756                                  methodInliningUniqueCounter))));
757        }
758
759        if (methodInliningShort)
760        {
761            // Inline short methods.
762            programClassPool.classesAccept(
763                new AllMethodVisitor(
764                new AllAttributeVisitor(
765                new MethodInliner(configuration.microEdition,
766                                  configuration.allowAccessModification,
767                                  false,
768                                  methodInliningShortCounter))));
769        }
770
771        if (methodInliningTailrecursion)
772        {
773            // Simplify tail recursion calls.
774            programClassPool.classesAccept(
775                new AllMethodVisitor(
776                new AllAttributeVisitor(
777                new TailRecursionSimplifier(methodInliningTailrecursionCounter))));
778        }
779
780        if (fieldMarkingPrivate ||
781            methodMarkingPrivate)
782        {
783            // Mark all class members that can not be made private.
784            programClassPool.classesAccept(
785                new NonPrivateMemberMarker());
786        }
787
788        if (fieldMarkingPrivate)
789        {
790            // Make all non-private fields private, whereever possible.
791            programClassPool.classesAccept(
792                new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE,
793                new AllFieldVisitor(
794                new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE,
795                new MemberPrivatizer(fieldMarkingPrivateCounter)))));
796        }
797
798        if (methodMarkingPrivate)
799        {
800            // Make all non-private methods private, whereever possible.
801            programClassPool.classesAccept(
802                new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE,
803                new AllMethodVisitor(
804                new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE,
805                new MemberPrivatizer(methodMarkingPrivateCounter)))));
806        }
807
808        if ((methodInliningUniqueCounter       .getCount() > 0 ||
809             methodInliningShortCounter        .getCount() > 0 ||
810             methodInliningTailrecursionCounter.getCount() > 0) &&
811            configuration.allowAccessModification)
812        {
813            // Fix the access flags of referenced classes and class members,
814            // for MethodInliner.
815            programClassPool.classesAccept(
816                new AccessFixer());
817        }
818
819        if (methodRemovalParameterCounter .getCount() > 0 ||
820            classMergingVerticalCounter   .getCount() > 0 ||
821            classMergingHorizontalCounter .getCount() > 0 ||
822            methodMarkingPrivateCounter   .getCount() > 0 )
823        {
824            // Fix invocations of interface methods, of methods that have become
825            // non-abstract or private, and of methods that have moved to a
826            // different package.
827            programClassPool.classesAccept(
828                new AllMemberVisitor(
829                new AllAttributeVisitor(
830                new MethodInvocationFixer())));
831        }
832
833        if (codeMerging)
834        {
835            // Share common blocks of code at branches.
836            programClassPool.classesAccept(
837                new AllMethodVisitor(
838                new AllAttributeVisitor(
839                new GotoCommonCodeReplacer(codeMergingCounter))));
840        }
841
842        // Create a branch target marker and a code attribute editor that can
843        // be reused for all code attributes.
844        BranchTargetFinder  branchTargetFinder  = new BranchTargetFinder();
845        CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
846
847        List peepholeOptimizations = new ArrayList();
848        if (codeSimplificationVariable)
849        {
850            // Peephole optimizations involving local variables.
851            peepholeOptimizations.add(
852                new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS,
853                                                 InstructionSequenceConstants.VARIABLE,
854                                                 branchTargetFinder, codeAttributeEditor, codeSimplificationVariableCounter));
855        }
856
857        if (codeSimplificationArithmetic)
858        {
859            // Peephole optimizations involving arithmetic operations.
860            peepholeOptimizations.add(
861                new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS,
862                                                 InstructionSequenceConstants.ARITHMETIC,
863                                                 branchTargetFinder, codeAttributeEditor, codeSimplificationArithmeticCounter));
864        }
865
866        if (codeSimplificationCast)
867        {
868            // Peephole optimizations involving cast operations.
869            peepholeOptimizations.add(
870                new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS,
871                                                 InstructionSequenceConstants.CAST,
872                                                 branchTargetFinder, codeAttributeEditor, codeSimplificationCastCounter));
873        }
874
875        if (codeSimplificationField)
876        {
877            // Peephole optimizations involving fields.
878            peepholeOptimizations.add(
879                new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS,
880                                                 InstructionSequenceConstants.FIELD,
881                                                 branchTargetFinder, codeAttributeEditor, codeSimplificationFieldCounter));
882        }
883
884        if (codeSimplificationBranch)
885        {
886            // Peephole optimizations involving branches.
887            peepholeOptimizations.add(
888                new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS,
889                                                 InstructionSequenceConstants.BRANCH,
890                                                 branchTargetFinder, codeAttributeEditor, codeSimplificationBranchCounter));
891
892            // Include optimization of branches to branches and returns.
893            peepholeOptimizations.add(
894                new GotoGotoReplacer(codeAttributeEditor, codeSimplificationBranchCounter));
895            peepholeOptimizations.add(
896                new GotoReturnReplacer(codeAttributeEditor, codeSimplificationBranchCounter));
897        }
898
899        if (codeSimplificationString)
900        {
901            // Peephole optimizations involving branches.
902            peepholeOptimizations.add(
903                new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS,
904                                                 InstructionSequenceConstants.STRING,
905                                                 branchTargetFinder, codeAttributeEditor, codeSimplificationStringCounter));
906        }
907
908        if (!peepholeOptimizations.isEmpty())
909        {
910            // Convert the list into an array.
911            InstructionVisitor[] peepholeOptimizationsArray =
912                new InstructionVisitor[peepholeOptimizations.size()];
913            peepholeOptimizations.toArray(peepholeOptimizationsArray);
914
915            // Perform the peephole optimisations.
916            programClassPool.classesAccept(
917                new AllMethodVisitor(
918                new AllAttributeVisitor(
919                new PeepholeOptimizer(branchTargetFinder, codeAttributeEditor,
920                new MultiInstructionVisitor(
921                peepholeOptimizationsArray)))));
922        }
923
924        if (codeRemovalException)
925        {
926            // Remove unnecessary exception handlers.
927            programClassPool.classesAccept(
928                new AllMethodVisitor(
929                new AllAttributeVisitor(
930                new UnreachableExceptionRemover(codeRemovalExceptionCounter))));
931        }
932
933        if (codeRemovalSimple)
934        {
935            // Remove unreachable code.
936            programClassPool.classesAccept(
937                new AllMethodVisitor(
938                new AllAttributeVisitor(
939                new UnreachableCodeRemover(deletedCounter))));
940        }
941
942        if (codeRemovalVariable)
943        {
944            // Remove all unused local variables.
945            programClassPool.classesAccept(
946                new AllMethodVisitor(
947                new AllAttributeVisitor(
948                new VariableShrinker(codeRemovalVariableCounter))));
949        }
950
951        if (codeAllocationVariable)
952        {
953            // Optimize the variables.
954            programClassPool.classesAccept(
955                new AllMethodVisitor(
956                new AllAttributeVisitor(
957                new VariableOptimizer(false, codeAllocationVariableCounter))));
958        }
959
960
961        // Remove unused constants.
962        programClassPool.classesAccept(
963            new ConstantPoolShrinker());
964
965        int classMarkingFinalCount            = classMarkingFinalCounter           .getCount();
966        int classUnboxingEnumCount            = classUnboxingEnumCounter           .getCount();
967        int classMergingVerticalCount         = classMergingVerticalCounter        .getCount();
968        int classMergingHorizontalCount       = classMergingHorizontalCounter      .getCount();
969        int fieldRemovalWriteonlyCount        = fieldRemovalWriteonlyCounter       .getCount();
970        int fieldMarkingPrivateCount          = fieldMarkingPrivateCounter         .getCount();
971        int fieldPropagationValueCount        = fieldPropagationValueCounter       .getCount();
972        int methodMarkingPrivateCount         = methodMarkingPrivateCounter        .getCount();
973        int methodMarkingStaticCount          = methodMarkingStaticCounter         .getCount();
974        int methodMarkingFinalCount           = methodMarkingFinalCounter          .getCount();
975        int methodRemovalParameterCount       = methodRemovalParameterCounter      .getCount() - methodMarkingStaticCounter.getCount() - initializerFixCounter1.getCount() - initializerFixCounter2.getCount();
976        int methodPropagationParameterCount   = methodPropagationParameterCounter  .getCount();
977        int methodPropagationReturnvalueCount = methodPropagationReturnvalueCounter.getCount();
978        int methodInliningShortCount          = methodInliningShortCounter         .getCount();
979        int methodInliningUniqueCount         = methodInliningUniqueCounter        .getCount();
980        int methodInliningTailrecursionCount  = methodInliningTailrecursionCounter .getCount();
981        int codeMergingCount                  = codeMergingCounter                 .getCount();
982        int codeSimplificationVariableCount   = codeSimplificationVariableCounter  .getCount();
983        int codeSimplificationArithmeticCount = codeSimplificationArithmeticCounter.getCount();
984        int codeSimplificationCastCount       = codeSimplificationCastCounter      .getCount();
985        int codeSimplificationFieldCount      = codeSimplificationFieldCounter     .getCount();
986        int codeSimplificationBranchCount     = codeSimplificationBranchCounter    .getCount();
987        int codeSimplificationStringCount     = codeSimplificationStringCounter    .getCount();
988        int codeSimplificationAdvancedCount   = codeSimplificationAdvancedCounter  .getCount();
989        int codeRemovalCount                  = deletedCounter                     .getCount() - addedCounter.getCount();
990        int codeRemovalVariableCount          = codeRemovalVariableCounter         .getCount();
991        int codeRemovalExceptionCount         = codeRemovalExceptionCounter        .getCount();
992        int codeAllocationVariableCount       = codeAllocationVariableCounter      .getCount();
993
994        // Forget about constant fields, parameters, and return values, if they
995        // didn't lead to any useful optimizations. We want to avoid fruitless
996        // additional optimization passes.
997        if (codeSimplificationAdvancedCount == 0)
998        {
999            fieldPropagationValueCount        = 0;
1000            methodPropagationParameterCount   = 0;
1001            methodPropagationReturnvalueCount = 0;
1002        }
1003
1004        if (configuration.verbose)
1005        {
1006            System.out.println("  Number of finalized classes:                 " + classMarkingFinalCount            + disabled(classMarkingFinal));
1007            System.out.println("  Number of unboxed enum classes:              " + classUnboxingEnumCount            + disabled(classUnboxingEnum));
1008            System.out.println("  Number of vertically merged classes:         " + classMergingVerticalCount         + disabled(classMergingVertical));
1009            System.out.println("  Number of horizontally merged classes:       " + classMergingHorizontalCount       + disabled(classMergingHorizontal));
1010            System.out.println("  Number of removed write-only fields:         " + fieldRemovalWriteonlyCount        + disabled(fieldRemovalWriteonly));
1011            System.out.println("  Number of privatized fields:                 " + fieldMarkingPrivateCount          + disabled(fieldMarkingPrivate));
1012            System.out.println("  Number of inlined constant fields:           " + fieldPropagationValueCount        + disabled(fieldPropagationValue));
1013            System.out.println("  Number of privatized methods:                " + methodMarkingPrivateCount         + disabled(methodMarkingPrivate));
1014            System.out.println("  Number of staticized methods:                " + methodMarkingStaticCount          + disabled(methodMarkingStatic));
1015            System.out.println("  Number of finalized methods:                 " + methodMarkingFinalCount           + disabled(methodMarkingFinal));
1016            System.out.println("  Number of removed method parameters:         " + methodRemovalParameterCount       + disabled(methodRemovalParameter));
1017            System.out.println("  Number of inlined constant parameters:       " + methodPropagationParameterCount   + disabled(methodPropagationParameter));
1018            System.out.println("  Number of inlined constant return values:    " + methodPropagationReturnvalueCount + disabled(methodPropagationReturnvalue));
1019            System.out.println("  Number of inlined short method calls:        " + methodInliningShortCount          + disabled(methodInliningShort));
1020            System.out.println("  Number of inlined unique method calls:       " + methodInliningUniqueCount         + disabled(methodInliningUnique));
1021            System.out.println("  Number of inlined tail recursion calls:      " + methodInliningTailrecursionCount  + disabled(methodInliningTailrecursion));
1022            System.out.println("  Number of merged code blocks:                " + codeMergingCount                  + disabled(codeMerging));
1023            System.out.println("  Number of variable peephole optimizations:   " + codeSimplificationVariableCount   + disabled(codeSimplificationVariable));
1024            System.out.println("  Number of arithmetic peephole optimizations: " + codeSimplificationArithmeticCount + disabled(codeSimplificationArithmetic));
1025            System.out.println("  Number of cast peephole optimizations:       " + codeSimplificationCastCount       + disabled(codeSimplificationCast));
1026            System.out.println("  Number of field peephole optimizations:      " + codeSimplificationFieldCount      + disabled(codeSimplificationField));
1027            System.out.println("  Number of branch peephole optimizations:     " + codeSimplificationBranchCount     + disabled(codeSimplificationBranch));
1028            System.out.println("  Number of string peephole optimizations:     " + codeSimplificationStringCount     + disabled(codeSimplificationString));
1029            System.out.println("  Number of simplified instructions:           " + codeSimplificationAdvancedCount   + disabled(codeSimplificationAdvanced));
1030            System.out.println("  Number of removed instructions:              " + codeRemovalCount                  + disabled(codeRemovalAdvanced));
1031            System.out.println("  Number of removed local variables:           " + codeRemovalVariableCount          + disabled(codeRemovalVariable));
1032            System.out.println("  Number of removed exception blocks:          " + codeRemovalExceptionCount         + disabled(codeRemovalException));
1033            System.out.println("  Number of optimized local variable frames:   " + codeAllocationVariableCount       + disabled(codeAllocationVariable));
1034        }
1035
1036        return classMarkingFinalCount            > 0 ||
1037               classUnboxingEnumCount            > 0 ||
1038               classMergingVerticalCount         > 0 ||
1039               classMergingHorizontalCount       > 0 ||
1040               fieldRemovalWriteonlyCount        > 0 ||
1041               fieldMarkingPrivateCount          > 0 ||
1042               methodMarkingPrivateCount         > 0 ||
1043               methodMarkingStaticCount          > 0 ||
1044               methodMarkingFinalCount           > 0 ||
1045               fieldPropagationValueCount        > 0 ||
1046               methodRemovalParameterCount       > 0 ||
1047               methodPropagationParameterCount   > 0 ||
1048               methodPropagationReturnvalueCount > 0 ||
1049               methodInliningShortCount          > 0 ||
1050               methodInliningUniqueCount         > 0 ||
1051               methodInliningTailrecursionCount  > 0 ||
1052               codeMergingCount                  > 0 ||
1053               codeSimplificationVariableCount   > 0 ||
1054               codeSimplificationArithmeticCount > 0 ||
1055               codeSimplificationCastCount       > 0 ||
1056               codeSimplificationFieldCount      > 0 ||
1057               codeSimplificationBranchCount     > 0 ||
1058               codeSimplificationStringCount     > 0 ||
1059               codeSimplificationAdvancedCount   > 0 ||
1060               codeRemovalCount                  > 0 ||
1061               codeRemovalVariableCount          > 0 ||
1062               codeRemovalExceptionCount         > 0 ||
1063               codeAllocationVariableCount       > 0;
1064    }
1065
1066
1067    /**
1068     * Returns a String indicating whether the given flag is enabled or
1069     * disabled.
1070     */
1071    private String disabled(boolean flag)
1072    {
1073        return flag ? "" : "   (disabled)";
1074    }
1075
1076
1077    /**
1078     * Returns a String indicating whether the given flags are enabled or
1079     * disabled.
1080     */
1081    private String disabled(boolean flag1, boolean flag2)
1082    {
1083        return flag1 && flag2 ? "" :
1084               flag1 || flag2 ? "   (partially disabled)" :
1085                                "   (disabled)";
1086    }
1087}
1088