/*
* This file is part of JOP, the Java Optimized Processor
* see <http://www.jopdesign.com/>
*
* Copyright (C) 2010-2011, Benedikt Huber (benedikt.huber@gmail.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.common.code;
import com.jopdesign.common.MethodInfo;
import com.jopdesign.common.code.ControlFlowGraph.CFGNode;
import com.jopdesign.common.code.ControlFlowGraph.CFGEdge;
import com.jopdesign.common.code.ControlFlowGraph.InvokeNode;
import com.jopdesign.common.code.ControlFlowGraph.ReturnNode;
import com.jopdesign.common.code.ControlFlowGraph.VirtualNode;
import com.jopdesign.common.code.ControlFlowGraph.VirtualNodeKind;
import com.jopdesign.common.graphutils.AdvancedDOTExporter;
import com.jopdesign.common.graphutils.Pair;
import com.jopdesign.common.graphutils.TopOrder;
import com.jopdesign.common.graphutils.AdvancedDOTExporter.DOTLabeller;
import com.jopdesign.common.misc.BadGraphException;
import com.jopdesign.common.misc.Filter;
import com.jopdesign.common.misc.MappedIterable;
import org.apache.log4j.Logger;
import org.jgrapht.graph.DirectedMultigraph;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Stack;
import java.util.Vector;
/**
* <p>A supergraph merges call graph and control flow graph representations.
* It has two kind of edges: intraprocedural edges {@code IntraEdge}, which
* connect CFG nodes within one CFG, and interprocedural edges ({@code SuperEdge}),
* which connect CFG nodes from different CFGs.</p>
* <p>There may be several instances of a control flow graph in the supergraph,
* which are distinguished by their call context</p>
*
* @author Benedikt Huber <benedikt.huber@gmail.com>
*/
public class SuperGraph {
/**
* Call contexts distinguish different instances of one control flow graph
* FIXME: Merge me with the callstring concept
*/
public static class CallContext implements CallStringProvider {
private CallString cs;
private int ccid;
public CallContext(CallString cs) {
this.cs = cs;
this.ccid = 0;
}
@Override
public CallString getCallString() {
return cs;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ccid;
result = prime * result + ((cs == null) ? 0 : cs.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CallContext other = (CallContext) obj;
if (ccid != other.ccid)
return false;
if (cs == null) {
if (other.cs != null)
return false;
} else if (!cs.equals(other.cs))
return false;
return true;
}
@Override
public String toString() {
if(ccid == 0) {
return cs.toString();
} else {
return cs.toString()+"/"+ccid;
}
}
}
/**
* Purpose: Represents one CFG instance (in a certain context) in the supergraph.
*
* FIXME: Merge me with callgraph node
*
* @author Benedikt Huber (benedikt@vmars.tuwien.ac.at)
*/
public class ContextCFG {
private ControlFlowGraph cfg;
private CallContext context;
public ContextCFG(ControlFlowGraph cfg, CallString cs) {
this(cfg, new CallContext(cs));
}
public ContextCFG(ControlFlowGraph cfg, CallContext ctx) {
this.cfg = cfg;
this.context = ctx;
}
/** @return the cfg */
public ControlFlowGraph getCfg() {
return cfg;
}
/** @return the callstring of this CFG instance */
public CallString getCallString() {
return context.getCallString();
}
/**
* @return the context, distinguishing two supergraph nodes for the
* same control flow graph
*/
public SuperGraph.CallContext getContext() {
return context;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((cfg == null) ? 0 : cfg.hashCode());
result = prime * result + ((context == null) ? 0 : context.hashCode());
return result;
}
@Override
@SuppressWarnings({"AccessingNonPublicFieldOfAnotherObject"})
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ContextCFG other = (ContextCFG) obj;
if (cfg == null) {
if (other.cfg != null)
return false;
} else if (!cfg.equals(other.cfg))
return false;
if (context == null) {
if (other.context != null)
return false;
} else if (!context.equals(other.context))
return false;
return true;
}
@Override
public String toString() {
return "CFG{" + cfg.getMethodInfo() + "," + context + "}";
}
}
/**
* Supergraph nodes are pairs(context-cfg, cfg-node).
*
* @author Benedikt Huber (benedikt@vmars.tuwien.ac.at)
*/
public static class SuperGraphNode {
private ContextCFG ccfg;
private CFGNode node;
public SuperGraphNode(ContextCFG ccfg, CFGNode node) {
this.ccfg = ccfg;
this.node = node;
}
public CFGNode getCFGNode() {
return node;
}
public ContextCFG getContextCFG() {
return this.ccfg;
}
@Override
public int hashCode() {
return ccfg.hashCode() * 31 + node.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
SuperGraphNode other = (SuperGraphNode) obj;
return (ccfg.equals(other.ccfg) && node.equals(other.node));
}
@Override
public String toString() {
return getCfg().getMethodInfo().getShortName() + "(" + node + ")";
}
/**
* @return return the CFG represented by the supergraph node
*/
public ControlFlowGraph getCfg() {
return ccfg.getCfg();
}
}
/**
* Edges in a supergraph, including interprocedural edges,
* and CFG edges.
*/
public interface SuperGraphEdge {
public SuperGraph getSuperGraph();
/**
* The source of a supergraph edge.
* <ul>
* <li/>For intraedges: the corresponding lifted source in the CFG
* <li/>For invoke edges: the lifted node at the call site
* <li/>For return edges: the lifted exit node in the invoked CFG
* @param current
* @return
*/
public abstract SuperGraphNode getSource();
/**
* The target of a supergraph edge.
* <ul>
* <li/>For intraedges: the corresponding lifted target in the CFG
* <li/>For invoke edges: the lifted entry node of the invoked function
* <li/>For return edges: the lifted return node at the call site
* @param current
* @return
*/
public abstract SuperGraphNode getTarget();
}
/**
* Intraprocedural edges in the supergraph
*/
public final class IntraEdge implements SuperGraphEdge {
private ContextCFG ccfg;
private CFGEdge cfgEdge;
public IntraEdge(ContextCFG ccfg, ControlFlowGraph.CFGEdge e) {
this.ccfg = ccfg;
this.cfgEdge = e;
}
public ContextCFG getContextCFG() {
return ccfg;
}
public ControlFlowGraph getCFG() {
return ccfg.getCfg();
}
public CallContext getCallContext() {
return ccfg.getContext();
}
public CFGEdge getCFGEdge() {
return cfgEdge;
}
/* the corresponding lifted source in the CFG */
@Override
public SuperGraphNode getSource() {
ContextCFG ccfg = getContextCFG();
CFGNode target = ccfg.getCfg().getEdgeSource(getCFGEdge());
return new SuperGraphNode(ccfg, target);
}
/* the corresponding lifted target in the CFG */
@Override
public SuperGraphNode getTarget() {
ContextCFG ccfg = getContextCFG();
CFGNode target = ccfg.getCfg().getEdgeTarget(getCFGEdge());
return new SuperGraphNode(ccfg, target);
}
@Override
public SuperGraph getSuperGraph() {
return SuperGraph.this;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
IntraEdge other = (IntraEdge) obj;
return(this.ccfg.equals(other.getContextCFG()) && this.getCFGEdge().equals(other.getCFGEdge()));
}
@Override
public int hashCode() {
return 31 * ccfg.hashCode() + cfgEdge.hashCode();
}
@Override
public String toString() {
return this.ccfg.getCfg().getMethodInfo().getShortName() + this.getCFGEdge().toString();
}
}
/**
* Interprocedural edges representing a method invocation or a method return
* There are explicitly represented in the supergraph
*/
public abstract class SuperEdge implements SuperGraphEdge {
private final ControlFlowGraph.InvokeNode invoker;
private SuperEdge(ControlFlowGraph.InvokeNode invokeNode) {
this.invoker = invokeNode;
}
public abstract boolean isReturnEdge();
public ControlFlowGraph.InvokeNode getInvokeNode() {
return invoker;
}
public abstract ContextCFG getCaller();
public abstract ContextCFG getCallee();
@Override
public SuperGraph getSuperGraph() {
return SuperGraph.this;
}
}
/**
* Edge representing a method invocation
*/
public final class SuperInvokeEdge extends SuperEdge {
private final int hashCode;
public SuperInvokeEdge(ControlFlowGraph.InvokeNode invokeNode, ContextCFG invoker, ContextCFG invoked) {
super(invokeNode);
hashCode = calculateHashCode(invoker, invoked);
}
@Override
public boolean isReturnEdge() {
return false;
}
/* the lifted node at the call site */
@Override
public SuperGraphNode getSource() {
ContextCFG ccfg = superGraph.getEdgeSource(this);
return new SuperGraphNode(ccfg, this.getInvokeNode());
}
/* the lifted entry node of the invoked function */
@Override
public SuperGraphNode getTarget() {
ContextCFG ccfg = superGraph.getEdgeTarget(this);
return new SuperGraphNode(ccfg, ccfg.getCfg().getEntry());
}
@Override
public ContextCFG getCaller() {
return superGraph.getEdgeSource(this);
}
@Override
public ContextCFG getCallee() {
return superGraph.getEdgeTarget(this);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
SuperInvokeEdge other = (SuperInvokeEdge) obj;
if (hashCode != other.hashCode) return false;
/* Should almost always be true */
return(this.getInvokeNode().equals(other.getInvokeNode()) &&
this.getCallee().equals(other.getCallee()) &&
this.getCaller().equals(other.getCaller()));
}
private int calculateHashCode(ContextCFG invoker, ContextCFG invoked) {
int hashCode = invoker.hashCode() ;
hashCode = hashCode * 37 + invoked.hashCode();
hashCode = hashCode * 37 + getInvokeNode().hashCode();
return hashCode;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("SupInvEdge@");
sb.append(hashCode());
sb.append("(");
sb.append(this.getCaller());
sb.append(" -> ");
sb.append(this.getCallee());
sb.append(")");
return sb.toString();
}
}
/**
* Edge representing return to the invoking method
*/
public final class SuperReturnEdge extends SuperEdge {
private final CFGNode returnNode;
private final int hashCode;
public SuperReturnEdge(ControlFlowGraph.InvokeNode invokeNode, ControlFlowGraph.CFGNode returnNode, ContextCFG invoker, ContextCFG invoked) {
super(invokeNode);
this.returnNode = returnNode;
this.hashCode = calculateHashCode(invoker, invoked);
}
/**
* Get the node control flow returns to after the invocation
*/
public CFGNode getReturnNode() {
return returnNode;
}
@Override
public boolean isReturnEdge() {
return true;
}
/* the lifted exit node in the invoked CFG */
@Override
public SuperGraphNode getSource() {
ContextCFG ccfg = superGraph.getEdgeSource(this);
return new SuperGraphNode(ccfg, ccfg.getCfg().getExit());
}
/* successors are the outgoing edges of the return node */
@Override
public SuperGraphNode getTarget() {
ContextCFG ccfg = superGraph.getEdgeTarget(this);
return new SuperGraphNode(ccfg, this.getReturnNode());
}
@Override
public ContextCFG getCaller() {
return superGraph.getEdgeTarget(this);
}
@Override
public ContextCFG getCallee() {
return superGraph.getEdgeSource(this);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
SuperReturnEdge other = (SuperReturnEdge) obj;
if (hashCode != other.hashCode) return false;
boolean r = (this.getReturnNode().equals(other.getReturnNode()) &&
this.getCallee().equals(other.getCallee()) &&
this.getCaller().equals(other.getCaller()));
return r;
}
private int calculateHashCode(ContextCFG invoker, ContextCFG invoked) {
int hashCode = invoker.hashCode() ;
hashCode = hashCode * 37 + invoked.hashCode();
hashCode = hashCode * 37 + getReturnNode().hashCode();
return hashCode;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("SupRetEdge@");
sb.append(hashCode());
sb.append("(");
sb.append(this.getCallee());
sb.append(" -> ");
sb.append(this.getCaller());
sb.append(")");
return sb.toString();
}
}
/**
* @return the <b>set</b> of all distinct CFG nodes in the supergraph
*/
public Iterable<CFGNode> cfgNodeSet() {
return new Iterable<CFGNode>() {
@Override
public Iterator<CFGNode> iterator() {
return new CFGNodeIterator();
}
};
}
private class CFGNodeIterator implements Iterator<CFGNode> {
private Iterator<ContextCFG> sgIterator;
private Iterator<CFGNode> nodeIterator;
private Set<ControlFlowGraph> cfgsVisited;
public CFGNodeIterator() {
this.sgIterator = superGraph.vertexSet().iterator();
this.cfgsVisited = new LinkedHashSet<ControlFlowGraph>();
if (sgIterator.hasNext()) {
sgIterator = null; // empty
} else {
ContextCFG firstNode = sgIterator.next();
cfgsVisited.add(firstNode.getCfg());
nodeIterator = firstNode.getCfg().vertexSet().iterator();
}
}
@Override
public boolean hasNext() {
if (sgIterator == null) return false;
return nodeIterator.hasNext();
}
@Override
public CFGNode next() {
if (!nodeIterator.hasNext()) throw new NoSuchElementException("Iterator.next(): no more CFG node");
CFGNode next = nodeIterator.next();
if (!nodeIterator.hasNext()) { /* See if there are more CFGs to investigate */
while (sgIterator.hasNext()) { /* Find unvisited super graph nodes */
ContextCFG nextCCFG = sgIterator.next();
if (!cfgsVisited.contains(nextCCFG.getCfg())) { /* If CFG has not been visited */
cfgsVisited.add(nextCCFG.getCfg()); /* Mark CFG as visited */
nodeIterator = nextCCFG.getCfg().vertexSet().iterator(); /* Reset Node iterator */
break;
}
}
}
return next;
}
@Override
public void remove() {
throw new UnsupportedOperationException("SuperGraph.CFGNodeIterator does not support remove");
}
}
/**
* Root node of the call graph
*/
private ContextCFG rootNode;
/**
* The JGraphT storage for the supergraph structure (build from CFGs and edges connecting control flow graphs)
*
* FIXME: should be replaced by a callgraph; to this end, we need to consolidate call context and callstring in
* the CallGraph datastructure.
*/
private DirectedMultigraph<ContextCFG, SuperEdge> superGraph;
private CFGProvider cfgProvider;
/**
* (invoke,return) edge pairs
*/
private Map<SuperInvokeEdge, SuperReturnEdge> superEdgePairs;
private int callstringLength;
private InfeasibleEdgeProvider infeasibleEdgeProvider;
public SuperGraph(CFGProvider cfgProvider, ControlFlowGraph rootFlowGraph, int callstringLength) {
this(cfgProvider, rootFlowGraph, CallString.EMPTY, callstringLength, InfeasibleEdgeProvider.NO_INFEASIBLES);
}
public SuperGraph(CFGProvider cfgProvider,
ControlFlowGraph rootCFG,
CallString rootCallString,
int callstringLength,
InfeasibleEdgeProvider infeasibles) {
this.cfgProvider = cfgProvider;
this.infeasibleEdgeProvider = infeasibles;
this.callstringLength = callstringLength;
this.rootNode = new ContextCFG(rootCFG, rootCallString);
this.superGraph = new DirectedMultigraph<ContextCFG, SuperEdge>(SuperEdge.class);
this.superEdgePairs = new LinkedHashMap<SuperInvokeEdge, SuperReturnEdge>();
createSuperGraph();
}
/**
* @return the CFG provider used for building the supergraph
*/
public CFGProvider getCFGProvider() {
return this.cfgProvider;
}
/**
* @return the infeasible edge provider used in this supergraph
*/
public InfeasibleEdgeProvider getInfeasibleEdgeProvider() {
return this.infeasibleEdgeProvider;
}
/**
* @return the maximum length of a callstring
*/
public int getCallStringLength() {
return this.callstringLength;
}
public DirectedMultigraph<ContextCFG, SuperEdge> getCallGraph() {
return superGraph;
}
public Set<ContextCFG> getCallGraphNodes() {
return superGraph.vertexSet();
}
public Set<SuperEdge> getCallGraphEdges() {
return superGraph.edgeSet();
}
public Map<SuperInvokeEdge, SuperReturnEdge> getSuperEdgePairs() {
return superEdgePairs;
}
/**
* The incoming edges are generated as follows:
* <ul>
* <li/>return node: superedge returning to the caller
* <li/>entry node: superedge invoking the node's method
* <li/>other: intraprocedural CFG edge
* </ul>
* @param node
* @return
*/
public Iterable<SuperGraphEdge> incomingEdgesOf(SuperGraphNode node) {
CFGNode cfgNode = node.getCFGNode();
if(cfgNode instanceof ReturnNode) {
/* return node: incoming edges are callgraph return edges */
final ReturnNode retNode = (ReturnNode) cfgNode;
Set<SuperEdge> cgReturnEdges = superGraph.incomingEdgesOf(node.getContextCFG());
return new Filter<SuperGraphEdge>() {
@Override
protected boolean include(SuperGraphEdge e) {
if(! (e instanceof SuperReturnEdge)) return false;
SuperReturnEdge retEdge = (SuperReturnEdge) e;
return retEdge.getReturnNode().equals(retNode);
}
}.<SuperGraphEdge>filter(cgReturnEdges);
} else if(cfgNode instanceof VirtualNode && ((VirtualNode)cfgNode).getKind() == VirtualNodeKind.ENTRY) {
/* entry node: superedge invoking the node's method */
Set<SuperEdge> cgInvokeEdges = superGraph.incomingEdgesOf(node.getContextCFG());
return new Filter<SuperGraphEdge>() {
@Override
protected boolean include(SuperGraphEdge e) {
return (e instanceof SuperInvokeEdge);
}
}.<SuperGraphEdge>filter(cgInvokeEdges);
} else {
/* standard edges: incoming edges of cfg node */
return liftCFGEdges(node.getContextCFG(), node.getContextCFG().getCfg().incomingEdgesOf(cfgNode));
}
}
/**
* The outgoing edges are generated as follows:
* <ul>
* <li/>invoke node: superedge invoking the callee
* <li/>exit node: superedge returning to the caller
* <li/>other: intraprocedural CFG edge
* </ul>
* @param node
* @return
*/
public Iterable<SuperGraphEdge> outgoingEdgesOf(SuperGraphNode node) {
CFGNode cfgNode = node.getCFGNode();
if(cfgNode instanceof InvokeNode) {
/* invoke node: outgoing SuperInvoke edges */
final InvokeNode invNode = (InvokeNode) cfgNode;
Set<SuperEdge> outgoingInvokeEdges = superGraph.outgoingEdgesOf(node.getContextCFG());
return new Filter<SuperGraphEdge>() {
@Override
protected boolean include(SuperGraphEdge e) {
if(! (e instanceof SuperInvokeEdge)) return false;
SuperInvokeEdge invoke = (SuperInvokeEdge) e;
return invoke.getInvokeNode().equals(invNode);
}
}.<SuperGraphEdge>filter(outgoingInvokeEdges);
} else if(cfgNode instanceof VirtualNode && ((VirtualNode)cfgNode).getKind() == VirtualNodeKind.EXIT) {
/* exit node: outgoing SuperReturn edges */
Set<SuperEdge> outgoingReturnEdges = superGraph.outgoingEdgesOf(node.getContextCFG());
return new Filter<SuperGraphEdge>() {
@Override
protected boolean include(SuperGraphEdge e) {
return (e instanceof SuperReturnEdge);
}
}.<SuperGraphEdge>filter(outgoingReturnEdges);
} else {
/* standard edges: outgoing edges of cfg node */
Set<CFGEdge> outgoingCFGEdges = node.getContextCFG().getCfg().outgoingEdgesOf(cfgNode);
return liftCFGEdges(node.getContextCFG(), outgoingCFGEdges);
}
}
/**
* <p>Get inter- and intraprocedural successor edges</p>
* <p>The successors of the edge e are those edges which have e's target as source
* @return all successors of the target of the given supergraph edge
*/
public Iterable<? extends SuperGraphEdge> getSuccessorEdges(SuperGraphEdge edge) {
return outgoingEdgesOf(edge.getTarget());
}
/**
* Get the intraprocedural entry edges for the given callgraph node
* @param node the callgraph node
* @return the CFG edges which start the execution of the given node
*/
public Iterable<SuperGraphEdge> getCFGEntryEdges(ContextCFG ccfg) {
ControlFlowGraph cfg = ccfg.getCfg();
return liftCFGEdges(ccfg, cfg.outgoingEdgesOf(cfg.getEntry()));
}
/**
* Get the intraprocedural exit edges for the given callgraph node
* @param node the callgraph node
* @return the CFG edges which end the execution of the given node
*/
public Iterable<SuperGraphEdge> getCFGExitEdges(ContextCFG node) {
ControlFlowGraph cfg = node.getCfg();
return liftCFGEdges(node, cfg.incomingEdgesOf(cfg.getExit()));
}
public Iterable<SuperGraphEdge> liftCFGEdges(final ContextCFG node, final Iterable<? extends CFGEdge> iEdges) {
return new MappedIterable<CFGEdge, SuperGraphEdge>(iEdges) {
@Override
public SuperGraphEdge map(CFGEdge e) {
return new IntraEdge(node, e);
}
};
}
/** Get all call sites located in the given cfg instance
* @param ccfg the calling cfg instance
* @return all list of invoke/return super edges from the given cfg instance
*/
public List<Pair<SuperInvokeEdge, SuperReturnEdge>> getCallSitesFrom(ContextCFG ccfg) {
Vector<Pair<SuperInvokeEdge, SuperReturnEdge>> callSites =
new Vector<Pair<SuperInvokeEdge, SuperReturnEdge>>();
for (SuperEdge e : superGraph.outgoingEdgesOf(ccfg)) {
if (e instanceof SuperInvokeEdge) {
SuperInvokeEdge ei = (SuperInvokeEdge) e;
callSites.add(new Pair<SuperInvokeEdge, SuperReturnEdge>(ei, superEdgePairs.get(ei)));
}
}
return callSites;
}
/** Get all call sites invoking the given cfg instance
* @param ccfg the cfg instance invoked
* @return all list of invoke/return super edges for the call site
*/
public List<Pair<SuperInvokeEdge, SuperReturnEdge>> getCallSitesInvoking(ContextCFG ccfg) {
Vector<Pair<SuperInvokeEdge, SuperReturnEdge>> callSites =
new Vector<Pair<SuperInvokeEdge, SuperReturnEdge>>();
for (SuperEdge e : superGraph.incomingEdgesOf(ccfg)) {
if (e instanceof SuperInvokeEdge) {
SuperInvokeEdge ei = (SuperInvokeEdge) e;
callSites.add(new Pair<SuperInvokeEdge, SuperReturnEdge>(ei, superEdgePairs.get(ei)));
}
}
return callSites;
}
/**
* @return return all callsite invoke/return superedge pairs, grouped by the invoked method
*/
public Map<MethodInfo, List<Pair<SuperInvokeEdge, SuperReturnEdge>>> getCallSites() {
Map<MethodInfo, List<Pair<SuperInvokeEdge, SuperReturnEdge>>> iMap =
new LinkedHashMap<MethodInfo, List<Pair<SuperInvokeEdge, SuperReturnEdge>>>();
for (ContextCFG node : superGraph.vertexSet()) {
List<Pair<SuperInvokeEdge, SuperReturnEdge>> callSites = getCallSitesInvoking(node);
MethodInfo invoked = node.getCfg().getMethodInfo();
if (iMap.containsKey(invoked)) {
callSites.addAll(iMap.get(invoked));
}
iMap.put(invoked, callSites);
}
return iMap;
}
private void createSuperGraph() {
Stack<ContextCFG> todo = new Stack<ContextCFG>();
todo.push(rootNode);
superGraph.addVertex(rootNode);
while (!todo.empty()) {
ContextCFG current = todo.pop();
if(! current.getCfg().areVirtualInvokesResolved()) {
throw new AssertionError("Virtual dispatch nodes not yet supported for supergraph (file a bug)");
}
ControlFlowGraph currentCFG = current.getCfg();
CallString currentCS = current.getCallString();
Collection<CFGEdge> infeasibleEdges = infeasibleEdgeProvider.getInfeasibleEdges(currentCFG, currentCS);
for (CFGNode node : current.getCfg().vertexSet()) {
if (node instanceof ControlFlowGraph.InvokeNode) {
/* skip node if all incoming edges are infeasible in the current call context */
boolean infeasible = true;
for(CFGEdge e : current.getCfg().incomingEdgesOf(node)) {
if(! infeasibleEdges.contains(e)) {
infeasible = false;
}
}
if(infeasible) continue;
ControlFlowGraph.InvokeNode iNode = (ControlFlowGraph.InvokeNode) node;
Set<MethodInfo> impls = iNode.getImplementingMethods();
if(impls.size() == 0) {
throw new AssertionError("No implementations for iNode available");
} else if(impls.size() != 1) {
throw new AssertionError("Unresolved virtual Dispatch for " + iNode + ": " + impls);
}
for(MethodInfo impl : impls) {
ControlFlowGraph invokedCFG = cfgProvider.getFlowGraph(impl);
CallString invokedCS = currentCS.push(iNode, callstringLength);
/* skip node if receiver is infeasible in current call context */
if(infeasibleEdgeProvider.isInfeasibleReceiver(impl, invokedCS)) {
Logger.getLogger(this.getClass()).info("createSuperGraph(): infeasible receiver "+impl);
continue;
}
ContextCFG invoked = new ContextCFG(invokedCFG, invokedCS);
if (!superGraph.containsVertex(invoked)) {
superGraph.addVertex(invoked);
todo.push(invoked);
}
addEdge(iNode, current, invoked);
}
}
}
}
}
private void addEdge(ControlFlowGraph.InvokeNode invokeNode, ContextCFG invoker, ContextCFG invoked) {
SuperInvokeEdge iEdge = new SuperInvokeEdge(invokeNode, invoker, invoked);
superGraph.addEdge(invoker, invoked, iEdge);
CFGEdge returnEdge;
Set<CFGEdge> outEdges = invoker.getCfg().outgoingEdgesOf(invokeNode);
if (outEdges.size() != 1) {
throw new AssertionError("SuperGraph: Outdegree of invoker node != 1 (Missing return node?)");
} else {
returnEdge = outEdges.iterator().next();
}
CFGNode returnNode = invoker.getCfg().getEdgeTarget(returnEdge);
if (invoker.getCfg().incomingEdgesOf(returnNode).size() != 1) {
throw new AssertionError("SuperGraph: Indegree of return node != 1 (Missing return node?)");
}
SuperReturnEdge rEdge = new SuperReturnEdge(invokeNode, returnNode, invoker, invoked);
superGraph.addEdge(invoked, invoker, rEdge);
superEdgePairs.put(iEdge, rEdge);
}
public void exportDOT(File dotFile) throws IOException {
FileWriter dotWriter = new FileWriter(dotFile);
AdvancedDOTExporter.DOTNodeLabeller<ContextCFG> nodeLabeller =
new AdvancedDOTExporter.DefaultNodeLabeller<ContextCFG>() {
@Override
public String getLabel(ContextCFG node) {
StringBuilder sb = new StringBuilder();
sb.append(node.getCfg().getMethodInfo().getFQMethodName()+"\n");
int i = 1;
for(InvokeSite is : node.getContext().getCallString()) {
sb.append(" #"+i+" "+is.getInvoker().getFQMethodName()+" / "+is.getInstructionHandle().getPosition()+"\n");
i+=1;
}
return sb.toString();
}
};
DOTLabeller<SuperEdge> edgeLabeller =
new AdvancedDOTExporter.DefaultDOTLabeller<SuperEdge>() {
@Override
public String getLabel(SuperEdge edge) {
return "";
}
@Override
public boolean setAttributes(SuperEdge edge,
Map<String, String> ht) {
super.setAttributes(edge, ht);
if (edge instanceof SuperReturnEdge) {
ht.put("style", "dotted");
ht.put("arrowhead", "empty");
}
return true;
}
};
AdvancedDOTExporter<ContextCFG, SuperEdge> de = new AdvancedDOTExporter<ContextCFG, SuperEdge>(
nodeLabeller, edgeLabeller );
de.exportDOT(dotWriter, this.superGraph);
dotWriter.close();
}
/**
* @return the root node of the supergraph
*/
public ContextCFG getRootNode() {
return this.rootNode;
}
/**
* If the graph is callgraph is acyclic, return a topological order iterator
*
* @return an iterator which yields N before M if there is a path of invoke edges from N to M
* @throws BadGraphException if the graph is not acyclic
*/
public TopOrder<ContextCFG, SuperEdge> topologicalOrderIterator() throws BadGraphException {
return new TopOrder<ContextCFG, SuperEdge>(superGraph, rootNode);
}
}