package jqian.sootex.dependency.pdg.builder;
import java.util.*;
import jqian.sootex.location.ArrayElmt;
import jqian.sootex.location.CommonInstObject;
import jqian.sootex.location.GlobalLocation;
import jqian.sootex.location.HeapField;
import jqian.sootex.location.InvokeInfo;
import jqian.sootex.location.Location;
import jqian.sootex.location.HeapAbstraction;
import jqian.sootex.location.MethodRet;
import jqian.sootex.location.StackLocation;
import jqian.sootex.ptsto.IPtsToQuery;
import jqian.sootex.util.CFGEntry;
import jqian.sootex.util.CFGExit;
import jqian.sootex.util.callgraph.Callees;
import jqian.sootex.dependency.pdg.ActualIn;
import jqian.sootex.dependency.pdg.ActualNode;
import jqian.sootex.dependency.pdg.ActualOut;
import jqian.sootex.dependency.pdg.CallNode;
import jqian.sootex.dependency.pdg.CallsiteNode;
import jqian.sootex.dependency.pdg.CtrlDependenceEdge;
import jqian.sootex.dependency.pdg.DependenceEdge;
import jqian.sootex.dependency.pdg.DependenceNode;
import jqian.sootex.dependency.pdg.FormalNode;
import jqian.sootex.dependency.pdg.FormalOut;
import jqian.sootex.dependency.pdg.FormalIn;
import jqian.sootex.dependency.pdg.DepGraphOptions;
import jqian.sootex.du.IReachingDUQuery;
import soot.*;
import soot.jimple.Stmt;
import soot.jimple.toolkits.callgraph.*;
import soot.toolkits.graph.*;
/**
* A PDG builder with side-effect of the calls under consideration.
*/
public class PDGBuilder extends AbstractPDGBuilder {
protected SDGBuilder _sdgBuilder;
private BindingObjectCollector _bindingCollector;
public PDGBuilder(SootMethod m, UnitGraph cfg, DepGraphOptions dgOptions,
IPtsToQuery ptsTo, HeapAbstraction heapAbstraction, IReachingDUQuery rd,
SDGBuilder sdgBuilder){
super(m, cfg, dgOptions, ptsTo, heapAbstraction, rd);
this._sdgBuilder = sdgBuilder;
this._bindingCollector = getBindingObjectCollector(_pdgOptions.getInterfaceLocationAbstraction());
}
/** Use a binding object to connect the formals and the actuals. */
static abstract class BindingObjectCollector{
public abstract Object getBindingForActualAndFormal(Location loc);
public Collection<Object> getBindingSet(Collection<Location> locs){
Set<Object> bindingSet = new HashSet<Object>();
for(Location loc: locs){
Object binding = getBindingForActualAndFormal(loc);
bindingSet.add(binding);
}
return bindingSet;
}
}
static class FieldSensitiveBindingObjectCollector extends BindingObjectCollector{
public Object getBindingForActualAndFormal(Location loc){
return loc;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Collection<Object> getBindingSet(Collection<Location> locs){
return (Collection)locs;
}
}
static class FieldBasedBindingObjectCollector extends BindingObjectCollector{
public Object getBindingForActualAndFormal(Location loc){
Object binding = null;
if(loc instanceof StackLocation || loc instanceof GlobalLocation || loc instanceof MethodRet){
binding = loc;
}
else if(loc instanceof HeapField){
binding = ((HeapField)loc).getField();
}
else if(loc instanceof ArrayElmt){
//binding = loc.getType();
binding = loc.getType().makeArrayType();
}
else if(loc instanceof Location.TypeLocation){
// can not build field-based summary edges on type-based heap abstraction
binding = loc;
}
else{
throw new RuntimeException("Unknown locations");
}
return binding;
}
}
static class TypeBasedBindingObjectCollector extends BindingObjectCollector{
public Object getBindingForActualAndFormal(Location loc){
Object binding = null;
if(loc instanceof StackLocation || loc instanceof MethodRet){
binding = loc;
}
else if(loc instanceof GlobalLocation){
binding = ((GlobalLocation)loc).getSootField().getDeclaringClass().getType();
}
else if(loc instanceof HeapField){
HeapField f = (HeapField)loc;
CommonInstObject obj = (CommonInstObject)f.getWrapperObject();
if(obj==null){
// in field-based analysis
binding = f.getField().getDeclaringClass().getType();
}
else{
binding = obj.getType();
}
}
else if(loc instanceof ArrayElmt){
binding = loc.getType().makeArrayType();
}
else if(loc instanceof Location.TypeLocation){
binding = loc;
}
else{
throw new RuntimeException("Unknown locations");
}
return binding;
}
}
static class HeapUndistinguishedBindingObjectCollector extends BindingObjectCollector{
Type objectType = Scene.v().getObjectType();
Type objectArrayType = objectType.makeArrayType();
public Object getBindingForActualAndFormal(Location loc){
Object binding = null;
if(loc instanceof StackLocation || loc instanceof MethodRet){
binding = loc;
}
else if(loc instanceof GlobalLocation){
binding = objectType;
}
else if(loc instanceof HeapField){
binding = objectType;
}
else if(loc instanceof ArrayElmt){
binding = objectArrayType;
}
else if(loc instanceof Location.TypeLocation){
Location.TypeLocation tloc = (Location.TypeLocation)loc;
Type t = tloc.getObjectType();
if(t instanceof ArrayType){
binding = objectArrayType;
}
else{
binding = objectType;
}
}
else{
throw new RuntimeException("Unknown locations");
}
return binding;
}
}
private BindingObjectCollector getBindingObjectCollector(HeapAbstraction locAbstraction){
BindingObjectCollector bindingCollector = null;
if(locAbstraction==HeapAbstraction.FIELD_SENSITIVE){
bindingCollector = new FieldSensitiveBindingObjectCollector();
}
else if(locAbstraction==HeapAbstraction.FIELD_BASED){
bindingCollector = new FieldBasedBindingObjectCollector();
}
else if(locAbstraction==HeapAbstraction.TYPE_BASED){
bindingCollector = new TypeBasedBindingObjectCollector();
}
else if(locAbstraction==HeapAbstraction.NO_DISTINGUISH){
bindingCollector = new HeapUndistinguishedBindingObjectCollector();
}
return bindingCollector;
}
private void buildFormalForGlobals(){
Collection<Location> modGlobals = _sdgBuilder.getTgtModGlobals(_method);
Collection<Object> bindingSet = _bindingCollector.getBindingSet(modGlobals);
for(Object binding: bindingSet){
FormalNode node = new FormalOut(_method,binding);
_pdg.addNode(node);
}
Collection<Location> useGlobals = _sdgBuilder.getTgtUsedGlobals(_method);
bindingSet = _bindingCollector.getBindingSet(useGlobals);
for(Object binding: bindingSet){
FormalNode node = new FormalIn(_method, binding, FormalIn.HEAP_INDEX);
_pdg.addNode(node);
}
}
private void buildFormalForHeaps(){
Collection<Location> modHeaps = _sdgBuilder.getTgtModHeapLocs(_method);
Collection<Location> useHeaps = _sdgBuilder.getTgtUsedHeapLocs(_method);
Collection<Object> bindingSet = _bindingCollector.getBindingSet(modHeaps);
for(Object binding: bindingSet){
FormalNode node = new FormalOut(_method,binding);
_pdg.addNode(node);
}
bindingSet = _bindingCollector.getBindingSet(useHeaps);
for(Object binding: bindingSet){
FormalNode node = new FormalIn(_method,binding,FormalIn.HEAP_INDEX);
_pdg.addNode(node);
}
}
protected void buildFormals(){
// formal out for the returns
if(!(_method.getReturnType() instanceof VoidType)){
FormalNode node = new FormalOut(_method,Location.methodToRet(_method));
_pdg.addNode(node);
}
//formal ins for parameters
int size = _method.getParameterCount();
Body body = _method.getActiveBody();
for(int i=0; i<size;i++){
Local p = body.getParameterLocal(i);
Location param = Location.valueToLocation(p);
FormalNode node = new FormalIn(_method,param,i);
_pdg.addNode(node);
}
//formal in for the receiver
if(!_method.isStatic()){
Location loc = Location.getThisPointer(_method);
FormalNode node = new FormalIn(_method,loc,FormalIn.THIS_INDEX);
_pdg.addNode(node);
}
buildFormalForGlobals();
buildFormalForHeaps();
}
protected void buildFormalInDependences(){
DependenceNode entry = _pdg.entry();
Collection<FormalNode> ins = _pdg.getFormalIns();
for(FormalNode f: ins){
DependenceEdge edge = new CtrlDependenceEdge(entry, f);
_pdg.addEdge(edge);
}
}
protected void buildFormalOutDependences(){
DependenceNode entry = _pdg.entry();
Collection<FormalNode> outs = _pdg.getFormalOuts();
// build control dependence
for(FormalNode f: outs){
DependenceEdge edge = new CtrlDependenceEdge(entry, f);
_pdg.addEdge(edge);
}
// build data-flow dependences
HeapAbstraction locAbstraction = _pdgOptions.getInterfaceLocationAbstraction();
if(locAbstraction==HeapAbstraction.FIELD_SENSITIVE){
for(FormalNode f: outs){
Location loc = (Location)f.getBinding();
buildDepForLocation(CFGExit.v(),loc,f);
}
}
else{
// dependence for method return
if(!(_method.getReturnType() instanceof VoidType)){
Location loc = Location.methodToRet(_method);
Object binding = _bindingCollector.getBindingForActualAndFormal(loc);
FormalNode f = _pdg.getBindingFormal(binding, false);
buildDepForLocation(CFGExit.v(),loc,f);
}
Collection<Location> modHeaps = _sdgBuilder.getTgtModHeapLocs(_method);
for(Location loc: modHeaps){
Object binding = _bindingCollector.getBindingForActualAndFormal(loc);
FormalNode f = _pdg.getBindingFormal(binding, false);
buildDepForLocation(CFGExit.v(),loc,f);
}
Collection<Location> modGlobals = _sdgBuilder.getTgtModGlobals(_method);
for(Location loc: modGlobals){
Object binding = _bindingCollector.getBindingForActualAndFormal(loc);
FormalNode f = _pdg.getBindingFormal(binding, false);
buildDepForLocation(CFGExit.v(),loc,f);
}
}
}
@Override
protected void buildNodesForCall(Unit callsite){
//also build a unique node to link all possible calls
CallsiteNode callsiteNode = new CallsiteNode(_method,callsite);
_pdg.addNode(callsiteNode);
//We do not build call nodes for callsites whose callee are discarded during call graph simplification
CallGraph cg = Scene.v().getCallGraph();
Callees callees = new Callees(cg,callsite);
for(SootMethod tgt: callees.explicits()){
//add call node
CallNode call = new CallNode(_method,callsite,tgt);
_pdg.addNode(call);
callsiteNode.addCalleeNode(call);
//add actual nodes
buildActuals(call,tgt,callsite);
//add control dependence
DependenceEdge edge = new CtrlDependenceEdge(callsiteNode, call);
_pdg.addEdge(edge);
}
}
private void buildActualsForLocalParameters(CallNode call,SootMethod callee,Unit callsite, Collection<ActualNode> list){
boolean concrete = callee.isConcrete();
InvokeInfo info = new InvokeInfo((Stmt)callsite);
// if have a return and the return value is used
if(info.getRetLoc()!=null){
Location ret = null;
if(concrete)
ret = Location.methodToRet(callee);
ActualOut node = new ActualOut(_method, callsite, callee, info.getRetLoc(), ret);
_pdg.addNode(node);
list.add(node);
}
// explicit parameter node
Location[] args = info.getArgLocs();
int size = args.length;
for(int i=0;i<size;i++){
Location actual = args[i];
if(actual!=null){
Location formal = null;
if(concrete){
Local p = callee.getActiveBody().getParameterLocal(i);
formal = Location.valueToLocation(p);
}
ActualIn node = new ActualIn(_method,callsite,callee,actual,formal);
_pdg.addNode(node);
list.add(node);
}
}
// this pointer
if(!callee.isStatic()){
Location formal = null;
if(concrete)
formal = Location.getThisPointer(callee);
Location actual = info.receiver();
ActualIn node = new ActualIn(_method,callsite,callee,actual,formal);
_pdg.addNode(node);
list.add(node);;
}
}
private void buildActualsForGlobals(CallNode call,SootMethod callee,Unit callsite, Collection<ActualNode> list){
Collection<Location> modGlobals = _sdgBuilder.getTgtModGlobals(callee);
Collection<Object> bindingSet = _bindingCollector.getBindingSet(modGlobals);
for(Object binding: bindingSet){
ActualOut node = new ActualOut(_method, callsite, callee, binding,binding);
_pdg.addNode(node);
list.add(node);
}
Collection<Location> useGlobals = _sdgBuilder.getTgtUsedGlobals(callee);
bindingSet = _bindingCollector.getBindingSet(useGlobals);
for (Object binding: bindingSet) {
ActualIn node = new ActualIn(_method, callsite, callee, binding, binding);
_pdg.addNode(node);
list.add(node);
}
}
private void buildActualsForHeaps(CallNode call,SootMethod callee,Unit callsite, Collection<ActualNode> list){
Collection<Location> modHeaps = _sdgBuilder.getTgtModHeapLocs(callee);
Collection<Location> useHeaps = _sdgBuilder.getTgtUsedHeapLocs(callee);
// build actual -in/-out nodes for each binding object
Collection<Object> bindingSet = _bindingCollector.getBindingSet(modHeaps);
for(Object binding: bindingSet){
ActualOut node = new ActualOut(_method, callsite, callee, binding, binding);
_pdg.addNode(node);
list.add(node);
}
bindingSet = _bindingCollector.getBindingSet(useHeaps);
for (Object binding: bindingSet) {
ActualIn node = new ActualIn(_method, callsite, callee, binding, binding);
_pdg.addNode(node);
list.add(node);
}
}
/** Build actual-in/-outs for method call. */
private void buildActuals(CallNode call,SootMethod callee,Unit callsite){
Collection<ActualNode> list = new LinkedList<ActualNode>();
buildActualsForLocalParameters(call,callee,callsite, list);
// TODO: need more consideration for native methods here
boolean concrete = callee.isConcrete();
// actual -in/-outs due to global locations and heap locations
if(concrete){
buildActualsForGlobals(call, callee, callsite, list);
buildActualsForHeaps(call, callee, callsite, list);
}
//build control dependences
for(DependenceNode n: list){
DependenceEdge edge = new CtrlDependenceEdge(call,n);
_pdg.addEdge(edge);
}
}
@Override
protected Collection<DependenceNode> getDefinitionNodes(Unit stmt, Location loc){
Collection<DependenceNode> nodes = new LinkedList<DependenceNode>();
if(stmt==CFGEntry.v()){
Object binding = _bindingCollector.getBindingForActualAndFormal(loc);
DependenceNode n = _pdg.getBindingFormal(binding, true);
nodes.add(n);
}
else{
if((stmt instanceof Stmt) && ((Stmt)stmt).containsInvokeExpr()){
DependenceNode src = _pdg.getStmtBindingNode(stmt);
if(src instanceof CallsiteNode){
CallsiteNode callsiteNode = (CallsiteNode)src;
Collection<CallNode> calleeNodes = callsiteNode.getCalleeNodes();
//If all targets are tailed during call graph simplification
if(calleeNodes.isEmpty()){
nodes.add(src);
}
else{
Object binding = _bindingCollector.getBindingForActualAndFormal(loc);
for(CallNode call: calleeNodes){
DependenceNode n=_pdg.getBindingActual(stmt,call.getCallee(), binding, false);
if(n!=null) nodes.add(n);
}
}
}
else{
nodes.add(src);
}
}
else{
DependenceNode src = _pdg.getStmtBindingNode(stmt);
nodes.add(src);
}
}
return nodes;
}
/**
* Just treat the call as a common statement.
* (Can only define value by method return. No extra side-effects)
*/
private void buildDepForTailoredCallee(InvokeInfo invoke, DependenceNode dest){
Unit invokeStmt = invoke.getInvokeStmt();
Location[] args = invoke.getArgLocs();
int count = args.length;
for(int i=0 ;i<count; i++){
Location arg = args[i];
if(arg!=null && arg instanceof StackLocation){
buildDepForLocation(invokeStmt,arg,dest);
}
}
if(!invoke.getInvokeExpr().getMethod().isStatic()){
Location receiver = invoke.receiver();
if(receiver instanceof StackLocation)
buildDepForLocation(invokeStmt,receiver,dest);
}
}
/**
* Safely handle the method invocation. Consider the side effect of each callee.
* @param dest The node that represent the call statement
*/
protected void buildDepForInvoke(Unit curStmt, DependenceNode dest){
InvokeInfo invoke = new InvokeInfo((Stmt)curStmt);
Unit invokeStmt=invoke.getInvokeStmt();
CallGraph cg = Scene.v().getCallGraph();
//FIXED 2008-07-13: Here we only consider explicit callees
/*
Set processedTgts = new HashSet();
Iterator edgeIt = cg.edgesOutOf(invokeStmt);
while(edgeIt.hasNext()){
Edge edge = (Edge)edgeIt.next();
SootMethod tgt = edge.tgt();
if(processedTgts.contains(tgt))
continue;
processedTgts.add(tgt);
*/
Callees callees = new Callees(cg,invokeStmt);
//If the callees are tailed during call graph simplification
if(callees.explicits().size()==0){
buildDepForTailoredCallee(invoke, dest);
return;
}
for(SootMethod tgt: callees.explicits()){
// dependence for arguments
int pCount = tgt.getParameterCount();
Location[] args = invoke.getArgLocs();
for(int i=0;i<pCount;i++){
Location arg = args[i];
if(arg!=null && arg instanceof StackLocation){
buildDepForActualIn(dest,curStmt,tgt,arg);
}
}
//make sure the dependence on recevier is kept
if(!invoke.getInvokeExpr().getMethod().isStatic()){
Location receiver = invoke.receiver();
if(receiver instanceof StackLocation)
buildDepForActualIn(dest,curStmt,tgt,receiver);
}
//TODO need more consideration for native method
if(!tgt.isConcrete()){//side effect free or we just do not want to consider
continue;
}
//Get dependence for side effected heap locations
//handle ordinary calls
Collection<Location> useHeaps = _sdgBuilder.getTgtUsedHeapLocs(tgt);
for(Location loc: useHeaps){
buildDepForActualIn(dest,curStmt,tgt,loc);
}
Collection<Location> useGlobals = _sdgBuilder.getTgtUsedGlobals(tgt);
for(Location loc: useGlobals){
buildDepForActualIn(dest,curStmt,tgt,loc);
}
}
}
private void buildDepForActualIn(DependenceNode dest,Unit callsite,SootMethod callee,Location loc){
Object binding = _bindingCollector.getBindingForActualAndFormal(loc);
ActualNode in = _pdg.getBindingActual(callsite, callee, binding, true);
buildDepForLocation(callsite,loc,in);
}
}