Changes to extend supported context sensitivity to CPA-style:
1) extend ContextSelector interface to allow it to specify parameters of interest 2) extend filtering mechanism at call sites to allow CPA-style filtering when requested by contexts 3) various related fixes and extensions: a) removed redundant code to handle dispatch for JavaScript, so now it shares the core mechanism b) tighten types for operators that take an array of args - now the array is T[] at the cost of a few array allocation methods c) a bit more support for empty int sets d) void function objects e) bug fixes for lexical scoping support, and adaptation to work with core dispatch mechanism f) example of CPA-style sensitivity to handle nastiness in a JavaScript for(.. in ...) loop git-svn-id: https://wala.svn.sourceforge.net/svnroot/wala/trunk@4150 f5eafffb-2e1d-0410-98e4-8ec43c5233c4
This commit is contained in:
parent
3fffba2c87
commit
4da02a9125
|
@ -117,7 +117,7 @@ public class AstJavaTypeInference extends AstTypeInference {
|
|||
private PrimAndStringOp() {
|
||||
}
|
||||
|
||||
public byte evaluate(TypeVariable lhs, IVariable[] rhs) {
|
||||
public byte evaluate(TypeVariable lhs, TypeVariable[] rhs) {
|
||||
TypeAbstraction meet = null;
|
||||
|
||||
for (int i = 0; i < rhs.length; i++) {
|
||||
|
|
|
@ -132,7 +132,7 @@ public class AstJavaSSAPropagationCallGraphBuilder extends AstSSAPropagationCall
|
|||
|
||||
public AstJavaConstraintVisitor(
|
||||
AstSSAPropagationCallGraphBuilder builder,
|
||||
ExplicitCallGraph.ExplicitNode node)
|
||||
CGNode node)
|
||||
{
|
||||
super(builder, node);
|
||||
}
|
||||
|
@ -277,7 +277,7 @@ public class AstJavaSSAPropagationCallGraphBuilder extends AstSSAPropagationCall
|
|||
}
|
||||
}
|
||||
|
||||
protected ConstraintVisitor makeVisitor(ExplicitCallGraph.ExplicitNode node) {
|
||||
protected ConstraintVisitor makeVisitor(CGNode node) {
|
||||
return new AstJavaConstraintVisitor(this, node);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ public class AstJavaZeroOneContainerCFABuilder extends AstJavaCFABuilder {
|
|||
ContextSelector appContextSelector, SSAContextInterpreter appContextInterpreter) {
|
||||
super(cha, options, cache);
|
||||
|
||||
ContextSelector def = new DefaultContextSelector(options);
|
||||
ContextSelector def = new DefaultContextSelector(options, cha);
|
||||
ContextSelector contextSelector = appContextSelector == null ? def : new DelegatingContextSelector(appContextSelector, def);
|
||||
|
||||
SSAContextInterpreter contextInterpreter = makeDefaultContextInterpreters(appContextInterpreter, options, cha);
|
||||
|
|
|
@ -32,7 +32,7 @@ public class AstJavaZeroXCFABuilder extends AstJavaCFABuilder {
|
|||
SSAContextInterpreter contextInterpreter = makeDefaultContextInterpreters(appContextInterpreter, options, cha);
|
||||
setContextInterpreter(contextInterpreter);
|
||||
|
||||
ContextSelector def = new DefaultContextSelector(options);
|
||||
ContextSelector def = new DefaultContextSelector(options, cha);
|
||||
ContextSelector contextSelector = appContextSelector == null ? def : new DelegatingContextSelector(appContextSelector, def);
|
||||
|
||||
setContextSelector(contextSelector);
|
||||
|
|
|
@ -74,7 +74,7 @@ public class JavaCAst2IRTranslator extends AstTranslator {
|
|||
}
|
||||
|
||||
protected TypeReference defaultCatchType() {
|
||||
return TypeReference.JavaLangException;
|
||||
return TypeReference.JavaLangThrowable;
|
||||
}
|
||||
|
||||
protected TypeReference makeType(CAstType type) {
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package com.ibm.wala.cast.js.test;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
import com.ibm.wala.cast.js.translator.CAstRhinoTranslatorFactory;
|
||||
|
||||
public class TestForInLoopHackRhino extends TestForInLoopHack {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
com.ibm.wala.cast.js.ipa.callgraph.Util.setTranslatorFactory(new CAstRhinoTranslatorFactory());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
|
||||
(function () {
|
||||
(function _top_level () {
|
||||
|
||||
|
||||
var jQuery = window.jQuery = window.$ = function (selector, context) {
|
||||
|
@ -10,7 +10,7 @@
|
|||
|
||||
var undefined;
|
||||
|
||||
jQuery.extend = jQuery.fn.extend = function () {
|
||||
jQuery.extend = jQuery.fn.extend = function _extend () {
|
||||
var target = arguments[0] || {},
|
||||
i = 1,
|
||||
length = arguments.length,
|
||||
|
@ -26,21 +26,24 @@
|
|||
target = this;
|
||||
--i;
|
||||
}
|
||||
for (; i < length; i++) if ((options = arguments[i]) != null) for (var name in options) {
|
||||
var src = target[name],
|
||||
copy = options[name];
|
||||
if (target === copy) continue;
|
||||
if (deep && copy && typeof copy == "object" && !copy.nodeType) {
|
||||
target[name] = jQuery.extend(deep, src || (copy.length != null ? [] : {}), copy);
|
||||
}
|
||||
else if (copy !== undefined) target[name] = copy;
|
||||
// else target[name] = copy;
|
||||
for (; i < length; i++) if ((options = arguments[i]) != null)
|
||||
for (var name in options) {
|
||||
(function _forin_body (name) {
|
||||
var src = target[name],
|
||||
copy = options[name];
|
||||
if (target === copy) return;
|
||||
if (deep && copy && typeof copy == "object" && !copy.nodeType) {
|
||||
target[name] = jQuery.extend(deep, src || (copy.length != null ? [] : {}), copy);
|
||||
}
|
||||
else if (copy !== undefined) target[name] = copy;
|
||||
else target[name] = copy;
|
||||
})(name);
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
||||
jQuery.extend({
|
||||
speed: function (speed, easing, fn) {
|
||||
speed: function _speed (speed, easing, fn) {
|
||||
var opt = speed && speed.constructor == Object ? speed : {
|
||||
complete: fn || !fn && easing || jQuery.isFunction(speed) && speed,
|
||||
duration: speed,
|
||||
|
@ -48,7 +51,7 @@
|
|||
};
|
||||
opt.duration = (opt.duration && opt.duration.constructor == Number ? opt.duration : jQuery.fx.speeds[opt.duration]) || jQuery.fx.speeds.def;
|
||||
opt.old = opt.complete;
|
||||
opt.complete = function () {
|
||||
opt.complete = function _complete () {
|
||||
if (opt.queue !== false) jQuery(this).dequeue();
|
||||
if (jQuery.isFunction(opt.old)) opt.old.call(this);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
function copyObj(to, from) {
|
||||
for(var p in from) {
|
||||
(function _forin_body (name) {
|
||||
to[name] = from[name];
|
||||
})(p);
|
||||
}
|
||||
}
|
||||
|
||||
function testForIn( x ) {
|
||||
var z;
|
||||
for(var y in x) {
|
||||
if (y in x) {
|
||||
z = (x[y])();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var obj = {
|
||||
foo: function testForIn1() { return 7; },
|
||||
bar: function testForIn2() { return "whatever"; }
|
||||
}
|
||||
|
||||
testForIn(obj);
|
||||
|
||||
(function _check_obj_foo () { obj.foo(); })();
|
||||
(function _check_obj_bar () { obj.bar(); })();
|
||||
|
||||
var copy = new Object();
|
||||
copyObj(copy, obj);
|
||||
|
||||
(function _check_copy_foo () { copy.foo(); })();
|
||||
(function _check_copy_bar () { copy.bar(); })();
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
package com.ibm.wala.cast.js.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.wala.cast.js.html.JSSourceExtractor;
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JSCFABuilder;
|
||||
import com.ibm.wala.classLoader.CallSiteReference;
|
||||
import com.ibm.wala.classLoader.IMethod;
|
||||
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||
import com.ibm.wala.ipa.callgraph.CallGraph;
|
||||
import com.ibm.wala.ipa.callgraph.Context;
|
||||
import com.ibm.wala.ipa.callgraph.ContextItem;
|
||||
import com.ibm.wala.ipa.callgraph.ContextKey;
|
||||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.util.CancelException;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
|
||||
public class TestForInLoopHack extends TestJSCallGraphShape {
|
||||
|
||||
@Before
|
||||
public void config() {
|
||||
JSSourceExtractor.USE_TEMP_NAME = false;
|
||||
JSSourceExtractor.DELETE_UPON_EXIT = false;
|
||||
}
|
||||
|
||||
@Test public void testPage3WithoutHack() throws IOException, IllegalArgumentException, CancelException {
|
||||
URL url = getClass().getClassLoader().getResource("pages/page3.html");
|
||||
JSCFABuilder builder = Util.makeHTMLCGBuilder(url);
|
||||
CallGraph CG = builder.makeCallGraph(builder.getOptions());
|
||||
Util.dumpCG(builder, CG);
|
||||
}
|
||||
|
||||
@Test public void testPage3WithHack() throws IOException, IllegalArgumentException, CancelException {
|
||||
URL url = getClass().getClassLoader().getResource("pages/page3.html");
|
||||
JSCFABuilder builder = Util.makeHTMLCGBuilder(url);
|
||||
addHackedForInLoopSensitivity(builder);
|
||||
CallGraph CG = builder.makeCallGraph(builder.getOptions());
|
||||
Util.dumpCG(builder, CG);
|
||||
}
|
||||
|
||||
private static final Object[][] assertionsForBadForin = new Object[][] {
|
||||
new Object[] { ROOT,
|
||||
new String[] { "tests/badforin.js" } },
|
||||
new Object[] { "tests/badforin.js",
|
||||
new String[] { "tests/badforin.js/testForIn", "tests/badforin.js/_check_obj_foo", "tests/badforin.js/_check_obj_bar", "tests/badforin.js/_check_copy_foo", "tests/badforin.js/_check_copy_bar"} },
|
||||
new Object[] { "tests/badforin.js/testForIn",
|
||||
new String[] { "tests/badforin.js/testForIn1", "tests/badforin.js/testForIn2" } },
|
||||
new Object[] { "tests/badforin.js/_check_obj_foo",
|
||||
new String[] { "tests/badforin.js/testForIn1" } },
|
||||
new Object[] { "tests/badforin.js/_check_copy_foo",
|
||||
new String[] { "tests/badforin.js/testForIn1" } },
|
||||
new Object[] { "tests/badforin.js/_check_obj_bar",
|
||||
new String[] { "tests/badforin.js/testForIn2" } },
|
||||
new Object[] { "tests/badforin.js/_check_copy_bar",
|
||||
new String[] { "tests/badforin.js/testForIn2" } }
|
||||
};
|
||||
|
||||
@Test public void testBadForInWithoutHack() throws IOException, IllegalArgumentException, CancelException {
|
||||
JSCFABuilder B = Util.makeScriptCGBuilder("tests", "badforin.js");
|
||||
CallGraph CG = B.makeCallGraph(B.getOptions());
|
||||
Util.dumpCG(B, CG);
|
||||
verifyGraphAssertions(CG, assertionsForBadForin);
|
||||
}
|
||||
|
||||
private static final Object[][] assertionsForBadForinHackPrecision = new Object[][] {
|
||||
new Object[] { "tests/badforin.js/_check_obj_foo",
|
||||
new String[] { "!tests/badforin.js/testForIn2" } },
|
||||
new Object[] { "tests/badforin.js/_check_copy_foo",
|
||||
new String[] { "!tests/badforin.js/testForIn2" } },
|
||||
new Object[] { "tests/badforin.js/_check_obj_bar",
|
||||
new String[] { "!tests/badforin.js/testForIn1" } },
|
||||
new Object[] { "tests/badforin.js/_check_copy_bar",
|
||||
new String[] { "!tests/badforin.js/testForIn1" } }
|
||||
};
|
||||
|
||||
@Test public void testBadForInWithHack() throws IOException, IllegalArgumentException, CancelException {
|
||||
JSCFABuilder B = Util.makeScriptCGBuilder("tests", "badforin.js");
|
||||
addHackedForInLoopSensitivity(B);
|
||||
CallGraph CG = B.makeCallGraph(B.getOptions());
|
||||
Util.dumpCG(B, CG);
|
||||
verifyGraphAssertions(CG, assertionsForBadForin);
|
||||
verifyGraphAssertions(CG, assertionsForBadForinHackPrecision);
|
||||
}
|
||||
|
||||
private void addHackedForInLoopSensitivity(JSCFABuilder builder) {
|
||||
final ContextSelector orig = builder.getContextSelector();
|
||||
builder.setContextSelector(new ContextSelector() {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, final InstanceKey[] receiver) {
|
||||
final Context origContext = orig.getCalleeTarget(caller, site, callee, receiver);
|
||||
if (callee.getDeclaringClass().getName().toString().contains("_forin_body")) {
|
||||
class ForInContext implements Context {
|
||||
private final InstanceKey obj = receiver[2];
|
||||
public ContextItem get(ContextKey name) {
|
||||
if (name.equals(ContextKey.PARAMETERS[2])) {
|
||||
return new FilteredPointerKey.SingleInstanceFilter(obj);
|
||||
} else {
|
||||
return origContext.get(name);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return obj.hashCode();
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other != null &&
|
||||
getClass().equals(other.getClass()) &&
|
||||
obj.equals(((ForInContext)other).obj);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "for in hack filter for " + obj;
|
||||
}
|
||||
};
|
||||
return new ForInContext();
|
||||
} else {
|
||||
return origContext;
|
||||
}
|
||||
}
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
if (caller.getIR().getCalls(site)[0].getNumberOfUses() > 2) {
|
||||
return IntSetUtil.make(new int[]{2}).union(orig.getRelevantParameters(caller, site));
|
||||
} else {
|
||||
return orig.getRelevantParameters(caller, site);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -136,9 +136,9 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
|
|||
"tests/simple-lexical.js/outer",
|
||||
new String[] { "tests/simple-lexical.js/outer/indirect", "tests/simple-lexical.js/outer/inner",
|
||||
"tests/simple-lexical.js/outer/inner2", "tests/simple-lexical.js/outer/inner3" } },
|
||||
new Object[] { "tests/simple-lexical.js/outer/indirect",
|
||||
new String[] { "tests/simple-lexical.js/outer/inner", "tests/simple-lexical.js/outer/inner3" } },
|
||||
new Object[] { "tests/simple-lexical.js/outer/inner2",
|
||||
new String[] { "tests/simple-lexical.js/outer/inner", "tests/simple-lexical.js/outer/inner3" } },
|
||||
new Object[] { "tests/simple-lexical.js/outer/indirect",
|
||||
new String[] { "tests/simple-lexical.js/outer/inner", "tests/simple-lexical.js/outer/inner3" } } };
|
||||
|
||||
@Test public void testSimpleLexical() throws IOException, IllegalArgumentException, CancelException {
|
||||
|
@ -277,10 +277,6 @@ public abstract class TestSimpleCallGraphShape extends TestJSCallGraphShape {
|
|||
// all we need is for it to finish building CG successfully.
|
||||
}
|
||||
|
||||
// @Test public void testCalls() throws IOException, IllegalArgumentException, CancelException {
|
||||
// CallGraph CG = Util.makeScriptCG("tests", "calls.js");
|
||||
// }
|
||||
|
||||
protected IVector<Set<Pair<CGNode, Integer>>> computeIkIdToVns(PointerAnalysis pa) {
|
||||
|
||||
// Created by reversing the points to mapping for local pointer keys.
|
||||
|
|
|
@ -29,9 +29,6 @@ import com.ibm.wala.cast.js.ssa.JavaScriptWithRegion;
|
|||
import com.ibm.wala.cast.js.types.JavaScriptTypes;
|
||||
import com.ibm.wala.classLoader.IClass;
|
||||
import com.ibm.wala.fixpoint.AbstractOperator;
|
||||
import com.ibm.wala.fixpoint.IVariable;
|
||||
import com.ibm.wala.fixpoint.IntSetVariable;
|
||||
import com.ibm.wala.fixpoint.UnaryOperator;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisCache;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
|
||||
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||
|
@ -55,13 +52,12 @@ import com.ibm.wala.shrikeBT.BinaryOpInstruction;
|
|||
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
|
||||
import com.ibm.wala.ssa.DefUse;
|
||||
import com.ibm.wala.ssa.IR;
|
||||
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
|
||||
import com.ibm.wala.ssa.SSABinaryOpInstruction;
|
||||
import com.ibm.wala.ssa.SSAUnaryOpInstruction;
|
||||
import com.ibm.wala.ssa.SymbolTable;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
import com.ibm.wala.util.intset.IntSetAction;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
import com.ibm.wala.util.intset.MutableIntSet;
|
||||
import com.ibm.wala.util.intset.MutableMapping;
|
||||
|
||||
public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraphBuilder {
|
||||
|
@ -251,13 +247,13 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
|
|||
//
|
||||
// ///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected ConstraintVisitor makeVisitor(ExplicitCallGraph.ExplicitNode node) {
|
||||
protected JSConstraintVisitor makeVisitor(CGNode node) {
|
||||
return new JSConstraintVisitor(this, node);
|
||||
}
|
||||
|
||||
public static class JSConstraintVisitor extends AstConstraintVisitor implements InstructionVisitor {
|
||||
|
||||
public JSConstraintVisitor(AstSSAPropagationCallGraphBuilder builder, ExplicitCallGraph.ExplicitNode node) {
|
||||
public JSConstraintVisitor(AstSSAPropagationCallGraphBuilder builder, CGNode node) {
|
||||
super(builder, node);
|
||||
}
|
||||
|
||||
|
@ -326,200 +322,7 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
|
|||
}
|
||||
|
||||
public void visitJavaScriptInvoke(JavaScriptInvoke instruction) {
|
||||
PointerKey F = getPointerKeyForLocal(instruction.getFunction());
|
||||
|
||||
InstanceKey[][] consts = computeInvariantParameters(instruction);
|
||||
|
||||
PointerKey uniqueCatch = null;
|
||||
if (hasUniqueCatchBlock(instruction, ir)) {
|
||||
uniqueCatch = getBuilder().getUniqueCatchKey(instruction, ir, node);
|
||||
}
|
||||
|
||||
if (contentsAreInvariant(symbolTable, du, instruction.getFunction())) {
|
||||
system.recordImplicitPointsToSet(F);
|
||||
InstanceKey[] ik = getInvariantContents(instruction.getFunction());
|
||||
for (int i = 0; i < ik.length; i++) {
|
||||
system.findOrCreateIndexForInstanceKey(ik[i]);
|
||||
CGNode n = getTargetForCall(node, instruction.getCallSite(), ik[i]);
|
||||
if (n != null) {
|
||||
processJSCall(node, instruction, n, ik[i], consts, uniqueCatch);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
system.newSideEffect(new JSDispatchOperator(instruction, node, consts, uniqueCatch), F);
|
||||
}
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// function call handling
|
||||
//
|
||||
// ///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class JSDispatchOperator extends UnaryOperator<PointsToSetVariable> {
|
||||
private final JavaScriptInvoke call;
|
||||
|
||||
private final ExplicitCallGraph.ExplicitNode node;
|
||||
|
||||
private final InstanceKey[][] constParams;
|
||||
|
||||
private final PointerKey uniqueCatch;
|
||||
|
||||
public boolean isLoadOperator() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "JSDispatch of " + call.getCallSite();
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return call.getCallSite().hashCode() * node.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof JSDispatchOperator) && ((JSDispatchOperator) o).node.equals(node)
|
||||
&& ((JSDispatchOperator) o).call.equals(call);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param call
|
||||
* @param node
|
||||
*/
|
||||
JSDispatchOperator(JavaScriptInvoke call, ExplicitCallGraph.ExplicitNode node, InstanceKey[][] constParams,
|
||||
PointerKey uniqueCatch) {
|
||||
this.call = call;
|
||||
this.node = node;
|
||||
this.constParams = constParams;
|
||||
this.uniqueCatch = uniqueCatch;
|
||||
}
|
||||
|
||||
private MutableIntSet previousReceivers = IntSetUtil.getDefaultIntSetFactory().make();
|
||||
|
||||
public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable rhs) {
|
||||
final IntSetVariable receivers = (IntSetVariable) rhs;
|
||||
if (receivers.getValue() != null) {
|
||||
receivers.getValue().foreachExcluding(previousReceivers, new IntSetAction() {
|
||||
public void act(int ptr) {
|
||||
InstanceKey iKey = system.getInstanceKey(ptr);
|
||||
CGNode target = getTargetForCall(node, call.getCallSite(), iKey);
|
||||
if (target != null) {
|
||||
processJSCall(node, call, target, iKey, constParams, uniqueCatch);
|
||||
if (!getBuilder().haveAlreadyVisited(target)) {
|
||||
getBuilder().markDiscovered(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
previousReceivers.addAll(receivers.getValue());
|
||||
}
|
||||
|
||||
return NOT_CHANGED;
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
void processJSCall(CGNode caller, JavaScriptInvoke instruction, CGNode target, InstanceKey function,
|
||||
InstanceKey constParams[][], PointerKey uniqueCatchKey) {
|
||||
caller.addTarget(instruction.getCallSite(), target);
|
||||
if (!getBuilder().haveAlreadyVisited(target)) {
|
||||
getBuilder().markDiscovered(target);
|
||||
}
|
||||
|
||||
IR sourceIR = getBuilder().getCFAContextInterpreter().getIR(caller);
|
||||
SymbolTable sourceST = sourceIR.getSymbolTable();
|
||||
|
||||
IR targetIR = getBuilder().getCFAContextInterpreter().getIR(target);
|
||||
SymbolTable targetST = targetIR.getSymbolTable();
|
||||
|
||||
int av = -1;
|
||||
for (int v = 0; v <= targetST.getMaxValueNumber(); v++) {
|
||||
String[] vns = targetIR.getLocalNames(1, v);
|
||||
for (int n = 0; vns != null && n < vns.length; n++) {
|
||||
if ("arguments".equals(vns[n])) {
|
||||
av = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int paramCount = targetST.getParameterValueNumbers().length;
|
||||
int argCount = instruction.getNumberOfParameters();
|
||||
|
||||
// pass actual arguments to formals in the normal way
|
||||
for (int i = 0; i < Math.min(paramCount, argCount); i++) {
|
||||
int fn = targetST.getConstant(i);
|
||||
PointerKey F = (i == 0) ? getBuilder().getFilteredPointerKeyForLocal(target, targetST.getParameter(i), function.getConcreteType())
|
||||
: getBuilder().getPointerKeyForLocal(target, targetST.getParameter(i));
|
||||
|
||||
if (constParams != null && constParams[i] != null) {
|
||||
for (int j = 0; j < constParams[i].length; j++) {
|
||||
system.newConstraint(F, constParams[i][j]);
|
||||
}
|
||||
|
||||
if (av != -1)
|
||||
newFieldWrite(target, av, fn, constParams[i]);
|
||||
|
||||
} else {
|
||||
PointerKey A = getBuilder().getPointerKeyForLocal(caller, instruction.getUse(i));
|
||||
system.newConstraint(F, (F instanceof FilteredPointerKey) ? getBuilder().filterOperator : assignOperator, A);
|
||||
|
||||
if (av != -1)
|
||||
newFieldWrite(target, av, fn, F);
|
||||
}
|
||||
}
|
||||
|
||||
// extra actual arguments get assigned into the ``arguments'' object
|
||||
if (paramCount < argCount) {
|
||||
if (av != -1) {
|
||||
for (int i = paramCount; i < argCount; i++) {
|
||||
int fn = targetST.getConstant(i);
|
||||
if (constParams != null && constParams[i] != null) {
|
||||
newFieldWrite(target, av, fn, constParams[i]);
|
||||
} else {
|
||||
PointerKey A = getBuilder().getPointerKeyForLocal(caller, instruction.getUse(i));
|
||||
newFieldWrite(target, av, fn, A);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extra formal parameters get null (extra args are ignored here)
|
||||
else if (argCount < paramCount) {
|
||||
int nullvn = sourceST.getNullConstant();
|
||||
DefUse sourceDU = getBuilder().getCFAContextInterpreter().getDU(caller);
|
||||
InstanceKey[] nullkeys = getInvariantContents(sourceST, sourceDU, caller, nullvn);
|
||||
for (int i = argCount; i < paramCount; i++) {
|
||||
PointerKey F = getBuilder().getPointerKeyForLocal(target, targetST.getParameter(i));
|
||||
for (int k = 0; k < nullkeys.length; k++) {
|
||||
system.newConstraint(F, nullkeys[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write `length' in argument objects
|
||||
if (av != -1) {
|
||||
int svn = targetST.getConstant(argCount);
|
||||
int lnv = targetST.getConstant("length");
|
||||
newFieldWrite(target, av, lnv, svn);
|
||||
}
|
||||
|
||||
// return values
|
||||
if (instruction.getDef(0) != -1) {
|
||||
PointerKey RF = getBuilder().getPointerKeyForReturnValue(target);
|
||||
PointerKey RA = getBuilder().getPointerKeyForLocal(caller, instruction.getDef(0));
|
||||
system.newConstraint(RA, assignOperator, RF);
|
||||
}
|
||||
|
||||
PointerKey EF = getBuilder().getPointerKeyForExceptionalReturnValue(target);
|
||||
if (SHORT_CIRCUIT_SINGLE_USES && uniqueCatchKey != null) {
|
||||
// e has exactly one use. so, represent e implicitly
|
||||
system.newConstraint(uniqueCatchKey, assignOperator, EF);
|
||||
} else {
|
||||
PointerKey EA = getBuilder().getPointerKeyForLocal(caller, instruction.getDef(1));
|
||||
system.newConstraint(EA, assignOperator, EF);
|
||||
}
|
||||
visitInvokeInternal(instruction);
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -600,7 +403,7 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
|
|||
}
|
||||
}
|
||||
|
||||
public byte evaluate(PointsToSetVariable lhs, final IVariable[] rhs) {
|
||||
public byte evaluate(PointsToSetVariable lhs, final PointsToSetVariable[] rhs) {
|
||||
boolean doDefault = false;
|
||||
byte changed = NOT_CHANGED;
|
||||
|
||||
|
@ -679,4 +482,111 @@ public class JSSSAPropagationCallGraphBuilder extends AstSSAPropagationCallGraph
|
|||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// function call handling
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void processCallingConstraints(CGNode caller, SSAAbstractInvokeInstruction instruction, CGNode target,
|
||||
InstanceKey[][] constParams, PointerKey uniqueCatchKey) {
|
||||
|
||||
IR sourceIR = getCFAContextInterpreter().getIR(caller);
|
||||
SymbolTable sourceST = sourceIR.getSymbolTable();
|
||||
|
||||
IR targetIR = getCFAContextInterpreter().getIR(target);
|
||||
SymbolTable targetST = targetIR.getSymbolTable();
|
||||
|
||||
JSConstraintVisitor targetVisitor = null;
|
||||
int av = -1;
|
||||
for (int v = 0; v <= targetST.getMaxValueNumber(); v++) {
|
||||
String[] vns = targetIR.getLocalNames(1, v);
|
||||
for (int n = 0; vns != null && n < vns.length; n++) {
|
||||
if ("arguments".equals(vns[n])) {
|
||||
av = v;
|
||||
targetVisitor = makeVisitor(target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int paramCount = targetST.getParameterValueNumbers().length;
|
||||
int argCount = instruction.getNumberOfParameters();
|
||||
|
||||
// pass actual arguments to formals in the normal way
|
||||
for (int i = 0; i < Math.min(paramCount, argCount); i++) {
|
||||
int fn = targetST.getConstant(i);
|
||||
PointerKey F = getTargetPointerKey(target, i);
|
||||
|
||||
if (constParams != null && constParams[i] != null) {
|
||||
for (int j = 0; j < constParams[i].length; j++) {
|
||||
system.newConstraint(F, constParams[i][j]);
|
||||
}
|
||||
|
||||
if (av != -1)
|
||||
targetVisitor.newFieldWrite(target, av, fn, constParams[i]);
|
||||
|
||||
} else {
|
||||
PointerKey A = getPointerKeyForLocal(caller, instruction.getUse(i));
|
||||
system.newConstraint(F, (F instanceof FilteredPointerKey) ? filterOperator : assignOperator, A);
|
||||
|
||||
if (av != -1)
|
||||
targetVisitor.newFieldWrite(target, av, fn, F);
|
||||
}
|
||||
}
|
||||
|
||||
// extra actual arguments get assigned into the ``arguments'' object
|
||||
if (paramCount < argCount) {
|
||||
if (av != -1) {
|
||||
for (int i = paramCount; i < argCount; i++) {
|
||||
int fn = targetST.getConstant(i);
|
||||
if (constParams != null && constParams[i] != null) {
|
||||
targetVisitor.newFieldWrite(target, av, fn, constParams[i]);
|
||||
} else {
|
||||
PointerKey A = getPointerKeyForLocal(caller, instruction.getUse(i));
|
||||
targetVisitor.newFieldWrite(target, av, fn, A);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extra formal parameters get null (extra args are ignored here)
|
||||
else if (argCount < paramCount) {
|
||||
int nullvn = sourceST.getNullConstant();
|
||||
DefUse sourceDU = getCFAContextInterpreter().getDU(caller);
|
||||
InstanceKey[] nullkeys = getInvariantContents(sourceST, sourceDU, caller, nullvn, this);
|
||||
for (int i = argCount; i < paramCount; i++) {
|
||||
PointerKey F = getPointerKeyForLocal(target, targetST.getParameter(i));
|
||||
for (int k = 0; k < nullkeys.length; k++) {
|
||||
system.newConstraint(F, nullkeys[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write `length' in argument objects
|
||||
if (av != -1) {
|
||||
int svn = targetST.getConstant(argCount);
|
||||
int lnv = targetST.getConstant("length");
|
||||
targetVisitor.newFieldWrite(target, av, lnv, svn);
|
||||
}
|
||||
|
||||
// return values
|
||||
if (instruction.getDef(0) != -1) {
|
||||
PointerKey RF = getPointerKeyForReturnValue(target);
|
||||
PointerKey RA = getPointerKeyForLocal(caller, instruction.getDef(0));
|
||||
system.newConstraint(RA, assignOperator, RF);
|
||||
}
|
||||
|
||||
PointerKey EF = getPointerKeyForExceptionalReturnValue(target);
|
||||
if (SHORT_CIRCUIT_SINGLE_USES && uniqueCatchKey != null) {
|
||||
// e has exactly one use. so, represent e implicitly
|
||||
system.newConstraint(uniqueCatchKey, assignOperator, EF);
|
||||
} else {
|
||||
PointerKey EA = getPointerKeyForLocal(caller, instruction.getDef(1));
|
||||
system.newConstraint(EA, assignOperator, EF);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ public class JSZeroOrOneXCFABuilder extends JSCFABuilder {
|
|||
options.setSelector(new JavaScriptConstructTargetSelector(cha, options.getMethodTargetSelector()));
|
||||
options.setSelector(new LoadFileTargetSelector(options.getMethodTargetSelector(), this));
|
||||
|
||||
ContextSelector def = new DefaultContextSelector(options);
|
||||
ContextSelector def = new DefaultContextSelector(options, cha);
|
||||
ContextSelector contextSelector = appContextSelector == null ? def : new DelegatingContextSelector(appContextSelector, def);
|
||||
contextSelector = new ScopeMappingKeysContextSelector(contextSelector);
|
||||
contextSelector = new JavaScriptConstructorContextSelector(contextSelector);
|
||||
|
|
|
@ -8,6 +8,7 @@ import com.ibm.wala.ipa.callgraph.CGNode;
|
|||
import com.ibm.wala.ipa.callgraph.Context;
|
||||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
|
||||
public class JavaScriptConstructorContextSelector implements ContextSelector {
|
||||
private final ContextSelector base;
|
||||
|
@ -16,7 +17,13 @@ public class JavaScriptConstructorContextSelector implements ContextSelector {
|
|||
this.base = base;
|
||||
}
|
||||
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return base.getRelevantParameters(caller, site);
|
||||
}
|
||||
|
||||
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (callee instanceof JavaScriptConstructor && caller.getContext() instanceof ScopeMappingContext) {
|
||||
return caller.getContext();
|
||||
} else {
|
||||
|
|
|
@ -48,4 +48,20 @@ public class JSCallSiteReference extends CallSiteReference {
|
|||
return new JSCallSiteReference(pc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDispatch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStatic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFixed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -727,7 +727,11 @@ public class JavaScriptLoader extends CAstAbstractModuleLoader {
|
|||
}
|
||||
|
||||
public TypeReference getParameterType(int i) {
|
||||
return JavaScriptTypes.Root;
|
||||
if (i == 0) {
|
||||
return getDeclaringClass().getReference();
|
||||
} else {
|
||||
return JavaScriptTypes.Root;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -122,27 +122,40 @@ public abstract class TestCallGraphShape extends WalaTestCase {
|
|||
|
||||
Assert.assertTrue("cannot find " + assertionData[i][0], srcs.hasNext());
|
||||
|
||||
while (srcs.hasNext()) {
|
||||
boolean checkAbsence = false;
|
||||
String targetName = ((String[]) assertionData[i][1])[j];
|
||||
if (targetName.startsWith("!")) {
|
||||
checkAbsence = true;
|
||||
targetName = targetName.substring(1);
|
||||
}
|
||||
|
||||
check_edges: while (srcs.hasNext()) {
|
||||
CGNode src = (CGNode) srcs.next();
|
||||
for (Iterator sites = src.iterateCallSites(); sites.hasNext();) {
|
||||
CallSiteReference sr = (CallSiteReference) sites.next();
|
||||
|
||||
Iterator dsts = getNodes(CG, ((String[]) assertionData[i][1])[j]).iterator();
|
||||
Assert.assertTrue("cannot find " + ((String[]) assertionData[i][1])[j], dsts.hasNext());
|
||||
|
||||
Iterator dsts = getNodes(CG, targetName).iterator();
|
||||
Assert.assertTrue("cannot find " + targetName, dsts.hasNext());
|
||||
|
||||
while (dsts.hasNext()) {
|
||||
CGNode dst = (CGNode) dsts.next();
|
||||
for (Iterator tos = CG.getPossibleTargets(src, sr).iterator(); tos.hasNext();) {
|
||||
if (tos.next().equals(dst)) {
|
||||
System.err.println(("found expected " + src + " --> " + dst + " at " + sr));
|
||||
continue check_target;
|
||||
if (checkAbsence) {
|
||||
System.err.println(("found unexpected " + src + " --> " + dst + " at " + sr));
|
||||
Assert.assertTrue("found edge " + assertionData[i][0] + " ---> " + targetName, false);
|
||||
} else {
|
||||
System.err.println(("found expected " + src + " --> " + dst + " at " + sr));
|
||||
continue check_target;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertTrue("cannot find edge " + assertionData[i][0] + " ---> " + ((String[]) assertionData[i][1])[j], false);
|
||||
System.err.println("cannot find edge " + assertionData[i][0] + " ---> " + targetName);
|
||||
Assert.assertTrue("cannot find edge " + assertionData[i][0] + " ---> " + targetName, checkAbsence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,6 +151,10 @@ public class AstCallGraph extends ExplicitCallGraph {
|
|||
cachedDU = new DefUse(ir);
|
||||
}
|
||||
|
||||
public void clearMutatedCache(CallSiteReference cs) {
|
||||
targets.remove(cs.getProgramCounter());
|
||||
}
|
||||
|
||||
public IR getLexicallyMutatedIR() {
|
||||
if (lexicalScopingChanges) {
|
||||
return cachedIR;
|
||||
|
|
|
@ -39,7 +39,6 @@ import com.ibm.wala.classLoader.CallSiteReference;
|
|||
import com.ibm.wala.classLoader.IClass;
|
||||
import com.ibm.wala.classLoader.IMethod;
|
||||
import com.ibm.wala.fixpoint.AbstractOperator;
|
||||
import com.ibm.wala.fixpoint.IVariable;
|
||||
import com.ibm.wala.fixpoint.IntSetVariable;
|
||||
import com.ibm.wala.fixpoint.UnaryOperator;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisCache;
|
||||
|
@ -64,6 +63,8 @@ import com.ibm.wala.ipa.callgraph.propagation.cfa.DelegatingSSAContextInterprete
|
|||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||
import com.ibm.wala.ssa.DefUse;
|
||||
import com.ibm.wala.ssa.IR;
|
||||
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
|
||||
import com.ibm.wala.ssa.SSAInstruction;
|
||||
import com.ibm.wala.ssa.SSAPutInstruction;
|
||||
import com.ibm.wala.ssa.SymbolTable;
|
||||
import com.ibm.wala.util.collections.EmptyIterator;
|
||||
|
@ -263,7 +264,7 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
|
|||
|
||||
private final CallGraph cg;
|
||||
|
||||
public AstConstraintVisitor(AstSSAPropagationCallGraphBuilder builder, ExplicitCallGraph.ExplicitNode node) {
|
||||
public AstConstraintVisitor(AstSSAPropagationCallGraphBuilder builder, CGNode node) {
|
||||
super(builder, node);
|
||||
this.cg = builder.callGraph;
|
||||
}
|
||||
|
@ -502,6 +503,30 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
|
|||
|
||||
}
|
||||
|
||||
protected void visitInvokeInternal(final SSAAbstractInvokeInstruction instruction) {
|
||||
super.visitInvokeInternal(instruction);
|
||||
if (instruction instanceof AbstractLexicalInvoke) {
|
||||
AbstractLexicalInvoke I = (AbstractLexicalInvoke) instruction;
|
||||
for(int wi = 0; wi < I.getNumberOfDefs(); wi++) {
|
||||
if (I.isLexicalDef(wi)) {
|
||||
Access w = I.getLexicalDef(wi);
|
||||
for(int ri = 0; ri < I.getNumberOfUses(); ri++) {
|
||||
if (I.isLexicalUse(ri)) {
|
||||
Access r = I.getLexicalUse(ri);
|
||||
if (w.variableName.equals(r.variableName)) {
|
||||
if (w.variableDefiner==null? r.variableDefiner==null: w.variableDefiner.equals(r.variableDefiner)) {
|
||||
PointerKey rk = getBuilder().getPointerKeyForLocal(node, r.valueNumber);
|
||||
PointerKey wk = getBuilder().getPointerKeyForLocal(node, w.valueNumber);
|
||||
system.newConstraint(wk, assignOperator, rk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// lexical scoping handling
|
||||
|
@ -835,17 +860,48 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
|
|||
if (values[i] == -1)
|
||||
return null;
|
||||
|
||||
// find calls that may be altered, and clear their caches
|
||||
DefUse newDU = getAnalysisCache().getSSACache().findOrCreateDU(ir, n.getContext());
|
||||
Iterator<SSAInstruction> insts = newDU.getUses(values[i]);
|
||||
while(insts.hasNext()) {
|
||||
SSAInstruction inst = insts.next();
|
||||
if (inst instanceof SSAAbstractInvokeInstruction) {
|
||||
System.err.println("clearing for " + inst);
|
||||
CallSiteReference cs = ((SSAAbstractInvokeInstruction)inst).getCallSite();
|
||||
((AstCallGraph.AstCGNode)n).clearMutatedCache(cs);
|
||||
}
|
||||
}
|
||||
|
||||
// if values[i] was altered by copy propagation, we must undo
|
||||
// that to ensure we do not bash the wrong value number in the
|
||||
// the next steps.
|
||||
SSAConversion.undoCopyPropagation(ir, pc, -i - 1);
|
||||
|
||||
// possibly new instruction due to renames, so get it again
|
||||
I = (AbstractLexicalInvoke) ir.getInstructions()[pc];
|
||||
|
||||
// we assume that the callee might not necessarily write,
|
||||
// so the call becomes like a phi node. hence it needs a
|
||||
// read of the old value
|
||||
ensureRead: {
|
||||
for(int l = 0; l < I.getNumberOfUses(); l++) {
|
||||
if (I.isLexicalUse(l)) {
|
||||
Access r = I.getLexicalUse(l);
|
||||
if (name.equals(r.variableName)) {
|
||||
if (definer==null? r.variableDefiner == null: definer.equals(r.variableDefiner)) {
|
||||
break ensureRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
I.addLexicalUse(new Access(name, definer, values[i]));
|
||||
}
|
||||
|
||||
// add new lexical definition
|
||||
I.addLexicalDef(new Access(name, definer, values[i]));
|
||||
|
||||
if (SSAConversion.DEBUG_UNDO)
|
||||
System.err.println(("new def of " + values[i] + " at inst " + pc + ": " + I));
|
||||
System.err.println("new def of " + values[i] + " at inst " + pc + ": " + I);
|
||||
|
||||
// new def has broken SSA form for values[i], so fix for that
|
||||
// value
|
||||
|
@ -853,14 +909,13 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
|
|||
vs.add(values[i]);
|
||||
SSAConversion.convert(AstM, ir, getOptions().getSSAOptions());
|
||||
|
||||
// now redo analysis
|
||||
// force analysis to be redone
|
||||
// TODO: only values[i] uses need to be re-done.
|
||||
ir.lexicalInfo().handleAlteration();
|
||||
((AstCallGraph.AstCGNode)n).setLexicallyMutatedIR(ir);
|
||||
getAnalysisCache().getSSACache().invalidateDU(M, n.getContext());
|
||||
// addConstraintsFromChangedNode(n);
|
||||
getBuilder().markChanged(n);
|
||||
|
||||
|
||||
// get SSA-renamed def from call site instruction
|
||||
return getLocalWriteKey(n, callSite, name, definer, definingNode);
|
||||
}
|
||||
|
@ -1070,7 +1125,7 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
|
|||
|
||||
} else {
|
||||
system.newSideEffect(new AbstractOperator<PointsToSetVariable>() {
|
||||
public byte evaluate(PointsToSetVariable lhs, final IVariable[] rhs) {
|
||||
public byte evaluate(PointsToSetVariable lhs, final PointsToSetVariable[] rhs) {
|
||||
final IntSetVariable receivers = (IntSetVariable) rhs[0];
|
||||
final IntSetVariable fields = (IntSetVariable) rhs[1];
|
||||
if (receivers.getValue() != null && fields.getValue() != null) {
|
||||
|
@ -1124,7 +1179,7 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
|
|||
}
|
||||
}
|
||||
|
||||
protected void newFieldWrite(CGNode opNode, int objVn, int fieldsVn, int rhsVn) {
|
||||
public void newFieldWrite(CGNode opNode, int objVn, int fieldsVn, int rhsVn) {
|
||||
IR ir = getBuilder().getCFAContextInterpreter().getIR(opNode);
|
||||
SymbolTable symtab = ir.getSymbolTable();
|
||||
DefUse du = getBuilder().getCFAContextInterpreter().getDU(opNode);
|
||||
|
@ -1137,7 +1192,7 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
|
|||
}
|
||||
}
|
||||
|
||||
protected void newFieldWrite(CGNode opNode, int objVn, int fieldsVn, final InstanceKey[] rhsFixedValues) {
|
||||
public void newFieldWrite(CGNode opNode, int objVn, int fieldsVn, final InstanceKey[] rhsFixedValues) {
|
||||
try {
|
||||
|
||||
newFieldOperation(opNode, objVn, fieldsVn, false, new ReflectedFieldAction() {
|
||||
|
@ -1165,7 +1220,7 @@ public abstract class AstSSAPropagationCallGraphBuilder extends SSAPropagationCa
|
|||
}
|
||||
}
|
||||
|
||||
protected void newFieldWrite(CGNode opNode, int objVn, int fieldsVn, final PointerKey rhs) {
|
||||
public void newFieldWrite(CGNode opNode, int objVn, int fieldsVn, final PointerKey rhs) {
|
||||
newFieldOperation(opNode, objVn, fieldsVn, false, new ReflectedFieldAction() {
|
||||
public void dump(AbstractFieldPointerKey fieldKey, boolean constObj, boolean constProp) {
|
||||
System.err.println(("write " + rhs + " to " + fieldKey + " " + constObj + ", " + constProp));
|
||||
|
|
|
@ -18,6 +18,7 @@ import com.ibm.wala.ipa.callgraph.CGNode;
|
|||
import com.ibm.wala.ipa.callgraph.Context;
|
||||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.strings.Atom;
|
||||
|
||||
/**
|
||||
|
@ -52,7 +53,11 @@ public class CrossLanguageContextSelector implements ContextSelector {
|
|||
return (ContextSelector)languageSelectors.get(getLanguage(site));
|
||||
}
|
||||
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
return getSelector(site).getCalleeTarget(caller, site, callee, receiver);
|
||||
}
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return getSelector(site).getRelevantParameters(caller, site);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import com.ibm.wala.types.TypeName;
|
|||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
import com.ibm.wala.util.debug.Assertions;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.strings.Atom;
|
||||
|
||||
public class MiscellaneousHacksContextSelector implements ContextSelector {
|
||||
|
@ -107,7 +108,7 @@ public class MiscellaneousHacksContextSelector implements ContextSelector {
|
|||
System.err.println(("hacking context selector for methods " + methodsToSpecialize));
|
||||
}
|
||||
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (methodsToSpecialize.contains(site.getDeclaredTarget()) || methodsToSpecialize.contains(callee.getReference())) {
|
||||
return specialPolicy.getCalleeTarget(caller, site, callee, receiver);
|
||||
} else {
|
||||
|
@ -115,15 +116,8 @@ public class MiscellaneousHacksContextSelector implements ContextSelector {
|
|||
}
|
||||
}
|
||||
|
||||
public int getBoundOnNumberOfTargets(CGNode caller, CallSiteReference reference, IMethod targetMethod) {
|
||||
return -1;
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return specialPolicy.getRelevantParameters(caller, site).union(basePolicy.getRelevantParameters(caller, site));
|
||||
}
|
||||
|
||||
public boolean mayUnderstand(CGNode caller, CallSiteReference site, IMethod targetMethod, InstanceKey instance) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean allSitesDispatchIdentically(CGNode node, CallSiteReference site) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ public abstract class ReflectedFieldPointerKey extends AbstractFieldPointerKey {
|
|||
};
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof ReflectedFieldPointerKey) {
|
||||
ReflectedFieldPointerKey other = (ReflectedFieldPointerKey) obj;
|
||||
return
|
||||
|
@ -37,11 +40,11 @@ public abstract class ReflectedFieldPointerKey extends AbstractFieldPointerKey {
|
|||
}
|
||||
|
||||
public int hashCode() {
|
||||
return getFieldIdentifier().hashCode();
|
||||
return getFieldIdentifier().hashCode() ^ getInstanceKey().hashCode();
|
||||
}
|
||||
|
||||
public String toString() { return "field:" + getFieldIdentifier(); }
|
||||
|
||||
|
||||
public static ReflectedFieldPointerKey literal(final String lit, InstanceKey instance) {
|
||||
return new ReflectedFieldPointerKey(instance) {
|
||||
public Object getFieldIdentifier() {
|
||||
|
|
|
@ -21,12 +21,13 @@ import com.ibm.wala.classLoader.IClass;
|
|||
import com.ibm.wala.classLoader.NewSiteReference;
|
||||
import com.ibm.wala.classLoader.ProgramCounter;
|
||||
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||
import com.ibm.wala.ipa.callgraph.CallGraph;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKeyFactory;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
import com.ibm.wala.util.graph.impl.GraphInverter;
|
||||
import com.ibm.wala.util.graph.traverse.DFS;
|
||||
|
||||
/**
|
||||
* An {@link InstanceKeyFactory} that returns {@link ScopeMappingInstanceKey}s as necessary to handle interprocedural lexical
|
||||
|
@ -60,6 +61,27 @@ abstract public class ScopeMappingInstanceKeys implements InstanceKeyFactory {
|
|||
private static final long serialVersionUID = 3645910671551712906L;
|
||||
|
||||
private void scan(int level, int toDo, LexicalParent parents[], CGNode node, Set<CGNode> parentNodes) {
|
||||
Iterator<CGNode> preds = DFS.iterateDiscoverTime(GraphInverter.invert(builder.getCallGraph()), node);
|
||||
while (preds.hasNext()) {
|
||||
CGNode pred = preds.next();
|
||||
for (int i = 0; i < parents.length; i++) {
|
||||
if (parents[i] != null) {
|
||||
if (pred.getMethod() == parents[i].getMethod()) {
|
||||
if (containsKey(parents[i].getName()))
|
||||
assert get(parents[i].getName()) == pred;
|
||||
else {
|
||||
toDo--;
|
||||
put(parents[i].getName(), pred);
|
||||
if (AstTranslator.DEBUG_LEXICAL)
|
||||
System.err.println((level + ": Adding lexical parent " + parents[i].getName() + " for " + base + " at " + creator
|
||||
+ "(toDo is now " + toDo + ")"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (toDo > 0) {
|
||||
int restoreIndex = -1;
|
||||
LexicalParent restoreParent = null;
|
||||
|
@ -109,6 +131,7 @@ abstract public class ScopeMappingInstanceKeys implements InstanceKeyFactory {
|
|||
parents[restoreIndex] = restoreParent;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private ScopeMap() {
|
||||
|
|
|
@ -9,6 +9,8 @@ import com.ibm.wala.ipa.callgraph.ContextItem;
|
|||
import com.ibm.wala.ipa.callgraph.ContextKey;
|
||||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
|
||||
public class ScopeMappingKeysContextSelector implements ContextSelector {
|
||||
|
||||
|
@ -59,12 +61,20 @@ public class ScopeMappingKeysContextSelector implements ContextSelector {
|
|||
this.base = base;
|
||||
}
|
||||
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
Context bc = base.getCalleeTarget(caller, site, callee, receiver);
|
||||
if (receiver instanceof ScopeMappingInstanceKey) {
|
||||
return new ScopeMappingContext(bc, (ScopeMappingInstanceKey) receiver);
|
||||
if (receiver[0] instanceof ScopeMappingInstanceKey) {
|
||||
return new ScopeMappingContext(bc, (ScopeMappingInstanceKey) receiver[0]);
|
||||
} else {
|
||||
return bc;
|
||||
}
|
||||
}
|
||||
|
||||
private static final IntSet thisParameter = IntSetUtil.make(new int[]{0});
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return thisParameter;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ public abstract class AbstractLexicalInvoke extends MultiReturnValueInvokeInstru
|
|||
}
|
||||
|
||||
public boolean isLexicalUse(int use) {
|
||||
return use >= super.getNumberOfUses();
|
||||
return use >= getNumberOfParameters();
|
||||
}
|
||||
|
||||
public void addLexicalUse(Access use) {
|
||||
|
|
|
@ -135,6 +135,7 @@ public class CFGTest extends WalaTestCase {
|
|||
IMethod m = cha.resolveMethod(mr);
|
||||
AnalysisCache cache = new AnalysisCache();
|
||||
IR ir = cache.getIR(m);
|
||||
System.out.println(ir);
|
||||
SSACFG controlFlowGraph = ir.getControlFlowGraph();
|
||||
Assert.assertEquals(1, controlFlowGraph.getSuccNodeCount(controlFlowGraph.getBlockForInstruction(21)));
|
||||
}
|
||||
|
@ -164,4 +165,14 @@ public class CFGTest extends WalaTestCase {
|
|||
SSACFG controlFlowGraph = ir.getControlFlowGraph();
|
||||
Assert.assertEquals(1, controlFlowGraph.getSuccNodeCount(controlFlowGraph.getBlockForInstruction(33)));
|
||||
}
|
||||
|
||||
public static void testCFG(SSACFG cfg, int[][] assertions) {
|
||||
for(int i = 0; i < assertions.length; i++) {
|
||||
SSACFG.BasicBlock bb= cfg.getNode(i);
|
||||
Assert.assertEquals("basic block " + i, assertions[i].length, cfg.getSuccNodeCount(bb));
|
||||
for(int j = 0; j < assertions[i].length; j++) {
|
||||
Assert.assertTrue(cfg.hasEdge(bb, cfg.getNode(assertions[i][j])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ public class ClassFactoryContextInterpreter implements SSAContextInterpreter {
|
|||
if (!(node.getContext() instanceof JavaTypeContext)) {
|
||||
return false;
|
||||
}
|
||||
return ClassFactoryContextSelector.isClassFactory(node.getMethod());
|
||||
return ClassFactoryContextSelector.isClassFactory(node.getMethod().getReference());
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -26,6 +26,9 @@ import com.ibm.wala.types.ClassLoaderReference;
|
|||
import com.ibm.wala.types.Descriptor;
|
||||
import com.ibm.wala.types.MethodReference;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.intset.EmptyIntSet;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
import com.ibm.wala.util.strings.Atom;
|
||||
import com.ibm.wala.util.strings.StringStuff;
|
||||
|
||||
|
@ -55,11 +58,11 @@ class ClassFactoryContextSelector implements ContextSelector {
|
|||
public ClassFactoryContextSelector() {
|
||||
}
|
||||
|
||||
public static boolean isClassFactory(IMethod m) {
|
||||
if (m.getReference().equals(FOR_NAME_REF)) {
|
||||
public static boolean isClassFactory(MethodReference m) {
|
||||
if (m.equals(FOR_NAME_REF)) {
|
||||
return true;
|
||||
}
|
||||
if (m.getReference().equals(LOAD_CLASS_REF)) {
|
||||
if (m.equals(LOAD_CLASS_REF)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -79,10 +82,10 @@ class ClassFactoryContextSelector implements ContextSelector {
|
|||
*
|
||||
* @see com.ibm.wala.ipa.callgraph.ContextSelector#getCalleeTarget(com.ibm.wala.ipa.callgraph.CGNode,
|
||||
* com.ibm.wala.classLoader.CallSiteReference, com.ibm.wala.classLoader.IMethod,
|
||||
* com.ibm.wala.ipa.callgraph.propagation.InstanceKey)
|
||||
* InstanceKey[])
|
||||
*/
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
if (isClassFactory(callee)) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (isClassFactory(callee.getReference())) {
|
||||
IR ir = caller.getIR();
|
||||
SymbolTable symbolTable = ir.getSymbolTable();
|
||||
SSAAbstractInvokeInstruction[] invokeInstructions = caller.getIR().getCalls(site);
|
||||
|
@ -103,22 +106,24 @@ class ClassFactoryContextSelector implements ContextSelector {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This object may understand a dispatch to Class.forName(s) when s is a string constant.
|
||||
*/
|
||||
public boolean mayUnderstand(CGNode caller, CallSiteReference site, IMethod targetMethod, InstanceKey instance) {
|
||||
if (isClassFactory(targetMethod)) {
|
||||
IR ir = caller.getIR();
|
||||
SymbolTable symbolTable = ir.getSymbolTable();
|
||||
private static final IntSet thisParameter = IntSetUtil.make(new int[]{0});
|
||||
|
||||
private static final IntSet firstParameter = IntSetUtil.make(new int[]{1});
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
if (isClassFactory(site.getDeclaredTarget())) {
|
||||
SSAAbstractInvokeInstruction[] invokeInstructions = caller.getIR().getCalls(site);
|
||||
if (invokeInstructions.length != 1) {
|
||||
return false;
|
||||
}
|
||||
int use = getUseOfStringParameter(invokeInstructions[0]);
|
||||
if (symbolTable.isStringConstant(use)) {
|
||||
return true;
|
||||
if (invokeInstructions[0].isStatic()) {
|
||||
return thisParameter;
|
||||
} else {
|
||||
return firstParameter;
|
||||
}
|
||||
} else {
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
} else {
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,9 @@ import com.ibm.wala.ipa.callgraph.Context;
|
|||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.util.intset.EmptyIntSet;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
|
||||
/**
|
||||
* A {@link ContextSelector} to intercept calls to Class.newInstance()
|
||||
|
@ -33,9 +36,9 @@ class ClassNewInstanceContextSelector implements ContextSelector {
|
|||
* representing the type of the IClass. (This corresponds to the case where we know the exact type that will be
|
||||
* allocated by the <code>Class.newInstance()</code> call.) Otherwise, return <code>null</code>.
|
||||
*/
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
if (callee.getReference().equals(ClassNewInstanceContextInterpreter.CLASS_NEW_INSTANCE_REF) && isTypeConstant(receiver)) {
|
||||
IClass c = (IClass) ((ConstantKey) receiver).getValue();
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (callee.getReference().equals(ClassNewInstanceContextInterpreter.CLASS_NEW_INSTANCE_REF) && isTypeConstant(receiver[0])) {
|
||||
IClass c = (IClass) ((ConstantKey) receiver[0]).getValue();
|
||||
if (!c.isAbstract() && !c.isInterface()) {
|
||||
return new JavaTypeContext(new PointType(c));
|
||||
}
|
||||
|
@ -52,4 +55,14 @@ class ClassNewInstanceContextSelector implements ContextSelector {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final IntSet thisParameter = IntSetUtil.make(new int[]{0});
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
if (site.isDispatch() || site.getDeclaredTarget().getNumberOfParameters() > 0) {
|
||||
return thisParameter;
|
||||
} else {
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,8 @@ import com.ibm.wala.ipa.callgraph.ContextSelector;
|
|||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.cfa.CallString;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.cfa.CallStringContext;
|
||||
import com.ibm.wala.util.intset.EmptyIntSet;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
|
||||
/**
|
||||
* For synthetic methods marked as "Factories", we analyze in a context defined by the caller.
|
||||
|
@ -31,7 +33,7 @@ class FactoryContextSelector implements ContextSelector {
|
|||
/*
|
||||
* @see com.ibm.wala.ipa.callgraph.ContextSelector#getCalleeTarget(com.ibm.wala.ipa.callgraph.CGNode, com.ibm.wala.classLoader.CallSiteReference, com.ibm.wala.classLoader.IMethod)
|
||||
*/
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (callee == null) {
|
||||
throw new IllegalArgumentException("callee is null");
|
||||
}
|
||||
|
@ -44,4 +46,8 @@ class FactoryContextSelector implements ContextSelector {
|
|||
return null;
|
||||
}
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ import com.ibm.wala.ipa.callgraph.ContextSelector;
|
|||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.types.MethodReference;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.intset.EmptyIntSet;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
|
||||
/**
|
||||
* A {@link ContextSelector} to intercept calls to Object.getClass()
|
||||
|
@ -36,10 +39,21 @@ class GetClassContextSelector implements ContextSelector {
|
|||
* com.ibm.wala.classLoader.CallSiteReference, com.ibm.wala.classLoader.IMethod,
|
||||
* com.ibm.wala.ipa.callgraph.propagation.InstanceKey)
|
||||
*/
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (callee.getReference().equals(GET_CLASS)) {
|
||||
return new JavaTypeContext(new PointType(receiver.getConcreteType()));
|
||||
return new JavaTypeContext(new PointType(receiver[0].getConcreteType()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final IntSet thisParameter = IntSetUtil.make(new int[]{0});
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
if (site.isDispatch() || site.getDeclaredTarget().getNumberOfParameters() > 0) {
|
||||
return thisParameter;
|
||||
} else {
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -24,6 +24,9 @@ import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
|||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||
import com.ibm.wala.types.MethodReference;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
import com.ibm.wala.util.intset.EmptyIntSet;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
|
||||
/**
|
||||
* A {@link ContextSelector} to intercept calls to certain methods on java.lang.Class when the receiver is a type constant
|
||||
|
@ -45,9 +48,9 @@ class JavaLangClassContextSelector implements ContextSelector {
|
|||
* If the {@link CallSiteReference} invokes a method we understand and c is a type constant, return a {@link JavaTypeContext}
|
||||
* representing the type named by s, if we can resolve it in the {@link IClassHierarchy}.
|
||||
*/
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
if (mayUnderstand(caller, site, callee, receiver)) {
|
||||
return new JavaTypeContext(new PointType(getTypeConstant(receiver)));
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (receiver != null && receiver.length > 0 && mayUnderstand(caller, site, callee, receiver[0])) {
|
||||
return new JavaTypeContext(new PointType(getTypeConstant(receiver[0])));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -81,4 +84,14 @@ class JavaLangClassContextSelector implements ContextSelector {
|
|||
private boolean mayUnderstand(CGNode caller, CallSiteReference site, IMethod targetMethod, InstanceKey instance) {
|
||||
return UNDERSTOOD_METHOD_REFS.contains(targetMethod.getReference()) && getTypeConstant(instance) != null;
|
||||
}
|
||||
|
||||
private static final IntSet thisParameter = IntSetUtil.make(new int[]{0});
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
if (site.isDispatch() || site.getDeclaredTarget().getNumberOfParameters() > 0) {
|
||||
return thisParameter;
|
||||
} else {
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ public class JavaTypeContext implements Context {
|
|||
public ContextItem get(ContextKey name) {
|
||||
if (name == ContextKey.RECEIVER) {
|
||||
return type;
|
||||
} else if (name == ContextKey.FILTER) {
|
||||
} else if (name == ContextKey.PARAMETERS[0]) {
|
||||
if (type instanceof PointType) {
|
||||
IClass cls = ((PointType) type).getIClass();
|
||||
return new FilteredPointerKey.SingleClassFilter(cls);
|
||||
|
@ -44,7 +44,6 @@ public class JavaTypeContext implements Context {
|
|||
return null;
|
||||
}
|
||||
} else {
|
||||
Assertions.UNREACHABLE();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ import com.ibm.wala.ipa.callgraph.Context;
|
|||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.impl.DelegatingContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.util.intset.EmptyIntSet;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
|
||||
/**
|
||||
* A {@link ContextSelector} to handle default reflection logic.
|
||||
|
@ -32,9 +34,12 @@ public class ReflectionContextSelector {
|
|||
|
||||
// start with a dummy
|
||||
ContextSelector result = new ContextSelector() {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
return null;
|
||||
}
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
};
|
||||
if (options.getReflectionOptions().getNumFlowToCastIterations() > 0) {
|
||||
result = new DelegatingContextSelector(new FactoryContextSelector(), result);
|
||||
|
|
|
@ -24,6 +24,9 @@ import com.ibm.wala.ssa.SSAInstruction;
|
|||
import com.ibm.wala.ssa.SSANewInstruction;
|
||||
import com.ibm.wala.ssa.SymbolTable;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.intset.EmptyIntSet;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
|
||||
/**
|
||||
* A {@link ContextSelector} to intercept calls to reflective method invocations such as Constructor.newInstance and Method.invoke
|
||||
|
@ -44,20 +47,20 @@ class ReflectiveInvocationSelector implements ContextSelector {
|
|||
* <li>Otherwise, return a new {@link ReceiverInstanceContext} for <code>receiver</code>.
|
||||
* </ol>
|
||||
*/
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
if (!mayUnderstand(caller, site, callee, receiver)) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (receiver == null || receiver.length == 0 || !mayUnderstand(caller, site, callee, receiver[0])) {
|
||||
return null;
|
||||
}
|
||||
IR ir = caller.getIR();
|
||||
SSAAbstractInvokeInstruction[] invokeInstructions = ir.getCalls(site);
|
||||
if (invokeInstructions.length != 1) {
|
||||
return new ReceiverInstanceContext(receiver);
|
||||
return new ReceiverInstanceContext(receiver[0]);
|
||||
}
|
||||
SymbolTable st = ir.getSymbolTable();
|
||||
ConstantKey receiverConstantKey = (ConstantKey) receiver;
|
||||
ConstantKey receiverConstantKey = (ConstantKey) receiver[0];
|
||||
IMethod m = (IMethod) receiverConstantKey.getValue();
|
||||
boolean isStatic = m.isStatic();
|
||||
boolean isConstructor = isConstructorConstant(receiver);
|
||||
boolean isConstructor = isConstructorConstant(receiver[0]);
|
||||
|
||||
// If the method being invoked through reflection is not a constructor and is definitely static, then
|
||||
// we should not create a callee target for any method that is not static
|
||||
|
@ -79,7 +82,7 @@ class ReflectiveInvocationSelector implements ContextSelector {
|
|||
int paramUse = invokeInstructions[0].getUse(paramIndex);
|
||||
SSAInstruction instr = caller.getDU().getDef(paramUse);
|
||||
if (!(instr instanceof SSANewInstruction)) {
|
||||
return new ReceiverInstanceContext(receiver);
|
||||
return new ReceiverInstanceContext(receiver[0]);
|
||||
}
|
||||
SSANewInstruction newInstr = (SSANewInstruction) instr;
|
||||
if (!newInstr.getConcreteType().isArrayType()) {
|
||||
|
@ -89,12 +92,12 @@ class ReflectiveInvocationSelector implements ContextSelector {
|
|||
try {
|
||||
int arrayLength = st.getIntValue(vn);
|
||||
if (arrayLength == numberOfParams) {
|
||||
return new ReceiverInstanceContext(receiver);
|
||||
return new ReceiverInstanceContext(receiver[0]);
|
||||
} else {
|
||||
return new IllegalArgumentExceptionContext();
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
return new ReceiverInstanceContext(receiver);
|
||||
return new ReceiverInstanceContext(receiver[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,4 +123,14 @@ class ReflectiveInvocationSelector implements ContextSelector {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final IntSet thisParameter = IntSetUtil.make(new int[]{0});
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
if (site.isDispatch() || site.getDeclaredTarget().getNumberOfParameters() > 0) {
|
||||
return thisParameter;
|
||||
} else {
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -233,6 +233,11 @@ public abstract class AbstractIntStackMachine implements FixedPointConstants {
|
|||
super.initializeVariables();
|
||||
AbstractIntStackMachine.this.initializeVariables();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MachineState[] makeStmtRHS(int size) {
|
||||
return new MachineState[size];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -276,7 +281,7 @@ public abstract class AbstractIntStackMachine implements FixedPointConstants {
|
|||
}
|
||||
|
||||
@Override
|
||||
public byte evaluate(MachineState lhs, IVariable[] rhs) {
|
||||
public byte evaluate(MachineState lhs, MachineState[] rhs) {
|
||||
BasicBlock bb = lhs.getBasicBlock();
|
||||
if (!bb.isCatchBlock()) {
|
||||
return meet(lhs, rhs, bb, meeter) ? CHANGED : NOT_CHANGED;
|
||||
|
|
|
@ -296,7 +296,7 @@ public class TypeInference extends SSAInference<TypeVariable> implements FixedPo
|
|||
* TODO: work on efficiency shortcuts for this.
|
||||
*/
|
||||
@Override
|
||||
public byte evaluate(TypeVariable lhs, IVariable[] rhs) {
|
||||
public byte evaluate(TypeVariable lhs, TypeVariable[] rhs) {
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.print("PhiOperator.meet " + lhs + " ");
|
||||
|
@ -347,7 +347,7 @@ public class TypeInference extends SSAInference<TypeVariable> implements FixedPo
|
|||
* TODO: work on efficiency shortcuts for this.
|
||||
*/
|
||||
@Override
|
||||
public byte evaluate(TypeVariable lhs, IVariable[] rhsOperands) {
|
||||
public byte evaluate(TypeVariable lhs, TypeVariable[] rhsOperands) {
|
||||
TypeAbstraction lhsType = lhs.getType();
|
||||
|
||||
TypeVariable rhs = (TypeVariable) rhsOperands[0];
|
||||
|
@ -383,7 +383,7 @@ public class TypeInference extends SSAInference<TypeVariable> implements FixedPo
|
|||
}
|
||||
|
||||
@Override
|
||||
public byte evaluate(TypeVariable lhs, IVariable[] rhs) {
|
||||
public byte evaluate(TypeVariable lhs, TypeVariable[] rhs) {
|
||||
TypeAbstraction lhsType = lhs.getType();
|
||||
TypeAbstraction meet = TypeAbstraction.TOP;
|
||||
for (int i = 0; i < rhs.length; i++) {
|
||||
|
@ -429,7 +429,7 @@ public class TypeInference extends SSAInference<TypeVariable> implements FixedPo
|
|||
}
|
||||
|
||||
@Override
|
||||
public byte evaluate(TypeVariable lhs, IVariable[] rhs) {
|
||||
public byte evaluate(TypeVariable lhs, TypeVariable[] rhs) {
|
||||
TypeAbstraction arrayType = getType(load.getArrayRef());
|
||||
if (arrayType == null || arrayType.equals(TypeAbstraction.TOP)) {
|
||||
return NOT_CHANGED;
|
||||
|
@ -786,4 +786,9 @@ public class TypeInference extends SSAInference<TypeVariable> implements FixedPo
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeVariable[] makeStmtRHS(int size) {
|
||||
return new TypeVariable[size];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,7 +112,7 @@ public abstract class SSAInference<T extends IVariable> extends DefaultFixedPoin
|
|||
newStatement(def, (NullaryOperator<T>) op, false, false);
|
||||
} else {
|
||||
int n = s.getNumberOfUses();
|
||||
IVariable[] uses = new IVariable[n];
|
||||
T[] uses = makeStmtRHS(n);
|
||||
for (int j = 0; j < n; j++) {
|
||||
if (s.getUse(j) > -1) {
|
||||
uses[j] = getVariable(s.getUse(j));
|
||||
|
|
|
@ -81,7 +81,7 @@ class ThisFilteringHeapModel implements HeapModel {
|
|||
}
|
||||
|
||||
private FilteredPointerKey.TypeFilter getFilter(CGNode target) {
|
||||
FilteredPointerKey.TypeFilter filter = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.FILTER);
|
||||
FilteredPointerKey.TypeFilter filter = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.PARAMETERS[0]);
|
||||
|
||||
if (filter != null) {
|
||||
return filter;
|
||||
|
|
|
@ -36,14 +36,35 @@ public interface ContextKey {
|
|||
public final static ContextKey RECEIVER = new ContextKey() {
|
||||
};
|
||||
|
||||
/**
|
||||
* A property of contexts that might be generally useful: an identifier for the type filters applied to the receiver object ...
|
||||
* used for filtering propagation across dynamic dispatched
|
||||
*
|
||||
* Implementations (ContextItems) for FILTER are to be instances of FilteredContextKey.TypeFilter
|
||||
*
|
||||
*/
|
||||
public final static ContextKey FILTER = new ContextKey() {
|
||||
};
|
||||
public static class ParameterKey implements ContextKey {
|
||||
public final int index;
|
||||
|
||||
public ParameterKey(int index) {
|
||||
super();
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
public static final ContextKey PARAMETERS[] = new ContextKey[]{
|
||||
new ParameterKey(0),
|
||||
new ParameterKey(1),
|
||||
new ParameterKey(2),
|
||||
new ParameterKey(3),
|
||||
new ParameterKey(4),
|
||||
new ParameterKey(5),
|
||||
new ParameterKey(6),
|
||||
new ParameterKey(7),
|
||||
new ParameterKey(8),
|
||||
new ParameterKey(9),
|
||||
new ParameterKey(10),
|
||||
new ParameterKey(11),
|
||||
new ParameterKey(12),
|
||||
new ParameterKey(13),
|
||||
new ParameterKey(14),
|
||||
new ParameterKey(15),
|
||||
new ParameterKey(16),
|
||||
new ParameterKey(17),
|
||||
new ParameterKey(18),
|
||||
new ParameterKey(19)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,17 +13,30 @@ package com.ibm.wala.ipa.callgraph;
|
|||
import com.ibm.wala.classLoader.CallSiteReference;
|
||||
import com.ibm.wala.classLoader.IMethod;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
|
||||
/**
|
||||
* An interface to an object which helps control context-sensitivity
|
||||
* An interface to an object which helps control context-sensitivity.
|
||||
*/
|
||||
public interface ContextSelector {
|
||||
/**
|
||||
* Given a call site, returns the Context in which the callee should be evaluated.
|
||||
* Given a calling node and a call site, returns the Context in which the callee should be evaluated.
|
||||
*
|
||||
* @param caller the node containing the call site
|
||||
* @param site description of the call site
|
||||
* @param actualParameters the abstract objects (InstanceKeys) of parameters of interest to the selector
|
||||
* @return the Context in which the callee should be evaluated, or null if no information is available.
|
||||
*/
|
||||
Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver);
|
||||
Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] actualParameters);
|
||||
|
||||
/**
|
||||
* Given a calling node and a call site, return the set of parameters based
|
||||
* on which this selector may choose to specialize contexts.
|
||||
*
|
||||
* @param caller the calling node
|
||||
* @param site the specific call site
|
||||
* @return the set of parameters of interest
|
||||
*/
|
||||
IntSet getRelevantParameters(CGNode caller, CallSiteReference site);
|
||||
|
||||
}
|
||||
|
|
|
@ -17,35 +17,20 @@ import com.ibm.wala.ipa.callgraph.CGNode;
|
|||
import com.ibm.wala.ipa.callgraph.Context;
|
||||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.util.intset.EmptyIntSet;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
|
||||
/**
|
||||
* A basic context selector that ignores context.
|
||||
*/
|
||||
public class ContextInsensitiveSelector implements ContextSelector {
|
||||
|
||||
public boolean mayUnderstand(CGNode caller, CallSiteReference site, IMethod targetMethod, InstanceKey instance) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
return Everywhere.EVERYWHERE;
|
||||
}
|
||||
|
||||
public int getBoundOnNumberOfTargets(CGNode caller, CallSiteReference site, IMethod targetMethod) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see com.ibm.wala.ipa.callgraph.ContextSelector#contextIsIrrelevant(com.ibm.wala.classLoader.CallSiteReference)
|
||||
*/
|
||||
public boolean contextIsIrrelevant(CGNode node, CallSiteReference site) {
|
||||
return true;
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see com.ibm.wala.ipa.callgraph.ContextSelector#contextIsIrrelevant(com.ibm.wala.types.MethodReference)
|
||||
*/
|
||||
public boolean allSitesDispatchIdentically(CGNode node, CallSiteReference site) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ import com.ibm.wala.ipa.callgraph.Context;
|
|||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.CloneContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
|
||||
/**
|
||||
* Default object to control context-insensitive context selection, This includes reflection logic.
|
||||
|
@ -27,21 +29,25 @@ public class DefaultContextSelector implements ContextSelector {
|
|||
|
||||
private final ContextSelector delegate;
|
||||
|
||||
public DefaultContextSelector(AnalysisOptions options) {
|
||||
public DefaultContextSelector(AnalysisOptions options, IClassHierarchy cha) {
|
||||
if (options == null) {
|
||||
throw new IllegalArgumentException("null options");
|
||||
}
|
||||
ContextInsensitiveSelector ci = new ContextInsensitiveSelector();
|
||||
ContextSelector r = ReflectionContextSelector.createReflectionContextSelector(options);
|
||||
ContextSelector s = new DelegatingContextSelector(r, ci);
|
||||
delegate = new DelegatingContextSelector(new CloneContextSelector(), s);
|
||||
delegate = new DelegatingContextSelector(new CloneContextSelector(cha), s);
|
||||
}
|
||||
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (caller == null) {
|
||||
throw new IllegalArgumentException("null caller");
|
||||
}
|
||||
return delegate.getCalleeTarget(caller, site, callee, receiver);
|
||||
}
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return delegate.getRelevantParameters(caller, site);
|
||||
}
|
||||
|
||||
}
|
|
@ -16,6 +16,7 @@ import com.ibm.wala.ipa.callgraph.CGNode;
|
|||
import com.ibm.wala.ipa.callgraph.Context;
|
||||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
|
||||
/**
|
||||
* A context selector that first checks with A, then defaults to B.
|
||||
|
@ -38,7 +39,7 @@ public class DelegatingContextSelector implements ContextSelector {
|
|||
}
|
||||
}
|
||||
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (DEBUG) {
|
||||
System.err.println(("getCalleeTarget " + caller + " " + site + " " + callee));
|
||||
}
|
||||
|
@ -58,4 +59,8 @@ public class DelegatingContextSelector implements ContextSelector {
|
|||
return C;
|
||||
}
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return A.getRelevantParameters(caller, site).union(B.getRelevantParameters(caller, site));
|
||||
}
|
||||
|
||||
}
|
|
@ -16,6 +16,9 @@ import com.ibm.wala.classLoader.IMethod;
|
|||
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||
import com.ibm.wala.ipa.callgraph.Context;
|
||||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||
import com.ibm.wala.util.intset.EmptyIntSet;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
|
||||
/**
|
||||
* This context selector selects a context based on the concrete type of
|
||||
|
@ -25,11 +28,14 @@ public class CloneContextSelector implements ContextSelector {
|
|||
|
||||
private final ReceiverTypeContextSelector selector;
|
||||
|
||||
public CloneContextSelector() {
|
||||
private final IClassHierarchy cha;
|
||||
|
||||
public CloneContextSelector(IClassHierarchy cha) {
|
||||
this.selector = new ReceiverTypeContextSelector();
|
||||
this.cha = cha;
|
||||
}
|
||||
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (receiver == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -40,37 +46,14 @@ public class CloneContextSelector implements ContextSelector {
|
|||
}
|
||||
}
|
||||
|
||||
public int getBoundOnNumberOfTargets(CGNode caller, CallSiteReference reference, IMethod targetMethod) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean mayUnderstand(CGNode caller, CallSiteReference site, IMethod targetMethod, InstanceKey instance) {
|
||||
if (targetMethod == null) {
|
||||
throw new IllegalArgumentException("targetMethod is null");
|
||||
}
|
||||
return targetMethod.getReference().equals(CloneInterpreter.CLONE);
|
||||
}
|
||||
|
||||
public boolean contextIsIrrelevant(CGNode node, CallSiteReference site) {
|
||||
if (site == null) {
|
||||
throw new IllegalArgumentException("site is null");
|
||||
}
|
||||
if (!site.getDeclaredTarget().equals(CloneInterpreter.CLONE)) {
|
||||
return true;
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
IMethod declaredTarget = cha.resolveMethod(site.getDeclaredTarget());
|
||||
if (declaredTarget != null && declaredTarget.getReference().equals(CloneInterpreter.CLONE)) {
|
||||
return selector.getRelevantParameters(caller, site);
|
||||
} else {
|
||||
return false;
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean allSitesDispatchIdentically(CGNode node, CallSiteReference site) {
|
||||
if (site == null) {
|
||||
throw new IllegalArgumentException("site is null");
|
||||
}
|
||||
if (!site.getDeclaredTarget().equals(CloneInterpreter.CLONE)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ public interface FilteredPointerKey extends PointerKey {
|
|||
|
||||
boolean addInverseFiltered(PropagationSystem system, PointsToSetVariable L, PointsToSetVariable R);
|
||||
|
||||
boolean isRootFilter();
|
||||
|
||||
}
|
||||
|
||||
public class SingleClassFilter implements TypeFilter {
|
||||
|
@ -70,6 +72,10 @@ public interface FilteredPointerKey extends PointerKey {
|
|||
// use addAllInIntersection
|
||||
return (f == null) ? L.addAll(R) : L.addAll(IntSetUtil.diff(R.getValue(), f));
|
||||
}
|
||||
|
||||
public boolean isRootFilter() {
|
||||
return concreteType.equals(concreteType.getClassHierarchy().getRootClass());
|
||||
}
|
||||
}
|
||||
|
||||
public class MultipleClassesFilter implements TypeFilter {
|
||||
|
@ -139,6 +145,10 @@ public interface FilteredPointerKey extends PointerKey {
|
|||
// use addAllInIntersection
|
||||
return (f == null) ? L.addAll(R) : L.addAll(IntSetUtil.diff(R.getValue(), f));
|
||||
}
|
||||
|
||||
public boolean isRootFilter() {
|
||||
return concreteType.length == 1 && concreteType[0].getClassHierarchy().getRootClass().equals(concreteType[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public class SingleInstanceFilter implements TypeFilter {
|
||||
|
@ -189,6 +199,10 @@ public interface FilteredPointerKey extends PointerKey {
|
|||
return L.addAll(copy);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRootFilter() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class TargetMethodFilter implements TypeFilter {
|
||||
|
@ -263,6 +277,10 @@ public interface FilteredPointerKey extends PointerKey {
|
|||
return act.result;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRootFilter() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,14 +34,18 @@ import com.ibm.wala.ipa.callgraph.ContextSelector;
|
|||
import com.ibm.wala.ipa.callgraph.Entrypoint;
|
||||
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
|
||||
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder.DispatchOperator;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter;
|
||||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||
import com.ibm.wala.ssa.IR;
|
||||
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.CancelException;
|
||||
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
import com.ibm.wala.util.debug.Assertions;
|
||||
import com.ibm.wala.util.functions.VoidFunction;
|
||||
import com.ibm.wala.util.intset.IntIterator;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetAction;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
|
@ -685,9 +689,8 @@ public abstract class PropagationCallGraphBuilder implements CallGraphBuilder {
|
|||
* @param iKey an abstraction of the receiver of the call (or null if not applicable)
|
||||
* @return the CGNode to which this particular call should dispatch.
|
||||
*/
|
||||
public CGNode getTargetForCall(CGNode caller, CallSiteReference site, InstanceKey iKey) {
|
||||
protected CGNode getTargetForCall(CGNode caller, CallSiteReference site, IClass recv, InstanceKey iKey[]) {
|
||||
|
||||
IClass recv = (iKey != null) ? iKey.getConcreteType() : null;
|
||||
IMethod targetMethod = options.getMethodTargetSelector().getCalleeTarget(caller, site, recv);
|
||||
|
||||
// this most likely indicates an exclusion at work; the target selector
|
||||
|
@ -696,6 +699,7 @@ public abstract class PropagationCallGraphBuilder implements CallGraphBuilder {
|
|||
return null;
|
||||
}
|
||||
Context targetContext = contextSelector.getCalleeTarget(caller, site, targetMethod, iKey);
|
||||
|
||||
if (targetContext instanceof IllegalArgumentExceptionContext) {
|
||||
return null;
|
||||
}
|
||||
|
@ -705,7 +709,7 @@ public abstract class PropagationCallGraphBuilder implements CallGraphBuilder {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the context selector for this call graph builder
|
||||
*/
|
||||
|
|
|
@ -192,7 +192,7 @@ public class PropagationSystem extends DefaultFixedPointSolver<PointsToSetVariab
|
|||
if (klass == null) {
|
||||
throw new IllegalArgumentException("klass is null");
|
||||
}
|
||||
assert klass.getReference() != TypeReference.JavaLangObject;
|
||||
assert klass != klass.getClassHierarchy().getRootClass();
|
||||
return class2InstanceKey.get(klass);
|
||||
}
|
||||
|
||||
|
@ -568,6 +568,21 @@ public class PropagationSystem extends DefaultFixedPointSolver<PointsToSetVariab
|
|||
newStatement(null, op, v1, true, true);
|
||||
}
|
||||
|
||||
public void newSideEffect(AbstractOperator<PointsToSetVariable> op, PointerKey[] arg0) {
|
||||
if (arg0 == null) {
|
||||
throw new IllegalArgumentException("null arg0");
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.err.println("add constraint D: " + op + " " + arg0);
|
||||
}
|
||||
PointsToSetVariable[] vs = new PointsToSetVariable[ arg0.length ];
|
||||
for(int i = 0; i < arg0.length; i++) {
|
||||
assert !pointsToMap.isUnified(arg0[i]);
|
||||
vs[i] = findOrCreatePointsToSet(arg0[i]);
|
||||
}
|
||||
newStatement(null, op, vs, true, true);
|
||||
}
|
||||
|
||||
public void newSideEffect(AbstractOperator<PointsToSetVariable> op, PointerKey arg0, PointerKey arg1) {
|
||||
if (DEBUG) {
|
||||
System.err.println("add constraint D: " + op + " " + arg0);
|
||||
|
@ -888,4 +903,9 @@ public class PropagationSystem extends DefaultFixedPointSolver<PointsToSetVariab
|
|||
public int getNumber(PointerKey p) {
|
||||
return pointsToMap.getIndex(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PointsToSetVariable[] makeStmtRHS(int size) {
|
||||
return new PointsToSetVariable[size];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,10 +35,9 @@ public class ReceiverInstanceContext implements Context {
|
|||
public ContextItem get(ContextKey name) {
|
||||
if (name == ContextKey.RECEIVER)
|
||||
return ik;
|
||||
else if (name == ContextKey.FILTER)
|
||||
else if (name == ContextKey.PARAMETERS[0])
|
||||
return new FilteredPointerKey.SingleInstanceFilter(ik);
|
||||
else {
|
||||
Assertions.UNREACHABLE();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ import com.ibm.wala.classLoader.IMethod;
|
|||
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||
import com.ibm.wala.ipa.callgraph.Context;
|
||||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
|
||||
/**
|
||||
* This context selector selects a context based on the concrete type of the receiver.
|
||||
|
@ -26,27 +28,18 @@ public class ReceiverTypeContextSelector implements ContextSelector {
|
|||
public ReceiverTypeContextSelector() {
|
||||
}
|
||||
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (receiver == null) {
|
||||
throw new IllegalArgumentException("receiver is null");
|
||||
}
|
||||
PointType P = new PointType(receiver.getConcreteType());
|
||||
PointType P = new PointType(receiver[0].getConcreteType());
|
||||
return new JavaTypeContext(P);
|
||||
}
|
||||
|
||||
public int getBoundOnNumberOfTargets(CGNode caller, CallSiteReference reference, IMethod targetMethod) {
|
||||
return -1;
|
||||
private static final IntSet receiver = IntSetUtil.make(new int[]{ 0 });
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return receiver;
|
||||
}
|
||||
|
||||
public boolean mayUnderstand(CGNode caller, CallSiteReference site, IMethod targetMethod, InstanceKey instance) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean contextIsIrrelevant(CGNode node, CallSiteReference site) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean allSitesDispatchIdentically(CGNode node, CallSiteReference site) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,7 @@ import com.ibm.wala.classLoader.IField;
|
|||
import com.ibm.wala.classLoader.IMethod;
|
||||
import com.ibm.wala.classLoader.NewSiteReference;
|
||||
import com.ibm.wala.classLoader.ProgramCounter;
|
||||
import com.ibm.wala.fixpoint.IntSetVariable;
|
||||
import com.ibm.wala.fixpoint.UnaryOperator;
|
||||
import com.ibm.wala.fixpoint.AbstractOperator;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisCache;
|
||||
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
|
||||
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||
|
@ -71,6 +70,8 @@ import com.ibm.wala.types.Selector;
|
|||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
import com.ibm.wala.util.debug.Assertions;
|
||||
import com.ibm.wala.util.functions.VoidFunction;
|
||||
import com.ibm.wala.util.intset.IntIterator;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetAction;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
|
@ -227,7 +228,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
/**
|
||||
* @return a visitor to examine instructions in the ir
|
||||
*/
|
||||
protected ConstraintVisitor makeVisitor(ExplicitCallGraph.ExplicitNode node) {
|
||||
protected ConstraintVisitor makeVisitor(CGNode node) {
|
||||
return new ConstraintVisitor(this, node);
|
||||
}
|
||||
|
||||
|
@ -235,7 +236,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
* Add pointer flow constraints based on instructions in a given node
|
||||
*/
|
||||
protected void addNodeInstructionConstraints(CGNode node) {
|
||||
ConstraintVisitor v = makeVisitor((ExplicitCallGraph.ExplicitNode) node);
|
||||
ConstraintVisitor v = makeVisitor(node);
|
||||
|
||||
IR ir = getCFAContextInterpreter().getIR(node);
|
||||
ControlFlowGraph<SSAInstruction, ISSABasicBlock> cfg = ir.getControlFlowGraph();
|
||||
|
@ -492,7 +493,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
/**
|
||||
* The node whose statements we are currently traversing
|
||||
*/
|
||||
protected final ExplicitCallGraph.ExplicitNode node;
|
||||
protected final CGNode node;
|
||||
|
||||
/**
|
||||
* The governing call graph.
|
||||
|
@ -524,7 +525,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
*/
|
||||
protected final DefUse du;
|
||||
|
||||
public ConstraintVisitor(SSAPropagationCallGraphBuilder builder, ExplicitCallGraph.ExplicitNode node) {
|
||||
public ConstraintVisitor(SSAPropagationCallGraphBuilder builder, CGNode node) {
|
||||
this.builder = builder;
|
||||
this.node = node;
|
||||
|
||||
|
@ -601,14 +602,18 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
return getBuilder().getInstanceKeyForClassObject(type);
|
||||
}
|
||||
|
||||
public CGNode getTargetForCall(CGNode caller, CallSiteReference site, InstanceKey iKey) {
|
||||
return getBuilder().getTargetForCall(caller, site, iKey);
|
||||
public CGNode getTargetForCall(CGNode caller, CallSiteReference site, IClass recv, InstanceKey iKey[]) {
|
||||
return getBuilder().getTargetForCall(caller, site, recv, iKey);
|
||||
}
|
||||
|
||||
protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumber) {
|
||||
return getBuilder().contentsAreInvariant(symbolTable, du, valueNumber);
|
||||
}
|
||||
|
||||
protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumber[]) {
|
||||
return getBuilder().contentsAreInvariant(symbolTable, du, valueNumber);
|
||||
}
|
||||
|
||||
protected InstanceKey[] getInvariantContents(int valueNumber) {
|
||||
return getInvariantContents(ir.getSymbolTable(), du, node, valueNumber);
|
||||
}
|
||||
|
@ -1005,7 +1010,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
visitInvokeInternal(instruction);
|
||||
}
|
||||
|
||||
protected void visitInvokeInternal(SSAAbstractInvokeInstruction instruction) {
|
||||
protected void visitInvokeInternal(final SSAAbstractInvokeInstruction instruction) {
|
||||
if (DEBUG) {
|
||||
System.err.println("visitInvoke: " + instruction);
|
||||
}
|
||||
|
@ -1016,9 +1021,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
}
|
||||
|
||||
if (instruction.getCallSite().isStatic()) {
|
||||
CGNode n = getTargetForCall(node, instruction.getCallSite(), (InstanceKey) null);
|
||||
if (n == null) {
|
||||
} else {
|
||||
for (CGNode n : getBuilder().getTargetsForCall(node, instruction.getCallSite())) {
|
||||
getBuilder().processResolvedCall(node, instruction, n, computeInvariantParameters(instruction), uniqueCatch);
|
||||
if (DEBUG) {
|
||||
System.err.println("visitInvoke class init " + n);
|
||||
|
@ -1031,32 +1034,42 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
// Add a side effect that will fire when we determine a value
|
||||
// for the receiver. This side effect will create a new node
|
||||
// and new constraints based on the new callee context.
|
||||
// NOTE: This will not be adequate for CPA-style context selectors,
|
||||
// where the callee context may depend on state other than the
|
||||
// receiver. TODO: rectify this when needed.
|
||||
PointerKey receiver = getPointerKeyForLocal(instruction.getReceiver());
|
||||
// if (!supportFullPointerFlowGraph &&
|
||||
// contentsAreInvariant(instruction.getReceiver())) {
|
||||
if (contentsAreInvariant(symbolTable, du, instruction.getReceiver())) {
|
||||
system.recordImplicitPointsToSet(receiver);
|
||||
InstanceKey[] ik = getInvariantContents(instruction.getReceiver());
|
||||
for (int i = 0; i < ik.length; i++) {
|
||||
system.findOrCreateIndexForInstanceKey(ik[i]);
|
||||
CGNode n = getTargetForCall(node, instruction.getCallSite(), ik[i]);
|
||||
if (n == null) {
|
||||
} else {
|
||||
IntSet params = getBuilder().getContextSelector().getRelevantParameters(node, instruction.getCallSite());
|
||||
if (! params.contains(0)) {
|
||||
params = IntSetUtil.makeMutableCopy(params);
|
||||
((MutableIntSet)params).add(0);
|
||||
}
|
||||
final int vns[] = new int[ params.size() ];
|
||||
params.foreach(new IntSetAction() {
|
||||
private int i = 0;
|
||||
public void act(int x) {
|
||||
vns[i++] = instruction.getUse(x);
|
||||
}
|
||||
});
|
||||
|
||||
if (contentsAreInvariant(symbolTable, du, vns)) {
|
||||
for(CGNode n : getBuilder().getTargetsForCall(node, instruction.getCallSite())) {
|
||||
getBuilder().processResolvedCall(node, instruction, n, computeInvariantParameters(instruction), uniqueCatch);
|
||||
// side effect of invoke: may call class initializer
|
||||
processClassInitializer(n.getMethod().getDeclaringClass());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
System.err.println("Add side effect, dispatch to " + instruction + ", receiver " + receiver);
|
||||
System.err.println("Add side effect, dispatch to " + instruction + " for " + params);
|
||||
}
|
||||
|
||||
final List<PointerKey> pks = new ArrayList<PointerKey>(params.size());
|
||||
params.foreach(new IntSetAction() {
|
||||
public void act(int x) {
|
||||
if (! contentsAreInvariant(symbolTable, du, instruction.getUse(x))) {
|
||||
pks.add(getBuilder().getPointerKeyForLocal(node, instruction.getUse(x)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
DispatchOperator dispatchOperator = getBuilder().new DispatchOperator(instruction, node,
|
||||
computeInvariantParameters(instruction), uniqueCatch);
|
||||
system.newSideEffect(dispatchOperator, receiver);
|
||||
computeInvariantParameters(instruction), uniqueCatch, params);
|
||||
system.newSideEffect(dispatchOperator, pks.toArray(new PointerKey[pks.size()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1374,7 +1387,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
CallSiteReference site = CallSiteReference.make(1, m, IInvokeInstruction.Dispatch.STATIC);
|
||||
IMethod targetMethod = getOptions().getMethodTargetSelector().getCalleeTarget(callGraph.getFakeRootNode(), site, null);
|
||||
if (targetMethod != null) {
|
||||
CGNode target = getTargetForCall(callGraph.getFakeRootNode(), site, (InstanceKey) null);
|
||||
CGNode target = getTargetForCall(callGraph.getFakeRootNode(), site, null, null);
|
||||
if (target != null && callGraph.getPredNodeCount(target) == 0) {
|
||||
SSAAbstractInvokeInstruction s = fakeWorldClinitMethod.addInvocation(new int[0], site);
|
||||
PointerKey uniqueCatch = getBuilder().getPointerKeyForExceptionalReturnValue(callGraph.getFakeRootNode());
|
||||
|
@ -1422,7 +1435,12 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
if (!haveAlreadyVisited(target)) {
|
||||
markDiscovered(target);
|
||||
}
|
||||
|
||||
|
||||
processCallingConstraints(caller, instruction, target, constParams, uniqueCatchKey);
|
||||
}
|
||||
|
||||
protected void processCallingConstraints(CGNode caller, SSAAbstractInvokeInstruction instruction, CGNode target,
|
||||
InstanceKey[][] constParams, PointerKey uniqueCatchKey) {
|
||||
// TODO: i'd like to enable this optimization, but it's a little tricky
|
||||
// to recover the implicit points-to sets with recursion. TODO: don't
|
||||
// be lazy and code the recursive logic to enable this.
|
||||
|
@ -1457,57 +1475,27 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
return;
|
||||
}
|
||||
|
||||
boolean needsFilter = !instruction.getCallSite().isStatic() && needsFilterForReceiver(instruction, target);
|
||||
// we're a little sloppy for now ... we don't filter calls to
|
||||
// java.lang.Object.
|
||||
// TODO: we need much more precise filters than cones in order to handle
|
||||
// the various types of dispatch logic. We need a filter that expresses
|
||||
// "the set of types s.t. x.foo resolves to y.foo."
|
||||
for (int i = 0; i < instruction.getNumberOfParameters(); i++) {
|
||||
// we rely on the invariant that the value number for the ith parameter
|
||||
// is i+1
|
||||
final int vn = i + 1;
|
||||
|
||||
if (target.getMethod().getParameterType(i).isReferenceType()) {
|
||||
// if (constParams != null && constParams[i] != null &&
|
||||
// !supportFullPointerFlowGraph) {
|
||||
PointerKey formal = getTargetPointerKey(target, i);
|
||||
if (constParams != null && constParams[i] != null) {
|
||||
InstanceKey[] ik = constParams[i];
|
||||
for (int j = 0; j < ik.length; j++) {
|
||||
if (needsFilter && (i == 0)) {
|
||||
FilteredPointerKey.TypeFilter C = getFilter(target);
|
||||
PointerKey formal = null;
|
||||
if (isRootType(C)) {
|
||||
// TODO: we need much better filtering here ... see comments
|
||||
// above.
|
||||
formal = getPointerKeyForLocal(target, vn);
|
||||
} else {
|
||||
formal = getFilteredPointerKeyForLocal(target, vn, C);
|
||||
}
|
||||
system.newConstraint(formal, ik[j]);
|
||||
} else {
|
||||
PointerKey formal = getPointerKeyForLocal(target, vn);
|
||||
system.newConstraint(formal, ik[j]);
|
||||
}
|
||||
system.newConstraint(formal, ik[j]);
|
||||
}
|
||||
} else {
|
||||
if (instruction.getUse(i) < 0) {
|
||||
Assertions.UNREACHABLE("unexpected " + instruction + " in " + caller);
|
||||
}
|
||||
PointerKey actual = getPointerKeyForLocal(caller, instruction.getUse(i));
|
||||
if (needsFilter && (i == 0)) {
|
||||
FilteredPointerKey.TypeFilter C = getFilter(target);
|
||||
if (isRootType(C)) {
|
||||
// TODO: we need much better filtering here ... see comments
|
||||
// above.
|
||||
PointerKey formal = getPointerKeyForLocal(target, vn);
|
||||
system.newConstraint(formal, assignOperator, actual);
|
||||
} else {
|
||||
FilteredPointerKey formal = getFilteredPointerKeyForLocal(target, vn, C);
|
||||
system.newConstraint(formal, filterOperator, actual);
|
||||
}
|
||||
if (formal instanceof FilteredPointerKey) {
|
||||
system.newConstraint(formal, filterOperator, actual);
|
||||
} else {
|
||||
PointerKey formal = getPointerKeyForLocal(target, vn);
|
||||
system.newConstraint(formal, assignOperator, actual);
|
||||
}
|
||||
}
|
||||
|
@ -1536,127 +1524,138 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
* An operator to fire when we discover a potential new callee for a virtual or interface call site.
|
||||
*
|
||||
* This operator will create a new callee context and constraints if necessary.
|
||||
*
|
||||
* N.B: This implementation assumes that the calling context depends solely on the dataflow information computed for the receiver.
|
||||
* TODO: generalize this to have other forms of context selection, such as CPA-style algorithms.
|
||||
*/
|
||||
final class DispatchOperator extends UnaryOperator<PointsToSetVariable> implements IPointerOperator {
|
||||
final class DispatchOperator extends AbstractOperator<PointsToSetVariable> implements IPointerOperator {
|
||||
private final SSAAbstractInvokeInstruction call;
|
||||
|
||||
private final ExplicitCallGraph.ExplicitNode node;
|
||||
private final CGNode node;
|
||||
|
||||
private final InstanceKey[][] constParams;
|
||||
|
||||
private final PointerKey uniqueCatch;
|
||||
|
||||
private final int[] dispatchIndices;
|
||||
|
||||
/**
|
||||
* @param call
|
||||
* @param node
|
||||
* @param constParams if non-null, then constParams[i] holds the String constant that is passed as param i, or null if param i
|
||||
* is not a String constant
|
||||
*/
|
||||
DispatchOperator(SSAAbstractInvokeInstruction call, ExplicitCallGraph.ExplicitNode node, InstanceKey[][] constParams,
|
||||
PointerKey uniqueCatch) {
|
||||
DispatchOperator(SSAAbstractInvokeInstruction call, CGNode node, InstanceKey[][] constParams,
|
||||
PointerKey uniqueCatch, IntSet dispatchIndices) {
|
||||
this.call = call;
|
||||
this.node = node;
|
||||
this.constParams = constParams;
|
||||
this.uniqueCatch = uniqueCatch;
|
||||
this.dispatchIndices = IntSetUtil.toArray(dispatchIndices);
|
||||
previousPtrs = new MutableIntSet[dispatchIndices.size()];
|
||||
for(int i = 0; i < previousPtrs.length; i++) {
|
||||
previousPtrs[i] = IntSetUtil.getDefaultIntSetFactory().make();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The set of pointers that have already been processed.
|
||||
*/
|
||||
final private MutableIntSet previousReceivers = IntSetUtil.getDefaultIntSetFactory().make();
|
||||
final private MutableIntSet[] previousPtrs;
|
||||
|
||||
/*
|
||||
* @see com.ibm.wala.dataflow.fixpoint.UnaryOperator#evaluate(com.ibm.wala.dataflow.fixpoint.IVariable,
|
||||
* com.ibm.wala.dataflow.fixpoint.IVariable)
|
||||
*/
|
||||
@Override
|
||||
public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable rhs) {
|
||||
final IntSetVariable receivers = rhs;
|
||||
public byte evaluate(PointsToSetVariable lhs, final PointsToSetVariable[] rhs) {
|
||||
assert dispatchIndices.length >= rhs.length : "bad operator at " + call;
|
||||
final MutableBoolean sideEffect = new MutableBoolean();
|
||||
|
||||
// compute the set of pointers that were not previously handled
|
||||
IntSet value = receivers.getValue();
|
||||
if (value == null) {
|
||||
// this constraint was put on the work list, probably by
|
||||
// initialization,
|
||||
// even though the right-hand-side is empty.
|
||||
// TODO: be more careful about what goes on the worklist to
|
||||
// avoid this.
|
||||
if (DEBUG) {
|
||||
System.err.println("EVAL dispatch with value null");
|
||||
}
|
||||
return NOT_CHANGED;
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.err.println("EVAL dispatch to " + node + ":" + call);
|
||||
System.err.println("receivers: " + value);
|
||||
}
|
||||
|
||||
IntSetAction action = new IntSetAction() {
|
||||
public void act(int ptr) {
|
||||
for(PointsToSetVariable v : rhs) {
|
||||
if (v.getValue() == null) {
|
||||
// this constraint was put on the work list, probably by
|
||||
// initialization,
|
||||
// even though the right-hand-side is empty.
|
||||
// TODO: be more careful about what goes on the worklist to
|
||||
// avoid this.
|
||||
if (DEBUG) {
|
||||
System.err.println(" dispatch to ptr " + ptr);
|
||||
}
|
||||
InstanceKey iKey = system.getInstanceKey(ptr);
|
||||
CGNode target;
|
||||
|
||||
if (clone2Assign) {
|
||||
// for efficiency: assume that only call sites that reference
|
||||
// clone() might dispatch to clone methods
|
||||
if (call.getCallSite().getDeclaredTarget().getSelector().equals(cloneSelector)) {
|
||||
IClass recv = (iKey != null) ? iKey.getConcreteType() : null;
|
||||
IMethod targetMethod = getOptions().getMethodTargetSelector().getCalleeTarget(node, call.getCallSite(), recv);
|
||||
if (targetMethod != null && targetMethod.getReference().equals(CloneInterpreter.CLONE)) {
|
||||
// treat this call to clone as an assignment
|
||||
PointerKey result = getPointerKeyForLocal(node, call.getDef());
|
||||
PointerKey receiver = getPointerKeyForLocal(node, call.getReceiver());
|
||||
system.newConstraint(result, assignOperator, receiver);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target = getTargetForCall(node, call.getCallSite(), iKey);
|
||||
|
||||
if (target == null) {
|
||||
// This indicates an error; I sure hope getTargetForCall
|
||||
// raised a warning about this!
|
||||
if (DEBUG) {
|
||||
System.err.println("Warning: null target for call " + call + " " + iKey);
|
||||
}
|
||||
} else {
|
||||
IntSet targets = getCallGraph().getPossibleTargetNumbers(node, call.getCallSite());
|
||||
if (targets != null && targets.contains(target.getGraphNodeId())) {
|
||||
// do nothing; we've previously discovered and handled this
|
||||
// receiver for this call site.
|
||||
} else {
|
||||
// process the newly discovered target for this call
|
||||
sideEffect.b = true;
|
||||
processResolvedCall(node, call, target, constParams, uniqueCatch);
|
||||
if (!haveAlreadyVisited(target)) {
|
||||
markDiscovered(target);
|
||||
}
|
||||
}
|
||||
System.err.println("EVAL dispatch with value null");
|
||||
}
|
||||
return NOT_CHANGED;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
value.foreachExcluding(previousReceivers, action);
|
||||
} catch (Error e) {
|
||||
System.err.println("error in " + call + " on " + receivers + " of types " + value + " for " + node);
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
System.err.println("error in " + call + " on " + receivers + " of types " + value + " for " + node);
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
||||
new Object() {
|
||||
InstanceKey keys[] = new InstanceKey[constParams == null? dispatchIndices[dispatchIndices.length-1]+1: constParams.length];
|
||||
void rec(int index, int rhsIndex, boolean redundant) {
|
||||
if (index < dispatchIndices.length) {
|
||||
int pi = dispatchIndices[index];
|
||||
if (constParams != null && constParams[pi] != null) {
|
||||
for(int i = 0; i < constParams[pi].length; i++) {
|
||||
keys[pi] = constParams[pi][i];
|
||||
int ii = system.instanceKeys.getMappedIndex(constParams[pi][i]);
|
||||
rec(index+1, rhsIndex, redundant & previousPtrs[index].contains(ii));
|
||||
}
|
||||
} else {
|
||||
PointsToSetVariable v = rhs[rhsIndex];
|
||||
IntIterator ptrs = v.getValue().intIterator();
|
||||
while (ptrs.hasNext()) {
|
||||
int ptr = ptrs.next();
|
||||
keys[dispatchIndices[index]] = system.getInstanceKey(ptr);
|
||||
rec(index+1, rhsIndex+1, redundant & previousPtrs[index].contains(ptr));
|
||||
}
|
||||
}
|
||||
} else if (!redundant) {
|
||||
|
||||
if (clone2Assign) {
|
||||
// for efficiency: assume that only call sites that reference
|
||||
// clone() might dispatch to clone methods
|
||||
if (call.getCallSite().getDeclaredTarget().getSelector().equals(cloneSelector)) {
|
||||
IClass recv = (keys[0] != null) ? keys[0].getConcreteType() : null;
|
||||
IMethod targetMethod = getOptions().getMethodTargetSelector().getCalleeTarget(node, call.getCallSite(), recv);
|
||||
if (targetMethod != null && targetMethod.getReference().equals(CloneInterpreter.CLONE)) {
|
||||
// treat this call to clone as an assignment
|
||||
PointerKey result = getPointerKeyForLocal(node, call.getDef());
|
||||
PointerKey receiver = getPointerKeyForLocal(node, call.getReceiver());
|
||||
system.newConstraint(result, assignOperator, receiver);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
CGNode target = getTargetForCall(node, call.getCallSite(), keys[0].getConcreteType(), keys);
|
||||
if (target == null) {
|
||||
// This indicates an error; I sure hope getTargetForCall
|
||||
// raised a warning about this!
|
||||
if (DEBUG) {
|
||||
System.err.println("Warning: null target for call " + call);
|
||||
}
|
||||
} else {
|
||||
IntSet targets = getCallGraph().getPossibleTargetNumbers(node, call.getCallSite());
|
||||
if (targets != null && targets.contains(target.getGraphNodeId())) {
|
||||
// do nothing; we've previously discovered and handled this
|
||||
// receiver for this call site.
|
||||
} else {
|
||||
// process the newly discovered target for this call
|
||||
sideEffect.b = true;
|
||||
processResolvedCall(node, call, target, constParams, uniqueCatch);
|
||||
if (!haveAlreadyVisited(target)) {
|
||||
markDiscovered(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.rec(0, 0, true);
|
||||
|
||||
// update the set of receivers previously considered
|
||||
previousReceivers.copySet(value);
|
||||
|
||||
for(int ri = 0, i = 0; i < rhs.length; i++) {
|
||||
int pi = dispatchIndices[i];
|
||||
if (constParams != null && constParams[pi] != null) {
|
||||
for(int ci = 0; ci < constParams[pi].length; ci++) {
|
||||
previousPtrs[i].add(system.instanceKeys.getMappedIndex(constParams[i][ci]));
|
||||
}
|
||||
} else {
|
||||
previousPtrs[i].addAll(rhs[ri++].getValue());
|
||||
}
|
||||
}
|
||||
|
||||
byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
|
||||
return (byte) (NOT_CHANGED | sideEffectMask);
|
||||
}
|
||||
|
@ -1697,6 +1696,82 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
}
|
||||
}
|
||||
|
||||
protected void iterateCrossProduct(final CGNode caller, final CallSiteReference site, IntSet parameters, final VoidFunction<InstanceKey[]> f) {
|
||||
final IR ir = caller.getIR();
|
||||
final int params[] = IntSetUtil.toArray(parameters);
|
||||
for (final SSAAbstractInvokeInstruction call : ir.getCalls(site)) {
|
||||
final InstanceKey[] keys = new InstanceKey[call.getNumberOfParameters()];
|
||||
new Object() {
|
||||
private void rec(final int pi) {
|
||||
if (pi == params.length) {
|
||||
f.apply(keys);
|
||||
} else {
|
||||
final int p = params[pi];
|
||||
int vn = call.getUse(p);
|
||||
PointerKey var = getPointerKeyForLocal(caller, vn);
|
||||
if (contentsAreInvariant(ir.getSymbolTable(), caller.getDU(), vn)) {
|
||||
system.recordImplicitPointsToSet(var);
|
||||
InstanceKey[] ik = getInvariantContents(ir.getSymbolTable(), caller.getDU(), caller, vn, SSAPropagationCallGraphBuilder.this);
|
||||
if (ik != null && ik.length > 0) {
|
||||
for (int i = 0; i < ik.length; i++) {
|
||||
system.findOrCreateIndexForInstanceKey(ik[i]);
|
||||
keys[p] = ik[i];
|
||||
rec(pi+1);
|
||||
}
|
||||
} else {
|
||||
if (!site.isDispatch() || p != 0) {
|
||||
keys[p] = null;
|
||||
rec(pi+1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
IntSet s = system.findOrCreatePointsToSet(var).getValue();
|
||||
if (s != null && !s.isEmpty()) {
|
||||
s.foreach(new IntSetAction() {
|
||||
public void act(int x) {
|
||||
keys[p] = system.getInstanceKey(x);
|
||||
rec(pi+1);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (!site.isDispatch() || p != 0) {
|
||||
keys[p] = null;
|
||||
rec(pi+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.rec(0);
|
||||
}
|
||||
}
|
||||
|
||||
protected Set<CGNode> getTargetsForCall(final CGNode caller, final CallSiteReference site) {
|
||||
IntSet params = contextSelector.getRelevantParameters(caller, site);
|
||||
if (!site.isStatic() && !params.contains(0)) {
|
||||
params = IntSetUtil.makeMutableCopy(params);
|
||||
((MutableIntSet)params).add(0);
|
||||
}
|
||||
final Set<CGNode> targets = HashSetFactory.make();
|
||||
VoidFunction<InstanceKey[]> f = new VoidFunction<InstanceKey[]>() {
|
||||
public void apply(InstanceKey[] v) {
|
||||
IClass recv = null;
|
||||
if (site.isDispatch()) {
|
||||
recv = v[0].getConcreteType();
|
||||
}
|
||||
CGNode target = getTargetForCall(caller, site, recv, v);
|
||||
if (target != null) {
|
||||
targets.add(target);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (site.getDeclaredTarget().getName().toString().contains("numericToTextFormat")) {
|
||||
System.err.println(site + "\n" + params + "\n" + targets);
|
||||
}
|
||||
iterateCrossProduct(caller, site, params, f);
|
||||
return targets;
|
||||
}
|
||||
|
||||
public boolean hasNoInterestingUses(CGNode node, int vn, DefUse du) {
|
||||
|
||||
if (du == null) {
|
||||
|
@ -1808,7 +1883,7 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
*/
|
||||
private boolean needsFilterForReceiver(SSAAbstractInvokeInstruction instruction, CGNode target) {
|
||||
|
||||
FilteredPointerKey.TypeFilter f = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.FILTER);
|
||||
FilteredPointerKey.TypeFilter f = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.PARAMETERS[0]);
|
||||
|
||||
if (f != null) {
|
||||
// the context selects a particular concrete type for the receiver.
|
||||
|
@ -1851,16 +1926,30 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
* @param target
|
||||
* @return an IClass which represents
|
||||
*/
|
||||
private FilteredPointerKey.TypeFilter getFilter(CGNode target) {
|
||||
FilteredPointerKey.TypeFilter filter = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.FILTER);
|
||||
|
||||
if (filter != null) {
|
||||
return filter;
|
||||
protected PointerKey getTargetPointerKey(CGNode target, int index) {
|
||||
int vn;
|
||||
if (target.getIR() != null) {
|
||||
vn = target.getIR().getSymbolTable().getParameter(index);
|
||||
} else {
|
||||
vn = index+1;
|
||||
}
|
||||
|
||||
FilteredPointerKey.TypeFilter filter = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.PARAMETERS[index]);
|
||||
if (filter != null && !filter.isRootFilter()) {
|
||||
return pointerKeyFactory.getFilteredPointerKeyForLocal(target, vn, filter);
|
||||
|
||||
} else if (index == 0 && !target.getMethod().isStatic()) {
|
||||
// the context does not select a particular concrete type for the
|
||||
// receiver.
|
||||
// receiver, so use the type of the method
|
||||
IClass C = getReceiverClass(target.getMethod());
|
||||
return new FilteredPointerKey.SingleClassFilter(C);
|
||||
if (C.getClassHierarchy().getRootClass().equals(C)) {
|
||||
return pointerKeyFactory.getPointerKeyForLocal(target, vn);
|
||||
} else {
|
||||
return pointerKeyFactory.getFilteredPointerKeyForLocal(target, vn, new FilteredPointerKey.SingleClassFilter(C));
|
||||
}
|
||||
|
||||
} else {
|
||||
return pointerKeyFactory.getPointerKeyForLocal(target, vn);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1901,6 +1990,15 @@ public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGrap
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumbers[]) {
|
||||
for(int i = 0; i < valueNumbers.length; i++) {
|
||||
if (! contentsAreInvariant(symbolTable, du, valueNumbers[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* precondition:contentsAreInvariant(valueNumber)
|
||||
*
|
||||
|
|
|
@ -19,6 +19,8 @@ import com.ibm.wala.ipa.callgraph.ContextKey;
|
|||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||
import com.ibm.wala.types.Selector;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
|
||||
/**
|
||||
* This context selector selects a context based on whether the receiver type
|
||||
|
@ -32,12 +34,12 @@ public class TargetMethodContextSelector implements ContextSelector {
|
|||
this.selector = selector;
|
||||
}
|
||||
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey R) {
|
||||
if (R == null) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] R) {
|
||||
if (R == null || R[0] == null) {
|
||||
throw new IllegalArgumentException("R is null");
|
||||
}
|
||||
|
||||
final IMethod M = R.getConcreteType().getMethod(selector);
|
||||
final IMethod M = R[0].getConcreteType().getMethod(selector);
|
||||
|
||||
class MethodDispatchContext implements Context {
|
||||
|
||||
|
@ -46,7 +48,7 @@ public class TargetMethodContextSelector implements ContextSelector {
|
|||
}
|
||||
|
||||
public ContextItem get(ContextKey name) {
|
||||
if (name.equals(ContextKey.FILTER)) {
|
||||
if (name.equals(ContextKey.PARAMETERS[0])) {
|
||||
return new FilteredPointerKey.TargetMethodFilter(M);
|
||||
} else {
|
||||
return null;
|
||||
|
@ -73,19 +75,10 @@ public class TargetMethodContextSelector implements ContextSelector {
|
|||
return new MethodDispatchContext();
|
||||
}
|
||||
|
||||
public int getBoundOnNumberOfTargets(CGNode caller, CallSiteReference reference, IMethod targetMethod) {
|
||||
return -1;
|
||||
private static final IntSet thisParameter = IntSetUtil.make(new int[]{0});
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return thisParameter;
|
||||
}
|
||||
|
||||
public boolean mayUnderstand(CGNode caller, CallSiteReference site, IMethod targetMethod, InstanceKey instance) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean contextIsIrrelevant(CGNode node, CallSiteReference site) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean allSitesDispatchIdentically(CGNode node, CallSiteReference site) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ import com.ibm.wala.ipa.callgraph.ContextKey;
|
|||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.util.intset.EmptyIntSet;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
|
||||
public abstract class CallStringContextSelector implements ContextSelector {
|
||||
|
||||
|
@ -88,7 +90,7 @@ public abstract class CallStringContextSelector implements ContextSelector {
|
|||
/*
|
||||
* @see com.ibm.wala.ipa.callgraph.ContextSelector#getCalleeTarget(com.ibm.wala.ipa.callgraph.CGNode, com.ibm.wala.classLoader.CallSiteReference, com.ibm.wala.classLoader.IMethod, com.ibm.wala.ipa.callgraph.propagation.InstanceKey)
|
||||
*/
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
Context baseContext = base.getCalleeTarget(caller, site, callee, receiver);
|
||||
CallString cs = getCallString(caller, site, callee);
|
||||
if (cs == null) {
|
||||
|
@ -99,4 +101,9 @@ public abstract class CallStringContextSelector implements ContextSelector {
|
|||
return new CallStringContextPair(cs, baseContext);
|
||||
}
|
||||
}
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,9 @@ import com.ibm.wala.types.MethodReference;
|
|||
import com.ibm.wala.types.TypeName;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.debug.Assertions;
|
||||
import com.ibm.wala.util.intset.EmptyIntSet;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
import com.ibm.wala.util.strings.Atom;
|
||||
|
||||
/**
|
||||
|
@ -100,11 +103,15 @@ public class ContainerContextSelector implements ContextSelector {
|
|||
* com.ibm.wala.classLoader.CallSiteReference, com.ibm.wala.classLoader.IMethod,
|
||||
* com.ibm.wala.ipa.callgraph.propagation.InstanceKey)
|
||||
*/
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] keys) {
|
||||
if (DEBUG) {
|
||||
System.err.println("ContainerContextSelector: getCalleeTarget " + callee);
|
||||
}
|
||||
if (mayUnderstand(caller, site, callee, receiver)) {
|
||||
InstanceKey receiver = null;
|
||||
if (keys != null && keys.length > 0 && keys[0] != null) {
|
||||
receiver = keys[0];
|
||||
}
|
||||
if (receiver != null && mayUnderstand(caller, site, callee, receiver)) {
|
||||
if (DEBUG) {
|
||||
System.err.println("May Understand: " + callee + " recv " + receiver);
|
||||
}
|
||||
|
@ -258,12 +265,6 @@ public class ContainerContextSelector implements ContextSelector {
|
|||
return (n == null) ? null : n.getContext();
|
||||
}
|
||||
|
||||
public int getBoundOnNumberOfTargets(CGNode caller, CallSiteReference site, IMethod targetMethod) {
|
||||
// if we understand this call, we don't know how many target contexts we may
|
||||
// create.
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean mayUnderstand(CGNode caller, CallSiteReference site, IMethod targetMethod, InstanceKey receiver) {
|
||||
if (targetMethod == null) {
|
||||
throw new IllegalArgumentException("targetMethod is null");
|
||||
|
@ -326,15 +327,18 @@ public class ContainerContextSelector implements ContextSelector {
|
|||
return ContainerUtil.isContainer(C);
|
||||
}
|
||||
|
||||
public boolean contextIsIrrelevant(CGNode node, CallSiteReference site) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected IClassHierarchy getClassHierarchy() {
|
||||
return cha;
|
||||
}
|
||||
|
||||
public boolean allSitesDispatchIdentically(CGNode node, CallSiteReference site) {
|
||||
return false;
|
||||
private static final IntSet thisParameter = IntSetUtil.make(new int[]{0});
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
if (site.isDispatch() || site.getDeclaredTarget().getNumberOfParameters() > 0) {
|
||||
return thisParameter;
|
||||
} else {
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import com.ibm.wala.ipa.callgraph.Context;
|
|||
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
||||
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
|
||||
/**
|
||||
* This is a context selector that adds one level of calling context to a base context selector.
|
||||
|
@ -36,7 +37,7 @@ public class OneLevelSiteContextSelector implements ContextSelector {
|
|||
this.baseSelector = baseSelector;
|
||||
}
|
||||
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
Context baseContext = baseSelector.getCalleeTarget(caller, site, callee, receiver);
|
||||
if (baseContext.equals(Everywhere.EVERYWHERE)) {
|
||||
return new CallerSiteContext(caller, site);
|
||||
|
@ -45,4 +46,8 @@ public class OneLevelSiteContextSelector implements ContextSelector {
|
|||
}
|
||||
}
|
||||
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return baseSelector.getRelevantParameters(caller, site);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ public class ZeroXCFABuilder extends SSAPropagationCallGraphBuilder {
|
|||
|
||||
super(cha, options, cache, new DefaultPointerKeyFactory());
|
||||
|
||||
ContextSelector def = new DefaultContextSelector(options);
|
||||
ContextSelector def = new DefaultContextSelector(options, cha);
|
||||
ContextSelector contextSelector = appContextSelector == null ? def : new DelegatingContextSelector(appContextSelector, def);
|
||||
setContextSelector(contextSelector);
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ public class nCFABuilder extends SSAPropagationCallGraphBuilder {
|
|||
|
||||
setInstanceKeys(new ClassBasedInstanceKeys(options, cha));
|
||||
|
||||
ContextSelector def = new DefaultContextSelector(options);
|
||||
ContextSelector def = new DefaultContextSelector(options, cha);
|
||||
ContextSelector contextSelector = new DelegatingContextSelector(appContextSelector, def);
|
||||
contextSelector = new nCFAContextSelector(n, contextSelector);
|
||||
setContextSelector(contextSelector);
|
||||
|
|
|
@ -238,7 +238,7 @@ public abstract class AbstractRTABuilder extends PropagationCallGraphBuilder {
|
|||
IInvokeInstruction.IDispatch code = site.getInvocationCode();
|
||||
|
||||
if (code == IInvokeInstruction.Dispatch.STATIC) {
|
||||
CGNode n = getTargetForCall(node, site, (InstanceKey) null);
|
||||
CGNode n = getTargetForCall(node, site, null, null);
|
||||
if (n != null) {
|
||||
processResolvedCall(node, site, n);
|
||||
|
||||
|
@ -372,7 +372,7 @@ public abstract class AbstractRTABuilder extends PropagationCallGraphBuilder {
|
|||
}
|
||||
|
||||
protected ContextSelector makeContextSelector(ContextSelector appContextSelector) {
|
||||
ContextSelector def = new DefaultContextSelector(options);
|
||||
ContextSelector def = new DefaultContextSelector(options, cha);
|
||||
ContextSelector contextSelector = appContextSelector == null ? def : new DelegatingContextSelector(appContextSelector, def);
|
||||
return contextSelector;
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ public class BasicRTABuilder extends AbstractRTABuilder {
|
|||
}
|
||||
InstanceKey iKey = system.getInstanceKey(ptr);
|
||||
|
||||
CGNode target = getTargetForCall(caller, site, iKey);
|
||||
CGNode target = getTargetForCall(caller, site, iKey.getConcreteType(), new InstanceKey[]{iKey});
|
||||
if (target == null) {
|
||||
// This indicates an error; I sure hope getTargetForCall
|
||||
// raised a warning about this!
|
||||
|
|
|
@ -36,6 +36,8 @@ public class SymbolTable implements Cloneable {
|
|||
*/
|
||||
private HashMap<ConstantValue, Integer> constants = HashMapFactory.make(10);
|
||||
|
||||
private boolean copy = false;
|
||||
|
||||
/**
|
||||
* @param numberOfParameters in the IR .. should be ir.getNumberOfParameters()
|
||||
*/
|
||||
|
@ -75,6 +77,7 @@ public class SymbolTable implements Cloneable {
|
|||
ConstantValue v = new ConstantValue(o);
|
||||
Integer result = constants.get(v);
|
||||
if (result == null) {
|
||||
assert ! copy : "making value for " + o;
|
||||
int r = getNewValueNumber();
|
||||
result = Integer.valueOf(r);
|
||||
constants.put(v, result);
|
||||
|
@ -446,6 +449,7 @@ public class SymbolTable implements Cloneable {
|
|||
nt.defaultValues = this.defaultValues.clone();
|
||||
}
|
||||
nt.constants = HashMapFactory.make(this.constants);
|
||||
nt.copy = true;
|
||||
return nt;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
Assertions.UNREACHABLE();
|
||||
|
|
|
@ -186,5 +186,10 @@ public class DeadAssignmentElimination {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanVariable[] makeStmtRHS(int size) {
|
||||
return new BooleanVariable[size];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ import com.ibm.wala.types.MemberReference;
|
|||
import com.ibm.wala.types.MethodReference;
|
||||
import com.ibm.wala.types.TypeName;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.intset.EmptyIntSet;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.strings.Atom;
|
||||
|
||||
/**
|
||||
|
@ -54,7 +56,7 @@ public class J2EEContextSelector implements ContextSelector {
|
|||
/**
|
||||
* Analyze each call to Command.execute() in a different context
|
||||
*/
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey receiver) {
|
||||
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
||||
if (callee.getReference().equals(ExecuteMethod)) {
|
||||
ReceiverTypeInference R = typeInference.findOrCreate(caller);
|
||||
if (R == null) {
|
||||
|
@ -71,42 +73,8 @@ public class J2EEContextSelector implements ContextSelector {
|
|||
}
|
||||
}
|
||||
|
||||
public int getBoundOnNumberOfTargets(CGNode caller, CallSiteReference site, IMethod callee) {
|
||||
if (callee.getReference().equals(ExecuteMethod)) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contextIsIrrelevant(CGNode node, CallSiteReference site) {
|
||||
Atom name = site.getDeclaredTarget().getName();
|
||||
Descriptor d = site.getDeclaredTarget().getDescriptor();
|
||||
if (name.equals(ExecuteAtom) && d.equals(ExecuteDesc)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean allSitesDispatchIdentically(CGNode node, CallSiteReference site) {
|
||||
// Atom name = site.getDeclaredTarget().getName();
|
||||
// Descriptor d = site.getDeclaredTarget().getDescriptor();
|
||||
// if (name.equals(ExecuteAtom) && d.equals(ExecuteDesc)) {
|
||||
// return false;
|
||||
// } else {
|
||||
// return true;
|
||||
// }
|
||||
// todo: fix me
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean mayUnderstand(CGNode caller, CallSiteReference site, IMethod targetMethod, InstanceKey instance) {
|
||||
if (targetMethod.getReference().equals(ExecuteMethod)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
||||
return EmptyIntSet.instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,4 +31,9 @@ public class BitVectorSolver<T> extends DataflowSolver<T, BitVectorVariable> {
|
|||
return new BitVectorVariable();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BitVectorVariable[] makeStmtRHS(int size) {
|
||||
return new BitVectorVariable[size];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ package com.ibm.wala.dataflow.graph;
|
|||
|
||||
import com.ibm.wala.fixpoint.BitVectorVariable;
|
||||
import com.ibm.wala.fixpoint.FixedPointConstants;
|
||||
import com.ibm.wala.fixpoint.IVariable;
|
||||
|
||||
/**
|
||||
* Operator U(n) = U(n) U U(j)
|
||||
|
@ -50,7 +49,7 @@ public class BitVectorUnion extends AbstractMeetOperator<BitVectorVariable> impl
|
|||
* @see com.ibm.wala.dataflow.fixpoint.Operator#evaluate(com.ibm.wala.dataflow.fixpoint.IVariable[])
|
||||
*/
|
||||
@Override
|
||||
public byte evaluate(BitVectorVariable lhs, @SuppressWarnings("rawtypes") IVariable[] rhs) throws IllegalArgumentException {
|
||||
public byte evaluate(BitVectorVariable lhs, BitVectorVariable[] rhs) throws IllegalArgumentException {
|
||||
if (lhs == null) {
|
||||
throw new IllegalArgumentException("null lhs");
|
||||
}
|
||||
|
|
|
@ -22,15 +22,20 @@ public class BooleanSolver<T> extends DataflowSolver<T, BooleanVariable> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected BooleanVariable makeNodeVariable(Object n, boolean IN) {
|
||||
protected BooleanVariable makeNodeVariable(T n, boolean IN) {
|
||||
return new BooleanVariable();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanVariable makeEdgeVariable(Object src, Object dst) {
|
||||
protected BooleanVariable makeEdgeVariable(T src, T dst) {
|
||||
return new BooleanVariable();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanVariable[] makeStmtRHS(int size) {
|
||||
return new BooleanVariable[size];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ package com.ibm.wala.dataflow.graph;
|
|||
|
||||
import com.ibm.wala.fixpoint.BooleanVariable;
|
||||
import com.ibm.wala.fixpoint.FixedPointConstants;
|
||||
import com.ibm.wala.fixpoint.IVariable;
|
||||
|
||||
/**
|
||||
* Operator U(n) = U(n) U U(j)
|
||||
|
@ -47,7 +46,7 @@ public class BooleanUnion extends AbstractMeetOperator<BooleanVariable> implemen
|
|||
}
|
||||
|
||||
@Override
|
||||
public byte evaluate(BooleanVariable lhs, @SuppressWarnings("rawtypes") IVariable[] rhs) throws NullPointerException {
|
||||
public byte evaluate(BooleanVariable lhs, BooleanVariable[] rhs) throws NullPointerException {
|
||||
if (rhs == null) {
|
||||
throw new IllegalArgumentException("null rhs");
|
||||
}
|
||||
|
|
|
@ -202,7 +202,7 @@ public abstract class DataflowSolver<T, V extends IVariable> extends DefaultFixe
|
|||
int nPred = G.getPredNodeCount(node);
|
||||
if (nPred >= meetThreshold) {
|
||||
// todo: optimize further using unary operators when possible?
|
||||
V[] rhs = (V[]) new IVariable[nPred];
|
||||
V[] rhs = makeStmtRHS(nPred);
|
||||
int i = 0;
|
||||
for (Iterator<?> it2 = G.getPredNodes(node); it2.hasNext();) {
|
||||
rhs[i++] = (functions.hasEdgeTransferFunctions()) ? getEdge(it2.next(), node) : getOut(it2.next());
|
||||
|
|
|
@ -101,6 +101,8 @@ public abstract class AbstractFixedPointSolver<T extends IVariable> implements I
|
|||
*/
|
||||
private boolean firstSolve = true;
|
||||
|
||||
protected abstract T[] makeStmtRHS(int size);
|
||||
|
||||
/**
|
||||
* Some setup which occurs only before the first solve
|
||||
*/
|
||||
|
@ -331,6 +333,31 @@ public abstract class AbstractFixedPointSolver<T extends IVariable> implements I
|
|||
return true;
|
||||
}
|
||||
|
||||
protected class Statement extends GeneralStatement<T> {
|
||||
|
||||
public Statement(T lhs, AbstractOperator<T> operator, T op1, T op2, T op3) {
|
||||
super(lhs, operator, op1, op2, op3);
|
||||
}
|
||||
|
||||
public Statement(T lhs, AbstractOperator<T> operator, T op1, T op2) {
|
||||
super(lhs, operator, op1, op2);
|
||||
}
|
||||
|
||||
public Statement(T lhs, AbstractOperator<T> operator, T[] rhs) {
|
||||
super(lhs, operator, rhs);
|
||||
}
|
||||
|
||||
public Statement(T lhs, AbstractOperator<T> operator) {
|
||||
super(lhs, operator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T[] makeRHS(int size) {
|
||||
return makeStmtRHS(size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an equation with two operands on the right-hand side.
|
||||
*
|
||||
|
@ -342,7 +369,7 @@ public abstract class AbstractFixedPointSolver<T extends IVariable> implements I
|
|||
public void newStatement(T lhs, AbstractOperator<T> operator, T op1, T op2, boolean toWorkList, boolean eager) {
|
||||
// add to the list of graph
|
||||
|
||||
GeneralStatement<T> s = new GeneralStatement<T>(lhs, operator, op1, op2);
|
||||
GeneralStatement<T> s = new Statement(lhs, operator, op1, op2);
|
||||
if (getFixedPointSystem().containsStatement(s)) {
|
||||
return;
|
||||
}
|
||||
|
@ -371,7 +398,7 @@ public abstract class AbstractFixedPointSolver<T extends IVariable> implements I
|
|||
}
|
||||
// add to the list of graph
|
||||
lhs.setOrderNumber(nextOrderNumber++);
|
||||
GeneralStatement<T> s = new GeneralStatement<T>(lhs, operator, op1, op2, op3);
|
||||
GeneralStatement<T> s = new Statement(lhs, operator, op1, op2, op3);
|
||||
if (getFixedPointSystem().containsStatement(s)) {
|
||||
nextOrderNumber--;
|
||||
return;
|
||||
|
@ -390,11 +417,11 @@ public abstract class AbstractFixedPointSolver<T extends IVariable> implements I
|
|||
* @param operator the operator
|
||||
* @param rhs the operands on the rhs
|
||||
*/
|
||||
public void newStatement(T lhs, AbstractOperator<T> operator, IVariable[] rhs, boolean toWorkList, boolean eager) {
|
||||
public void newStatement(T lhs, AbstractOperator<T> operator, T[] rhs, boolean toWorkList, boolean eager) {
|
||||
// add to the list of graph
|
||||
if (lhs != null)
|
||||
lhs.setOrderNumber(nextOrderNumber++);
|
||||
GeneralStatement<T> s = new GeneralStatement<T>(lhs, operator, rhs);
|
||||
GeneralStatement<T> s = new Statement(lhs, operator, rhs);
|
||||
if (getFixedPointSystem().containsStatement(s)) {
|
||||
nextOrderNumber--;
|
||||
return;
|
||||
|
|
|
@ -17,12 +17,11 @@ import com.ibm.wala.fixpoint.IVariable;
|
|||
/**
|
||||
* Represents a single step in an iterative solver
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class GeneralStatement<T extends IVariable> extends AbstractStatement<T, AbstractOperator<T>> {
|
||||
public abstract class GeneralStatement<T extends IVariable<?>> extends AbstractStatement<T, AbstractOperator<T>> {
|
||||
|
||||
protected final T lhs;
|
||||
|
||||
protected final IVariable[] rhs;
|
||||
protected final T[] rhs;
|
||||
|
||||
private final int hashCode;
|
||||
|
||||
|
@ -55,7 +54,7 @@ public class GeneralStatement<T extends IVariable> extends AbstractStatement<T,
|
|||
* @param cell the cell in question
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean hasVariable(IVariable cell) {
|
||||
public boolean hasVariable(T cell) {
|
||||
if (lhs == cell) {
|
||||
return true;
|
||||
}
|
||||
|
@ -98,7 +97,7 @@ public class GeneralStatement<T extends IVariable> extends AbstractStatement<T,
|
|||
}
|
||||
this.operator = operator;
|
||||
this.lhs = lhs;
|
||||
rhs = new IVariable[2];
|
||||
rhs = makeRHS(2);
|
||||
rhs[0] = op1;
|
||||
rhs[1] = op2;
|
||||
this.hashCode = makeHashCode();
|
||||
|
@ -119,7 +118,7 @@ public class GeneralStatement<T extends IVariable> extends AbstractStatement<T,
|
|||
throw new IllegalArgumentException("null operator");
|
||||
}
|
||||
this.operator = operator;
|
||||
rhs = new IVariable[3];
|
||||
rhs = makeRHS(3);
|
||||
this.lhs = lhs;
|
||||
rhs[0] = op1;
|
||||
rhs[1] = op2;
|
||||
|
@ -135,7 +134,7 @@ public class GeneralStatement<T extends IVariable> extends AbstractStatement<T,
|
|||
* @param rhs the operands of the right-hand side in order
|
||||
* @throws IllegalArgumentException if rhs is null
|
||||
*/
|
||||
public GeneralStatement(T lhs, AbstractOperator<T> operator, IVariable[] rhs) {
|
||||
public GeneralStatement(T lhs, AbstractOperator<T> operator, T[] rhs) {
|
||||
super();
|
||||
if (operator == null) {
|
||||
throw new IllegalArgumentException("null operator");
|
||||
|
@ -166,6 +165,8 @@ public class GeneralStatement<T extends IVariable> extends AbstractStatement<T,
|
|||
return result;
|
||||
}
|
||||
|
||||
protected abstract T[] makeRHS(int size);
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
|
@ -177,7 +178,7 @@ public class GeneralStatement<T extends IVariable> extends AbstractStatement<T,
|
|||
return false;
|
||||
}
|
||||
if (getClass().equals(o.getClass())) {
|
||||
GeneralStatement other = (GeneralStatement) o;
|
||||
GeneralStatement<?> other = (GeneralStatement<?>) o;
|
||||
if (hashCode == other.hashCode) {
|
||||
if (lhs == null || other.lhs == null) {
|
||||
if (other.lhs != lhs) {
|
||||
|
@ -208,7 +209,7 @@ public class GeneralStatement<T extends IVariable> extends AbstractStatement<T,
|
|||
return operator;
|
||||
}
|
||||
|
||||
public IVariable[] getRHS() {
|
||||
public T[] getRHS() {
|
||||
return rhs;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import com.ibm.wala.fixpoint.IVariable;
|
|||
public abstract class NullaryOperator<T extends IVariable> extends AbstractOperator<T> implements FixedPointConstants {
|
||||
|
||||
@Override
|
||||
public byte evaluate(T lhs, IVariable[] rhs) throws UnsupportedOperationException {
|
||||
public byte evaluate(T lhs, T[] rhs) throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
/**
|
||||
|
|
|
@ -28,7 +28,7 @@ public abstract class AbstractOperator<T extends IVariable> implements FixedPoin
|
|||
* @return a code that indicates: 1) has the lhs value changed? 2) has this equation reached a fixed-point, in that we never have
|
||||
* to evaluate the equation again, even if rhs operands change?
|
||||
*/
|
||||
public abstract byte evaluate(T lhs, IVariable[] rhs);
|
||||
public abstract byte evaluate(T lhs, T[] rhs);
|
||||
|
||||
@Override
|
||||
public abstract int hashCode();
|
||||
|
|
|
@ -38,7 +38,7 @@ public interface IFixedPointStatement<T extends IVariable> extends INodeWithNumb
|
|||
* returns the list of free variables appearing in the right-hand side of the
|
||||
* statement
|
||||
*/
|
||||
public IVariable[] getRHS();
|
||||
public T[] getRHS();
|
||||
|
||||
/**
|
||||
* Evaluate this statement, setting a new value for the left-hand side. The
|
||||
|
|
|
@ -39,7 +39,7 @@ public abstract class UnaryOperator<T extends IVariable> extends AbstractOperato
|
|||
}
|
||||
|
||||
@Override
|
||||
public byte evaluate(T lhs, IVariable[] rhs) throws UnimplementedError {
|
||||
public byte evaluate(T lhs, T[] rhs) throws UnimplementedError {
|
||||
// this should never be called. Use the other, more efficient form.
|
||||
Assertions.UNREACHABLE();
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package com.ibm.wala.util.functions;
|
||||
|
||||
public interface VoidFunction<T> {
|
||||
|
||||
void apply(T v);
|
||||
|
||||
}
|
|
@ -78,6 +78,8 @@ public class IntSetUtil {
|
|||
MutableIntSet pCopy = makeMutableCopy(((DebuggingMutableIntSet) set).primaryImpl);
|
||||
MutableIntSet sCopy = makeMutableCopy(((DebuggingMutableIntSet) set).secondaryImpl);
|
||||
return new DebuggingMutableIntSet(pCopy, sCopy);
|
||||
} else if (set instanceof EmptyIntSet) {
|
||||
return IntSetUtil.make();
|
||||
} else {
|
||||
Assertions.UNREACHABLE(set.getClass().toString());
|
||||
return null;
|
||||
|
|
Loading…
Reference in New Issue