WALA/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/RecursionBoundContextSelect...

112 lines
4.5 KiB
Java

/*******************************************************************************
* Copyright (c) 2013 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.cast.js.ipa.callgraph;
import com.ibm.wala.analysis.reflection.InstanceKeyWithNode;
import com.ibm.wala.cast.ipa.callgraph.ScopeMappingInstanceKeys.ScopeMappingInstanceKey;
import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptConstructTargetSelector.JavaScriptConstructor;
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.Context;
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.FilteredPointerKey;
import com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey.SingleInstanceFilter;
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.IntSet;
/**
* A context selector that attempts to detect recursion beyond some depth in a
* base selector. If such recursion is detected, the base selector's context is
* replaced with {@link Everywhere#EVERYWHERE}.
*/
public class RecursionBoundContextSelector implements ContextSelector {
private final ContextSelector base;
private final int recursionBound;
/**
* the highest parameter index that we'll check . this is a HACK. ideally,
* given a context, we'd have some way to know all the {@link ContextKey}s
* that it knows about.
*
* @see ContextKey#PARAMETERS
*/
private static final int MAX_INTERESTING_PARAM = 5;
/**
* @param base
* @param recursionBound
* bound on recursion depth, with the top level of the context
* returned by the base selector being depth 0. The
* {@link Everywhere#EVERYWHERE} context is returned if the base
* context <em>exceeds</em> this bound.
*/
public RecursionBoundContextSelector(ContextSelector base, int recursionBound) {
this.base = base;
this.recursionBound = recursionBound;
}
@Override
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] actualParameters) {
Context baseContext = base.getCalleeTarget(caller, site, callee, actualParameters);
final boolean exceedsRecursionBound = exceedsRecursionBound(baseContext, 0);
if (!exceedsRecursionBound) {
return baseContext;
} else if (callee instanceof JavaScriptConstructor) {
// for constructors, we want to keep some basic context sensitivity to
// avoid horrible imprecision
return new CallStringContext(new CallString(site, caller.getMethod()));
} else {
// TODO somehow k-limit more smartly?
return Everywhere.EVERYWHERE;
}
}
private boolean exceedsRecursionBound(Context baseContext, int curLevel) {
if (curLevel > recursionBound) {
return true;
}
// we just do a case analysis here. we might have to add cases later to
// account for new types of context / recursion.
CGNode callerNode = (CGNode) baseContext.get(ContextKey.CALLER);
if (callerNode != null && exceedsRecursionBound(callerNode.getContext(), curLevel + 1)) {
return true;
}
for (int i = 0; i < MAX_INTERESTING_PARAM; i++) {
FilteredPointerKey.SingleInstanceFilter filter = (SingleInstanceFilter) baseContext.get(ContextKey.PARAMETERS[i]);
if (filter != null) {
InstanceKey ik = filter.getInstance();
if (ik instanceof ScopeMappingInstanceKey) {
ik = ((ScopeMappingInstanceKey) ik).getBase();
}
if (ik instanceof InstanceKeyWithNode) {
if (exceedsRecursionBound(((InstanceKeyWithNode) ik).getNode().getContext(), curLevel + 1)) {
return true;
}
}
}
}
return false;
}
@Override
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
return base.getRelevantParameters(caller, site);
}
}