diff options
Diffstat (limited to 'src/proguard/optimize/Optimizer.java')
-rw-r--r-- | src/proguard/optimize/Optimizer.java | 168 |
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 || |