2011-07-01 16:48:18 +00:00
|
|
|
/*******************************************************************************
|
|
|
|
* Copyright (c) 2008 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;
|
|
|
|
|
2011-07-02 15:14:57 +00:00
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
|
|
import com.ibm.wala.cast.ir.ssa.AbstractReflectiveGet;
|
2013-06-06 06:30:52 +00:00
|
|
|
import com.ibm.wala.cast.ir.ssa.AstIRFactory;
|
2012-01-06 21:35:38 +00:00
|
|
|
import com.ibm.wala.cast.ir.ssa.AstIsDefinedInstruction;
|
2013-06-03 04:18:29 +00:00
|
|
|
import com.ibm.wala.cast.js.ipa.callgraph.correlations.CorrelationFinder;
|
|
|
|
import com.ibm.wala.cast.js.ipa.callgraph.correlations.extraction.ClosureExtractor;
|
2012-01-06 21:19:14 +00:00
|
|
|
import com.ibm.wala.cast.js.types.JavaScriptTypes;
|
2011-07-01 16:48:18 +00:00
|
|
|
import com.ibm.wala.classLoader.CallSiteReference;
|
2012-01-06 21:19:14 +00:00
|
|
|
import com.ibm.wala.classLoader.IClass;
|
2011-07-01 16:48:18 +00:00
|
|
|
import com.ibm.wala.classLoader.IMethod;
|
2013-06-03 06:13:37 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.AnalysisCache;
|
2011-07-01 16:48:18 +00:00
|
|
|
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;
|
2013-06-06 06:30:52 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
|
2012-01-06 21:19:14 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.propagation.ConcreteTypeKey;
|
2012-02-17 20:26:24 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey.SingleInstanceFilter;
|
2012-02-17 20:26:36 +00:00
|
|
|
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
2011-07-02 15:14:57 +00:00
|
|
|
import com.ibm.wala.ssa.DefUse;
|
2013-06-06 06:30:52 +00:00
|
|
|
import com.ibm.wala.ssa.IR;
|
2012-01-06 21:34:54 +00:00
|
|
|
import com.ibm.wala.ssa.ReflectiveMemberAccess;
|
2011-07-02 15:14:57 +00:00
|
|
|
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
|
|
|
|
import com.ibm.wala.ssa.SSAGetInstruction;
|
|
|
|
import com.ibm.wala.ssa.SSAInstruction;
|
2013-06-06 06:30:52 +00:00
|
|
|
import com.ibm.wala.ssa.SSAOptions;
|
2012-01-06 21:35:38 +00:00
|
|
|
import com.ibm.wala.types.MethodReference;
|
2012-01-06 21:34:27 +00:00
|
|
|
import com.ibm.wala.util.collections.HashMapFactory;
|
|
|
|
import com.ibm.wala.util.collections.Iterator2Iterable;
|
2011-07-01 16:48:18 +00:00
|
|
|
import com.ibm.wala.util.intset.IntSet;
|
|
|
|
import com.ibm.wala.util.intset.IntSetUtil;
|
2011-07-02 15:14:57 +00:00
|
|
|
import com.ibm.wala.util.intset.MutableIntSet;
|
2011-07-01 16:48:18 +00:00
|
|
|
|
2013-06-03 04:18:29 +00:00
|
|
|
/**
|
|
|
|
* A context selector that applies object sensitivity for the i'th parameter
|
|
|
|
* if it is used as a property name in a dynamic property access.
|
|
|
|
*
|
|
|
|
* Works together with {@link CorrelationFinder} and {@link ClosureExtractor}
|
|
|
|
* to implement correlation tracking.
|
|
|
|
*/
|
2013-06-03 06:13:37 +00:00
|
|
|
public class PropertyNameContextSelector implements ContextSelector {
|
|
|
|
public final static ContextKey PROPNAME_KEY = new ContextKey() { };
|
|
|
|
public static final ContextItem PROPNAME_MARKER = new ContextItem() { };
|
2011-07-02 15:14:57 +00:00
|
|
|
|
2013-06-03 06:13:37 +00:00
|
|
|
public final static ContextKey PROPNAME_PARM_INDEX = new ContextKey() { };
|
2011-07-01 16:48:18 +00:00
|
|
|
|
2013-06-03 06:13:37 +00:00
|
|
|
/** Context representing a particular name accessed by a correlated read/write pair. */
|
|
|
|
public class PropNameContext extends SelectiveCPAContext {
|
|
|
|
PropNameContext(Context base, InstanceKey obj) {
|
2012-02-17 20:21:18 +00:00
|
|
|
super(base, Collections.singletonMap(ContextKey.PARAMETERS[index], obj));
|
2011-07-01 16:48:18 +00:00
|
|
|
}
|
2011-07-02 15:14:57 +00:00
|
|
|
|
2013-06-03 04:18:29 +00:00
|
|
|
@Override
|
2011-07-02 15:14:57 +00:00
|
|
|
public ContextItem get(ContextKey key) {
|
2013-06-03 06:13:37 +00:00
|
|
|
if (PROPNAME_KEY.equals(key)) {
|
|
|
|
return PROPNAME_MARKER;
|
|
|
|
} else if(PROPNAME_PARM_INDEX.equals(key)) {
|
2012-02-17 20:27:16 +00:00
|
|
|
return ContextItem.Value.make(index);
|
2011-07-01 16:48:18 +00:00
|
|
|
} else {
|
2011-07-02 15:14:57 +00:00
|
|
|
return super.get(key);
|
2011-07-01 16:48:18 +00:00
|
|
|
}
|
|
|
|
}
|
2011-07-02 15:14:57 +00:00
|
|
|
|
2011-07-01 16:48:18 +00:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
2013-06-03 06:13:37 +00:00
|
|
|
return "property name context for " + get(ContextKey.PARAMETERS[index]) + " over " + this.base;
|
2011-07-01 16:48:18 +00:00
|
|
|
}
|
2012-02-17 20:26:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* get the {@link InstanceKey} used to distinguish this context
|
|
|
|
*/
|
2012-02-17 20:26:24 +00:00
|
|
|
public InstanceKey getInstanceKey() {
|
|
|
|
return ((SingleInstanceFilter)get(ContextKey.PARAMETERS[index])).getInstance();
|
|
|
|
}
|
2011-07-01 16:48:18 +00:00
|
|
|
}
|
2012-02-17 20:26:24 +00:00
|
|
|
|
2012-02-17 20:26:36 +00:00
|
|
|
/**
|
|
|
|
* A "dummy" for-in context used for callees of a method analyzed in a real
|
2013-06-03 06:13:37 +00:00
|
|
|
* {@link PropNameContext}. The purpose of this class is to clone callees based
|
2012-02-17 20:26:36 +00:00
|
|
|
* on the same {@link InstanceKey} used for the caller context, but without
|
|
|
|
* returning a {@link SingleInstanceFilter} {@link ContextItem} that filters
|
|
|
|
* possible parameter values.
|
|
|
|
*/
|
2013-06-03 06:13:37 +00:00
|
|
|
class MarkerForInContext extends PropNameContext {
|
2012-02-17 20:26:24 +00:00
|
|
|
|
|
|
|
MarkerForInContext(Context base, InstanceKey obj) {
|
|
|
|
super(base, obj);
|
|
|
|
}
|
|
|
|
|
2012-02-17 20:26:36 +00:00
|
|
|
/**
|
2013-06-03 06:13:37 +00:00
|
|
|
* Like {@link PropNameContext#get(ContextKey)}, but don't return a
|
2012-02-17 20:26:36 +00:00
|
|
|
* {@link SingleInstanceFilter} for the distinguishing {@link InstanceKey}
|
|
|
|
*/
|
2012-02-17 20:26:24 +00:00
|
|
|
@Override
|
|
|
|
public ContextItem get(ContextKey key) {
|
|
|
|
final ContextItem contextItem = super.get(key);
|
|
|
|
return (contextItem instanceof SingleInstanceFilter) ? null : contextItem;
|
|
|
|
}
|
|
|
|
|
2012-02-17 20:26:36 +00:00
|
|
|
/**
|
|
|
|
* we need to override this method since
|
|
|
|
* {@link MarkerForInContext#get(ContextKey)} does not return the
|
|
|
|
* {@link SingleInstanceFilter} containing the {@link InstanceKey}. Instead,
|
2013-06-03 06:13:37 +00:00
|
|
|
* we invoke {@link PropNameContext#get(ContextKey)} from the superclass.
|
2012-02-17 20:26:36 +00:00
|
|
|
*/
|
2012-02-17 20:26:24 +00:00
|
|
|
@Override
|
|
|
|
public InstanceKey getInstanceKey() {
|
|
|
|
return ((SingleInstanceFilter)super.get(ContextKey.PARAMETERS[index])).getInstance();
|
|
|
|
}
|
2011-07-02 15:14:57 +00:00
|
|
|
|
2012-02-17 20:26:24 +00:00
|
|
|
}
|
2013-06-03 04:18:29 +00:00
|
|
|
|
2013-06-03 06:13:37 +00:00
|
|
|
private final AnalysisCache cache;
|
2012-02-17 20:21:18 +00:00
|
|
|
private final ContextSelector base;
|
2012-01-06 21:49:08 +00:00
|
|
|
private final int index;
|
2011-07-02 15:14:57 +00:00
|
|
|
|
|
|
|
private void collectValues(DefUse du, SSAInstruction inst, MutableIntSet values) {
|
|
|
|
if (inst instanceof SSAGetInstruction) {
|
|
|
|
SSAGetInstruction g = (SSAGetInstruction) inst;
|
|
|
|
values.add(g.getRef());
|
|
|
|
if (g.getRef() != -1) {
|
|
|
|
collectValues(du, du.getDef(g.getRef()), values);
|
|
|
|
}
|
|
|
|
} else if (inst instanceof AbstractReflectiveGet) {
|
|
|
|
AbstractReflectiveGet g = (AbstractReflectiveGet) inst;
|
|
|
|
values.add(g.getObjectRef());
|
|
|
|
collectValues(du, du.getDef(g.getObjectRef()), values);
|
|
|
|
values.add(g.getMemberRef());
|
|
|
|
collectValues(du, du.getDef(g.getMemberRef()), values);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private IntSet identifyDependentParameters(CGNode caller, CallSiteReference site) {
|
|
|
|
MutableIntSet dependentParameters = IntSetUtil.make();
|
|
|
|
SSAAbstractInvokeInstruction inst = caller.getIR().getCalls(site)[0];
|
|
|
|
DefUse du = caller.getDU();
|
2013-06-03 06:13:37 +00:00
|
|
|
|
2011-07-02 15:14:57 +00:00
|
|
|
for(int i = 0; i < inst.getNumberOfParameters(); i++) {
|
|
|
|
MutableIntSet values = IntSetUtil.make();
|
|
|
|
values.add(inst.getUse(i));
|
2013-06-03 06:13:37 +00:00
|
|
|
collectValues(du, du.getDef(inst.getUse(i)), values);
|
|
|
|
if (values.contains(index+1))
|
2011-07-02 15:14:57 +00:00
|
|
|
dependentParameters.add(i);
|
|
|
|
}
|
2013-06-03 06:13:37 +00:00
|
|
|
|
2011-07-02 15:14:57 +00:00
|
|
|
return dependentParameters;
|
|
|
|
}
|
2011-07-01 16:48:18 +00:00
|
|
|
|
2013-06-03 06:13:37 +00:00
|
|
|
public PropertyNameContextSelector(AnalysisCache cache, ContextSelector base) {
|
|
|
|
this(cache, 2, base);
|
2012-01-06 21:49:08 +00:00
|
|
|
}
|
|
|
|
|
2013-06-03 06:13:37 +00:00
|
|
|
public PropertyNameContextSelector(AnalysisCache cache, int index, ContextSelector base) {
|
|
|
|
this.cache = cache;
|
2012-01-06 21:49:08 +00:00
|
|
|
this.index = index;
|
2012-02-17 20:21:18 +00:00
|
|
|
this.base = base;
|
2011-07-02 15:14:57 +00:00
|
|
|
}
|
2012-01-06 21:19:14 +00:00
|
|
|
|
2012-01-06 21:35:08 +00:00
|
|
|
private enum Frequency { NEVER, SOMETIMES, ALWAYS };
|
2012-01-06 21:35:38 +00:00
|
|
|
private final HashMap<MethodReference, Frequency> usesFirstArgAsPropertyName_cache = HashMapFactory.make();
|
2012-01-06 21:35:08 +00:00
|
|
|
|
2013-06-03 06:13:37 +00:00
|
|
|
/** Determine whether the method never/sometimes/always uses its first argument as a property name. */
|
2012-01-06 21:35:08 +00:00
|
|
|
private Frequency usesFirstArgAsPropertyName(IMethod method) {
|
2012-01-06 21:35:38 +00:00
|
|
|
MethodReference mref = method.getReference();
|
2012-01-06 21:49:08 +00:00
|
|
|
if(method.getNumberOfParameters() < index)
|
2012-01-06 21:35:08 +00:00
|
|
|
return Frequency.NEVER;
|
2012-01-06 21:35:38 +00:00
|
|
|
Frequency f = usesFirstArgAsPropertyName_cache.get(mref);
|
2012-01-06 21:35:08 +00:00
|
|
|
if(f != null)
|
|
|
|
return f;
|
|
|
|
boolean usedAsPropertyName = false, usedAsSomethingElse = false;
|
2013-06-03 06:13:37 +00:00
|
|
|
DefUse du = cache.getDefUse(cache.getIR(method));
|
2012-02-17 20:16:36 +00:00
|
|
|
for(SSAInstruction use : Iterator2Iterable.make(du.getUses(index+1))) {
|
2012-01-06 21:34:54 +00:00
|
|
|
if(use instanceof ReflectiveMemberAccess) {
|
|
|
|
ReflectiveMemberAccess rma = (ReflectiveMemberAccess)use;
|
2012-02-17 20:16:36 +00:00
|
|
|
if(rma.getMemberRef() == index+1) {
|
2012-01-06 21:35:08 +00:00
|
|
|
usedAsPropertyName = true;
|
|
|
|
continue;
|
2012-01-06 21:34:54 +00:00
|
|
|
}
|
2012-01-06 21:35:38 +00:00
|
|
|
} else if(use instanceof AstIsDefinedInstruction) {
|
|
|
|
AstIsDefinedInstruction aidi = (AstIsDefinedInstruction)use;
|
2012-02-17 20:16:36 +00:00
|
|
|
if(aidi.getNumberOfUses() > 1 && aidi.getUse(1) == index+1) {
|
2012-01-06 21:35:38 +00:00
|
|
|
usedAsPropertyName = true;
|
|
|
|
continue;
|
|
|
|
}
|
2012-01-06 21:34:54 +00:00
|
|
|
}
|
2012-01-06 21:35:08 +00:00
|
|
|
usedAsSomethingElse = true;
|
2012-01-06 21:34:54 +00:00
|
|
|
}
|
2012-01-06 21:35:08 +00:00
|
|
|
if(!usedAsPropertyName)
|
|
|
|
f = Frequency.NEVER;
|
|
|
|
else if(usedAsSomethingElse)
|
|
|
|
f = Frequency.SOMETIMES;
|
|
|
|
else
|
|
|
|
f = Frequency.ALWAYS;
|
2012-01-06 21:35:38 +00:00
|
|
|
usesFirstArgAsPropertyName_cache.put(mref, f);
|
2012-01-06 21:35:08 +00:00
|
|
|
return f;
|
2012-01-06 21:34:54 +00:00
|
|
|
}
|
2012-01-06 21:35:08 +00:00
|
|
|
|
2011-07-01 16:48:18 +00:00
|
|
|
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, final InstanceKey[] receiver) {
|
2012-02-17 20:21:18 +00:00
|
|
|
Context baseContext = base.getCalleeTarget(caller, site, callee, receiver);
|
2013-06-03 06:13:37 +00:00
|
|
|
|
2013-06-03 04:18:29 +00:00
|
|
|
if(receiver.length > index) {
|
2012-01-06 21:43:00 +00:00
|
|
|
Frequency f = usesFirstArgAsPropertyName(callee);
|
2013-04-09 22:47:22 +00:00
|
|
|
if(f == Frequency.ALWAYS|| f == Frequency.SOMETIMES) {
|
2012-01-06 21:50:22 +00:00
|
|
|
if(receiver[index] == null) {
|
|
|
|
IClass undef = caller.getClassHierarchy().lookupClass(JavaScriptTypes.Undefined);
|
2013-06-03 06:13:37 +00:00
|
|
|
return new PropNameContext(baseContext, new ConcreteTypeKey(undef));
|
2012-01-06 21:50:22 +00:00
|
|
|
} else {
|
2013-06-03 06:13:37 +00:00
|
|
|
return new PropNameContext(baseContext, receiver[index]);
|
2012-01-06 21:50:22 +00:00
|
|
|
}
|
2013-04-09 22:47:22 +00:00
|
|
|
}
|
2012-01-06 21:35:08 +00:00
|
|
|
}
|
2013-06-03 06:13:37 +00:00
|
|
|
|
|
|
|
if (PROPNAME_MARKER.equals(caller.getContext().get(PROPNAME_KEY))) {
|
|
|
|
if (!identifyDependentParameters(caller, site).isEmpty()) {
|
2012-02-17 20:26:36 +00:00
|
|
|
// use a MarkerForInContext to clone based on the InstanceKey used in the caller context
|
|
|
|
// TODO the cast below isn't safe; fix
|
2013-06-03 06:13:37 +00:00
|
|
|
InstanceKey callerIk = ((PropNameContext)caller.getContext()).getInstanceKey();
|
2012-02-17 20:26:24 +00:00
|
|
|
return new MarkerForInContext(baseContext, callerIk);
|
2011-07-02 15:14:57 +00:00
|
|
|
} else {
|
2012-02-17 20:21:18 +00:00
|
|
|
return baseContext;
|
2011-07-02 15:14:57 +00:00
|
|
|
}
|
2012-02-17 20:17:44 +00:00
|
|
|
}
|
2012-02-17 20:21:18 +00:00
|
|
|
return baseContext;
|
2011-07-01 16:48:18 +00:00
|
|
|
}
|
|
|
|
|
2012-02-17 20:26:24 +00:00
|
|
|
|
2011-07-01 16:48:18 +00:00
|
|
|
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
|
2013-06-03 04:18:29 +00:00
|
|
|
if (caller.getIR().getCalls(site)[0].getNumberOfUses() > index) {
|
2012-02-17 20:21:18 +00:00
|
|
|
return IntSetUtil.make(new int[]{index}).union(base.getRelevantParameters(caller, site));
|
2011-07-01 16:48:18 +00:00
|
|
|
} else {
|
2012-02-17 20:21:18 +00:00
|
|
|
return base.getRelevantParameters(caller, site);
|
2011-07-01 16:48:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|