diff options
Diffstat (limited to 'src/proguard/optimize/peephole/ClassMerger.java')
-rw-r--r-- | src/proguard/optimize/peephole/ClassMerger.java | 160 |
1 files changed, 130 insertions, 30 deletions
diff --git a/src/proguard/optimize/peephole/ClassMerger.java b/src/proguard/optimize/peephole/ClassMerger.java index 1e1a950..aa40c75 100644 --- a/src/proguard/optimize/peephole/ClassMerger.java +++ b/src/proguard/optimize/peephole/ClassMerger.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -21,6 +21,7 @@ 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.*; @@ -50,7 +51,7 @@ implements ClassVisitor, //* private static final boolean DEBUG = false; /*/ - private static boolean DEBUG = true; + private static boolean DEBUG = System.getProperty("cm") != null; //*/ @@ -59,6 +60,8 @@ implements ClassVisitor, 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 @@ -151,7 +154,7 @@ implements ClassVisitor, // infinite recursion. (programClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_ANNOTATTION) == 0 && - // Only merge classes if we can change the access permissioms, or + // 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. @@ -196,12 +199,19 @@ implements ClassVisitor, !(DotClassMarker.isDotClassed(programClass) && DotClassMarker.isDotClassed(targetClass)) && + // The classes must not have clashing fields. + !haveAnyIdenticalFields(programClass, targetClass) && + // The two classes must not introduce any unwanted fields. !introducesUnwantedFields(programClass, targetClass) && !introducesUnwantedFields(targetClass, programClass) && - // The classes must not have clashing constructors. - !haveAnyIdenticalInitializers(programClass, targetClass) && + // The two classes must not shadow each others fields. + !shadowsAnyFields(programClass, targetClass) && + !shadowsAnyFields(targetClass, programClass) && + + // The classes must not have clashing methods. + !haveAnyIdenticalMethods(programClass, targetClass) && // The classes must not introduce abstract methods, unless // explicitly allowed. @@ -226,6 +236,10 @@ implements ClassVisitor, 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. @@ -235,11 +249,12 @@ implements ClassVisitor, targetClass.u2accessFlags = ((targetAccessFlags & sourceAccessFlags) & - (ClassConstants.INTERNAL_ACC_INTERFACE | + (ClassConstants.INTERNAL_ACC_INTERFACE | ClassConstants.INTERNAL_ACC_ABSTRACT)) | ((targetAccessFlags | sourceAccessFlags) & - (ClassConstants.INTERNAL_ACC_PUBLIC | + (ClassConstants.INTERNAL_ACC_PUBLIC | + ClassConstants.INTERNAL_ACC_SUPER | ClassConstants.INTERNAL_ACC_ANNOTATTION | ClassConstants.INTERNAL_ACC_ENUM)); @@ -260,14 +275,18 @@ implements ClassVisitor, // Copy over the class members. MemberAdder memberAdder = - new MemberAdder(targetClass); + new MemberAdder(targetClass, fieldOptimizationInfoCopier); programClass.fieldsAccept(memberAdder); programClass.methodsAccept(memberAdder); // Copy over the other attributes. programClass.attributesAccept( - new AttributeAdder(targetClass, true)); + new AttributeNameFilter(new NotMatcher(new OrMatcher(new OrMatcher( + new FixedStringMatcher(ClassConstants.ATTR_SourceFile), + new FixedStringMatcher(ClassConstants.ATTR_InnerClasses)), + new FixedStringMatcher(ClassConstants.ATTR_EnclosingMethod))), + new AttributeAdder(targetClass, true))); // Update the optimization information of the target class. ClassOptimizationInfo info = @@ -280,6 +299,12 @@ implements ClassVisitor, // 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) { @@ -336,10 +361,8 @@ implements ClassVisitor, // Visit all superclasses and interfaces, collecting the ones that have // static initializers. clazz.hierarchyAccept(true, true, true, false, - new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, - ClassConstants.INTERNAL_METHOD_TYPE_INIT, - new MemberToClassVisitor( - new ClassCollector(set)))); + new StaticInitializerContainingClassFilter( + new ClassCollector(set))); return set; } @@ -368,9 +391,16 @@ implements ClassVisitor, */ private Set caughtSuperClasses(Clazz clazz) { + // Don't bother if this isn't an exception at all. + if (!clazz.extends_(ClassConstants.INTERNAL_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(); - // Visit all superclasses, collecting the ones that are caught. clazz.hierarchyAccept(true, true, false, false, new CaughtClassFilter( new ClassCollector(set))); @@ -380,38 +410,82 @@ implements ClassVisitor, /** + * Returns whether the two given classes have class members with the same + * name and descriptor. + */ + 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) { - // The class must not have any fields, or it must not be instantiated, - // without any other subclasses. - return - programClass.u2fieldsCount != 0 && - (InstantiationClassMarker.isInstantiated(targetClass) || - (targetClass.subClasses != null && - !isOnlySubClass(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.INTERNAL_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.INTERNAL_ACC_PRIVATE, + counter)))); + + return counter.getCount() > 0; } /** - * Returns whether the two given classes have initializers with the same - * descriptors. + * Returns whether the two given classes have class members with the same + * name and descriptor. */ - private boolean haveAnyIdenticalInitializers(Clazz clazz, Clazz targetClass) + private boolean haveAnyIdenticalMethods(Clazz clazz, Clazz targetClass) { MemberCounter counter = new MemberCounter(); - // TODO: Currently checking shared methods, not just initializers. - // TODO: Allow identical methods. - // Visit all methods, counting the ones that are also present in the - // target class. - clazz.methodsAccept(//new MemberNameFilter(new FixedStringMatcher(ClassConstants.INTERNAL_METHOD_NAME_INIT), + // Visit all non-abstract methods, counting the ones that are also + // present in the target class. + clazz.methodsAccept(new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_ABSTRACT, new SimilarMemberVisitor(targetClass, true, false, false, false, new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_ABSTRACT, - counter))); + counter)))); return counter.getCount() > 0; } @@ -538,4 +612,30 @@ implements ClassVisitor, 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. + } + } }
\ No newline at end of file |