WALA/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/callgraph/fieldbased/flowgraph/FlowGraph.java

562 lines
22 KiB
Java

/*******************************************************************************
* 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
*******************************************************************************/
package com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.analysis.pointers.HeapGraph;
import com.ibm.wala.cast.ipa.callgraph.AstHeapModel;
import com.ibm.wala.cast.ir.ssa.AstGlobalWrite;
import com.ibm.wala.cast.ir.ssa.AstPropertyWrite;
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.AbstractVertexVisitor;
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.CreationSiteVertex;
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.FuncVertex;
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.ObjectVertex;
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.PropVertex;
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.PrototypeFieldVertex;
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.PrototypeFieldVertex.PrototypeField;
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.UnknownVertex;
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.VarVertex;
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.Vertex;
import com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.vertices.VertexFactory;
import com.ibm.wala.cast.js.ssa.JavaScriptInvoke;
import com.ibm.wala.cast.js.ssa.JavaScriptPropertyWrite;
import com.ibm.wala.cast.js.ssa.SetPrototype;
import com.ibm.wala.cast.js.types.JavaScriptTypes;
import com.ibm.wala.cast.types.AstMethodReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.ProgramCounter;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.IAnalysisCacheView;
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
import com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey;
import com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey.TypeFilter;
import com.ibm.wala.ipa.callgraph.propagation.HeapModel;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.collections.CompoundIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Iterable;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.GraphReachability;
import com.ibm.wala.util.graph.GraphSlicer;
import com.ibm.wala.util.graph.NumberedGraph;
import com.ibm.wala.util.graph.impl.ExtensionGraph;
import com.ibm.wala.util.graph.impl.InvertedGraph;
import com.ibm.wala.util.graph.impl.SlowSparseNumberedGraph;
import com.ibm.wala.util.graph.traverse.DFS;
import com.ibm.wala.util.intset.OrdinalSet;
import com.ibm.wala.util.intset.OrdinalSetMapping;
/**
* A flow graph models data flow between vertices representing local variables, properties,
* return values, and so forth.
*
* @author mschaefer
*/
public class FlowGraph implements Iterable<Vertex> {
// the actual flow graph representation
private final NumberedGraph<Vertex> graph;
// a factory that allows us to build canonical vertices
private final VertexFactory factory;
// the transitive closure of the inverse of this.graph,
// but without paths going through the Unknown vertex
private GraphReachability<Vertex,FuncVertex> optimistic_closure;
public FlowGraph() {
this.graph = new SlowSparseNumberedGraph<>(1);
this.factory = new VertexFactory();
}
// (re-)compute optimistic_closure
private void compute_optimistic_closure(IProgressMonitor monitor) throws CancelException {
if(optimistic_closure != null)
return;
optimistic_closure = computeClosure(graph, monitor, FuncVertex.class);
}
private static <T> GraphReachability<Vertex, T> computeClosure(NumberedGraph<Vertex> graph, IProgressMonitor monitor, final Class<?> type) throws CancelException {
// prune flowgraph by taking out 'unknown' vertex
Graph<Vertex> pruned_flowgraph = GraphSlicer.prune(graph, t -> t.accept(new AbstractVertexVisitor<Boolean>() {
@Override
public Boolean visitVertex() {
return true;
}
@Override
public Boolean visitUnknownVertex(UnknownVertex unknownVertex) {
return false;
}
}));
// compute transitive closure
GraphReachability<Vertex, T> optimistic_closure =
new GraphReachability<>(
new InvertedGraph<>(pruned_flowgraph),
type::isInstance
);
optimistic_closure.solve(monitor);
return optimistic_closure;
}
public VertexFactory getVertexFactory() {
return factory;
}
/**
* Adds an edge from vertex <code>from</code> to vertex <code>to</code>, adding the vertices
* to the graph if they are not in there yet.
*/
public void addEdge(Vertex from, Vertex to) {
if(!graph.containsNode(from))
graph.addNode(from);
if(!graph.containsNode(to))
graph.addNode(to);
if(!graph.hasEdge(from, to)) {
optimistic_closure = null;
graph.addEdge(from, to);
}
}
/**
* Computes the set of vertices that may reach <code>dest</code> along paths not containing an
* {@link UnknownVertex}.
*/
public OrdinalSet<FuncVertex> getReachingSet(Vertex dest, IProgressMonitor monitor) throws CancelException {
if(!graph.containsNode(dest))
return OrdinalSet.empty();
compute_optimistic_closure(monitor);
return optimistic_closure.getReachableSet(dest);
}
public Iterator<Vertex> getSucc(Vertex v) {
return graph.getSuccNodes(v);
}
@Override
public Iterator<Vertex> iterator() {
return graph.iterator();
}
public PointerAnalysis<ObjectVertex> getPointerAnalysis(final CallGraph cg, final IAnalysisCacheView cache, final IProgressMonitor monitor) throws CancelException {
return new PointerAnalysis<ObjectVertex>() {
private final Map<Pair<PrototypeField,ObjectVertex>,PrototypeFieldVertex> proto = HashMapFactory.make();
private GraphReachability<Vertex,ObjectVertex> pointerAnalysis = computeClosure(graph, monitor, ObjectVertex.class);
private final ExtensionGraph<Vertex> dataflow = new ExtensionGraph<>(graph);
protected IR getIR(final IAnalysisCacheView cache, FuncVertex func) {
return cache.getIR(func.getConcreteType().getMethod(AstMethodReference.fnSelector));
}
private PointerKey propertyKey(String property, ObjectVertex o) {
if ("__proto__".equals(property) || "prototype".equals(property)) {
return get(PrototypeField.valueOf(property), o);
} else {
return factory.makePropVertex(property);
}
}
{
PropVertex proto = factory.makePropVertex("prototype");
if (graph.containsNode(proto)) {
for(Vertex p : Iterator2Iterable.make(graph.getPredNodes(proto))) {
if (p instanceof VarVertex) {
int rval = ((VarVertex) p).getValueNumber();
FuncVertex func = ((VarVertex) p).getFunction();
DefUse du = cache.getDefUse(getIR(cache, func));
for(SSAInstruction inst : Iterator2Iterable.make(du.getUses(rval))) {
if (inst instanceof JavaScriptPropertyWrite) {
int obj = ((AstPropertyWrite) inst).getObjectRef();
VarVertex object = factory.makeVarVertex(func, obj);
for(ObjectVertex o : getPointsToSet(object)) {
PrototypeFieldVertex prototype = get(PrototypeField.prototype, o);
if (! dataflow.containsNode(prototype)) {
dataflow.addNode(prototype);
}
System.err.println("adding " + p + " --> " + prototype);
dataflow.addEdge(p, prototype);
}
}
}
}
}
}
pointerAnalysis = computeClosure(dataflow, monitor, ObjectVertex.class);
}
private PrototypeFieldVertex get(PrototypeField f, ObjectVertex o) {
Pair<PrototypeField,ObjectVertex> key = Pair.make(f, o);
if (! proto.containsKey(key)) {
proto.put(key, new PrototypeFieldVertex(f, o));
}
return proto.get(key);
}
private FuncVertex getVertex(CGNode n) {
IMethod m = n.getMethod();
if (m.getSelector().equals(AstMethodReference.fnSelector)) {
IClass fun = m.getDeclaringClass();
return factory.makeFuncVertex(fun);
} else {
return null;
}
}
@Override
public OrdinalSet<ObjectVertex> getPointsToSet(PointerKey key) {
if (dataflow.containsNode((Vertex)key)) {
return pointerAnalysis.getReachableSet(key);
} else {
return OrdinalSet.empty();
}
}
@Override
public Collection<ObjectVertex> getInstanceKeys() {
Set<ObjectVertex> result = HashSetFactory.make();
for(CreationSiteVertex cs : factory.creationSites()) {
if (cg.getNode(cs.getMethod(), Everywhere.EVERYWHERE) != null) {
result.add(cs);
}
}
result.addAll(factory.getFuncVertices());
result.add(factory.global());
return result;
}
@Override
public boolean isFiltered(PointerKey pk) {
return false;
}
@Override
public OrdinalSetMapping<ObjectVertex> getInstanceKeyMapping() {
assert false;
return null;
}
@Override
public Iterable<PointerKey> getPointerKeys() {
return () -> new CompoundIterator<>(factory.getArgVertices().iterator(),
new CompoundIterator<>(factory.getRetVertices().iterator(),
new CompoundIterator<PointerKey>(factory.getVarVertices().iterator(),
factory.getPropVertices().iterator())));
}
@Override
public HeapModel getHeapModel() {
return new AstHeapModel() {
@Override
public PointerKey getPointerKeyForLocal(CGNode node, int valueNumber) {
FuncVertex function = getVertex(node);
if (function != null) {
return factory.makeVarVertex(function, valueNumber);
} else {
assert false;
return null;
}
}
@Override
public PointerKey getPointerKeyForReturnValue(CGNode node) {
FuncVertex function = getVertex(node);
if (function != null) {
return factory.makeRetVertex(function);
} else {
assert false;
return null;
}
}
@Override
public PointerKey getPointerKeyForInstanceField(InstanceKey I, IField field) {
String f = field.getName().toString();
if ("__proto__".equals(f) || "prototype".equals(f)) {
return get(PrototypeField.valueOf(f), (ObjectVertex)I);
} else {
return factory.makePropVertex(f);
}
}
@Override
public InstanceKey getInstanceKeyForAllocation(CGNode node, NewSiteReference allocation) {
// TODO Auto-generated method stub
return null;
}
@Override
public InstanceKey getInstanceKeyForMultiNewArray(CGNode node, NewSiteReference allocation, int dim) {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> InstanceKey getInstanceKeyForConstant(TypeReference type, T S) {
// TODO Auto-generated method stub
return null;
}
@Override
public InstanceKey getInstanceKeyForPEI(CGNode node, ProgramCounter instr, TypeReference type) {
// TODO Auto-generated method stub
return null;
}
@Override
public InstanceKey getInstanceKeyForMetadataObject(Object obj, TypeReference objType) {
// TODO Auto-generated method stub
return null;
}
@Override
public FilteredPointerKey getFilteredPointerKeyForLocal(CGNode node, int valueNumber, TypeFilter filter) {
// TODO Auto-generated method stub
return null;
}
@Override
public PointerKey getPointerKeyForExceptionalReturnValue(CGNode node) {
// TODO Auto-generated method stub
return null;
}
@Override
public PointerKey getPointerKeyForStaticField(IField f) {
// TODO Auto-generated method stub
return null;
}
@Override
public PointerKey getPointerKeyForArrayContents(InstanceKey I) {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterator<PointerKey> iteratePointerKeys() {
// TODO Auto-generated method stub
return null;
}
@Override
public IClassHierarchy getClassHierarchy() {
assert false;
return null;
}
@Override
public PointerKey getPointerKeyForArrayLength(InstanceKey I) {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterator<PointerKey> getPointerKeysForReflectedFieldRead(InstanceKey I, InstanceKey F) {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterator<PointerKey> getPointerKeysForReflectedFieldWrite(InstanceKey I, InstanceKey F) {
// TODO Auto-generated method stub
return null;
}
@Override
public PointerKey getPointerKeyForObjectCatalog(InstanceKey I) {
// TODO Auto-generated method stub
return null;
}
};
}
private HeapGraph<ObjectVertex> heapGraph;
@Override
public HeapGraph<ObjectVertex> getHeapGraph() {
if (heapGraph == null) {
final PointerAnalysis<ObjectVertex> pa = this;
class FieldBasedHeapGraph extends SlowSparseNumberedGraph<Object> implements HeapGraph<ObjectVertex> {
private static final long serialVersionUID = -3544629644808422215L;
private <X> X ensureNode(X n) {
if (!containsNode(n)) {
addNode(n);
}
return n;
}
private PropVertex getCoreProto(TypeReference coreType) {
if (coreType.equals(JavaScriptTypes.Object)) {
return factory.makePropVertex("Object$proto$__WALA__");
} else if (coreType.equals(JavaScriptTypes.Function)) {
return factory.makePropVertex("Function$proto$__WALA__");
} else if (coreType.equals(JavaScriptTypes.Number) || coreType.equals(JavaScriptTypes.NumberObject)) {
return factory.makePropVertex("Number$proto$__WALA__");
} else if (coreType.equals(JavaScriptTypes.Array)) {
return factory.makePropVertex("Array$proto$__WALA__");
} else if (coreType.equals(JavaScriptTypes.String) || coreType.equals(JavaScriptTypes.StringObject)) {
return factory.makePropVertex("String$proto$__WALA__");
} else if (coreType.equals(JavaScriptTypes.Date)) {
return factory.makePropVertex("Date$proto$__WALA__");
} else if (coreType.equals(JavaScriptTypes.RegExp) || coreType.equals(JavaScriptTypes.RegExpObject)) {
return factory.makePropVertex("RegExp$proto$__WALA__");
} else {
return null;
}
}
{
for(PropVertex property : factory.getPropVertices()) {
// edges from objects to properties assigned to them
for(Vertex p : Iterator2Iterable.make(dataflow.getPredNodes(property))) {
if (p instanceof VarVertex) {
int rval = ((VarVertex) p).getValueNumber();
FuncVertex func = ((VarVertex) p).getFunction();
DefUse du = cache.getDefUse(getIR(cache, func));
for(SSAInstruction inst : Iterator2Iterable.make(du.getUses(rval))) {
if (inst instanceof JavaScriptPropertyWrite) {
int obj = ((AstPropertyWrite) inst).getObjectRef();
VarVertex object = factory.makeVarVertex(func, obj);
for(ObjectVertex o : getPointsToSet(object)) {
addEdge(ensureNode(o), ensureNode(propertyKey(property.getPropName(), o)));
for(ObjectVertex v : getPointsToSet(property)) {
addEdge(ensureNode(propertyKey(property.getPropName(), o)), ensureNode(v));
}
}
} else if (inst instanceof AstGlobalWrite) {
addEdge(ensureNode(factory.global()), ensureNode(property));
for(ObjectVertex v : getPointsToSet(property)) {
addEdge(ensureNode(property), ensureNode(v));
}
} else if (inst instanceof SetPrototype) {
int obj = inst.getUse(0);
for(ObjectVertex o : getPointsToSet(factory.makeVarVertex(func, obj))) {
for(ObjectVertex v : getPointsToSet(property)) {
addEdge(ensureNode(o), ensureNode(get(PrototypeField.prototype, o)));
addEdge(ensureNode(get(PrototypeField.prototype, o)), ensureNode(v));
}
}
} else {
System.err.println("ignoring " + inst);
}
}
}
}
}
// prototype dataflow for function creations
for(FuncVertex f : factory.getFuncVertices()) {
ensureNode(get(PrototypeField.__proto__, f));
addEdge(
ensureNode(getCoreProto(JavaScriptTypes.Function)),
ensureNode(get(PrototypeField.prototype, f))
);
}
// prototype dataflow for object creations
for(CreationSiteVertex cs : factory.creationSites()) {
if (cg.getNode(cs.getMethod(), Everywhere.EVERYWHERE) != null) {
for(Pair<CGNode, NewSiteReference> site : Iterator2Iterable.make(cs.getCreationSites(cg))) {
IR ir = site.fst.getIR();
SSAInstruction creation = ir.getInstructions()[ site.snd.getProgramCounter() ];
if (creation instanceof JavaScriptInvoke) {
for(ObjectVertex f : getPointsToSet(factory.makeVarVertex(getVertex(site.fst), creation.getUse(0)))) {
for(ObjectVertex o : getPointsToSet(factory.makeVarVertex(getVertex(site.fst), creation.getDef(0)))) {
addEdge(
ensureNode(get(PrototypeField.prototype, f)),
ensureNode(get(PrototypeField.__proto__, o)));
}
}
} else if (creation instanceof SSANewInstruction) {
PointerKey proto = getCoreProto(((SSANewInstruction) creation).getConcreteType());
if (proto != null) {
for(ObjectVertex f : getPointsToSet(proto)) {
for(ObjectVertex o : getPointsToSet(factory.makeVarVertex(getVertex(site.fst), creation.getDef(0)))) {
addEdge(
ensureNode(get(PrototypeField.__proto__, o)),
ensureNode(f));
}
}
}
}
}
}
}
}
@Override
public Collection<Object> getReachableInstances(Set<Object> roots) {
return DFS.getReachableNodes(this, roots, ObjectVertex.class::isInstance);
}
@Override
public HeapModel getHeapModel() {
return pa.getHeapModel();
}
@Override
public PointerAnalysis<ObjectVertex> getPointerAnalysis() {
return pa;
}
}
heapGraph = new FieldBasedHeapGraph();
}
return heapGraph;
}
@Override
public IClassHierarchy getClassHierarchy() {
assert false;
return null;
}
};
}
}