package jqian.sootex.util.callgraph; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import soot.MethodOrMethodContext; import soot.jimple.toolkits.callgraph.CallGraph; import soot.jimple.toolkits.callgraph.Edge; import soot.toolkits.graph.DirectedGraph; /** Turn a call graph into a DirectedGraph interface. */ @SuppressWarnings("unchecked") public class DirectedCallGraph implements DirectedGraph<MethodOrMethodContext>{ private Map<MethodOrMethodContext,List<MethodOrMethodContext>> _node2succs; private Map<MethodOrMethodContext,List<MethodOrMethodContext>> _node2preds; private List<MethodOrMethodContext> _heads; private List<MethodOrMethodContext> _tails; public DirectedCallGraph(CallGraph cg, Collection<MethodOrMethodContext> entries){ this(cg, entries, new CallGraphNodeFilter(), new CallGraphEdgeFilter()); } public DirectedCallGraph(CallGraph cg, Collection<MethodOrMethodContext> entries, CallGraphNodeFilter nodeFilter, CallGraphEdgeFilter edgeFilter){ _node2succs = new HashMap<MethodOrMethodContext,List<MethodOrMethodContext>>(); _node2preds = new HashMap<MethodOrMethodContext,List<MethodOrMethodContext>>(); _heads = new ArrayList<MethodOrMethodContext>(100); _tails = new ArrayList<MethodOrMethodContext>(100); Set<MethodOrMethodContext> allTgts = new HashSet<MethodOrMethodContext>(); for(Iterator<MethodOrMethodContext> it=cg.sourceMethods();it.hasNext();){ MethodOrMethodContext m = it.next(); if(nodeFilter.isIngored(m)){ continue; } Set<MethodOrMethodContext> predSet = new HashSet<MethodOrMethodContext>(); for(Iterator<Edge> eIt=cg.edgesInto(m);eIt.hasNext();){ Edge e = eIt.next(); MethodOrMethodContext src = e.getSrc(); if(!nodeFilter.isIngored(src) && !edgeFilter.isIgnored(e)){ predSet.add(src); } } Set<MethodOrMethodContext> succSet = new HashSet<MethodOrMethodContext>(); for(Iterator<Edge> eIt=cg.edgesOutOf(m);eIt.hasNext();){ Edge e = eIt.next(); MethodOrMethodContext tgt = e.getTgt(); if(!nodeFilter.isIngored(tgt) && !edgeFilter.isIgnored(e)){ succSet.add(tgt); } } List<MethodOrMethodContext> preds = new ArrayList<MethodOrMethodContext>(predSet); List<MethodOrMethodContext> succs = new ArrayList<MethodOrMethodContext>(succSet); _node2preds.put(m, preds); _node2succs.put(m, succs); if(preds.isEmpty()){ _heads.add(m); } if(succs.isEmpty()){ _tails.add(m); } allTgts.addAll(succSet); } // some nodes not reachable by sourceMethods() call for(MethodOrMethodContext m: allTgts){ if(_node2succs.get(m)!=null){ continue; } _node2succs.put(m, Collections.EMPTY_LIST); _tails.add(m); Set<MethodOrMethodContext> predSet = new HashSet<MethodOrMethodContext>(); for (Iterator<Edge> eIt = cg.edgesInto(m); eIt.hasNext();) { Edge e = eIt.next(); MethodOrMethodContext src = e.getSrc(); if (!nodeFilter.isIngored(src) && !edgeFilter.isIgnored(e)) { predSet.add(src); } } List<MethodOrMethodContext> preds = new ArrayList<MethodOrMethodContext>(predSet); _node2preds.put(m, preds); } //augment with entries methods that has not out coming edge for(MethodOrMethodContext m: entries){ if(_node2succs.get(m)==null){ _node2succs.put(m, Collections.EMPTY_LIST); } if(_node2preds.get(m)==null){ _node2preds.put(m, Collections.EMPTY_LIST); _heads.add(m); } } } public List<MethodOrMethodContext> getHeads(){ return _heads; } public List<MethodOrMethodContext> getTails(){ return _tails; } public List<MethodOrMethodContext> getPredsOf(MethodOrMethodContext s){ List<MethodOrMethodContext> preds = _node2preds.get(s); if(preds==null) return Collections.EMPTY_LIST; else return preds; } public List<MethodOrMethodContext> getSuccsOf(MethodOrMethodContext s){ List<MethodOrMethodContext> succs = _node2succs.get(s); if(succs==null) return Collections.EMPTY_LIST; else return succs; } public Iterator<MethodOrMethodContext> iterator(){ return _node2succs.keySet().iterator(); } public int size() { return _node2succs.keySet().size(); } }