package jqian.sootex.util.callgraph;
import java.util.*;
import jqian.sootex.util.SootUtils;
import soot.*;
import soot.jimple.toolkits.callgraph.*;
/**
* A framework for worklist algorithm on the call graph
*/
public class CallGraphWorklist {
private final MethodVisitor _visitor;
private final CallGraph _cg;
private List<SootMethod> _topoOrder;
/**
* Note that when visiting a method at the first time, the return of visit()
* should better be true, given the data of m is not empty.
*/
public static interface MethodVisitor{
/** return if the analysis result of method m has been changed. */
public boolean visit(SootMethod m, Set<SootMethod> changeSources);
}
public CallGraphWorklist(CallGraph cg, MethodVisitor visitor){
this._cg = cg;
this._visitor=visitor;
TopologicalOrderer orderer = new TopologicalOrderer(cg);
orderer.go();
this._topoOrder = orderer.order();
}
public CallGraphWorklist(CallGraph cg, MethodVisitor visitor,List<SootMethod> topoOrder){
this._cg = cg;
this._visitor = visitor;
this._topoOrder = topoOrder;
}
public CallGraphWorklist(CallGraph cg, MethodVisitor visitor,Collection<SootMethod> entries){
this._visitor=visitor;
this._cg = cg;
assert(entries!=null);
TopologicalOrderer orderer = new PartialTopologicalOrderer(cg,entries);
orderer.go();
_topoOrder = orderer.order();
}
private static class MethodComparator implements Comparator<SootMethod>{
private int[] _order;
public MethodComparator(int[] order){
this._order = order;
}
public int compare(SootMethod m1, SootMethod m2){
return _order[m1.getNumber()] - _order[m2.getNumber()];
}
public boolean equals(Object obj){
if(obj==this)
return true;
else
return false;
}
}
@SuppressWarnings("unchecked")
public void process(){
//get an order number for each method
int[] order = new int[SootUtils.getMethodCount()];
int i = 1;
for(Iterator<SootMethod> it=_topoOrder.iterator();it.hasNext();i++){
SootMethod m = it.next();
order[m.getNumber()] = i;
}
//initialize the queue
Comparator<SootMethod> comparator = new MethodComparator(order);
PriorityQueue<SootMethod> worklist = new PriorityQueue<SootMethod>(_topoOrder.size(), comparator);
worklist.addAll(_topoOrder);
//set change source
Map<SootMethod, Set<SootMethod>> inque = new HashMap<SootMethod, Set<SootMethod>>(_topoOrder.size());
for(SootMethod m: _topoOrder){
Callees callees = new Callees(_cg,m);
inque.put(m, callees.explicits());
}
//worklist processing
while(!worklist.isEmpty()){
SootMethod m = worklist.poll();
Set<SootMethod> changeSources = inque.get(m);
inque.remove(m);
boolean changed = _visitor.visit(m,changeSources);
if(changed){
Iterator<SootMethod> it = new Sources(_cg.edgesInto(m));
while(it.hasNext()){
SootMethod caller = it.next();
int id = caller.getNumber();
//if originally reachable
if(order[id]>0){
changeSources = inque.get(caller);
//if not in worklist
if(changeSources==null){
worklist.offer(caller);
changeSources = new TreeSet<SootMethod>(comparator);
inque.put(caller,changeSources);
}
changeSources.add(m);
}
}
}
}
}
}