blob: 7035b8df32d03f9ea24cc6d0429486889443ba82 [file] [log] [blame]
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2019 Guardsquare NV
*
* 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.ClassConstants;
import proguard.classfile.attribute.visitor.*;
import proguard.classfile.visitor.*;
import java.util.*;
/**
* This factory creates visitors to efficiently travel to specified classes and
* class members.
*
* @author Eric Lafortune
*/
public class KeepClassSpecificationVisitorFactory
extends ClassSpecificationVisitorFactory
{
private final boolean shrinking;
private final boolean optimizing;
private final boolean obfuscating;
/**
* Creates a new KeepClassSpecificationVisitorFactory that creates
* visitors for the specified goal.
*
* @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 KeepClassSpecificationVisitorFactory(boolean shrinking,
boolean optimizing,
boolean obfuscating)
{
this.shrinking = shrinking;
this.optimizing = optimizing;
this.obfuscating = obfuscating;
}
// Overriding implementations for ClassSpecificationVisitorFactory.
/**
* Constructs a ClassPoolVisitor to efficiently travel to the specified
* classes, class members and code attributes.
*
* @param keepClassSpecifications the specifications of the class(es) and
* class members to visit.
* @param classVisitor an optional ClassVisitor to be applied to
* matching classes.
* @param fieldVisitor an optional MemberVisitor to be applied
* to matching fields.
* @param methodVisitor an optional MemberVisitor to be applied
* to matching methods.
* @param attributeVisitor an optional AttributeVisitor to be applied
* to matching code attributes.
*/
public ClassPoolVisitor createClassPoolVisitor(List keepClassSpecifications,
ClassVisitor classVisitor,
MemberVisitor fieldVisitor,
MemberVisitor methodVisitor,
AttributeVisitor attributeVisitor)
{
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,
fieldVisitor,
methodVisitor,
attributeVisitor));
}
}
}
return multiClassPoolVisitor;
}
/**
* Constructs a ClassPoolVisitor to efficiently travel to the specified
* classes, class members, and attributes.
*
* @param keepClassSpecification the specifications of the class(es) and
* class members to visit.
* @param classVisitor an optional ClassVisitor to be applied to
* matching classes.
* @param fieldVisitor an optional MemberVisitor to be applied
* to matching fields.
* @param methodVisitor an optional MemberVisitor to be applied
* to matching methods.
* @param attributeVisitor an optional AttributeVisitor to be applied
* to matching code attributes.
*/
public ClassPoolVisitor createClassPoolVisitor(KeepClassSpecification keepClassSpecification,
ClassVisitor classVisitor,
MemberVisitor fieldVisitor,
MemberVisitor methodVisitor,
AttributeVisitor attributeVisitor)
{
// Start a global list of wilcdard matchers, so they can be referenced
// from regular expressions. They are identified by their indices,
// which imposes a number of tricky constraints:
// - They need to be parsed in the right order, so the list is filled
// out in the expected order (corresponding to the text file
// configuration).
// - They need to be matched in the right order, so the variable
// matchers are matched before they are referenced.
List variableStringMatchers = new ArrayList();
// If specified, let the class visitor also visit the descriptor
// classes and the signature classes.
if (keepClassSpecification.markDescriptorClasses &&
classVisitor != null)
{
fieldVisitor = fieldVisitor == null ?
new MemberDescriptorReferencedClassVisitor(classVisitor) :
new MultiMemberVisitor(
new MemberVisitor[]
{
fieldVisitor,
new MemberDescriptorReferencedClassVisitor(classVisitor)
});
methodVisitor = methodVisitor == null ?
new MemberDescriptorReferencedClassVisitor(classVisitor) :
new MultiMemberVisitor(
new MemberVisitor[]
{
methodVisitor,
new MemberDescriptorReferencedClassVisitor(classVisitor)
});
}
// Don't visit the classes if not specified.
if (!keepClassSpecification.markClasses &&
!keepClassSpecification.markConditionally)
{
classVisitor = null;
}
// Do we have an attribute visitor?
if (attributeVisitor != null)
{
// Don't visit the code attributes if not specified.
attributeVisitor = keepClassSpecification.markCodeAttributes ?
new AttributeNameFilter(ClassConstants.ATTR_Code, attributeVisitor) :
null;
}
ClassSpecification condition = keepClassSpecification.condition;
if (condition != null)
{
// Parse the condition. We need to parse it before the actual keep
// specification, to make sure the list of variable string matchers
// is filled out in the right order.
// Create a placeholder for the class pool visitor that
// corresponds to the actual keep specification. Note that we
// visit the entire class pool for each matched class.
MultiClassPoolVisitor keepClassPoolVisitor =
new MultiClassPoolVisitor();
// Parse the condition.
ClassPoolVisitor conditionalKeepClassPoolVisitor =
createClassTester(condition,
keepClassPoolVisitor,
variableStringMatchers);
// Parse the actual keep specification and add it to the
// placeholder.
keepClassPoolVisitor.addClassPoolVisitor(
createClassPoolVisitor(keepClassSpecification,
classVisitor,
fieldVisitor,
methodVisitor,
attributeVisitor,
variableStringMatchers));
return conditionalKeepClassPoolVisitor;
}
else
{
// Just parse the actual keep specification.
return createClassPoolVisitor(keepClassSpecification,
classVisitor,
fieldVisitor,
methodVisitor,
attributeVisitor,
variableStringMatchers);
}
}
/**
* 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 an optional ClassVisitor to be applied to
* matching classes.
* @param fieldVisitor an optional MemberVisitor to be applied
* to matching fields.
* @param methodVisitor an optional MemberVisitor to be applied
* to matching methods.
* @param attributeVisitor an optional AttributeVisitor to be applied
* to matching code attributes.
* @param variableStringMatchers a mutable list of VariableStringMatcher
* instances that match the wildcards.
*/
private ClassPoolVisitor createClassPoolVisitor(KeepClassSpecification keepClassSpecification,
ClassVisitor classVisitor,
MemberVisitor fieldVisitor,
MemberVisitor methodVisitor,
AttributeVisitor attributeVisitor,
List variableStringMatchers)
{
// If specified, let the marker visit the class and its class
// members conditionally.
if (keepClassSpecification.markConditionally)
{
// Parse the condition. We need to parse it before the actual keep
// specification, to make sure the list of variable string matchers
// is filled out in the right order.
// Create a placeholder for the class visitor that corresponds to
// the actual keep specification.
MultiClassVisitor keepClassVisitor =
new MultiClassVisitor();
// Parse the condition. Only add string matchers locally.
ClassPoolVisitor conditionalKeepClassPoolVisitor =
createClassTester(keepClassSpecification,
keepClassVisitor,
new ArrayList(variableStringMatchers));
// Parse the actual keep specification and add it to the
// placeholder.
keepClassVisitor.addClassVisitor(
createCombinedClassVisitor(keepClassSpecification.attributeNames,
keepClassSpecification.fieldSpecifications,
keepClassSpecification.methodSpecifications,
classVisitor,
fieldVisitor,
methodVisitor,
attributeVisitor,
variableStringMatchers));
return conditionalKeepClassPoolVisitor;
}
else
{
// Just parse the actual keep specification.
return super.createClassPoolVisitor(keepClassSpecification,
classVisitor,
fieldVisitor,
methodVisitor,
attributeVisitor,
variableStringMatchers);
}
}
}