summaryrefslogtreecommitdiff
path: root/src/proguard/optimize/Optimizer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/proguard/optimize/Optimizer.java')
-rw-r--r--src/proguard/optimize/Optimizer.java168
1 files changed, 146 insertions, 22 deletions
diff --git a/src/proguard/optimize/Optimizer.java b/src/proguard/optimize/Optimizer.java
index 8042825..20f4083 100644
--- a/src/proguard/optimize/Optimizer.java
+++ b/src/proguard/optimize/Optimizer.java
@@ -2,7 +2,7 @@
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
- * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu)
+ * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
*
* 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
@@ -23,13 +23,12 @@ package proguard.optimize;
import proguard.*;
import proguard.classfile.*;
import proguard.classfile.attribute.visitor.*;
-import proguard.classfile.constant.Constant;
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.InvocationUnit;
import proguard.evaluation.value.*;
import proguard.optimize.evaluation.*;
import proguard.optimize.info.*;
@@ -47,6 +46,7 @@ import java.util.*;
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";
@@ -141,6 +141,7 @@ public class Optimizer
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);
@@ -171,6 +172,7 @@ public class Optimizer
// 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();
@@ -251,11 +253,13 @@ public class Optimizer
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)))));
+ new DotClassClassVisitor(keepMarker))))));
// We also keep all classes that are accessed dynamically.
programClassPool.classesAccept(
@@ -271,7 +275,7 @@ public class Optimizer
// We also keep all bootstrap method signatures.
programClassPool.classesAccept(
- new ClassVersionFilter(ClassConstants.INTERNAL_CLASS_VERSION_1_7,
+ new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_7,
new AllAttributeVisitor(
new AttributeNameFilter(ClassConstants.ATTR_BootstrapMethods,
new AllBootstrapMethodInfoVisitor(
@@ -279,6 +283,32 @@ public class Optimizer
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());
@@ -311,8 +341,9 @@ public class Optimizer
{
// Make methods final, whereever possible.
programClassPool.classesAccept(
+ new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE,
new AllMethodVisitor(
- new MethodFinalizer(methodMarkingFinalCounter)));
+ new MethodFinalizer(methodMarkingFinalCounter))));
}
if (fieldRemovalWriteonly)
@@ -337,6 +368,55 @@ public class Optimizer
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 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());
+
+ // 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(
@@ -354,25 +434,36 @@ public class Optimizer
// programClassPool.classAccept("abc/Def", new NamedMethodVisitor("abc", null, new ClassPrinter()));
// Perform partial evaluation for filling out fields, method parameters,
- // and method return values.
- ValueFactory valueFactory = new IdentifiedValueFactory();
-
+ // and method return values, so they can be propagated.
if (fieldPropagationValue ||
methodPropagationParameter ||
methodPropagationReturnvalue)
{
- // Fill out fields, method parameters, and return values, so they
- // can be propagated.
+ // 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))));
+ new PartialEvaluator(valueFactory, storingInvocationUnit, false)))));
if (fieldPropagationValue)
{
@@ -397,8 +488,38 @@ public class Optimizer
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,
@@ -446,7 +567,7 @@ public class Optimizer
programClassPool.classesAccept(
new AllMethodVisitor(
new OptimizationInfoMemberFilter(
- new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_STATIC,
+ new MemberAccessFilter(0, ClassConstants.ACC_STATIC,
new MethodStaticizer(methodMarkingStaticCounter)))));
}
@@ -548,6 +669,7 @@ public class Optimizer
new DotClassMarker(),
new MethodInvocationMarker(),
new SuperInvocationMarker(),
+ new DynamicInvocationMarker(),
new BackwardBranchMarker(),
new AccessMethodMarker(),
})),
@@ -561,7 +683,8 @@ public class Optimizer
if (classMergingVertical)
{
- // Merge classes into their superclasses or interfaces.
+ // Merge subclasses up into their superclasses or
+ // merge interfaces down into their implementing classes.
programClassPool.classesAccept(
new VerticalClassMerger(configuration.allowAccessModification,
configuration.mergeInterfacesAggressively,
@@ -593,8 +716,7 @@ public class Optimizer
// Fix the access flags of referenced merged classes and their
// class members.
programClassPool.classesAccept(
- new AllConstantVisitor(
- new AccessFixer()));
+ new AccessFixer());
}
// Fix the access flags of the inner classes information.
@@ -667,9 +789,9 @@ public class Optimizer
{
// Make all non-private fields private, whereever possible.
programClassPool.classesAccept(
- new ClassAccessFilter(0, ClassConstants.INTERNAL_ACC_INTERFACE,
+ new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE,
new AllFieldVisitor(
- new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+ new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE,
new MemberPrivatizer(fieldMarkingPrivateCounter)))));
}
@@ -677,9 +799,9 @@ public class Optimizer
{
// Make all non-private methods private, whereever possible.
programClassPool.classesAccept(
- new ClassAccessFilter(0, ClassConstants.INTERNAL_ACC_INTERFACE,
+ new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE,
new AllMethodVisitor(
- new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+ new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE,
new MemberPrivatizer(methodMarkingPrivateCounter)))));
}
@@ -691,8 +813,7 @@ public class Optimizer
// Fix the access flags of referenced classes and class members,
// for MethodInliner.
programClassPool.classesAccept(
- new AllConstantVisitor(
- new AccessFixer()));
+ new AccessFixer());
}
if (methodRemovalParameterCounter .getCount() > 0 ||
@@ -842,6 +963,7 @@ public class Optimizer
new ConstantPoolShrinker());
int classMarkingFinalCount = classMarkingFinalCounter .getCount();
+ int classUnboxingEnumCount = classUnboxingEnumCounter .getCount();
int classMergingVerticalCount = classMergingVerticalCounter .getCount();
int classMergingHorizontalCount = classMergingHorizontalCounter .getCount();
int fieldRemovalWriteonlyCount = fieldRemovalWriteonlyCounter .getCount();
@@ -882,6 +1004,7 @@ public class Optimizer
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));
@@ -911,6 +1034,7 @@ public class Optimizer
}
return classMarkingFinalCount > 0 ||
+ classUnboxingEnumCount > 0 ||
classMergingVerticalCount > 0 ||
classMergingHorizontalCount > 0 ||
fieldRemovalWriteonlyCount > 0 ||