| /* |
| * ProGuard -- shrinking, optimization, obfuscation, and preverification |
| * of Java bytecode. |
| * |
| * Copyright (c) 2002-2014 Eric Lafortune ([email protected]) |
| * |
| * 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; |
| |
| import proguard.classfile.attribute.annotation.visitor.*; |
| import proguard.classfile.attribute.visitor.AllAttributeVisitor; |
| import proguard.classfile.visitor.*; |
| |
| import java.util.List; |
| |
| /** |
| * This factory creates visitors to efficiently travel to specified classes and |
| * class members. |
| * |
| * @author Eric Lafortune |
| */ |
| public class ClassSpecificationVisitorFactory |
| { |
| /** |
| * Constructs a ClassPoolVisitor to efficiently travel to the specified |
| * classes and class members. |
| * |
| * @param keepClassSpecifications the list of KeepClassSpecification |
| * instances that specify the classes and |
| * class members to visit. |
| * @param classVisitor the ClassVisitor to be applied to matching |
| * classes. |
| * @param memberVisitor the MemberVisitor to be applied to matching |
| * class members. |
| * @param shrinking a flag that specifies whether the visitors |
| * are intended for the shrinking step. |
| * @param optimizing a flag that specifies whether the visitors |
| * are intended for the optimization step. |
| * @param obfuscating a flag that specifies whether the visitors |
| * are intended for the obfuscation step. |
| */ |
| public static ClassPoolVisitor createClassPoolVisitor(List keepClassSpecifications, |
| ClassVisitor classVisitor, |
| MemberVisitor memberVisitor, |
| boolean shrinking, |
| boolean optimizing, |
| boolean obfuscating) |
| { |
| MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor(); |
| |
| if (keepClassSpecifications != null) |
| { |
| for (int index = 0; index < keepClassSpecifications.size(); index++) |
| { |
| KeepClassSpecification keepClassSpecification = |
| (KeepClassSpecification)keepClassSpecifications.get(index); |
| |
| if ((shrinking && !keepClassSpecification.allowShrinking) || |
| (optimizing && !keepClassSpecification.allowOptimization) || |
| (obfuscating && !keepClassSpecification.allowObfuscation)) |
| { |
| multiClassPoolVisitor.addClassPoolVisitor( |
| createClassPoolVisitor(keepClassSpecification, |
| classVisitor, |
| memberVisitor)); |
| } |
| } |
| } |
| |
| return multiClassPoolVisitor; |
| } |
| |
| |
| /** |
| * Constructs a ClassPoolVisitor to efficiently travel to the specified |
| * classes and class members. |
| * |
| * @param classSpecifications the list of ClassSpecification instances |
| * that specify the classes and class members |
| * to visit. |
| * @param classVisitor the ClassVisitor to be applied to matching |
| * classes. |
| * @param memberVisitor the MemberVisitor to be applied to matching |
| * class members. |
| */ |
| public static ClassPoolVisitor createClassPoolVisitor(List classSpecifications, |
| ClassVisitor classVisitor, |
| MemberVisitor memberVisitor) |
| { |
| MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor(); |
| |
| if (classSpecifications != null) |
| { |
| for (int index = 0; index < classSpecifications.size(); index++) |
| { |
| ClassSpecification classSpecification = |
| (ClassSpecification)classSpecifications.get(index); |
| |
| multiClassPoolVisitor.addClassPoolVisitor( |
| createClassPoolVisitor(classSpecification, |
| classVisitor, |
| memberVisitor)); |
| } |
| } |
| |
| return multiClassPoolVisitor; |
| } |
| |
| |
| /** |
| * Constructs a ClassPoolVisitor to efficiently travel to the specified |
| * classes and class members. |
| * |
| * @param keepClassSpecification the specifications of the class(es) and |
| * class members to visit. |
| * @param classVisitor the ClassVisitor to be applied to |
| * matching classes. |
| * @param memberVisitor the MemberVisitor to be applied to |
| * matching class members. |
| */ |
| public static ClassPoolVisitor createClassPoolVisitor(KeepClassSpecification keepClassSpecification, |
| ClassVisitor classVisitor, |
| MemberVisitor memberVisitor) |
| { |
| // If specified, let the class visitor also visit the descriptor |
| // classes. |
| if (keepClassSpecification.markDescriptorClasses && |
| classVisitor != null) |
| { |
| memberVisitor = memberVisitor == null ? |
| new MemberDescriptorReferencedClassVisitor(classVisitor) : |
| new MultiMemberVisitor(new MemberVisitor[] |
| { |
| memberVisitor, |
| new MemberDescriptorReferencedClassVisitor(classVisitor) |
| }); |
| } |
| |
| // Don't visit the classes if not specified. |
| if (!keepClassSpecification.markClasses && |
| !keepClassSpecification.markConditionally) |
| { |
| classVisitor = null; |
| } |
| |
| // If specified, let the marker visit the class and its class |
| // members conditionally. |
| if (keepClassSpecification.markConditionally) |
| { |
| // Combine both visitors. |
| ClassVisitor composedClassVisitor = |
| createCombinedClassVisitor(keepClassSpecification, |
| classVisitor, |
| memberVisitor); |
| |
| // Replace the class visitor. |
| classVisitor = |
| createClassMemberTester(keepClassSpecification, |
| composedClassVisitor); |
| |
| // Discard the member visitor, because it has already been included. |
| memberVisitor = null; |
| } |
| |
| return createClassPoolVisitor((ClassSpecification)keepClassSpecification, |
| classVisitor, |
| memberVisitor); |
| } |
| |
| |
| /** |
| * Constructs a ClassPoolVisitor to efficiently travel to the specified |
| * classes and class members. |
| * |
| * @param classSpecification the specifications of the class(es) and class |
| * members to visit. |
| * @param classVisitor the ClassVisitor to be applied to matching |
| * classes. |
| * @param memberVisitor the MemberVisitor to be applied to matching |
| * class members. |
| */ |
| public static ClassPoolVisitor createClassPoolVisitor(ClassSpecification classSpecification, |
| ClassVisitor classVisitor, |
| MemberVisitor memberVisitor) |
| { |
| // Combine both visitors. |
| ClassVisitor composedClassVisitor = |
| createCombinedClassVisitor(classSpecification, |
| classVisitor, |
| memberVisitor); |
| |
| // By default, start visiting from the named class name, if specified. |
| String className = classSpecification.className; |
| |
| // Although we may have to start from the extended class. |
| String extendsAnnotationType = classSpecification.extendsAnnotationType; |
| String extendsClassName = classSpecification.extendsClassName; |
| |
| // If wildcarded, only visit classes with matching names. |
| if (className != null && |
| (extendsAnnotationType != null || |
| extendsClassName != null || |
| containsWildCards(className))) |
| { |
| composedClassVisitor = |
| new ClassNameFilter(className, composedClassVisitor); |
| |
| // We'll have to visit all classes now. |
| className = null; |
| } |
| |
| // If specified, only visit classes with the right annotation. |
| String annotationType = classSpecification.annotationType; |
| |
| if (annotationType != null) |
| { |
| composedClassVisitor = |
| new AllAttributeVisitor( |
| new AllAnnotationVisitor( |
| new AnnotationTypeFilter(annotationType, |
| new AnnotatedClassVisitor(composedClassVisitor)))); |
| } |
| |
| // If specified, only visit classes with the right access flags. |
| if (classSpecification.requiredSetAccessFlags != 0 || |
| classSpecification.requiredUnsetAccessFlags != 0) |
| { |
| composedClassVisitor = |
| new ClassAccessFilter(classSpecification.requiredSetAccessFlags, |
| classSpecification.requiredUnsetAccessFlags, |
| composedClassVisitor); |
| } |
| |
| // If it's specified, start visiting from the extended class. |
| if (extendsAnnotationType != null || |
| extendsClassName != null) |
| { |
| // Start visiting from the extended class. |
| composedClassVisitor = |
| new ClassHierarchyTraveler(false, false, false, true, |
| composedClassVisitor); |
| |
| // If specified, only visit extended classes with the right annotation. |
| if (extendsAnnotationType != null) |
| { |
| composedClassVisitor = |
| new AllAttributeVisitor( |
| new AllAnnotationVisitor( |
| new AnnotationTypeFilter(extendsAnnotationType, |
| new AnnotatedClassVisitor(composedClassVisitor)))); |
| } |
| |
| // If specified, only visit extended classes with matching names. |
| if (extendsClassName != null) |
| { |
| // If wildcarded, only visit extended classes with matching names. |
| if (containsWildCards(extendsClassName)) |
| { |
| composedClassVisitor = |
| new ClassNameFilter(extendsClassName, |
| composedClassVisitor); |
| } |
| else |
| { |
| // Start visiting from the named extended class. |
| className = extendsClassName; |
| } |
| } |
| } |
| |
| // If specified, visit a single named class, otherwise visit all classes. |
| return className != null ? |
| (ClassPoolVisitor)new NamedClassVisitor(composedClassVisitor, className) : |
| (ClassPoolVisitor)new AllClassVisitor(composedClassVisitor); |
| } |
| |
| |
| /** |
| * Constructs a ClassVisitor to efficiently travel to the specified |
| * classes and class members. |
| * |
| * @param classSpecification the specifications of the class(es) and class |
| * members to visit. |
| * @param classVisitor the ClassVisitor to be applied to matching |
| * classes. |
| * @param memberVisitor the MemberVisitor to be applied to matching |
| * class members. |
| */ |
| private static ClassVisitor createCombinedClassVisitor(ClassSpecification classSpecification, |
| ClassVisitor classVisitor, |
| MemberVisitor memberVisitor) |
| { |
| // Don't visit any members if there aren't any member specifications. |
| if (classSpecification.fieldSpecifications == null && |
| classSpecification.methodSpecifications == null) |
| { |
| memberVisitor = null; |
| } |
| |
| // The class visitor for classes and their members. |
| MultiClassVisitor multiClassVisitor = new MultiClassVisitor(); |
| |
| // If specified, let the class visitor visit the class itself. |
| if (classVisitor != null) |
| { |
| // This class visitor may be the only one. |
| if (memberVisitor == null) |
| { |
| return classVisitor; |
| } |
| |
| multiClassVisitor.addClassVisitor(classVisitor); |
| } |
| |
| // If specified, let the member info visitor visit the class members. |
| if (memberVisitor != null) |
| { |
| ClassVisitor memberClassVisitor = |
| createClassVisitor(classSpecification, memberVisitor); |
| |
| // This class visitor may be the only one. |
| if (classVisitor == null) |
| { |
| return memberClassVisitor; |
| } |
| |
| multiClassVisitor.addClassVisitor(memberClassVisitor); |
| } |
| |
| return multiClassVisitor; |
| } |
| |
| |
| /** |
| * Constructs a ClassVisitor to efficiently travel to the specified class |
| * members. |
| * |
| * @param classSpecification the specifications of the class members to visit. |
| * @param memberVisitor the MemberVisitor to be applied to matching |
| * class members. |
| */ |
| private static ClassVisitor createClassVisitor(ClassSpecification classSpecification, |
| MemberVisitor memberVisitor) |
| { |
| MultiClassVisitor multiClassVisitor = new MultiClassVisitor(); |
| |
| addMemberVisitors(classSpecification.fieldSpecifications, true, multiClassVisitor, memberVisitor); |
| addMemberVisitors(classSpecification.methodSpecifications, false, multiClassVisitor, memberVisitor); |
| |
| // Mark the class member in this class and in super classes. |
| return new ClassHierarchyTraveler(true, true, false, false, |
| multiClassVisitor); |
| } |
| |
| |
| /** |
| * Adds elements to the given MultiClassVisitor, to apply the given |
| * MemberVisitor to all class members that match the given List |
| * of options (of the given type). |
| */ |
| private static void addMemberVisitors(List memberSpecifications, |
| boolean isField, |
| MultiClassVisitor multiClassVisitor, |
| MemberVisitor memberVisitor) |
| { |
| if (memberSpecifications != null) |
| { |
| for (int index = 0; index < memberSpecifications.size(); index++) |
| { |
| MemberSpecification memberSpecification = |
| (MemberSpecification)memberSpecifications.get(index); |
| |
| multiClassVisitor.addClassVisitor( |
| createClassVisitor(memberSpecification, |
| isField, |
| memberVisitor)); |
| } |
| } |
| } |
| |
| |
| /** |
| * Constructs a ClassVisitor that conditionally applies the given |
| * ClassVisitor to all classes that contain the given class members. |
| */ |
| private static ClassVisitor createClassMemberTester(ClassSpecification classSpecification, |
| ClassVisitor classVisitor) |
| { |
| // Create a linked list of conditional visitors, for fields and for |
| // methods. |
| return createClassMemberTester(classSpecification.fieldSpecifications, |
| true, |
| createClassMemberTester(classSpecification.methodSpecifications, |
| false, |
| classVisitor)); |
| } |
| |
| |
| /** |
| * Constructs a ClassVisitor that conditionally applies the given |
| * ClassVisitor to all classes that contain the given List of class |
| * members (of the given type). |
| */ |
| private static ClassVisitor createClassMemberTester(List memberSpecifications, |
| boolean isField, |
| ClassVisitor classVisitor) |
| { |
| // Create a linked list of conditional visitors. |
| if (memberSpecifications != null) |
| { |
| for (int index = 0; index < memberSpecifications.size(); index++) |
| { |
| MemberSpecification memberSpecification = |
| (MemberSpecification)memberSpecifications.get(index); |
| |
| classVisitor = |
| createClassVisitor(memberSpecification, |
| isField, |
| new MemberToClassVisitor(classVisitor)); |
| } |
| } |
| |
| return classVisitor; |
| } |
| |
| |
| /** |
| * Creates a new ClassVisitor to efficiently travel to the specified class |
| * members. |
| * |
| * @param memberSpecification the specification of the class member(s) to |
| * visit. |
| * @param memberVisitor the MemberVisitor to be applied to matching |
| * class member(s). |
| */ |
| private static ClassVisitor createClassVisitor(MemberSpecification memberSpecification, |
| boolean isField, |
| MemberVisitor memberVisitor) |
| { |
| String name = memberSpecification.name; |
| String descriptor = memberSpecification.descriptor; |
| |
| // If name or descriptor are not fully specified, only visit matching |
| // class members. |
| boolean fullySpecified = |
| name != null && |
| descriptor != null && |
| !containsWildCards(name) && |
| !containsWildCards(descriptor); |
| |
| if (!fullySpecified) |
| { |
| if (descriptor != null) |
| { |
| memberVisitor = |
| new MemberDescriptorFilter(descriptor, memberVisitor); |
| } |
| |
| if (name != null) |
| { |
| memberVisitor = |
| new MemberNameFilter(name, memberVisitor); |
| } |
| } |
| |
| // If specified, only visit class members with the right annotation. |
| if (memberSpecification.annotationType != null) |
| { |
| memberVisitor = |
| new AllAttributeVisitor( |
| new AllAnnotationVisitor( |
| new AnnotationTypeFilter(memberSpecification.annotationType, |
| new AnnotationToMemberVisitor(memberVisitor)))); |
| } |
| |
| // If any access flags are specified, only visit matching class members. |
| if (memberSpecification.requiredSetAccessFlags != 0 || |
| memberSpecification.requiredUnsetAccessFlags != 0) |
| { |
| memberVisitor = |
| new MemberAccessFilter(memberSpecification.requiredSetAccessFlags, |
| memberSpecification.requiredUnsetAccessFlags, |
| memberVisitor); |
| } |
| |
| // Depending on what's specified, visit a single named class member, |
| // or all class members, filtering the matching ones. |
| return isField ? |
| fullySpecified ? |
| (ClassVisitor)new NamedFieldVisitor(name, descriptor, memberVisitor) : |
| (ClassVisitor)new AllFieldVisitor(memberVisitor) : |
| fullySpecified ? |
| (ClassVisitor)new NamedMethodVisitor(name, descriptor, memberVisitor) : |
| (ClassVisitor)new AllMethodVisitor(memberVisitor); |
| } |
| |
| |
| // Small utility methods. |
| |
| private static boolean containsWildCards(String string) |
| { |
| return string != null && |
| (string.indexOf('!') >= 0 || |
| string.indexOf('*') >= 0 || |
| string.indexOf('?') >= 0 || |
| string.indexOf('%') >= 0 || |
| string.indexOf(',') >= 0 || |
| string.indexOf("///") >= 0); |
| } |
| } |