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:25:38 +00:00
|
|
|
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.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;
|
|
|
|
|
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 {
|
|
|
|
|
|
|
|
private final ContextSelector base;
|
|
|
|
|
2012-01-06 21:26:01 +00:00
|
|
|
public JavaScriptFunctionApplyContextSelector(ContextSelector base) {
|
2012-01-06 21:25:38 +00:00
|
|
|
this.base = base;
|
|
|
|
}
|
|
|
|
|
|
|
|
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:29:01 +00:00
|
|
|
private final CallSiteReference site;
|
|
|
|
|
2012-01-06 21:25:38 +00:00
|
|
|
/**
|
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:26:23 +00:00
|
|
|
private final boolean isNonNullArray;
|
2012-01-06 21:25:38 +00:00
|
|
|
|
2012-01-06 21:29:01 +00:00
|
|
|
ApplyContext(Context delegate, CallSiteReference site, boolean isNonNullArray) {
|
2012-01-06 21:25:38 +00:00
|
|
|
this.delegate = delegate;
|
2012-01-06 21:29:01 +00:00
|
|
|
this.site = site;
|
2012-01-06 21:26:23 +00:00
|
|
|
this.isNonNullArray = isNonNullArray;
|
2012-01-06 21:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ContextItem get(ContextKey name) {
|
|
|
|
return delegate.get(name);
|
|
|
|
}
|
|
|
|
|
2012-01-06 21:26:23 +00:00
|
|
|
public boolean isNonNullArray() {
|
|
|
|
return isNonNullArray;
|
2012-01-06 21:26:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
|
|
|
final int prime = 31;
|
|
|
|
int result = 1;
|
|
|
|
result = prime * result + ((delegate == null) ? 0 : delegate.hashCode());
|
2012-01-06 21:26:23 +00:00
|
|
|
result = prime * result + (isNonNullArray ? 1231 : 1237);
|
2012-01-06 21:29:01 +00:00
|
|
|
result = prime * result + ((site == null) ? 0 : site.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;
|
|
|
|
if (delegate == null) {
|
|
|
|
if (other.delegate != null)
|
|
|
|
return false;
|
|
|
|
} else if (!delegate.equals(other.delegate))
|
|
|
|
return false;
|
2012-01-06 21:26:23 +00:00
|
|
|
if (isNonNullArray != other.isNonNullArray)
|
|
|
|
return false;
|
2012-01-06 21:29:01 +00:00
|
|
|
if (site == null) {
|
|
|
|
if (other.site != null)
|
|
|
|
return false;
|
|
|
|
} else if (!site.equals(other.site))
|
|
|
|
return false;
|
2012-01-06 21:26:01 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-01-06 21:29:01 +00:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return "ApplyContext [delegate=" + delegate + ", site=" + site + ", isNonNullArray=" + isNonNullArray + "]";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-01-06 21:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
|
|
|
|
if (callee.toString().equals("<Code body of function Lprologue.js/functionApply>")) {
|
2012-01-06 21:26:23 +00:00
|
|
|
boolean isNonNullArray = false;
|
2012-01-06 21:25:38 +00:00
|
|
|
if (receiver.length >= 4) {
|
2012-01-06 21:26:23 +00:00
|
|
|
InstanceKey argsList = receiver[3];
|
2012-01-06 21:28:02 +00:00
|
|
|
if (argsList != null && argsList.getConcreteType().equals(caller.getClassHierarchy().lookupClass(JavaScriptTypes.Array))) {
|
2012-01-06 21:26:23 +00:00
|
|
|
isNonNullArray = true;
|
|
|
|
}
|
2012-01-06 21:25:38 +00:00
|
|
|
}
|
2012-01-06 21:29:01 +00:00
|
|
|
return new ApplyContext(base.getCalleeTarget(caller, site, callee, receiver), site, isNonNullArray);
|
2012-01-06 21:25:38 +00:00
|
|
|
}
|
|
|
|
return base.getCalleeTarget(caller, site, callee, receiver);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|