com.ibm.wala.cast.js.callgraph.fieldbased.flowgraph.FlowGraph Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.ibm.wala.cast.js Show documentation
Show all versions of com.ibm.wala.cast.js Show documentation
T. J. Watson Libraries for Analysis
/*
* 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 com.google.gson.Gson;
import com.google.gson.GsonBuilder;
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.AstIRFactory;
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.AnalysisCacheImpl;
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.MapUtil;
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;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* A flow graph models data flow between vertices representing local variables, properties, return
* values, and so forth.
*
* @author mschaefer
*/
public class FlowGraph implements Iterable {
// the actual flow graph representation
private final NumberedGraph 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 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 GraphReachability computeClosure(
NumberedGraph graph, IProgressMonitor monitor, final Class> type)
throws CancelException {
// prune flowgraph by taking out 'unknown' vertex
Graph pruned_flowgraph =
GraphSlicer.prune(
graph,
t ->
t.accept(
new AbstractVertexVisitor<>() {
@Override
public Boolean visitVertex() {
return true;
}
@Override
public Boolean visitUnknownVertex(UnknownVertex unknownVertex) {
return false;
}
}));
// compute transitive closure
GraphReachability 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} to vertex {@code to}, 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} along paths not containing an {@link
* UnknownVertex}.
*/
public OrdinalSet 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 getSucc(Vertex v) {
return graph.getSuccNodes(v);
}
@Override
public Iterator iterator() {
return graph.iterator();
}
public PointerAnalysis getPointerAnalysis(
final CallGraph cg, final IAnalysisCacheView cache, final IProgressMonitor monitor)
throws CancelException {
return new PointerAnalysis<>() {
private final Map, PrototypeFieldVertex> proto =
HashMapFactory.make();
private GraphReachability pointerAnalysis;
private final ExtensionGraph dataflow = new ExtensionGraph<>(graph);
private 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);
}
}
{
// This initial value of `pointerAnalysis` is used by the `getPointsToSet` call below.
pointerAnalysis = computeClosure(graph, monitor, ObjectVertex.class);
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);
}
dataflow.addEdge(p, prototype);
}
}
}
}
}
}
pointerAnalysis = computeClosure(dataflow, monitor, ObjectVertex.class);
}
private PrototypeFieldVertex get(PrototypeField f, ObjectVertex o) {
Pair 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 getPointsToSet(PointerKey key) {
if (dataflow.containsNode((Vertex) key)) {
return pointerAnalysis.getReachableSet(key);
} else {
return OrdinalSet.empty();
}
}
@Override
public Collection getInstanceKeys() {
Set 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 getInstanceKeyMapping() {
assert false;
return null;
}
@Override
public Iterable getPointerKeys() {
return () ->
new CompoundIterator<>(
factory.getArgVertices().iterator(),
new CompoundIterator<>(
factory.getRetVertices().iterator(),
new CompoundIterator(
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 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 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 getPointerKeysForReflectedFieldRead(
InstanceKey I, InstanceKey F) {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterator 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 heapGraph;
@Override
public HeapGraph getHeapGraph() {
if (heapGraph == null) {
final PointerAnalysis pa = this;
class FieldBasedHeapGraph extends SlowSparseNumberedGraph