443 lines
14 KiB
Java
443 lines
14 KiB
Java
/*******************************************************************************
|
|
* Copyright (c) 2002 - 2006 IBM Corporation.
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial API and implementation
|
|
*******************************************************************************/
|
|
package com.ibm.wala.ipa.callgraph;
|
|
|
|
import com.ibm.wala.analysis.reflection.ReflectionContextInterpreter;
|
|
import com.ibm.wala.analysis.reflection.ReflectionContextSelector;
|
|
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
|
|
import com.ibm.wala.ipa.callgraph.propagation.ReflectionHandler;
|
|
import com.ibm.wala.ssa.SSAOptions;
|
|
|
|
/**
|
|
* Basic interface for options that control call graph generation.
|
|
*
|
|
* TODO: This class should be refactored into an abstract base class and language-specific subclasses.
|
|
*/
|
|
public class AnalysisOptions {
|
|
|
|
/**
|
|
* An object that represents the analysis scope
|
|
*/
|
|
private AnalysisScope analysisScope;
|
|
|
|
/**
|
|
* An object that identifies the entrypoints for the call graph
|
|
*/
|
|
private Iterable<? extends Entrypoint> entrypoints;
|
|
|
|
|
|
/**
|
|
* Policy that determines types allocated at new statements.
|
|
*/
|
|
private ClassTargetSelector classTargetSelector;
|
|
|
|
/**
|
|
* Policy that determines methods called at call sites.
|
|
*/
|
|
private MethodTargetSelector methodTargetSelector;
|
|
|
|
/**
|
|
* A tuning parameter; how may new equations must be added before doing a new topological sort?
|
|
*/
|
|
private int minEquationsForTopSort = 100;
|
|
|
|
/**
|
|
* A tuning parameter; by what percentage must the number of equations grow before we perform a topological sort?
|
|
*/
|
|
private double topologicalGrowthFactor = 0.5;
|
|
|
|
/**
|
|
* A tuning parameter: how many evaluations are allowed to take place between topological re-orderings. The idea is that many
|
|
* evaluations may be a sign of a bad ordering, even when few new equations are being added.
|
|
*/
|
|
private int maxEvalBetweenTopo = 1000000000;
|
|
|
|
/**
|
|
* options for handling reflection during call graph construction
|
|
*/
|
|
public static enum ReflectionOptions {
|
|
FULL("full", Integer.MAX_VALUE, false, false, false),
|
|
APPLICATION_GET_METHOD("application_get_method", Integer.MAX_VALUE, false, false, true),
|
|
NO_FLOW_TO_CASTS("no_flow_to_casts", 0, false, false, false),
|
|
NO_FLOW_TO_CASTS_APPLICATION_GET_METHOD("no_flow_to_casts_application_get_method", 0, false, false, true),
|
|
NO_METHOD_INVOKE("no_method_invoke", Integer.MAX_VALUE, true, false, false),
|
|
NO_FLOW_TO_CASTS_NO_METHOD_INVOKE("no_flow_to_casts_no_method_invoke", 0, true, false, false),
|
|
ONE_FLOW_TO_CASTS_NO_METHOD_INVOKE("one_flow_to_casts_no_method_invoke", 1, true, false, false),
|
|
ONE_FLOW_TO_CASTS_APPLICATION_GET_METHOD("one_flow_to_casts_application_get_method", 1, false, false, true),
|
|
MULTI_FLOW_TO_CASTS_APPLICATION_GET_METHOD("multi_flow_to_casts_application_get_method", 100, false, false, true),
|
|
NO_STRING_CONSTANTS("no_string_constants", Integer.MAX_VALUE, false, true, false),
|
|
STRING_ONLY("string_constants", 0, true, false, true),
|
|
NONE("none", 0, true, true, true);
|
|
|
|
private final String name;
|
|
|
|
/**
|
|
* how many times should flows from newInstance() calls to casts be analyzed?
|
|
*/
|
|
private final int numFlowToCastIterations;
|
|
|
|
/**
|
|
* should calls to Method.invoke() be ignored?
|
|
*/
|
|
private final boolean ignoreMethodInvoke;
|
|
|
|
/**
|
|
* should calls to Class.getMethod() be modeled only for application classes?
|
|
*/
|
|
private final boolean applicationClassesOnly;
|
|
|
|
/**
|
|
* should calls to reflective methods with String constant arguments be ignored?
|
|
*/
|
|
private final boolean ignoreStringConstants;
|
|
|
|
private ReflectionOptions(String name, int numFlowToCastIterations, boolean ignoreMethodInvoke, boolean ignoreInterpretCalls, boolean applicationClassesOnly) {
|
|
this.name = name;
|
|
this.numFlowToCastIterations = numFlowToCastIterations;
|
|
this.ignoreMethodInvoke = ignoreMethodInvoke;
|
|
this.ignoreStringConstants = ignoreInterpretCalls;
|
|
this.applicationClassesOnly = applicationClassesOnly;
|
|
}
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
public int getNumFlowToCastIterations() {
|
|
return numFlowToCastIterations;
|
|
}
|
|
|
|
public boolean isIgnoreMethodInvoke() {
|
|
return ignoreMethodInvoke;
|
|
}
|
|
|
|
public boolean isIgnoreStringConstants() {
|
|
return ignoreStringConstants;
|
|
}
|
|
|
|
public boolean isApplicationClassesOnly() {
|
|
return applicationClassesOnly;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Should call graph construction attempt to handle reflection via detection of flows to casts, analysis of string constant
|
|
* parameters to reflective methods, etc.?
|
|
*
|
|
* @see ReflectionHandler
|
|
* @see ReflectionContextInterpreter
|
|
* @see ReflectionContextSelector
|
|
*/
|
|
private ReflectionOptions reflectionOptions = ReflectionOptions.FULL;
|
|
|
|
/**
|
|
* Should call graph construction handle possible invocations of static initializer methods?
|
|
*/
|
|
private boolean handleStaticInit = true;
|
|
|
|
/**
|
|
* Options governing SSA construction
|
|
*/
|
|
private SSAOptions ssaOptions = new SSAOptions();
|
|
|
|
/**
|
|
* Use distinct instance keys for distinct string constants?
|
|
*
|
|
* TODO: Probably, this option should moved somewhere into the creation of instance keys. However, those factories are created
|
|
* within the various builders right now, and this is the most convenient place for an engine user to set an option which the
|
|
* creation of instance keys later picks up.
|
|
*/
|
|
private boolean useConstantSpecificKeys = false;
|
|
|
|
/**
|
|
* Should analysis of lexical scoping consider call stacks?
|
|
*
|
|
* TODO: this option does not apply to all languages. We could have a separation into core engine options and language-specific
|
|
* options.
|
|
*
|
|
* (be careful with multithreaded languages, as threading can break the stack discipline this option may assume)
|
|
*/
|
|
private boolean useStacksForLexicalScoping = false;
|
|
|
|
/**
|
|
* Should global variables be considered lexically-scoped from the root node?
|
|
*
|
|
* TODO: this option does not apply to all languages. We could have a separation into core engine options and language-specific
|
|
* options.
|
|
*
|
|
* (be careful with multithreaded languages, as threading can break the stack discipline this option may assume)
|
|
*/
|
|
private boolean useLexicalScopingForGlobals = false;
|
|
|
|
/**
|
|
* Should analysis try to understand the results of string constants flowing to a + operator? Note that this option does not apply
|
|
* to Java bytecode analysis, since the + operators have been compiled away for that. It is used for the Java CAst front end.
|
|
*/
|
|
private boolean traceStringConstants = false;
|
|
|
|
/**
|
|
* This numerical value indicates the maximum number of nodes that any {@link CallGraph} build with this {@link AnalysisOptions}
|
|
* object is allowed to have. During {@link CallGraph} construction, once <code>maxNumberOfNodes</code> {@link CGNode} objects
|
|
* have been added to the {@link CallGraph}, no more {@link CGNode} objects will be added. By default,
|
|
* <code>maxNumberOfNodes</code> is set to <code>-1</code>, which indicates that no restrictions are in place. See also
|
|
* {@link ExplicitCallGraph}.
|
|
*/
|
|
private long maxNumberOfNodes = -1;
|
|
|
|
/**
|
|
* Should call graph construction handle arrays of zero-length differently?
|
|
*/
|
|
private boolean handleZeroLengthArray = true;
|
|
|
|
// SJF: I'm not sure these factories and caches belong here.
|
|
// TODO: figure out how to clean this up.
|
|
|
|
public AnalysisOptions() {
|
|
}
|
|
|
|
public AnalysisOptions(AnalysisScope scope, Iterable<? extends Entrypoint> e) {
|
|
this.analysisScope = scope;
|
|
this.entrypoints = e;
|
|
}
|
|
|
|
public AnalysisScope getAnalysisScope() {
|
|
return analysisScope;
|
|
}
|
|
|
|
public void setAnalysisScope(AnalysisScope analysisScope) {
|
|
this.analysisScope = analysisScope;
|
|
}
|
|
|
|
/**
|
|
* TODO: this really should go away. The entrypoints don't belong here.
|
|
*/
|
|
public Iterable<? extends Entrypoint> getEntrypoints() {
|
|
return entrypoints;
|
|
}
|
|
|
|
public void setEntrypoints(Iterable<? extends Entrypoint> entrypoints) {
|
|
this.entrypoints = entrypoints;
|
|
}
|
|
|
|
public long getMaxNumberOfNodes() {
|
|
return maxNumberOfNodes;
|
|
}
|
|
|
|
public void setMaxNumberOfNodes(long maxNumberOfNodes) {
|
|
this.maxNumberOfNodes = maxNumberOfNodes;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return Policy that determines methods called at call sites.
|
|
*/
|
|
public MethodTargetSelector getMethodTargetSelector() {
|
|
return methodTargetSelector;
|
|
}
|
|
|
|
/**
|
|
* @return Policy that determines types allocated at new statements.
|
|
*/
|
|
public ClassTargetSelector getClassTargetSelector() {
|
|
return classTargetSelector;
|
|
}
|
|
|
|
/**
|
|
* install a method target selector
|
|
*
|
|
* @param x an object which controls the policy for selecting the target at a call site
|
|
*/
|
|
public void setSelector(MethodTargetSelector x) {
|
|
methodTargetSelector = x;
|
|
}
|
|
|
|
/**
|
|
* install a class target selector
|
|
*
|
|
* @param x an object which controls the policy for selecting the allocated object at a new site
|
|
*/
|
|
public void setSelector(ClassTargetSelector x) {
|
|
classTargetSelector = x;
|
|
}
|
|
|
|
/**
|
|
* @return the mininum number of equations that the pointer analysis system must contain before the solver will try to
|
|
* topologically sore
|
|
*/
|
|
public int getMinEquationsForTopSort() {
|
|
return minEquationsForTopSort;
|
|
}
|
|
|
|
/**
|
|
* @param i the mininum number of equations that the pointer analysis system must contain before the solver will try to
|
|
* topologically sore
|
|
*/
|
|
public void setMinEquationsForTopSort(int i) {
|
|
minEquationsForTopSort = i;
|
|
}
|
|
|
|
/**
|
|
* @return the maximum number of evaluations that the pointer analysis solver will perform before topologically resorting the
|
|
* system
|
|
*/
|
|
public int getMaxEvalBetweenTopo() {
|
|
return maxEvalBetweenTopo;
|
|
}
|
|
|
|
/**
|
|
* @return a fraction x s.t. the solver will resort the system when it grows by a factor of x
|
|
*/
|
|
public double getTopologicalGrowthFactor() {
|
|
return topologicalGrowthFactor;
|
|
}
|
|
|
|
/**
|
|
* @param i the maximum number of evaluations that the pointer analysis solver will perform before topologically resorting the
|
|
* system
|
|
*/
|
|
public void setMaxEvalBetweenTopo(int i) {
|
|
maxEvalBetweenTopo = i;
|
|
}
|
|
|
|
/**
|
|
* @param d a fraction x s.t. the solver will resort the system when it grows by a factor of x
|
|
*/
|
|
public void setTopologicalGrowthFactor(double d) {
|
|
topologicalGrowthFactor = d;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return options governing SSA construction
|
|
*/
|
|
public SSAOptions getSSAOptions() {
|
|
return ssaOptions;
|
|
}
|
|
|
|
/**
|
|
* @param ssaOptions options governing SSA construction
|
|
*/
|
|
public void setSSAOptions(SSAOptions ssaOptions) {
|
|
this.ssaOptions = ssaOptions;
|
|
}
|
|
|
|
/**
|
|
* Use distinct instance keys for distinct string constants?
|
|
*/
|
|
public boolean getUseConstantSpecificKeys() {
|
|
return useConstantSpecificKeys;
|
|
}
|
|
|
|
/**
|
|
* Use distinct instance keys for distinct string constants?
|
|
*/
|
|
public void setUseConstantSpecificKeys(boolean useConstantSpecificKeys) {
|
|
this.useConstantSpecificKeys = useConstantSpecificKeys;
|
|
}
|
|
|
|
/**
|
|
* Should analysis of lexical scoping consider call stacks?
|
|
*/
|
|
public boolean getUseStacksForLexicalScoping() {
|
|
return useStacksForLexicalScoping;
|
|
}
|
|
|
|
/**
|
|
* Should analysis of lexical scoping consider call stacks?
|
|
*/
|
|
public void setUseStacksForLexicalScoping(boolean v) {
|
|
useStacksForLexicalScoping = v;
|
|
}
|
|
|
|
/**
|
|
* Should global variables be considered lexically-scoped from the root node?
|
|
*/
|
|
public boolean getUseLexicalScopingForGlobals() {
|
|
return useLexicalScopingForGlobals;
|
|
}
|
|
|
|
/**
|
|
* Should global variables be considered lexically-scoped from the root node?
|
|
*/
|
|
public void setUseLexicalScopingForGlobals(boolean v) {
|
|
useLexicalScopingForGlobals = v;
|
|
}
|
|
|
|
/**
|
|
* Should analysis try to understand the results of string constants flowing to a + operator? Note that this option does not apply
|
|
* to Java bytecode analysis, since the + operators have been compiled away for that. It is used for the Java CAst front end.
|
|
*/
|
|
public void setTraceStringConstants(boolean v) {
|
|
traceStringConstants = v;
|
|
}
|
|
|
|
/**
|
|
* Should analysis try to understand the results of string constants flowing to a + operator? Note that this option does not apply
|
|
* to Java bytecode analysis, since the + operators have been compiled away for that. It is used for the Java CAst front end.
|
|
*/
|
|
public boolean getTraceStringConstants() {
|
|
return traceStringConstants;
|
|
}
|
|
|
|
/**
|
|
* Should call graph construction attempt to handle reflection via detection of flows to casts, analysis of string constant
|
|
* parameters to reflective methods, etc.?
|
|
*
|
|
* @see ReflectionHandler
|
|
* @see ReflectionContextInterpreter
|
|
* @see ReflectionContextSelector
|
|
*/
|
|
public ReflectionOptions getReflectionOptions() {
|
|
return reflectionOptions;
|
|
}
|
|
|
|
/**
|
|
* Should call graph construction attempt to handle reflection via detection of flows to casts, analysis of string constant
|
|
* parameters to reflective methods, etc.?
|
|
*
|
|
* @see ReflectionHandler
|
|
* @see ReflectionContextInterpreter
|
|
* @see ReflectionContextSelector
|
|
*/
|
|
public void setReflectionOptions(ReflectionOptions reflectionOptions) {
|
|
this.reflectionOptions = reflectionOptions;
|
|
}
|
|
|
|
/**
|
|
* Should call graph construction handle possible invocations of static initializer methods?
|
|
*/
|
|
public boolean getHandleStaticInit() {
|
|
return handleStaticInit;
|
|
}
|
|
|
|
/**
|
|
* Should call graph construction handle possible invocations of static initializer methods?
|
|
*/
|
|
public void setHandleStaticInit(boolean handleStaticInit) {
|
|
this.handleStaticInit = handleStaticInit;
|
|
}
|
|
|
|
/**
|
|
* Should call graph construction handle arrays of zero-length differently?
|
|
*/
|
|
public boolean getHandleZeroLengthArray() {
|
|
return handleZeroLengthArray;
|
|
}
|
|
|
|
/**
|
|
* Should call graph construction handle arrays of zero-length differently?
|
|
*/
|
|
public void setHandleZeroLengthArray(boolean handleZeroLengthArray) {
|
|
this.handleZeroLengthArray = handleZeroLengthArray;
|
|
}
|
|
}
|