package jqian.sootex.du;
import java.util.*;
import jqian.sootex.Cache;
import jqian.sootex.location.AccessPath;
import jqian.sootex.location.Location;
import jqian.sootex.location.GlobalLocation;
import jqian.sootex.location.HeapAbstraction;
import jqian.sootex.ptsto.IPtsToQuery;
import jqian.sootex.ptsto.PtsToHelper;
import jqian.sootex.sideeffect.ISideEffectAnalysis;
import jqian.sootex.util.CFGEntry;
import jqian.sootex.util.CFGExit;
import jqian.sootex.util.callgraph.Callees;
import soot.*;
import soot.jimple.DefinitionStmt;
import soot.jimple.IdentityStmt;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.NewArrayExpr;
import soot.jimple.NewExpr;
import soot.jimple.NewMultiArrayExpr;
import soot.jimple.ReturnStmt;
import soot.jimple.Stmt;
import soot.jimple.toolkits.callgraph.CallGraph;
import soot.toolkits.graph.*;
/**
* A reaching definition analysis implemented with bit vectors.
*/
public class RDAnalysis extends DUAnalysis{
protected IPtsToQuery _pt2Query;
protected HeapAbstraction _heapAbstraction;
public RDAnalysis(MethodOrMethodContext mc,DirectedGraph<Unit> graph,IPtsToQuery pt2Query,
HeapAbstraction heapAbstraction, ISideEffectAnalysis sideEffect,boolean verbose){
super(mc, graph, sideEffect, verbose);
this._pt2Query = pt2Query;
this._heapAbstraction = heapAbstraction;
}
public void build(){
super.build();
_pt2Query = null;
}
/** ID name of the analysis */
protected String getAnalysisName(){
return "RD";
}
/** DEF set at the method entry. */
protected Collection<ReachingDU> getEntryDU(){
Unit entry = CFGEntry.v();
Collection<ReachingDU> entrySet = new ArrayList<ReachingDU>();
// parameters, including the globals
Collection<Location> params = collectParams();
for (Location loc : params) {
AccessPath ap = AccessPath.getByRoot(loc);
entrySet.add(new ReachingDU(entry, ap, loc));
}
// for used heap location
Collection<Location> use = _sideEffect.getUseHeapLocs(_method);
if (use.size() > 0) {
entrySet.add(new ReachingDU(entry, null, use));
}
return entrySet;
}
/** collect the reaching definitions generated by a method call.
* NOTE: The definition on the return value has already been considered,
* this method just needs to collect the definition on global locations
*/
protected Collection<ReachingDU> collectInvokeDefs(Unit invokeStmt){
Collection<ReachingDU> rdSet = new ArrayList<ReachingDU>();
CallGraph cg = Scene.v().getCallGraph();
Callees callees = new Callees(cg, invokeStmt);
// XXX: We do not consider the effects of sub threads here
// For multiple thread programs, we need other analysis to find the inter-thread dependences
callees.all().removeAll(callees.threads());
// collect definition to globals
Set<Location> defGlobals = new HashSet<Location>();
for(SootMethod tgt: callees.all()){
if (!tgt.isConcrete())
continue;
Collection<Location> mod = _sideEffect.getModGlobals(tgt);
defGlobals.addAll(mod);
}
for(Location gb: defGlobals){
AccessPath ap = null;
if(gb instanceof GlobalLocation){
ap = AccessPath.getByRoot(gb);
}
ReachingDU gbrd = new ReachingDU(invokeStmt,ap,gb);
rdSet.add(gbrd);
}
// collect definitions to heap location
if(callees.all().size()==1){
SootMethod tgt = callees.all().iterator().next();
Collection<Location> defHeaps;
if(tgt.isConcrete()){
defHeaps = _sideEffect.getModHeapLocs(tgt);
}
else{
defHeaps = getNativeCallMod(invokeStmt, tgt);
}
if(!defHeaps.isEmpty()){
ReachingDU rd = new ReachingDU(invokeStmt,null,defHeaps);
rdSet.add(rd);
}
}
else{
Set<Location> defHeaps = new HashSet<Location>();
for(SootMethod tgt: callees.all()){
Collection<Location> mod;
if (tgt.isConcrete()){
mod = _sideEffect.getModHeapLocs(tgt);
}
else{
mod = getNativeCallMod(invokeStmt, tgt);
}
defHeaps.addAll(mod);
}
if(!defHeaps.isEmpty()){
ReachingDU rd = new ReachingDU(invokeStmt,null,defHeaps);
rdSet.add(rd);
}
}
return rdSet;
}
@SuppressWarnings("unchecked")
private Collection<Location> getNativeCallMod(Unit u, SootMethod tgt){
InvokeExpr invoke = ((Stmt)u).getInvokeExpr();
Value receiver = null;
if(!invoke.getMethod().isStatic()){
InstanceInvokeExpr iie = (InstanceInvokeExpr)invoke;
receiver = iie.getBase();
}
Collection<AccessPath> def = NativeMethodDUHelper.v().getDef(tgt, receiver, invoke.getArgs());
if(def.size()==0){
return Collections.emptyList();
}
else if(def.size()==1){
AccessPath d = def.iterator().next();
Collection<Location> defLocs = PtsToHelper.getAccessedLocations(_pt2Query, _heapAbstraction, u, d);
return defLocs;
}
else{
Collection<Location> locs = new HashSet<Location>();
for(AccessPath d: def){
Collection<Location> defLocs = PtsToHelper.getAccessedLocations(_pt2Query, _heapAbstraction, u, d);
locs.addAll(defLocs);
}
return locs;
}
}
/** Collect RD of each statement */
protected Collection<ReachingDU> collectStmtDU(Unit u){
if(u==CFGEntry.v()){
return getEntryDU();
}
if(u==CFGExit.v() || u instanceof IdentityStmt || !(u instanceof Stmt)){
return Collections.emptyList();
}
Collection<ReachingDU> rdSet = new ArrayList<ReachingDU>();
Stmt s = (Stmt)u;
for(Object box: s.getDefBoxes()){
Value v = ((ValueBox)box).getValue();
AccessPath def = AccessPath.valueToAccessPath(_method, s, v);
ReachingDU rd;
if (def.length() == 0) {
rd = new ReachingDU(u, def, def.getRoot());
} else {
Collection<Location> defLocs = PtsToHelper.getAccessedLocations(_pt2Query, _heapAbstraction, u, def);
rd = new ReachingDU(u, def, defLocs);
}
rdSet.add(rd);
}
//each new expression will generate a couple of assignments initializing the fields to their default value
if(s instanceof DefinitionStmt){
DefinitionStmt ds = (DefinitionStmt)s;
Value leftOp = ds.getLeftOp();
Value rightOp = ds.getRightOp();
if(rightOp instanceof NewExpr){
AccessPath left = AccessPath.valueToAccessPath(_method, s, leftOp);
RefType type = (RefType)rightOp.getType();
SootClass cls = type.getSootClass();
Collection<SootField> fields = Cache.v().getAllInstanceFields(cls);
for(SootField f: fields){
AccessPath ap = left.appendFieldRef(f);
Collection<Location> defLocs = PtsToHelper.getAccessedLocations(_pt2Query, _heapAbstraction, u, ap);
ReachingDU rd = new ReachingDU(u,ap,defLocs);
rdSet.add(rd);
}
}
else if(rightOp instanceof NewArrayExpr || rightOp instanceof NewMultiArrayExpr){
AccessPath left = AccessPath.valueToAccessPath(_method, s, leftOp);
AccessPath ap = left.appendArrayRef();
Collection<Location> defLocs = PtsToHelper.getAccessedLocations(_pt2Query, _heapAbstraction, u, ap);
ReachingDU rd = new ReachingDU(u,ap,defLocs);
rdSet.add(rd);
}
}
else if(s instanceof ReturnStmt){
// treat return as a definition to a unique return value
Location ret = Location.methodToRet(_method);
AccessPath def = AccessPath.getByRoot(ret);
ReachingDU rd = new ReachingDU(u, def, ret);
rdSet.add(rd);
}
//if there exits a method call
if(s.containsInvokeExpr()){
Collection<ReachingDU> rds = collectInvokeDefs(u);
rdSet.addAll(rds);
}
return rdSet;
}
}