package jqian.sootex.dependency.slicing;
import java.util.*;
import jqian.sootex.dependency.pdg.ActualNode;
import jqian.sootex.dependency.pdg.DataDependenceEdge;
import jqian.sootex.dependency.pdg.CtrlDependenceEdge;
import jqian.sootex.dependency.pdg.DependenceEdge;
import jqian.sootex.dependency.pdg.DependenceNode;
import jqian.sootex.dependency.pdg.PDG;
import jqian.sootex.location.Location;
import soot.*;
import soot.jimple.*;
/*
* FIX 2008-07-19 fix the problem when selecting InvokeStmt as slicing criterion
*/
public abstract class Slicer {
protected static interface TraverseHelper{
public boolean isExcluded(DependenceEdge e);
}
protected abstract Collection<DependenceEdge> getInEdges(DependenceNode n);
//protected abstract Collection<DependenceEdge> getOutEdges(DependenceNode n);
protected abstract PDG getDependenceGraph(MethodOrMethodContext mc);
/**
* Get the starting points for slicing traverse
* @param criteriaNodes DependenceNode's corresponding to the slicing criteria,
* This set is kept for constructing the final slices.
* @param startingNodes DependenceNode's where the slicing traverse should start from.
*/
protected void getStartingNodes(Collection<JimpleSlicingCriterion> stmtCriteria,
Collection<DependenceNode> criteriaNodes,Collection<DependenceNode> startingNodes){
for(JimpleSlicingCriterion criterion: stmtCriteria){
Stmt stmt = (Stmt) criterion.statement();
Set<Location> variables = criterion.variables();
boolean postExecution = criterion.startFromPostExecution();
PDG pdg = getDependenceGraph(criterion.context());
DependenceNode node = pdg.getStmtBindingNode(stmt);
criteriaNodes.add(node);
//if no variable is specified
if(variables==null || variables.isEmpty()){
startingNodes.add(node);
if(stmt.containsInvokeExpr()){
pdg.getActualIns(startingNodes, stmt);
}
}
else{
Collection<DependenceEdge> inEdges = getInEdges(node);
for(DependenceEdge e: inEdges){
DependenceNode from = e.getFrom();
if(e instanceof CtrlDependenceEdge){
startingNodes.add(from);
}
else{
DataDependenceEdge dd = (DataDependenceEdge)e;
Object reason = dd.getReason();
//if(!(reason instanceof StackLocation))
// continue;
//Value v = ((StackLocation)reason).getValue();
if(reason==null || variables.contains(reason)){
startingNodes.add(from);
}
}
}
// NOTE: Since a use defined slicing criterion can only
// concerns on stack variables,
// we just handle definition statements simply
boolean isDefinedVarConcerned = false;
if(postExecution && stmt instanceof DefinitionStmt){
DefinitionStmt def = (DefinitionStmt)stmt;
String defedvar = def.getLeftOp().toString();
for(Location v: variables){
String name = v.toString();
if(name.equals(defedvar)){
isDefinedVarConcerned = true;
break;
}
}
}
// add the node where slicing criteria is specified to starting nodes
if(isDefinedVarConcerned){
startingNodes.add(node);
}
if(stmt.containsInvokeExpr()){
//get actual in nodes, and find starting nodes from them
List<ActualNode> actualIns = new LinkedList<ActualNode>();
pdg.getActualIns(actualIns, stmt);
for(ActualNode actual: actualIns){
Object loc = actual.getBinding();
//if(!(loc instanceof StackLocation)){
// continue;
//}
//Value binding = ((StackLocation)loc).getValue();
if(loc==null || variables.contains(loc)){
startingNodes.add(actual);
}
}
//check whether the actual outs should be considered as starting nodes
if(isDefinedVarConcerned){
pdg.getActualOutsOfReturnValue(startingNodes, stmt);
}
}
}
}
}
protected void traverse(Set<DependenceNode> reached,TraverseHelper helper){
Set<DependenceNode> worklistElmts = new HashSet<DependenceNode>();
Stack<DependenceNode> worklist = new Stack<DependenceNode>();
//initialize worklist
for(DependenceNode node: reached){
worklist.add(node);
worklistElmts.add(node);
}
//traversing
while(!worklist.isEmpty()){
DependenceNode node = (DependenceNode)worklist.pop();
worklistElmts.remove(node);
reached.add(node);
//get incoming edges
Collection<DependenceEdge> ins = getInEdges(node);
if(ins!=null){
for (DependenceEdge edge: ins) {
if(helper.isExcluded(edge))
continue;
DependenceNode from = edge.getFrom();
if (!reached.contains(from) && !worklistElmts.contains(from)) {
worklist.push(from);
}
}
}
}
}
}