2013-05-22 22:39:19 +00:00
|
|
|
/*******************************************************************************
|
|
|
|
* 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
|
|
|
|
*******************************************************************************/
|
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;
|
|
|
|
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
2012-02-17 20:27:03 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.propagation.cfa.OneLevelSiteContextSelector;
|
2012-02-17 20:18:07 +00:00
|
|
|
import com.ibm.wala.types.TypeName;
|
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:21:18 +00:00
|
|
|
/* whether to use a one-level callstring context in addition to the apply context */
|
|
|
|
private static final boolean USE_ONE_LEVEL = true;
|
2012-01-06 21:25:38 +00:00
|
|
|
|
2012-09-18 17:22:17 +00:00
|
|
|
private static final TypeName APPLY_TYPE_NAME = TypeName.findOrCreate("Lprologue.js/Function_prototype_apply");
|
2012-02-17 20:18:07 +00:00
|
|
|
|
2012-09-18 17:22:17 +00:00
|
|
|
private static final TypeName CALL_TYPE_NAME = TypeName.findOrCreate("Lprologue.js/Function_prototype_call");
|
2012-02-17 20:27:03 +00:00
|
|
|
|
2012-01-06 21:47:09 +00:00
|
|
|
public static final ContextKey APPLY_NON_NULL_ARGS = new ContextKey() {
|
|
|
|
};
|
|
|
|
|
2012-02-17 20:21:18 +00:00
|
|
|
private final ContextSelector base;
|
2012-02-17 20:17:44 +00:00
|
|
|
private ContextSelector oneLevel;
|
2012-01-06 21:25:38 +00:00
|
|
|
|
2012-02-17 20:21:18 +00:00
|
|
|
public JavaScriptFunctionApplyContextSelector(ContextSelector base) {
|
|
|
|
this.base = base;
|
2012-02-17 20:27:03 +00:00
|
|
|
// this.oneLevel = new nCFAContextSelector(1, base);
|
|
|
|
this.oneLevel = new OneLevelSiteContextSelector(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) {
|
2012-02-17 20:21:18 +00:00
|
|
|
return IntSetUtil.make(new int[] { 3 }).union(base.getRelevantParameters(caller, site));
|
2012-01-06 21:25:38 +00:00
|
|
|
} else {
|
2012-02-17 20:21:18 +00:00
|
|
|
return base.getRelevantParameters(caller, site);
|
2012-01-06 21:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class ApplyContext implements Context {
|
2012-02-17 20:21:18 +00:00
|
|
|
private final Context delegate;
|
|
|
|
|
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-02-17 20:27:16 +00:00
|
|
|
private final ContextItem.Value<Boolean> isNonNullArray;
|
2012-01-06 21:25:38 +00:00
|
|
|
|
2012-02-17 20:27:16 +00:00
|
|
|
@SuppressWarnings("unchecked")
|
2012-02-17 20:21:18 +00:00
|
|
|
ApplyContext(Context delegate, boolean isNonNullArray) {
|
|
|
|
this.delegate = delegate;
|
2012-02-17 20:27:16 +00:00
|
|
|
this.isNonNullArray = ContextItem.Value.make(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 {
|
2012-02-17 20:21:18 +00:00
|
|
|
return delegate.get(name);
|
2012-01-06 21:47:09 +00:00
|
|
|
}
|
2012-01-06 21:26:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
|
|
|
final int prime = 31;
|
|
|
|
int result = 1;
|
2012-02-17 20:21:18 +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-02-17 20:21:18 +00:00
|
|
|
if (!delegate.equals(other.delegate))
|
|
|
|
return false;
|
|
|
|
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-02-17 20:21:18 +00:00
|
|
|
return "ApplyContext [delegate=" + delegate + ", isNonNullArray=" + isNonNullArray + "]";
|
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:21:18 +00:00
|
|
|
Context baseCtxt = base.getCalleeTarget(caller, site, callee, receiver);
|
2012-01-06 21:35:24 +00:00
|
|
|
if (method != null) {
|
2012-02-17 20:18:07 +00:00
|
|
|
TypeName tn = method.getReference().getDeclaringClass().getName();
|
|
|
|
if (tn.equals(APPLY_TYPE_NAME)) {
|
2012-01-06 21:35:24 +00:00
|
|
|
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
|
|
|
}
|
2013-06-03 23:47:02 +00:00
|
|
|
if (USE_ONE_LEVEL && caller.getContext().get(APPLY_NON_NULL_ARGS) == null)
|
2012-02-17 20:21:18 +00:00
|
|
|
baseCtxt = oneLevel.getCalleeTarget(caller, site, callee, receiver);
|
|
|
|
return new ApplyContext(baseCtxt, isNonNullArray);
|
2012-02-17 20:27:03 +00:00
|
|
|
} else if (USE_ONE_LEVEL && tn.equals(CALL_TYPE_NAME)) {
|
|
|
|
return oneLevel.getCalleeTarget(caller, site, callee, receiver);
|
2012-01-06 21:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2012-02-17 20:21:18 +00:00
|
|
|
return baseCtxt;
|
2012-01-06 21:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|