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

167 lines
5.0 KiB
Java

package com.ibm.wala.cast.js.ipa.callgraph;
import com.ibm.wala.cast.js.types.JavaScriptTypes;
import com.ibm.wala.cast.types.AstMethodReference;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
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.DelegatingContext;
import com.ibm.wala.ipa.callgraph.impl.ContextInsensitiveSelector;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.cfa.nCFAContextSelector;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.util.intset.EmptyIntSet;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetUtil;
/**
*
* @see <a
* href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Apply">MDN
* Function.prototype.apply() docs</a>
*/
public class JavaScriptFunctionApplyContextSelector implements ContextSelector {
/*
* whether to use a one-level callstring context in addition to the apply
* context
*/
private static final boolean USE_ONE_LEVEL = true;
private static final TypeName APPLY_TYPE_NAME = TypeName.findOrCreate("Lprologue.js/functionApply");
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 + "]";
}
}
private ContextSelector oneLevel;
public JavaScriptFunctionApplyContextSelector() {
this.oneLevel = new nCFAContextSelector(1, new ContextInsensitiveSelector());
// this.oneLevel = new OneLevelSiteContextSelector(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 });
} else {
return EmptyIntSet.instance;
}
}
public static class ApplyContext implements Context {
/**
* was the argsList argument a non-null Array?
*/
private final BooleanContextItem isNonNullArray;
ApplyContext(boolean isNonNullArray) {
this.isNonNullArray = new BooleanContextItem(isNonNullArray);
}
public ContextItem get(ContextKey name) {
if (APPLY_NON_NULL_ARGS.equals(name)) {
return isNonNullArray;
} else {
return null;
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((isNonNullArray == null) ? 0 : isNonNullArray.hashCode());
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 (isNonNullArray == null) {
if (other.isNonNullArray != null)
return false;
} else if (!isNonNullArray.equals(other.isNonNullArray))
return false;
return true;
}
@Override
public String toString() {
return "ApplyContext [isNonNullArray=" + isNonNullArray + "]";
}
}
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) {
IClass declaringClass = callee.getDeclaringClass();
IMethod method = declaringClass.getMethod(AstMethodReference.fnSelector);
if (method != null) {
TypeName tn = method.getReference().getDeclaringClass().getName();
if (tn.equals(APPLY_TYPE_NAME)) {
boolean isNonNullArray = false;
if (receiver.length >= 4) {
InstanceKey argsList = receiver[3];
if (argsList != null && argsList.getConcreteType().equals(caller.getClassHierarchy().lookupClass(JavaScriptTypes.Array))) {
isNonNullArray = true;
}
}
Context result = new ApplyContext(isNonNullArray);
if (USE_ONE_LEVEL)
result = new DelegatingContext(result, oneLevel.getCalleeTarget(caller, site, callee, receiver));
return result;
}
}
return null;
}
}