/******************************************************************************* * 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.ir.ssa.AstGlobalWrite; 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.AnalysisCache; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.CallGraph; 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.Predicate; import com.ibm.wala.util.collections.HashMapFactory; import com.ibm.wala.util.collections.HashSetFactory; 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<Vertex>(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 <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, new Predicate<Vertex>() { @Override public boolean test(Vertex t) { return t.accept(new AbstractVertexVisitor<Boolean>() { @Override public Boolean visitVertex(Vertex vertex) { return true; } @Override public Boolean visitUnknownVertex(UnknownVertex unknownVertex) { return false; } }); } }); // compute transitive closure GraphReachability<Vertex, T> optimistic_closure = new GraphReachability<Vertex,T>( new InvertedGraph<Vertex>(pruned_flowgraph), new Predicate<Vertex>() { @Override public boolean test(Vertex o) { return type.isInstance(o); } } ); 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 AnalysisCache 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<Vertex>(graph); protected IR getIR(final AnalysisCache 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(Iterator<Vertex> ps = graph.getPredNodes(proto); ps.hasNext(); ) { Vertex p = ps.next(); if (p instanceof VarVertex) { int rval = ((VarVertex) p).getValueNumber(); FuncVertex func = ((VarVertex) p).getFunction(); DefUse du = cache.getDefUse(getIR(cache, func)); for(Iterator<SSAInstruction> insts = du.getUses(rval); insts.hasNext(); ) { SSAInstruction inst = insts.next(); if (inst instanceof JavaScriptPropertyWrite) { int obj = ((JavaScriptPropertyWrite) 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() { // TODO Auto-generated method stub return null; } @Override public HeapModel getHeapModel() { return new HeapModel() { @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; } }; } 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 <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(Iterator<Vertex> ps = dataflow.getPredNodes(property); ps.hasNext(); ) { Vertex p = ps.next(); if (p instanceof VarVertex) { int rval = ((VarVertex) p).getValueNumber(); FuncVertex func = ((VarVertex) p).getFunction(); DefUse du = cache.getDefUse(getIR(cache, func)); for(Iterator<SSAInstruction> insts = du.getUses(rval); insts.hasNext(); ) { SSAInstruction inst = insts.next(); if (inst instanceof JavaScriptPropertyWrite) { int obj = ((JavaScriptPropertyWrite) 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(Iterator<Pair<CGNode, NewSiteReference>> sites = cs.getCreationSites(cg); sites.hasNext(); ) { Pair<CGNode, NewSiteReference> site = sites.next(); 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, new Predicate<Object>() { @Override public boolean test(Object o) { return o instanceof ObjectVertex; } }); } @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; } }; } }