package jqian.sootex.dependency.slicing;
import java.util.*;
import jqian.sootex.location.AccessPath;
import jqian.sootex.location.HeapAbstraction;
import jqian.sootex.location.Location;
import jqian.sootex.ptsto.IPtsToQuery;
import jqian.sootex.ptsto.PtsToHelper;
import jqian.sootex.util.SootUtils;
import soot.*;
import soot.jimple.*;
/**
* Slicing criterion on Java source code level
*/
public class JavaSlicingCriterion implements SlicingCriterion{
protected Set<String> _varNames = new HashSet<String>();
protected String _typeOrMethod;
protected int _line;
protected boolean _postExecution;
/**
* @param varNames A string identifier for the concern variable.
* The variable can be the name of a local, a full qualified field signature like
* "<java.lang.System: java.io.PrintStream out>", or an access path like
* "local.<java.util.List: int size>".
* @param scope Which could be a method signature or a full qualified type name
* @param postExecution Whether to start slicing from the state that the given line
* has been executed.
*/
public JavaSlicingCriterion(Collection<String> varNames,String scope,int line,boolean postExecution){
this._varNames.addAll(varNames);
this._typeOrMethod = scope;
this._line = line;
this._postExecution = postExecution;
}
public String getEnclosingScope(){
return _typeOrMethod;
}
/**
* Turn line into Units in bytecode level, map variables to Location in IR, and map type into SootClass
* @param query A points-to query is demanded when a slicing criterion concerns on access paths.
* This parameter can be null if you are sure the slicing criterion does not concerns
* on any access path.
* The specified points-to query should be the one used to construct dependence graph
*/
public Collection<JimpleSlicingCriterion> toJimpleCriterion(IPtsToQuery query, HeapAbstraction heapAbstraction){
//locate the method, and the units
LinkedList<Unit> units = new LinkedList<Unit>();
SootClass cls = null;
SootMethod method = null;
try{
cls = Scene.v().getSootClass(_typeOrMethod);
}catch(Exception e){}
try{
method = Scene.v().getMethod(_typeOrMethod);
}catch(Exception e){}
if(method==null && cls!=null){
for(SootMethod m: cls.getMethods()){
unitsInLine(m,_line,units);
if(units.size()>0){
method = m;
break;
}
}
}
else if(method!=null){
unitsInLine(method,_line,units);
}
// can not map the criterion from source code to bytecode
if(method==null)
return null;
//locate the concerned access paths
Set<AccessPath> aps = new HashSet<AccessPath>();
for(Local loc: method.getActiveBody().getLocals()){
String name = loc.getName();
if(_varNames.contains(name)){
Location stackLoc = Location.valueToLocation(loc);
AccessPath ap = AccessPath.getByRoot(stackLoc);
aps.add(ap);
}
}
//parse names with dots
for(String name: _varNames){
int index = name.indexOf('.');
if(index>=0){
//must be a global field
if(name.charAt(0)=='<'){
try{
SootField field = Scene.v().getField(name);
Location global = Location.getGlobalLocation(field);
AccessPath ap = AccessPath.getByRoot(global);
aps.add(ap);
}
catch(Exception e){}
}
else{//an instance field
String local = name.substring(0,index);
String fieldSignature = name.substring(index+1);
try{
SootField field = Scene.v().getField(fieldSignature);
//find match locals
if(local.equals("this")){
Local loc = method.getActiveBody().getThisLocal();
Location stackLoc = Location.valueToLocation(loc);
AccessPath ap = AccessPath.getByRoot(stackLoc);
ap = ap.appendFieldRef(field);
aps.add(ap);
}
else{
for(Local loc: method.getActiveBody().getLocals()){
if(loc.getName().equals(local)){
Location stackLoc = Location.valueToLocation(loc);
AccessPath ap = AccessPath.getByRoot(stackLoc);
ap = ap.appendFieldRef(field);
aps.add(ap);
}
}
}
}
catch(Exception e){}
}
}
}
Collection<JimpleSlicingCriterion> criteria = new HashSet<JimpleSlicingCriterion>();
if(_postExecution){
//collect in reverse order, choose slicing criterion on the tail Units first
while(!units.isEmpty() && !aps.isEmpty()){
Unit u = units.removeLast();
collectCriterion(method,query,heapAbstraction,u,aps,criteria);
}
}
else{
for(Unit u: units){
if(aps.isEmpty())
break;
collectCriterion(method,query,heapAbstraction,u,aps,criteria);
}
}
return criteria;
}
private void collectCriterion(SootMethod method, IPtsToQuery query, HeapAbstraction heapAbstraction,
Unit u, Set<AccessPath> aps, Collection<JimpleSlicingCriterion> out){
Set<AccessPath> occur = occuredAccessPaths(method,u);
Collection<AccessPath> tmp = new LinkedList<AccessPath>();
tmp.addAll(occur);
occur.retainAll(aps);
if(!occur.isEmpty()){
Set<Location> locs = getAliasedLocations(occur,u,query, heapAbstraction);
if(_postExecution && u instanceof DefinitionStmt){
Value left = ((DefinitionStmt)u).getLeftOp();
AccessPath ap = AccessPath.valueToAccessPath(method, u, left);
//If the postExecution flag is set, then the temporals should be
//considered in slicing Java stmts.
//Here we heuristically assume varibales with name start from '$'
//are temporals introduced in jimple.
if(ap!=null && aps.contains(ap)){
for(AccessPath x: tmp){
if(x.length()>0) continue;
Location loc = x.getRoot();
String name = loc.toString();
boolean isTemporal = name.length()>0 && name.charAt(0)=='$';
if(isTemporal){
locs.add(loc);
}
}
}
}
JimpleSlicingCriterion criterion = new JimpleSlicingCriterion(method,u,locs,_postExecution);
out.add(criterion);
}
aps.removeAll(occur);
}
private Set<Location> getAliasedLocations(Set<AccessPath> aps,Unit u,
IPtsToQuery query, HeapAbstraction heapAbstraction){
Set<Location> aliased = new HashSet<Location>();
for(AccessPath ap: aps){
if(ap.length()>0 && query!=null){
// TODO ����Ҫȷ���ѳ����һ����
Set<Location> x = PtsToHelper.getAccessedLocations(query, heapAbstraction, u, ap);
aliased.addAll(x);
}
else{
aliased.add(ap.getRoot());
}
}
return aliased;
}
protected Set<AccessPath> occuredAccessPaths(SootMethod method,Unit s){
Set<AccessPath> vars = new HashSet<AccessPath>();
List<?> boxed = s.getUseAndDefBoxes();
for(Iterator<?> it=boxed.iterator();it.hasNext();){
ValueBox box = (ValueBox)it.next();
Value v = box.getValue();
if(v instanceof Local || v instanceof FieldRef || v instanceof ArrayRef){
AccessPath ap = AccessPath.valueToAccessPath(method,s,v);
vars.add(ap);
}
}
return vars;
}
protected boolean useVariables(Unit s,Set<String> variables){
for(ValueBox box: s.getUseBoxes()){
if(variables.contains(box.toString()))
return true;
}
return false;
}
public static void unitsInLine(SootMethod m,int line,Collection<Unit> units){
if(m.isConcrete()&& !m.hasActiveBody()){
m.retrieveActiveBody();
}
if(m.hasActiveBody()){
for(Unit s: m.getActiveBody().getUnits()){
if(SootUtils.getLine(s)==line){
units.add(s);
}
}
}
}
}