/**
* Copyright (C) 2013 Rohan Padhye
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package vasco.callgraph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import soot.Kind;
import soot.Scene;
import soot.SceneTransformer;
import soot.SootMethod;
import soot.Unit;
import soot.jimple.InvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.toolkits.callgraph.CallGraph;
import soot.jimple.toolkits.callgraph.ContextSensitiveCallGraph;
import soot.jimple.toolkits.callgraph.ContextSensitiveEdge;
import soot.jimple.toolkits.callgraph.Edge;
import vasco.CallSite;
import vasco.Context;
import vasco.ContextTransitionTable;
/**
* A Soot {@link SceneTransformer} for performing {@link PointsToAnalysis}.
*
* @author Rohan Padhye
*/
public class CallGraphTransformer extends SceneTransformer {
private PointsToAnalysis pointsToAnalysis;
/**
* {@inheritDoc}
*/
@SuppressWarnings("deprecation")
@Override
protected void internalTransform(String arg0, @SuppressWarnings("rawtypes") Map arg1) {
// Perform the points-to analysis
pointsToAnalysis = new PointsToAnalysis();
pointsToAnalysis.doAnalysis();
// Use the context transition table generated by the analysis to construct soot call graphs
final ContextTransitionTable<SootMethod, Unit, PointsToGraph> ctt = pointsToAnalysis.getContextTransitionTable();
// Initialize collections (for creating the soot context-sensitive call graph)
final Set<SootMethod> allMethods = pointsToAnalysis.getMethods();
final Map<Context<SootMethod, Unit, PointsToGraph>, Collection<ContextSensitiveEdge>> csEdgesIntoContext = new HashMap<Context<SootMethod, Unit, PointsToGraph>, Collection<ContextSensitiveEdge>>();
final Map<Context<SootMethod, Unit, PointsToGraph>, Collection<ContextSensitiveEdge>> csEdgesOutOfContext = new HashMap<Context<SootMethod, Unit, PointsToGraph>, Collection<ContextSensitiveEdge>>();
final Map<CallSite<SootMethod, Unit, PointsToGraph>, Collection<ContextSensitiveEdge>> csEdgesOutOfCallSite = new HashMap<CallSite<SootMethod, Unit, PointsToGraph>, Collection<ContextSensitiveEdge>>();
final Collection<ContextSensitiveEdge> csEdges = new ArrayList<ContextSensitiveEdge>();
// Initialize the context-insensitive call graph
CallGraph callGraph = new CallGraph();
// Create soot-style edges for every context transition
for (Map.Entry<CallSite<SootMethod, Unit, PointsToGraph>, Map<SootMethod, Context<SootMethod, Unit, PointsToGraph>>> e : ctt.getTransitions().entrySet()) {
CallSite<SootMethod, Unit, PointsToGraph> cs = e.getKey();
final Context<SootMethod, Unit, PointsToGraph> sourceContext = cs.getCallingContext();
final SootMethod sourceMethod = sourceContext.getMethod();
final Unit stmt = cs.getCallNode();
final Map<SootMethod, Context<SootMethod, Unit, PointsToGraph>> targets = e.getValue();
for (final SootMethod targetMethod : targets.keySet()) {
final Context<SootMethod, Unit, PointsToGraph> targetContext = targets.get(targetMethod);
Kind k;
if (stmt instanceof InvokeExpr) {
k = Edge.ieToKind((InvokeExpr) stmt);
} else {
k = Kind.INVALID;
}
// The context-insensitive edge
Edge cgEdge = new Edge(sourceMethod, stmt, targetMethod, k);
// Add it to the context-insensitive call-graph
callGraph.addEdge(cgEdge);
// The context-sensitive edge
ContextSensitiveEdge csEdge = new ContextSensitiveEdge() {
@Override
public Kind kind() {
if (stmt instanceof InvokeExpr) {
return Edge.ieToKind((InvokeExpr) stmt);
} else {
return Kind.INVALID;
}
}
@Override
public SootMethod src() {
return sourceMethod;
}
@Override
public soot.Context srcCtxt() {
return sourceContext;
}
@Override
public Stmt srcStmt() {
return (Stmt) stmt;
}
@Override
public Unit srcUnit() {
return stmt;
}
@Override
public SootMethod tgt() {
return targetMethod;
}
@Override
public soot.Context tgtCtxt() {
return targetContext;
}
};
// Add this in all the collections
csEdges.add(csEdge);
if (!csEdgesOutOfContext.containsKey(sourceContext))
csEdgesOutOfContext.put(sourceContext, new ArrayList<ContextSensitiveEdge>());
csEdgesOutOfContext.get(sourceContext).add(csEdge);
if (!csEdgesOutOfCallSite.containsKey(cs))
csEdgesOutOfCallSite.put(cs, new ArrayList<ContextSensitiveEdge>());
csEdgesOutOfCallSite.get(cs).add(csEdge);
if (!csEdgesIntoContext.containsKey(targetContext))
csEdgesIntoContext.put(targetContext, new ArrayList<ContextSensitiveEdge>());
csEdgesIntoContext.get(targetContext).add(csEdge);
}
}
// Set the scene's context-insensitive call-graph to what we just created
Scene.v().setCallGraph(callGraph);
// Set the scene's context-sensitive call graph to one that we construct on-the-fly using the above collections
Scene.v().setContextSensitiveCallGraph(new ContextSensitiveCallGraph() {
@SuppressWarnings("unchecked")
private Context<SootMethod, Unit, PointsToGraph> vContext(soot.Context sContext) {
return (Context<SootMethod, Unit, PointsToGraph>) sContext;
}
private CallSite<SootMethod, Unit, PointsToGraph> vCallSite(soot.Context sContext, Unit unit) {
return new CallSite<SootMethod, Unit, PointsToGraph>(vContext(sContext), unit);
}
@Override
public Iterator<?> edgesOutOf(soot.Context sContext, SootMethod m, Unit stmt) {
return csEdgesOutOfCallSite.get((vCallSite(sContext, stmt))).iterator();
}
@Override
public Iterator<?> edgesOutOf(soot.Context sContext, SootMethod m) {
return csEdgesOutOfContext.get(vContext(sContext)).iterator();
}
@Override
public Iterator<?> edgesInto(soot.Context sContext, SootMethod m) {
return csEdgesIntoContext.get(vContext(sContext)).iterator();
}
@Override
public Iterator<?> edgeSources() {
return allMethods.iterator();
}
@Override
public Iterator<?> allEdges() {
return csEdges.iterator();
}
});
}
/**
* Returns a reference to the {@link PointsToAnalysis} object.
* @return a reference to the {@link PointsToAnalysis} object
*/
public PointsToAnalysis getPointsToAnalysis() {
return pointsToAnalysis;
}
}