2012-01-06 21:25:38 +00:00
|
|
|
package com.ibm.wala.cast.js.ipa.callgraph;
|
|
|
|
|
2012-01-06 21:26:23 +00:00
|
|
|
import com.ibm.wala.cast.js.types.JavaScriptTypes;
|
2012-01-06 21:35:24 +00:00
|
|
|
import com.ibm.wala.cast.types.AstMethodReference;
|
2012-01-06 21:25:38 +00:00
|
|
|
import com.ibm.wala.classLoader.CallSiteReference;
|
2012-01-06 21:35:24 +00:00
|
|
|
import com.ibm.wala.classLoader.IClass;
|
2012-01-06 21:25:38 +00:00
|
|
|
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.ContextItem;
|
|
|
|
import com.ibm.wala.ipa.callgraph.ContextKey;
|
|
|
|
import com.ibm.wala.ipa.callgraph.ContextSelector;
|
2012-02-17 20:16:47 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.DelegatingContext;
|
2012-01-06 21:25:38 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
2012-02-17 20:16:47 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.propagation.cfa.nCFAContextSelector;
|
2012-01-06 21:25:38 +00:00
|
|
|
import com.ibm.wala.util.intset.IntSet;
|
|
|
|
import com.ibm.wala.util.intset.IntSetUtil;
|
|
|
|
|
2012-01-06 21:28:34 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @see <a
|
|
|
|
* href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Apply">MDN
|
2012-01-06 21:33:06 +00:00
|
|
|
* Function.prototype.apply() docs</a>
|
2012-01-06 21:28:34 +00:00
|
|
|
*/
|
2012-01-06 21:25:38 +00:00
|
|
|
public class JavaScriptFunctionApplyContextSelector implements ContextSelector {
|
2012-02-17 20:16:47 +00:00
|
|
|
/* whether to use a one-level callstring context in addition to the apply context */
|
|
|
|
private static final boolean USE_ONE_LEVEL_CALLSTRING = true;
|
2012-01-06 21:25:38 +00:00
|
|
|
|
2012-01-06 21:47:09 +00:00
|
|
|
public static final ContextKey APPLY_NON_NULL_ARGS = new ContextKey() {
|
|
|
|
};
|
|
|
|
|
|
|
|
public static class BooleanContextItem implements ContextItem {
|
|
|
|
final boolean val;
|
|
|
|
|
|
|
|
BooleanContextItem(boolean val) {
|
|
|
|
this.val = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
|
|
|
final int prime = 31;
|
|
|
|
int result = 1;
|
|
|
|
result = prime * result + (val ? 1231 : 1237);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean equals(Object obj) {
|
|
|
|
if (this == obj)
|
|
|
|
return true;
|
|
|
|
if (obj == null)
|
|
|
|
return false;
|
|
|
|
if (getClass() != obj.getClass())
|
|
|
|
return false;
|
|
|
|
BooleanContextItem other = (BooleanContextItem) obj;
|
|
|
|
if (val != other.val)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return "BooleanContextItem [val=" + val + "]";
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-01-06 21:25:38 +00:00
|
|
|
private final ContextSelector base;
|
2012-02-17 20:16:47 +00:00
|
|
|
private nCFAContextSelector oneLevel;
|
2012-01-06 21:25:38 +00:00
|
|
|
|
2012-01-06 21:26:01 +00:00
|
|
|
public JavaScriptFunctionApplyContextSelector(ContextSelector base) {
|
2012-01-06 21:25:38 +00:00
|
|
|
this.base = base;
|
2012-02-17 20:16:47 +00:00
|
|
|
this.oneLevel = new nCFAContextSelector(1, base);
|
2012-01-06 21:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
|
|
|
// 0 for function (synthetic apply), 1 for this (function being invoked), 2
|
|
|
|
// for this arg of function being invoked,
|
|
|
|
// 3 for arguments array
|
|
|
|
if (caller.getIR().getCalls(site)[0].getNumberOfUses() >= 4) {
|
|
|
|
return IntSetUtil.make(new int[] { 3 }).union(base.getRelevantParameters(caller, site));
|
|
|
|
} else {
|
|
|
|
return base.getRelevantParameters(caller, site);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class ApplyContext implements Context {
|
|
|
|
private final Context delegate;
|
|
|
|
|
|
|
|
/**
|
2012-01-06 21:26:23 +00:00
|
|
|
* was the argsList argument a non-null Array?
|
2012-01-06 21:25:38 +00:00
|
|
|
*/
|
2012-01-06 21:47:09 +00:00
|
|
|
private final BooleanContextItem isNonNullArray;
|
2012-01-06 21:25:38 +00:00
|
|
|
|
2012-01-06 21:47:09 +00:00
|
|
|
ApplyContext(Context delegate, boolean isNonNullArray) {
|
2012-01-06 21:25:38 +00:00
|
|
|
this.delegate = delegate;
|
2012-01-06 21:47:09 +00:00
|
|
|
this.isNonNullArray = new BooleanContextItem(isNonNullArray);
|
2012-01-06 21:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public ContextItem get(ContextKey name) {
|
2012-01-06 21:47:09 +00:00
|
|
|
if (APPLY_NON_NULL_ARGS.equals(name)) {
|
|
|
|
return isNonNullArray;
|
|
|
|
} else {
|
|
|
|
return delegate.get(name);
|
|
|
|
}
|
2012-01-06 21:26:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
|
|
|
final int prime = 31;
|
|
|
|
int result = 1;
|
2012-01-06 21:47:09 +00:00
|
|
|
result = prime * result + delegate.hashCode();
|
|
|
|
result = prime * result + isNonNullArray.hashCode();
|
2012-01-06 21:26:01 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean equals(Object obj) {
|
|
|
|
if (this == obj)
|
|
|
|
return true;
|
|
|
|
if (obj == null)
|
|
|
|
return false;
|
|
|
|
if (getClass() != obj.getClass())
|
|
|
|
return false;
|
|
|
|
ApplyContext other = (ApplyContext) obj;
|
2012-01-06 21:47:09 +00:00
|
|
|
if (!delegate.equals(other.delegate))
|
2012-01-06 21:26:23 +00:00
|
|
|
return false;
|
2012-01-06 21:47:09 +00:00
|
|
|
if (!isNonNullArray.equals(other.isNonNullArray))
|
2012-01-06 21:29:01 +00:00
|
|
|
return false;
|
2012-01-06 21:26:01 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-01-06 21:29:01 +00:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
2012-01-06 21:47:09 +00:00
|
|
|
return "ApplyContext [delegate=" + delegate + ", isNonNullArray=" + isNonNullArray + "]";
|
2012-01-06 21:29:01 +00:00
|
|
|
}
|
2012-01-06 21:47:09 +00:00
|
|
|
|
|
|
|
|
2012-01-06 21:29:01 +00:00
|
|
|
|
2012-01-06 21:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
2012-01-06 21:35:24 +00:00
|
|
|
IClass declaringClass = callee.getDeclaringClass();
|
|
|
|
IMethod method = declaringClass.getMethod(AstMethodReference.fnSelector);
|
2012-02-17 20:16:47 +00:00
|
|
|
Context baseCtxt = base.getCalleeTarget(caller, site, callee, receiver);
|
2012-01-06 21:35:24 +00:00
|
|
|
if (method != null) {
|
|
|
|
String s = method.getReference().getDeclaringClass().getName().toString();
|
|
|
|
if (s.equals("Lprologue.js/functionApply")) {
|
|
|
|
boolean isNonNullArray = false;
|
|
|
|
if (receiver.length >= 4) {
|
|
|
|
InstanceKey argsList = receiver[3];
|
|
|
|
if (argsList != null && argsList.getConcreteType().equals(caller.getClassHierarchy().lookupClass(JavaScriptTypes.Array))) {
|
|
|
|
isNonNullArray = true;
|
|
|
|
}
|
2012-01-06 21:26:23 +00:00
|
|
|
}
|
2012-02-17 20:17:22 +00:00
|
|
|
if (USE_ONE_LEVEL_CALLSTRING)
|
|
|
|
baseCtxt = new DelegatingContext(oneLevel.getCalleeTarget(caller, site, callee, receiver), baseCtxt);
|
2012-02-17 20:16:47 +00:00
|
|
|
return new ApplyContext(baseCtxt, isNonNullArray);
|
2012-01-06 21:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2012-02-17 20:16:47 +00:00
|
|
|
return baseCtxt;
|
2012-01-06 21:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|