blob: 33c3c0ca30cba5cbb88e0c56295103aae8db6b9f [file] [log] [blame]
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package proguard.optimize;
import proguard.*;
import proguard.classfile.*;
import proguard.classfile.attribute.visitor.*;
import proguard.classfile.constant.visitor.*;
import proguard.classfile.editor.*;
import proguard.classfile.instruction.visitor.*;
import proguard.classfile.util.MethodLinker;
import proguard.classfile.visitor.*;
import proguard.evaluation.*;
import proguard.evaluation.value.*;
import proguard.optimize.evaluation.*;
import proguard.optimize.info.*;
import proguard.optimize.peephole.*;
import proguard.util.*;
import java.io.IOException;
import java.util.*;
/**
* This class optimizes class pools according to a given configuration.
*
* @author Eric Lafortune
*/
public class Optimizer
{
private static final String CLASS_MARKING_FINAL = "class/marking/final";
private static final String CLASS_UNBOXING_ENUM = "class/unboxing/enum";
private static final String CLASS_MERGING_VERTICAL = "class/merging/vertical";
private static final String CLASS_MERGING_HORIZONTAL = "class/merging/horizontal";
private static final String FIELD_REMOVAL_WRITEONLY = "field/removal/writeonly";
private static final String FIELD_MARKING_PRIVATE = "field/marking/private";
private static final String FIELD_PROPAGATION_VALUE = "field/propagation/value";
private static final String METHOD_MARKING_PRIVATE = "method/marking/private";
private static final String METHOD_MARKING_STATIC = "method/marking/static";
private static final String METHOD_MARKING_FINAL = "method/marking/final";
private static final String METHOD_REMOVAL_PARAMETER = "method/removal/parameter";
private static final String METHOD_PROPAGATION_PARAMETER = "method/propagation/parameter";
private static final String METHOD_PROPAGATION_RETURNVALUE = "method/propagation/returnvalue";
private static final String METHOD_INLINING_SHORT = "method/inlining/short";
private static final String METHOD_INLINING_UNIQUE = "method/inlining/unique";
private static final String METHOD_INLINING_TAILRECURSION = "method/inlining/tailrecursion";
private static final String CODE_MERGING = "code/merging";
private static final String CODE_SIMPLIFICATION_VARIABLE = "code/simplification/variable";
private static final String CODE_SIMPLIFICATION_ARITHMETIC = "code/simplification/arithmetic";
private static final String CODE_SIMPLIFICATION_CAST = "code/simplification/cast";
private static final String CODE_SIMPLIFICATION_FIELD = "code/simplification/field";
private static final String CODE_SIMPLIFICATION_BRANCH = "code/simplification/branch";
private static final String CODE_SIMPLIFICATION_STRING = "code/simplification/string";
private static final String CODE_SIMPLIFICATION_ADVANCED = "code/simplification/advanced";
private static final String CODE_REMOVAL_ADVANCED = "code/removal/advanced";
private static final String CODE_REMOVAL_SIMPLE = "code/removal/simple";
private static final String CODE_REMOVAL_VARIABLE = "code/removal/variable";
private static final String CODE_REMOVAL_EXCEPTION = "code/removal/exception";
private static final String CODE_ALLOCATION_VARIABLE = "code/allocation/variable";
public static final String[] OPTIMIZATION_NAMES = new String[]
{
CLASS_MARKING_FINAL,
CLASS_MERGING_VERTICAL,
CLASS_MERGING_HORIZONTAL,
FIELD_REMOVAL_WRITEONLY,
FIELD_MARKING_PRIVATE,
FIELD_PROPAGATION_VALUE,
METHOD_MARKING_PRIVATE,
METHOD_MARKING_STATIC,
METHOD_MARKING_FINAL,
METHOD_REMOVAL_PARAMETER,
METHOD_PROPAGATION_PARAMETER,
METHOD_PROPAGATION_RETURNVALUE,
METHOD_INLINING_SHORT,
METHOD_INLINING_UNIQUE,
METHOD_INLINING_TAILRECURSION,
CODE_MERGING,
CODE_SIMPLIFICATION_VARIABLE,
CODE_SIMPLIFICATION_ARITHMETIC,
CODE_SIMPLIFICATION_CAST,
CODE_SIMPLIFICATION_FIELD,
CODE_SIMPLIFICATION_BRANCH,
CODE_SIMPLIFICATION_STRING,
CODE_SIMPLIFICATION_ADVANCED,
CODE_REMOVAL_ADVANCED,
CODE_REMOVAL_SIMPLE,
CODE_REMOVAL_VARIABLE,
CODE_REMOVAL_EXCEPTION,
CODE_ALLOCATION_VARIABLE,
};
private final Configuration configuration;
/**
* Creates a new Optimizer.
*/
public Optimizer(Configuration configuration)
{
this.configuration = configuration;
}
/**
* Performs optimization of the given program class pool.
*/
public boolean execute(ClassPool programClassPool,
ClassPool libraryClassPool) throws IOException
{
// Check if we have at least some keep commands.
if (configuration.keep == null &&
configuration.applyMapping == null &&
configuration.printMapping == null)
{
throw new IOException("You have to specify '-keep' options for the optimization step.");
}
// Create a matcher for filtering optimizations.
StringMatcher filter = configuration.optimizations != null ?
new ListParser(new NameParser()).parse(configuration.optimizations) :
new ConstantMatcher(true);
boolean classMarkingFinal = filter.matches(CLASS_MARKING_FINAL);
boolean classUnboxingEnum = filter.matches(CLASS_UNBOXING_ENUM);
boolean classMergingVertical = filter.matches(CLASS_MERGING_VERTICAL);
boolean classMergingHorizontal = filter.matches(CLASS_MERGING_HORIZONTAL);
boolean fieldRemovalWriteonly = filter.matches(FIELD_REMOVAL_WRITEONLY);
boolean fieldMarkingPrivate = filter.matches(FIELD_MARKING_PRIVATE);
boolean fieldPropagationValue = filter.matches(FIELD_PROPAGATION_VALUE);
boolean methodMarkingPrivate = filter.matches(METHOD_MARKING_PRIVATE);
boolean methodMarkingStatic = filter.matches(METHOD_MARKING_STATIC);
boolean methodMarkingFinal = filter.matches(METHOD_MARKING_FINAL);
boolean methodRemovalParameter = filter.matches(METHOD_REMOVAL_PARAMETER);
boolean methodPropagationParameter = filter.matches(METHOD_PROPAGATION_PARAMETER);
boolean methodPropagationReturnvalue = filter.matches(METHOD_PROPAGATION_RETURNVALUE);
boolean methodInliningShort = filter.matches(METHOD_INLINING_SHORT);
boolean methodInliningUnique = filter.matches(METHOD_INLINING_UNIQUE);
boolean methodInliningTailrecursion = filter.matches(METHOD_INLINING_TAILRECURSION);
boolean codeMerging = filter.matches(CODE_MERGING);
boolean codeSimplificationVariable = filter.matches(CODE_SIMPLIFICATION_VARIABLE);
boolean codeSimplificationArithmetic = filter.matches(CODE_SIMPLIFICATION_ARITHMETIC);
boolean codeSimplificationCast = filter.matches(CODE_SIMPLIFICATION_CAST);
boolean codeSimplificationField = filter.matches(CODE_SIMPLIFICATION_FIELD);
boolean codeSimplificationBranch = filter.matches(CODE_SIMPLIFICATION_BRANCH);
boolean codeSimplificationString = filter.matches(CODE_SIMPLIFICATION_STRING);
boolean codeSimplificationAdvanced = filter.matches(CODE_SIMPLIFICATION_ADVANCED);
boolean codeRemovalAdvanced = filter.matches(CODE_REMOVAL_ADVANCED);
boolean codeRemovalSimple = filter.matches(CODE_REMOVAL_SIMPLE);
boolean codeRemovalVariable = filter.matches(CODE_REMOVAL_VARIABLE);
boolean codeRemovalException = filter.matches(CODE_REMOVAL_EXCEPTION);
boolean codeAllocationVariable = filter.matches(CODE_ALLOCATION_VARIABLE);
// Create counters to count the numbers of optimizations.
ClassCounter classMarkingFinalCounter = new ClassCounter();
ClassCounter classUnboxingEnumCounter = new ClassCounter();
ClassCounter classMergingVerticalCounter = new ClassCounter();
ClassCounter classMergingHorizontalCounter = new ClassCounter();
MemberCounter fieldRemovalWriteonlyCounter = new MemberCounter();
MemberCounter fieldMarkingPrivateCounter = new MemberCounter();
MemberCounter fieldPropagationValueCounter = new MemberCounter();
MemberCounter methodMarkingPrivateCounter = new MemberCounter();
MemberCounter methodMarkingStaticCounter = new MemberCounter();
MemberCounter methodMarkingFinalCounter = new MemberCounter();
MemberCounter methodRemovalParameterCounter = new MemberCounter();
MemberCounter methodPropagationParameterCounter = new MemberCounter();
MemberCounter methodPropagationReturnvalueCounter = new MemberCounter();
InstructionCounter methodInliningShortCounter = new InstructionCounter();
InstructionCounter methodInliningUniqueCounter = new InstructionCounter();
InstructionCounter methodInliningTailrecursionCounter = new InstructionCounter();
InstructionCounter codeMergingCounter = new InstructionCounter();
InstructionCounter codeSimplificationVariableCounter = new InstructionCounter();
InstructionCounter codeSimplificationArithmeticCounter = new InstructionCounter();
InstructionCounter codeSimplificationCastCounter = new InstructionCounter();
InstructionCounter codeSimplificationFieldCounter = new InstructionCounter();
InstructionCounter codeSimplificationBranchCounter = new InstructionCounter();
InstructionCounter codeSimplificationStringCounter = new InstructionCounter();
InstructionCounter codeSimplificationAdvancedCounter = new InstructionCounter();
InstructionCounter deletedCounter = new InstructionCounter();
InstructionCounter addedCounter = new InstructionCounter();
MemberCounter codeRemovalVariableCounter = new MemberCounter();
ExceptionCounter codeRemovalExceptionCounter = new ExceptionCounter();
MemberCounter codeAllocationVariableCounter = new MemberCounter();
MemberCounter initializerFixCounter1 = new MemberCounter();
MemberCounter initializerFixCounter2 = new MemberCounter();
// Some optimizations are required by other optimizations.
codeSimplificationAdvanced =
codeSimplificationAdvanced ||
fieldPropagationValue ||
methodPropagationParameter ||
methodPropagationReturnvalue;
codeRemovalAdvanced =
codeRemovalAdvanced ||
fieldRemovalWriteonly ||
methodMarkingStatic ||
methodRemovalParameter;
codeRemovalSimple =
codeRemovalSimple ||
codeSimplificationBranch;
codeRemovalException =
codeRemovalException ||
codeRemovalAdvanced ||
codeRemovalSimple;
// Clean up any old visitor info.
programClassPool.classesAccept(new ClassCleaner());
libraryClassPool.classesAccept(new ClassCleaner());
// Link all methods that should get the same optimization info.
programClassPool.classesAccept(new BottomClassFilter(
new MethodLinker()));
libraryClassPool.classesAccept(new BottomClassFilter(
new MethodLinker()));
// Create a visitor for marking the seeds.
KeepMarker keepMarker = new KeepMarker();
ClassPoolVisitor classPoolvisitor =
ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
keepMarker,
keepMarker,
false,
true,
false);
// Mark the seeds.
programClassPool.accept(classPoolvisitor);
libraryClassPool.accept(classPoolvisitor);
// All library classes and library class members remain unchanged.
libraryClassPool.classesAccept(keepMarker);
libraryClassPool.classesAccept(new AllMemberVisitor(keepMarker));
// We also keep all classes that are involved in .class constructs.
// We're not looking at enum classes though, so they can be simplified.
programClassPool.classesAccept(
new ClassAccessFilter(0, ClassConstants.ACC_ENUM,
new AllMethodVisitor(
new AllAttributeVisitor(
new AllInstructionVisitor(
new DotClassClassVisitor(keepMarker))))));
// We also keep all classes that are accessed dynamically.
programClassPool.classesAccept(
new AllConstantVisitor(
new ConstantTagFilter(ClassConstants.CONSTANT_String,
new ReferencedClassVisitor(keepMarker))));
// We also keep all class members that are accessed dynamically.
programClassPool.classesAccept(
new AllConstantVisitor(
new ConstantTagFilter(ClassConstants.CONSTANT_String,
new ReferencedMemberVisitor(keepMarker))));
// We also keep all bootstrap method signatures.
programClassPool.classesAccept(
new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_7,
new AllAttributeVisitor(
new AttributeNameFilter(ClassConstants.ATTR_BootstrapMethods,
new AllBootstrapMethodInfoVisitor(
new BootstrapMethodHandleTraveler(
new MethodrefTraveler(
new ReferencedMemberVisitor(keepMarker))))))));
// We also keep all bootstrap method arguments that point to methods.
// These arguments are typically the method handles for
// java.lang.invoke.LambdaMetafactory#metafactory, which provides the
// implementations for closures.
programClassPool.classesAccept(
new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_7,
new AllAttributeVisitor(
new AttributeNameFilter(ClassConstants.ATTR_BootstrapMethods,
new AllBootstrapMethodInfoVisitor(
new BootstrapMethodArgumentVisitor(
new MethodrefTraveler(
new ReferencedMemberVisitor(keepMarker))))))));
// We also keep all classes (and their methods) returned by dynamic
// method invocations. They may return dynamic implementations of
// interfaces that otherwise appear unused.
programClassPool.classesAccept(
new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_7,
new AllConstantVisitor(
new DynamicReturnedClassVisitor(
new MultiClassVisitor(new ClassVisitor[]
{
keepMarker,
new AllMemberVisitor(keepMarker)
})))));
// Attach some optimization info to all classes and class members, so
// it can be filled out later.
programClassPool.classesAccept(new ClassOptimizationInfoSetter());
programClassPool.classesAccept(new AllMemberVisitor(
new MemberOptimizationInfoSetter()));
if (configuration.assumeNoSideEffects != null)
{
// Create a visitor for marking methods that don't have any side effects.
NoSideEffectMethodMarker noSideEffectMethodMarker = new NoSideEffectMethodMarker();
ClassPoolVisitor noClassPoolvisitor =
ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.assumeNoSideEffects,
null,
noSideEffectMethodMarker);
// Mark the seeds.
programClassPool.accept(noClassPoolvisitor);
libraryClassPool.accept(noClassPoolvisitor);
}
if (classMarkingFinal)
{
// Make classes final, whereever possible.
programClassPool.classesAccept(
new ClassFinalizer(classMarkingFinalCounter));
}
if (methodMarkingFinal)
{
// Make methods final, whereever possible.
programClassPool.classesAccept(
new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE,
new AllMethodVisitor(
new MethodFinalizer(methodMarkingFinalCounter))));
}
if (fieldRemovalWriteonly)
{
// Mark all fields that are write-only.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new AllInstructionVisitor(
new ReadWriteFieldMarker()))));
// Count the write-only fields.
programClassPool.classesAccept(
new AllFieldVisitor(
new WriteOnlyFieldFilter(fieldRemovalWriteonlyCounter)));
}
else
{
// Mark all fields as read/write.
programClassPool.classesAccept(
new AllFieldVisitor(
new ReadWriteFieldMarker()));
}
if (classUnboxingEnum)
{
ClassCounter counter = new ClassCounter();
// Mark all final enums that qualify as simple enums.
programClassPool.classesAccept(
new ClassAccessFilter(ClassConstants.ACC_FINAL |
ClassConstants.ACC_ENUM, 0,
new OptimizationInfoClassFilter(
new SimpleEnumClassChecker())));
// Count the preliminary number of simple enums.
programClassPool.classesAccept(
new SimpleEnumFilter(counter));
// Only continue checking simple enums if there are any candidates.
if (counter.getCount() > 0)
{
// Unmark all simple enums that are explicitly used as objects.
programClassPool.classesAccept(
new SimpleEnumUseChecker());
// Unmark all simple enums that are used in descriptors of
// kept class members. Changing their names could upset
// the name parameters of invokedynamic instructions.
programClassPool.classesAccept(
new SimpleEnumFilter(null,
new AllMemberVisitor(
new KeptMemberFilter(
new MemberDescriptorReferencedClassVisitor(
new OptimizationInfoClassFilter(
new SimpleEnumMarker(false)))))));
// Count the definitive number of simple enums.
programClassPool.classesAccept(
new SimpleEnumFilter(classUnboxingEnumCounter));
// Only start handling simple enums if there are any.
if (classUnboxingEnumCounter.getCount() > 0)
{
// Simplify the use of the enum classes in code.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new SimpleEnumUseSimplifier())));
// Simplify the static initializers of simple enum classes.
programClassPool.classesAccept(
new SimpleEnumFilter(
new SimpleEnumClassSimplifier()));
// Simplify the use of the enum classes in descriptors.
programClassPool.classesAccept(
new SimpleEnumDescriptorSimplifier());
// Update references to class members with simple enum classes.
programClassPool.classesAccept(new MemberReferenceFixer());
}
}
}
// Mark all used parameters, including the 'this' parameters.
programClassPool.classesAccept(
new AllMethodVisitor(
new OptimizationInfoMemberFilter(
new ParameterUsageMarker(!methodMarkingStatic,
!methodRemovalParameter))));
// Mark all classes that have static initializers.
programClassPool.classesAccept(new StaticInitializerContainingClassMarker());
// Mark all methods that have side effects.
programClassPool.accept(new SideEffectMethodMarker());
// System.out.println("Optimizer.execute: before evaluation simplification");
// programClassPool.classAccept("abc/Def", new NamedMethodVisitor("abc", null, new ClassPrinter()));
// Perform partial evaluation for filling out fields, method parameters,
// and method return values, so they can be propagated.
if (fieldPropagationValue ||
methodPropagationParameter ||
methodPropagationReturnvalue)
{
// We'll create values to be stored with fields, method parameters,
// and return values.
ValueFactory valueFactory = new ParticularValueFactory();
ValueFactory detailedValueFactory = new DetailedValueFactory();
InvocationUnit storingInvocationUnit =
new StoringInvocationUnit(valueFactory,
fieldPropagationValue,
methodPropagationParameter,
methodPropagationReturnvalue);
// Evaluate synthetic classes in more detail, notably to propagate
// the arrays of the classes generated for enum switch statements.
programClassPool.classesAccept(
new ClassAccessFilter(ClassConstants.ACC_SYNTHETIC, 0,
new AllMethodVisitor(
new AllAttributeVisitor(
new PartialEvaluator(detailedValueFactory, storingInvocationUnit, false)))));
// Evaluate non-synthetic classes.
programClassPool.classesAccept(
new ClassAccessFilter(0, ClassConstants.ACC_SYNTHETIC,
new AllMethodVisitor(
new AllAttributeVisitor(
new PartialEvaluator(valueFactory, storingInvocationUnit, false)))));
if (fieldPropagationValue)
{
// Count the constant fields.
programClassPool.classesAccept(
new AllFieldVisitor(
new ConstantMemberFilter(fieldPropagationValueCounter)));
}
if (methodPropagationParameter)
{
// Count the constant method parameters.
programClassPool.classesAccept(
new AllMethodVisitor(
new ConstantParameterFilter(methodPropagationParameterCounter)));
}
if (methodPropagationReturnvalue)
{
// Count the constant method return values.
programClassPool.classesAccept(
new AllMethodVisitor(
new ConstantMemberFilter(methodPropagationReturnvalueCounter)));
}
if (classUnboxingEnumCounter.getCount() > 0)
{
// Propagate the simple enum constant counts.
programClassPool.classesAccept(
new SimpleEnumFilter(
new SimpleEnumArrayPropagator()));
}
if (codeSimplificationAdvanced)
{
// Fill out constants into the arrays of synthetic classes,
// notably the arrays of the classes generated for enum switch
// statements.
InvocationUnit loadingInvocationUnit =
new LoadingInvocationUnit(valueFactory,
fieldPropagationValue,
methodPropagationParameter,
methodPropagationReturnvalue);
programClassPool.classesAccept(
new ClassAccessFilter(ClassConstants.ACC_SYNTHETIC, 0,
new AllMethodVisitor(
new AllAttributeVisitor(
new PartialEvaluator(valueFactory, loadingInvocationUnit, false)))));
}
}
// Perform partial evaluation again, now loading any previously stored
// values for fields, method parameters, and method return values.
ValueFactory valueFactory = new IdentifiedValueFactory();
InvocationUnit loadingInvocationUnit =
new LoadingInvocationUnit(valueFactory,
fieldPropagationValue,
methodPropagationParameter,
methodPropagationReturnvalue);
if (codeSimplificationAdvanced)
{
// Simplify based on partial evaluation, propagating constant
// field values, method parameter values, and return values.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new EvaluationSimplifier(
new PartialEvaluator(valueFactory, loadingInvocationUnit, false),
codeSimplificationAdvancedCounter))));
}
if (codeRemovalAdvanced)
{
// Remove code based on partial evaluation, also removing unused
// parameters from method invocations, and making methods static
// if possible.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new EvaluationShrinker(
new PartialEvaluator(valueFactory, loadingInvocationUnit, !codeSimplificationAdvanced),
deletedCounter, addedCounter))));
}
if (methodRemovalParameter)
{
// Shrink the parameters in the method descriptors.
programClassPool.classesAccept(
new AllMethodVisitor(
new OptimizationInfoMemberFilter(
new MethodDescriptorShrinker())));
}
if (methodMarkingStatic)
{
// Make all non-static methods that don't require the 'this'
// parameter static.
programClassPool.classesAccept(
new AllMethodVisitor(
new OptimizationInfoMemberFilter(
new MemberAccessFilter(0, ClassConstants.ACC_STATIC,
new MethodStaticizer(methodMarkingStaticCounter)))));
}
if (methodRemovalParameter)
{
// Fix all references to class members.
// This operation also updates the stack sizes.
programClassPool.classesAccept(
new MemberReferenceFixer());
// Remove unused bootstrap method arguments.
programClassPool.classesAccept(
new AllAttributeVisitor(
new AllBootstrapMethodInfoVisitor(
new BootstrapMethodArgumentShrinker())));
}
if (methodRemovalParameter ||
methodMarkingPrivate ||
methodMarkingStatic)
{
// Remove all unused parameters from the byte code, shifting all
// remaining variables.
// This operation also updates the local variable frame sizes.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new ParameterShrinker(methodRemovalParameterCounter))));
}
else if (codeRemovalAdvanced)
{
// Just update the local variable frame sizes.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new StackSizeUpdater())));
}
if (methodRemovalParameter &&
methodRemovalParameterCounter.getCount() > 0)
{
// Tweak the descriptors of duplicate initializers, due to removed
// method parameters.
programClassPool.classesAccept(
new AllMethodVisitor(
new DuplicateInitializerFixer(initializerFixCounter1)));
if (initializerFixCounter1.getCount() > 0)
{
// Fix all invocations of tweaked initializers.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new DuplicateInitializerInvocationFixer(addedCounter))));
// Fix all references to tweaked initializers.
programClassPool.classesAccept(new MemberReferenceFixer());
}
}
//// Specializing the class member descriptors seems to increase the
//// class file size, on average.
//// Specialize all class member descriptors.
//programClassPool.classesAccept(new AllMemberVisitor(
// new OptimizationInfoMemberFilter(
// new MemberDescriptorSpecializer())));
//
//// Fix all references to classes, for MemberDescriptorSpecializer.
//programClassPool.classesAccept(new AllMemberVisitor(
// new OptimizationInfoMemberFilter(
// new ClassReferenceFixer(true))));
// Mark all classes with package visible members.
// Mark all exception catches of methods.
// Count all method invocations.
// Mark super invocations and other access of methods.
StackSizeComputer stackSizeComputer = new StackSizeComputer();
programClassPool.classesAccept(
new MultiClassVisitor(
new ClassVisitor[]
{
// Mark classes.
new PackageVisibleMemberContainingClassMarker(),
new AllConstantVisitor(
new PackageVisibleMemberInvokingClassMarker()),
// Mark methods.
new AllMethodVisitor(
new OptimizationInfoMemberFilter(
new AllAttributeVisitor(
new MultiAttributeVisitor(
new AttributeVisitor[]
{
stackSizeComputer,
new CatchExceptionMarker(),
new AllInstructionVisitor(
new MultiInstructionVisitor(
new InstructionVisitor[]
{
new SuperInvocationMarker(),
new DynamicInvocationMarker(),
new BackwardBranchMarker(),
new AccessMethodMarker(),
new NonEmptyStackReturnMarker(stackSizeComputer),
})),
})))),
// Mark referenced classes and methods.
new AllMethodVisitor(
new AllAttributeVisitor(
new MultiAttributeVisitor(
new AttributeVisitor[]
{
new AllExceptionInfoVisitor(
new ExceptionHandlerConstantVisitor(
new ReferencedClassVisitor(
new CaughtClassMarker()))),
new AllInstructionVisitor(
new MultiInstructionVisitor(
new InstructionVisitor[]
{
new InstantiationClassMarker(),
new InstanceofClassMarker(),
new DotClassMarker(),
new MethodInvocationMarker()
})),
})))
}));
if (classMergingVertical)
{
// Merge subclasses up into their superclasses or
// merge interfaces down into their implementing classes.
programClassPool.classesAccept(
new VerticalClassMerger(configuration.allowAccessModification,
configuration.mergeInterfacesAggressively,
classMergingVerticalCounter));
}
if (classMergingHorizontal)
{
// Merge classes into their sibling classes.
programClassPool.classesAccept(
new HorizontalClassMerger(configuration.allowAccessModification,
configuration.mergeInterfacesAggressively,
classMergingHorizontalCounter));
}
if (classMergingVerticalCounter .getCount() > 0 ||
classMergingHorizontalCounter.getCount() > 0)
{
// Clean up inner class attributes to avoid loops.
programClassPool.classesAccept(new RetargetedInnerClassAttributeRemover());
// Update references to merged classes: first the referenced
// classes, then the various actual descriptors.
// Leave retargeted classes themselves unchanged and valid,
// in case they aren't shrunk later on.
programClassPool.classesAccept(new RetargetedClassFilter(null, new TargetClassChanger()));
programClassPool.classesAccept(new RetargetedClassFilter(null, new ClassReferenceFixer(true)));
programClassPool.classesAccept(new RetargetedClassFilter(null, new MemberReferenceFixer()));
if (configuration.allowAccessModification)
{
// Fix the access flags of referenced merged classes and their
// class members.
programClassPool.classesAccept(
new AccessFixer());
}
// Fix the access flags of the inner classes information.
programClassPool.classesAccept(
new AllAttributeVisitor(
new AllInnerClassesInfoVisitor(
new InnerClassesAccessFixer())));
// Tweak the descriptors of duplicate initializers, due to merged
// parameter classes.
programClassPool.classesAccept(
new AllMethodVisitor(
new DuplicateInitializerFixer(initializerFixCounter2)));
if (initializerFixCounter2.getCount() > 0)
{
// Fix all invocations of tweaked initializers.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new DuplicateInitializerInvocationFixer(addedCounter))));
// Fix all references to tweaked initializers.
programClassPool.classesAccept(new MemberReferenceFixer());
}
}
if (methodInliningUnique)
{
// Inline methods that are only invoked once.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new MethodInliner(configuration.microEdition,
configuration.allowAccessModification,
true,
methodInliningUniqueCounter))));
}
if (methodInliningShort)
{
// Inline short methods.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new MethodInliner(configuration.microEdition,
configuration.allowAccessModification,
false,
methodInliningShortCounter))));
}
if (methodInliningTailrecursion)
{
// Simplify tail recursion calls.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new TailRecursionSimplifier(methodInliningTailrecursionCounter))));
}
if (fieldMarkingPrivate ||
methodMarkingPrivate)
{
// Mark all class members that can not be made private.
programClassPool.classesAccept(
new NonPrivateMemberMarker());
}
if (fieldMarkingPrivate)
{
// Make all non-private fields private, whereever possible.
programClassPool.classesAccept(
new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE,
new AllFieldVisitor(
new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE,
new MemberPrivatizer(fieldMarkingPrivateCounter)))));
}
if (methodMarkingPrivate)
{
// Make all non-private methods private, whereever possible.
programClassPool.classesAccept(
new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE,
new AllMethodVisitor(
new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE,
new MemberPrivatizer(methodMarkingPrivateCounter)))));
}
if ((methodInliningUniqueCounter .getCount() > 0 ||
methodInliningShortCounter .getCount() > 0 ||
methodInliningTailrecursionCounter.getCount() > 0) &&
configuration.allowAccessModification)
{
// Fix the access flags of referenced classes and class members,
// for MethodInliner.
programClassPool.classesAccept(
new AccessFixer());
}
if (methodRemovalParameterCounter.getCount() > 0 ||
classMergingVerticalCounter .getCount() > 0 ||
classMergingHorizontalCounter.getCount() > 0 ||
methodMarkingPrivateCounter .getCount() > 0 ||
methodInliningUniqueCounter .getCount() > 0 ||
methodInliningShortCounter .getCount() > 0)
{
// Fix invocations of interface methods, of methods that have become
// non-abstract or private, and of methods that have moved to a
// different package.
programClassPool.classesAccept(
new AllMemberVisitor(
new AllAttributeVisitor(
new MethodInvocationFixer())));
}
if (codeMerging)
{
// Share common blocks of code at branches.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new GotoCommonCodeReplacer(codeMergingCounter))));
}
// Create a branch target marker and a code attribute editor that can
// be reused for all code attributes.
BranchTargetFinder branchTargetFinder = new BranchTargetFinder();
CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
InstructionSequenceConstants sequences =
new InstructionSequenceConstants(programClassPool,
libraryClassPool);
List peepholeOptimizations = new ArrayList();
if (codeSimplificationVariable)
{
// Peephole optimizations involving local variables.
peepholeOptimizations.add(
new InstructionSequencesReplacer(sequences.CONSTANTS,
sequences.VARIABLE,
branchTargetFinder, codeAttributeEditor, codeSimplificationVariableCounter));
}
if (codeSimplificationArithmetic)
{
// Peephole optimizations involving arithmetic operations.
peepholeOptimizations.add(
new InstructionSequencesReplacer(sequences.CONSTANTS,
sequences.ARITHMETIC,
branchTargetFinder, codeAttributeEditor, codeSimplificationArithmeticCounter));
}
if (codeSimplificationCast)
{
// Peephole optimizations involving cast operations.
peepholeOptimizations.add(
new InstructionSequencesReplacer(sequences.CONSTANTS,
sequences.CAST,
branchTargetFinder, codeAttributeEditor, codeSimplificationCastCounter));
}
if (codeSimplificationField)
{
// Peephole optimizations involving fields.
peepholeOptimizations.add(
new InstructionSequencesReplacer(sequences.CONSTANTS,
sequences.FIELD,
branchTargetFinder, codeAttributeEditor, codeSimplificationFieldCounter));
}
if (codeSimplificationBranch)
{
// Peephole optimizations involving branches.
peepholeOptimizations.add(
new InstructionSequencesReplacer(sequences.CONSTANTS,
sequences.BRANCH,
branchTargetFinder, codeAttributeEditor, codeSimplificationBranchCounter));
// Include optimization of branches to branches and returns.
peepholeOptimizations.add(
new GotoGotoReplacer(codeAttributeEditor, codeSimplificationBranchCounter));
peepholeOptimizations.add(
new GotoReturnReplacer(codeAttributeEditor, codeSimplificationBranchCounter));
}
if (codeSimplificationString)
{
// Peephole optimizations involving branches.
peepholeOptimizations.add(
new InstructionSequencesReplacer(sequences.CONSTANTS,
sequences.STRING,
branchTargetFinder, codeAttributeEditor, codeSimplificationStringCounter));
}
if (!peepholeOptimizations.isEmpty())
{
// Convert the list into an array.
InstructionVisitor[] peepholeOptimizationsArray =
new InstructionVisitor[peepholeOptimizations.size()];
peepholeOptimizations.toArray(peepholeOptimizationsArray);
// Perform the peephole optimisations.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new PeepholeOptimizer(branchTargetFinder, codeAttributeEditor,
new MultiInstructionVisitor(
peepholeOptimizationsArray)))));
}
if (codeRemovalException)
{
// Remove unnecessary exception handlers.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new UnreachableExceptionRemover(codeRemovalExceptionCounter))));
}
if (codeRemovalSimple)
{
// Remove unreachable code.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new UnreachableCodeRemover(deletedCounter))));
}
if (codeRemovalVariable)
{
// Remove all unused local variables.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new VariableShrinker(codeRemovalVariableCounter))));
}
if (codeAllocationVariable)
{
// Optimize the variables.
programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new VariableOptimizer(false, codeAllocationVariableCounter))));
}
// Remove unused constants.
programClassPool.classesAccept(
new ConstantPoolShrinker());
int classMarkingFinalCount = classMarkingFinalCounter .getCount();
int classUnboxingEnumCount = classUnboxingEnumCounter .getCount();
int classMergingVerticalCount = classMergingVerticalCounter .getCount();
int classMergingHorizontalCount = classMergingHorizontalCounter .getCount();
int fieldRemovalWriteonlyCount = fieldRemovalWriteonlyCounter .getCount();
int fieldMarkingPrivateCount = fieldMarkingPrivateCounter .getCount();
int fieldPropagationValueCount = fieldPropagationValueCounter .getCount();
int methodMarkingPrivateCount = methodMarkingPrivateCounter .getCount();
int methodMarkingStaticCount = methodMarkingStaticCounter .getCount();
int methodMarkingFinalCount = methodMarkingFinalCounter .getCount();
int methodRemovalParameterCount = methodRemovalParameterCounter .getCount() - methodMarkingStaticCounter.getCount() - initializerFixCounter1.getCount() - initializerFixCounter2.getCount();
int methodPropagationParameterCount = methodPropagationParameterCounter .getCount();
int methodPropagationReturnvalueCount = methodPropagationReturnvalueCounter.getCount();
int methodInliningShortCount = methodInliningShortCounter .getCount();
int methodInliningUniqueCount = methodInliningUniqueCounter .getCount();
int methodInliningTailrecursionCount = methodInliningTailrecursionCounter .getCount();
int codeMergingCount = codeMergingCounter .getCount();
int codeSimplificationVariableCount = codeSimplificationVariableCounter .getCount();
int codeSimplificationArithmeticCount = codeSimplificationArithmeticCounter.getCount();
int codeSimplificationCastCount = codeSimplificationCastCounter .getCount();
int codeSimplificationFieldCount = codeSimplificationFieldCounter .getCount();
int codeSimplificationBranchCount = codeSimplificationBranchCounter .getCount();
int codeSimplificationStringCount = codeSimplificationStringCounter .getCount();
int codeSimplificationAdvancedCount = codeSimplificationAdvancedCounter .getCount();
int codeRemovalCount = deletedCounter .getCount() - addedCounter.getCount();
int codeRemovalVariableCount = codeRemovalVariableCounter .getCount();
int codeRemovalExceptionCount = codeRemovalExceptionCounter .getCount();
int codeAllocationVariableCount = codeAllocationVariableCounter .getCount();
// Forget about constant fields, parameters, and return values, if they
// didn't lead to any useful optimizations. We want to avoid fruitless
// additional optimization passes.
if (codeSimplificationAdvancedCount == 0)
{
fieldPropagationValueCount = 0;
methodPropagationParameterCount = 0;
methodPropagationReturnvalueCount = 0;
}
if (configuration.verbose)
{
System.out.println(" Number of finalized classes: " + classMarkingFinalCount + disabled(classMarkingFinal));
System.out.println(" Number of unboxed enum classes: " + classUnboxingEnumCount + disabled(classUnboxingEnum));
System.out.println(" Number of vertically merged classes: " + classMergingVerticalCount + disabled(classMergingVertical));
System.out.println(" Number of horizontally merged classes: " + classMergingHorizontalCount + disabled(classMergingHorizontal));
System.out.println(" Number of removed write-only fields: " + fieldRemovalWriteonlyCount + disabled(fieldRemovalWriteonly));
System.out.println(" Number of privatized fields: " + fieldMarkingPrivateCount + disabled(fieldMarkingPrivate));
System.out.println(" Number of inlined constant fields: " + fieldPropagationValueCount + disabled(fieldPropagationValue));
System.out.println(" Number of privatized methods: " + methodMarkingPrivateCount + disabled(methodMarkingPrivate));
System.out.println(" Number of staticized methods: " + methodMarkingStaticCount + disabled(methodMarkingStatic));
System.out.println(" Number of finalized methods: " + methodMarkingFinalCount + disabled(methodMarkingFinal));
System.out.println(" Number of removed method parameters: " + methodRemovalParameterCount + disabled(methodRemovalParameter));
System.out.println(" Number of inlined constant parameters: " + methodPropagationParameterCount + disabled(methodPropagationParameter));
System.out.println(" Number of inlined constant return values: " + methodPropagationReturnvalueCount + disabled(methodPropagationReturnvalue));
System.out.println(" Number of inlined short method calls: " + methodInliningShortCount + disabled(methodInliningShort));
System.out.println(" Number of inlined unique method calls: " + methodInliningUniqueCount + disabled(methodInliningUnique));
System.out.println(" Number of inlined tail recursion calls: " + methodInliningTailrecursionCount + disabled(methodInliningTailrecursion));
System.out.println(" Number of merged code blocks: " + codeMergingCount + disabled(codeMerging));
System.out.println(" Number of variable peephole optimizations: " + codeSimplificationVariableCount + disabled(codeSimplificationVariable));
System.out.println(" Number of arithmetic peephole optimizations: " + codeSimplificationArithmeticCount + disabled(codeSimplificationArithmetic));
System.out.println(" Number of cast peephole optimizations: " + codeSimplificationCastCount + disabled(codeSimplificationCast));
System.out.println(" Number of field peephole optimizations: " + codeSimplificationFieldCount + disabled(codeSimplificationField));
System.out.println(" Number of branch peephole optimizations: " + codeSimplificationBranchCount + disabled(codeSimplificationBranch));
System.out.println(" Number of string peephole optimizations: " + codeSimplificationStringCount + disabled(codeSimplificationString));
System.out.println(" Number of simplified instructions: " + codeSimplificationAdvancedCount + disabled(codeSimplificationAdvanced));
System.out.println(" Number of removed instructions: " + codeRemovalCount + disabled(codeRemovalAdvanced));
System.out.println(" Number of removed local variables: " + codeRemovalVariableCount + disabled(codeRemovalVariable));
System.out.println(" Number of removed exception blocks: " + codeRemovalExceptionCount + disabled(codeRemovalException));
System.out.println(" Number of optimized local variable frames: " + codeAllocationVariableCount + disabled(codeAllocationVariable));
}
return classMarkingFinalCount > 0 ||
classUnboxingEnumCount > 0 ||
classMergingVerticalCount > 0 ||
classMergingHorizontalCount > 0 ||
fieldRemovalWriteonlyCount > 0 ||
fieldMarkingPrivateCount > 0 ||
methodMarkingPrivateCount > 0 ||
methodMarkingStaticCount > 0 ||
methodMarkingFinalCount > 0 ||
fieldPropagationValueCount > 0 ||
methodRemovalParameterCount > 0 ||
methodPropagationParameterCount > 0 ||
methodPropagationReturnvalueCount > 0 ||
methodInliningShortCount > 0 ||
methodInliningUniqueCount > 0 ||
methodInliningTailrecursionCount > 0 ||
codeMergingCount > 0 ||
codeSimplificationVariableCount > 0 ||
codeSimplificationArithmeticCount > 0 ||
codeSimplificationCastCount > 0 ||
codeSimplificationFieldCount > 0 ||
codeSimplificationBranchCount > 0 ||
codeSimplificationStringCount > 0 ||
codeSimplificationAdvancedCount > 0 ||
codeRemovalCount > 0 ||
codeRemovalVariableCount > 0 ||
codeRemovalExceptionCount > 0 ||
codeAllocationVariableCount > 0;
}
/**
* Returns a String indicating whether the given flag is enabled or
* disabled.
*/
private String disabled(boolean flag)
{
return flag ? "" : " (disabled)";
}
/**
* Returns a String indicating whether the given flags are enabled or
* disabled.
*/
private String disabled(boolean flag1, boolean flag2)
{
return flag1 && flag2 ? "" :
flag1 || flag2 ? " (partially disabled)" :
" (disabled)";
}
}