diff options
Diffstat (limited to 'src/proguard/optimize/peephole/ClassMerger.java')
-rw-r--r-- | src/proguard/optimize/peephole/ClassMerger.java | 718 |
1 files changed, 0 insertions, 718 deletions
diff --git a/src/proguard/optimize/peephole/ClassMerger.java b/src/proguard/optimize/peephole/ClassMerger.java deleted file mode 100644 index 9bcc993..0000000 --- a/src/proguard/optimize/peephole/ClassMerger.java +++ /dev/null @@ -1,718 +0,0 @@ -/* - * ProGuard -- shrinking, optimization, obfuscation, and preverification - * of Java bytecode. - * - * 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 - * 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.peephole; - -import proguard.classfile.*; -import proguard.classfile.attribute.visitor.AttributeNameFilter; -import proguard.classfile.constant.visitor.*; -import proguard.classfile.editor.*; -import proguard.classfile.util.*; -import proguard.classfile.visitor.*; -import proguard.optimize.KeepMarker; -import proguard.optimize.info.*; -import proguard.util.*; - -import java.util.*; - -/** - * This ClassVisitor inlines the classes that it visits in a given target class, - * whenever possible. - * - * @see RetargetedInnerClassAttributeRemover - * @see TargetClassChanger - * @see ClassReferenceFixer - * @see MemberReferenceFixer - * @see AccessFixer - * @author Eric Lafortune - */ -public class ClassMerger -extends SimplifiedVisitor -implements ClassVisitor, - ConstantVisitor -{ - //* - private static final boolean DEBUG = false; - private static final boolean DETAILS = false; - /*/ - private static boolean DEBUG = System.getProperty("cm") != null; - private static boolean DETAILS = System.getProperty("cmd") != null; - //*/ - - - private final ProgramClass targetClass; - private final boolean allowAccessModification; - private final boolean mergeInterfacesAggressively; - private final ClassVisitor extraClassVisitor; - - private final MemberVisitor fieldOptimizationInfoCopier = new FieldOptimizationInfoCopier(); - - - /** - * Creates a new ClassMerger that will merge classes into the given target - * class. - * @param targetClass the class into which all visited - * classes will be merged. - * @param allowAccessModification specifies whether the access modifiers - * of classes can be changed in order to - * merge them. - * @param mergeInterfacesAggressively specifies whether interfaces may - * be merged aggressively. - */ - public ClassMerger(ProgramClass targetClass, - boolean allowAccessModification, - boolean mergeInterfacesAggressively) - { - this(targetClass, allowAccessModification, mergeInterfacesAggressively, null); - } - - - /** - * Creates a new ClassMerger that will merge classes into the given target - * class. - * @param targetClass the class into which all visited - * classes will be merged. - * @param allowAccessModification specifies whether the access modifiers - * of classes can be changed in order to - * merge them. - * @param mergeInterfacesAggressively specifies whether interfaces may - * be merged aggressively. - * @param extraClassVisitor an optional extra visitor for all - * merged classes. - */ - public ClassMerger(ProgramClass targetClass, - boolean allowAccessModification, - boolean mergeInterfacesAggressively, - ClassVisitor extraClassVisitor) - { - this.targetClass = targetClass; - this.allowAccessModification = allowAccessModification; - this.mergeInterfacesAggressively = mergeInterfacesAggressively; - this.extraClassVisitor = extraClassVisitor; - } - - - // Implementations for ClassVisitor. - - public void visitProgramClass(ProgramClass programClass) - { - //final String CLASS_NAME = "abc/Def"; - //DEBUG = programClass.getName().equals(CLASS_NAME) || - // targetClass.getName().equals(CLASS_NAME); - - // TODO: Remove this when the class merger has stabilized. - // Catch any unexpected exceptions from the actual visiting method. - try - { - visitProgramClass0(programClass); - } - catch (RuntimeException ex) - { - System.err.println("Unexpected error while merging classes:"); - System.err.println(" Class = ["+programClass.getName()+"]"); - System.err.println(" Target class = ["+targetClass.getName()+"]"); - System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); - - if (DEBUG) - { - programClass.accept(new ClassPrinter()); - targetClass.accept(new ClassPrinter()); - } - - throw ex; - } - } - - public void visitProgramClass0(ProgramClass programClass) - { - if (!programClass.equals(targetClass) && - - // Don't merge classes that must be preserved. - !KeepMarker.isKept(programClass) && - !KeepMarker.isKept(targetClass) && - - // Only merge classes that haven't been retargeted yet. - getTargetClass(programClass) == null && - getTargetClass(targetClass) == null && - - // Don't merge annotation classes, with all their introspection and - // infinite recursion. - (programClass.getAccessFlags() & ClassConstants.ACC_ANNOTATTION) == 0 && - - (!DETAILS || print(programClass, "Package visibility?")) && - - // Only merge classes if we can change the access permissions, or - // if they are in the same package, or - // if they are public and don't contain or invoke package visible - // class members. - (allowAccessModification || - ((programClass.getAccessFlags() & - targetClass.getAccessFlags() & - ClassConstants.ACC_PUBLIC) != 0 && - !PackageVisibleMemberContainingClassMarker.containsPackageVisibleMembers(programClass) && - !PackageVisibleMemberInvokingClassMarker.invokesPackageVisibleMembers(programClass)) || - ClassUtil.internalPackageName(programClass.getName()).equals( - ClassUtil.internalPackageName(targetClass.getName()))) && - - (!DETAILS || print(programClass, "Interface/abstract/single?")) && - - // Only merge two classes or two interfaces or two abstract classes, - // or a single implementation into its interface. - ((programClass.getAccessFlags() & - (ClassConstants.ACC_INTERFACE | - ClassConstants.ACC_ABSTRACT)) == - (targetClass.getAccessFlags() & - (ClassConstants.ACC_INTERFACE | - ClassConstants.ACC_ABSTRACT)) || - (isOnlySubClass(programClass, targetClass) && - programClass.getSuperClass() != null && - (programClass.getSuperClass().equals(targetClass) || - programClass.getSuperClass().equals(targetClass.getSuperClass())))) && - - (!DETAILS || print(programClass, "Indirect implementation?")) && - - // One class must not implement the other class indirectly. - !indirectlyImplementedInterfaces(programClass).contains(targetClass) && - !targetClass.extendsOrImplements(programClass) && - - (!DETAILS || print(programClass, "Interfaces same subinterfaces?")) && - - // Interfaces must have exactly the same subinterfaces, not - // counting themselves, to avoid any loops in the interface - // hierarchy. - ((programClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) == 0 || - (targetClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) == 0 || - subInterfaces(programClass, targetClass).equals(subInterfaces(targetClass, programClass))) && - - (!DETAILS || print(programClass, "Same initialized superclasses?")) && - - // The two classes must have the same superclasses and interfaces - // with static initializers. - initializedSuperClasses(programClass).equals(initializedSuperClasses(targetClass)) && - - (!DETAILS || print(programClass, "Same instanceofed superclasses?")) && - - // The two classes must have the same superclasses and interfaces - // that are tested with 'instanceof'. - instanceofedSuperClasses(programClass).equals(instanceofedSuperClasses(targetClass)) && - - (!DETAILS || print(programClass, "Same caught superclasses?")) && - - // The two classes must have the same superclasses that are caught - // as exceptions. - caughtSuperClasses(programClass).equals(caughtSuperClasses(targetClass)) && - - (!DETAILS || print(programClass, "Not .classed?")) && - - // The two classes must not both be part of a .class construct. - !(DotClassMarker.isDotClassed(programClass) && - DotClassMarker.isDotClassed(targetClass)) && - - (!DETAILS || print(programClass, "No clashing fields?")) && - - // The classes must not have clashing fields. - !haveAnyIdenticalFields(programClass, targetClass) && - - (!DETAILS || print(programClass, "No unwanted fields?")) && - - // The two classes must not introduce any unwanted fields. - !introducesUnwantedFields(programClass, targetClass) && - !introducesUnwantedFields(targetClass, programClass) && - - (!DETAILS || print(programClass, "No shadowed fields?")) && - - // The two classes must not shadow each others fields. - !shadowsAnyFields(programClass, targetClass) && - !shadowsAnyFields(targetClass, programClass) && - - (!DETAILS || print(programClass, "No clashing methods?")) && - - // The classes must not have clashing methods. - !haveAnyIdenticalMethods(programClass, targetClass) && - - (!DETAILS || print(programClass, "No abstract methods?")) && - - // The classes must not introduce abstract methods, unless - // explicitly allowed. - (mergeInterfacesAggressively || - (!introducesUnwantedAbstractMethods(programClass, targetClass) && - !introducesUnwantedAbstractMethods(targetClass, programClass))) && - - (!DETAILS || print(programClass, "No overridden methods?")) && - - // The classes must not override each others concrete methods. - !overridesAnyMethods(programClass, targetClass) && - !overridesAnyMethods(targetClass, programClass) && - - (!DETAILS || print(programClass, "No shadowed methods?")) && - - // The classes must not shadow each others non-private methods. - !shadowsAnyMethods(programClass, targetClass) && - !shadowsAnyMethods(targetClass, programClass)) - { - // We're not actually merging the classes, but only copying the - // contents from the source class to the target class. We'll - // then let all other classes point to it. The shrinking step - // will finally remove the source class. - if (DEBUG) - { - System.out.println("ClassMerger ["+programClass.getName()+"] -> ["+targetClass.getName()+"]"); - System.out.println(" Source interface? ["+((programClass.getAccessFlags() & ClassConstants.ACC_INTERFACE)!=0)+"]"); - System.out.println(" Target interface? ["+((targetClass.getAccessFlags() & ClassConstants.ACC_INTERFACE)!=0)+"]"); - System.out.println(" Source subclasses ["+programClass.subClasses+"]"); - System.out.println(" Target subclasses ["+targetClass.subClasses+"]"); - System.out.println(" Source superclass ["+programClass.getSuperClass().getName()+"]"); - System.out.println(" Target superclass ["+targetClass.getSuperClass().getName()+"]"); - - //System.out.println("=== Before ==="); - //programClass.accept(new ClassPrinter()); - //targetClass.accept(new ClassPrinter()); - } - - // Combine the access flags. - int targetAccessFlags = targetClass.getAccessFlags(); - int sourceAccessFlags = programClass.getAccessFlags(); - - targetClass.u2accessFlags = - ((targetAccessFlags & - sourceAccessFlags) & - (ClassConstants.ACC_INTERFACE | - ClassConstants.ACC_ABSTRACT)) | - ((targetAccessFlags | - sourceAccessFlags) & - (ClassConstants.ACC_PUBLIC | - ClassConstants.ACC_SUPER | - ClassConstants.ACC_ANNOTATTION | - ClassConstants.ACC_ENUM)); - - // Copy over the superclass, if it's a non-interface class being - // merged into an interface class. - // However, we're currently never merging in a way that changes the - // superclass. - //if ((programClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) == 0 && - // (targetClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) != 0) - //{ - // targetClass.u2superClass = - // new ConstantAdder(targetClass).addConstant(programClass, programClass.u2superClass); - //} - - // Copy over the interfaces that aren't present yet and that - // wouldn't cause loops in the class hierarchy. - // Note that the code shouldn't be iterating over the original - // list at this point. This is why we only add subclasses in - // a separate step. - programClass.interfaceConstantsAccept( - new ExceptClassConstantFilter(targetClass.getName(), - new ImplementedClassConstantFilter(targetClass, - new ImplementingClassConstantFilter(targetClass, - new InterfaceAdder(targetClass))))); - - // Copy over the class members. - MemberAdder memberAdder = - new MemberAdder(targetClass, fieldOptimizationInfoCopier); - - programClass.fieldsAccept(memberAdder); - programClass.methodsAccept(memberAdder); - - // Copy over the other attributes. - programClass.attributesAccept( - new AttributeNameFilter(new NotMatcher( - new OrMatcher(new FixedStringMatcher(ClassConstants.ATTR_BootstrapMethods), - new OrMatcher(new FixedStringMatcher(ClassConstants.ATTR_SourceFile), - new OrMatcher(new FixedStringMatcher(ClassConstants.ATTR_InnerClasses), - new FixedStringMatcher(ClassConstants.ATTR_EnclosingMethod))))), - new AttributeAdder(targetClass, true))); - - // Update the optimization information of the target class. - ClassOptimizationInfo info = - ClassOptimizationInfo.getClassOptimizationInfo(targetClass); - if (info != null) - { - info.merge(ClassOptimizationInfo.getClassOptimizationInfo(programClass)); - } - - // Remember to replace the inlined class by the target class. - setTargetClass(programClass, targetClass); - - //if (DEBUG) - //{ - // System.out.println("=== After ===="); - // targetClass.accept(new ClassPrinter()); - //} - - // Visit the merged class, if required. - if (extraClassVisitor != null) - { - extraClassVisitor.visitProgramClass(programClass); - } - } - } - - - private boolean print(ProgramClass programClass, String message) - { - System.out.println("Merge ["+targetClass.getName()+"] <- ["+programClass.getName()+"] "+message); - - return true; - } - - - // Small utility methods. - - /** - * Returns whether a given class is the only subclass of another given class. - */ - private boolean isOnlySubClass(Clazz subClass, - ProgramClass clazz) - { - // TODO: The list of subclasses is not up to date. - return clazz.subClasses != null && - clazz.subClasses.length == 1 && - clazz.subClasses[0].equals(subClass); - } - - - /** - * Returns the set of indirectly implemented interfaces. - */ - private Set indirectlyImplementedInterfaces(Clazz clazz) - { - Set set = new HashSet(); - - ReferencedClassVisitor referencedInterfaceCollector = - new ReferencedClassVisitor( - new ClassHierarchyTraveler(false, false, true, false, - new ClassCollector(set))); - - // Visit all superclasses and collect their interfaces. - clazz.superClassConstantAccept(referencedInterfaceCollector); - - // Visit all interfaces and collect their interfaces. - clazz.interfaceConstantsAccept(referencedInterfaceCollector); - - return set; - } - - - /** - * Returns the set of interface subclasses, not including the given class. - */ - private Set subInterfaces(Clazz clazz, Clazz exceptClass) - { - Set set = new HashSet(); - - // Visit all subclasses, collecting the interface classes. - clazz.hierarchyAccept(false, false, false, true, - new ClassAccessFilter(ClassConstants.ACC_INTERFACE, 0, - new ExceptClassesFilter(new Clazz[] { exceptClass }, - new ClassCollector(set)))); - - return set; - } - - - /** - * Returns the set of superclasses and interfaces that are initialized. - */ - private Set initializedSuperClasses(Clazz clazz) - { - Set set = new HashSet(); - - // Visit all superclasses and interfaces, collecting the ones that have - // static initializers. - clazz.hierarchyAccept(true, true, true, false, - new StaticInitializerContainingClassFilter( - new ClassCollector(set))); - - return set; - } - - - /** - * Returns the set of superclasses and interfaces that are used in - * 'instanceof' tests. - */ - private Set instanceofedSuperClasses(Clazz clazz) - { - Set set = new HashSet(); - - // Visit all superclasses and interfaces, collecting the ones that are - // used in an 'instanceof' test. - clazz.hierarchyAccept(true, true, true, false, - new InstanceofClassFilter( - new ClassCollector(set))); - - return set; - } - - - /** - * Returns the set of superclasses that are caught as exceptions. - */ - private Set caughtSuperClasses(Clazz clazz) - { - // Don't bother if this isn't an exception at all. - if (!clazz.extends_(ClassConstants.NAME_JAVA_LANG_THROWABLE)) - { - return Collections.EMPTY_SET; - } - - // Visit all superclasses, collecting the ones that are caught - // (plus java.lang.Object, in the current implementation). - Set set = new HashSet(); - - clazz.hierarchyAccept(true, true, false, false, - new CaughtClassFilter( - new ClassCollector(set))); - - return set; - } - - - /** - * Returns whether the two given classes have fields with the same - * names and descriptors. - */ - private boolean haveAnyIdenticalFields(Clazz clazz, Clazz targetClass) - { - MemberCounter counter = new MemberCounter(); - - // Visit all fields, counting the with the same name and descriptor in - // the target class. - clazz.fieldsAccept(new SimilarMemberVisitor(targetClass, true, false, false, false, - counter)); - - return counter.getCount() > 0; - } - - - /** - * Returns whether the given class would introduce any unwanted fields - * in the target class. - */ - private boolean introducesUnwantedFields(ProgramClass programClass, - ProgramClass targetClass) - { - // It's ok if the target class is never instantiated, without any other - // subclasses except for maybe the source class. - if (!InstantiationClassMarker.isInstantiated(targetClass) && - (targetClass.subClasses == null || - isOnlySubClass(programClass, targetClass))) - { - return false; - } - - MemberCounter counter = new MemberCounter(); - - // Count all non-static fields in the the source class. - programClass.fieldsAccept(new MemberAccessFilter(0, ClassConstants.ACC_STATIC, - counter)); - - return counter.getCount() > 0; - } - - - /** - * Returns whether the given class or its subclasses shadow any fields in - * the given target class. - */ - private boolean shadowsAnyFields(Clazz clazz, Clazz targetClass) - { - MemberCounter counter = new MemberCounter(); - - // Visit all fields, counting the ones that are shadowing non-private - // fields in the class hierarchy of the target class. - clazz.hierarchyAccept(true, false, false, true, - new AllFieldVisitor( - new SimilarMemberVisitor(targetClass, true, true, true, false, - new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, - counter)))); - - return counter.getCount() > 0; - } - - - /** - * Returns whether the two given classes have class members with the same - * name and descriptor. - */ - private boolean haveAnyIdenticalMethods(Clazz clazz, Clazz targetClass) - { - MemberCounter counter = new MemberCounter(); - - // Visit all non-abstract methods, counting the ones that are also - // present in the target class. - clazz.methodsAccept(new MemberAccessFilter(0, ClassConstants.ACC_ABSTRACT, - new SimilarMemberVisitor(targetClass, true, false, false, false, - new MemberAccessFilter(0, ClassConstants.ACC_ABSTRACT, - counter)))); - - return counter.getCount() > 0; - } - - - /** - * Returns whether the given class would introduce any abstract methods - * in the target class. - */ - private boolean introducesUnwantedAbstractMethods(Clazz clazz, - ProgramClass targetClass) - { - // It's ok if the target class is already abstract and it has at most - // the class as a subclass. - if ((targetClass.getAccessFlags() & - (ClassConstants.ACC_ABSTRACT | - ClassConstants.ACC_INTERFACE)) != 0 && - (targetClass.subClasses == null || - isOnlySubClass(clazz, targetClass))) - { - return false; - } - - MemberCounter counter = new MemberCounter(); - Set targetSet = new HashSet(); - - // Collect all abstract methods, and similar abstract methods in the - // class hierarchy of the target class. - clazz.methodsAccept(new MemberAccessFilter(ClassConstants.ACC_ABSTRACT, 0, - new MultiMemberVisitor(new MemberVisitor[] - { - counter, - new SimilarMemberVisitor(targetClass, true, true, true, false, - new MemberAccessFilter(ClassConstants.ACC_ABSTRACT, 0, - new MemberCollector(targetSet))) - }))); - - return targetSet.size() < counter.getCount(); - } - - - /** - * Returns whether the given class overrides any methods in the given - * target class. - */ - private boolean overridesAnyMethods(Clazz clazz, Clazz targetClass) - { - MemberCounter counter = new MemberCounter(); - - // Visit all non-private non-static methods, counting the ones that are - // being overridden in the class hierarchy of the target class. - clazz.methodsAccept(new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC | ClassConstants.ACC_ABSTRACT, - new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_CLINIT)), - new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_INIT)), - new SimilarMemberVisitor(targetClass, true, true, false, false, - new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC | ClassConstants.ACC_ABSTRACT, - counter)))))); - - return counter.getCount() > 0; - } - - - /** - * Returns whether the given class or its subclasses shadow any methods in - * the given target class. - */ - private boolean shadowsAnyMethods(Clazz clazz, Clazz targetClass) - { - MemberCounter counter = new MemberCounter(); - - // Visit all private methods, counting the ones that are shadowing - // non-private methods in the class hierarchy of the target class. - clazz.hierarchyAccept(true, false, false, true, - new AllMethodVisitor( - new MemberAccessFilter(ClassConstants.ACC_PRIVATE, 0, - new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_INIT)), - new SimilarMemberVisitor(targetClass, true, true, true, false, - new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, - counter)))))); - - // Visit all static methods, counting the ones that are shadowing - // non-private methods in the class hierarchy of the target class. - clazz.hierarchyAccept(true, false, false, true, - new AllMethodVisitor( - new MemberAccessFilter(ClassConstants.ACC_STATIC, 0, - new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_CLINIT)), - new SimilarMemberVisitor(targetClass, true, true, true, false, - new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, - counter)))))); - - return counter.getCount() > 0; - } - - - public static void setTargetClass(Clazz clazz, Clazz targetClass) - { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - if (info != null) - { - info.setTargetClass(targetClass); - } - } - - - public static Clazz getTargetClass(Clazz clazz) - { - Clazz targetClass = null; - - // Return the last target class, if any. - while (true) - { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - if (info == null) - { - return targetClass; - } - - clazz = info.getTargetClass(); - if (clazz == null) - { - return targetClass; - } - - targetClass = clazz; - } - } - - - /** - * This MemberVisitor copies field optimization info from copied fields. - */ - private static class FieldOptimizationInfoCopier - extends SimplifiedVisitor - implements MemberVisitor - { - public void visitProgramField(ProgramClass programClass, ProgramField programField) - { - // Copy the optimization info from the field that was just copied. - ProgramField copiedField = (ProgramField)programField.getVisitorInfo(); - Object info = copiedField.getVisitorInfo(); - - programField.setVisitorInfo(info instanceof FieldOptimizationInfo ? - new FieldOptimizationInfo((FieldOptimizationInfo)info) : - info); - } - - - public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) - { - // Linked methods share their optimization info. - } - } -} |