package jqian.sootex.dependency.pdg; import java.util.*; import jqian.sootex.location.GlobalLocation; import jqian.sootex.location.HeapLocation; import jqian.sootex.location.MethodRet; import jqian.sootex.location.StackLocation; import jqian.sootex.util.NumberableComparator; import jqian.sootex.util.SootUtils; import jqian.util.*; import soot.*; import soot.jimple.*; import soot.toolkits.graph.*; /** * A context-sensitive procedure dependence graph. * * DependenceEdge(s) are shared in the DEPEND_BY set of the from node, * and the DEPEND_ON set of the to node. */ public class PDG implements DependenceGraph{ private Collection<DependenceNode> _nodes; private Map<DependenceNode,Collection<DependenceEdge>> _edgesOut; //edges out of each node private Map<DependenceNode,Collection<DependenceEdge>> _edgesIn; //edges into each node private Map<Object,DependenceNode> _obj2node; //from binding object to node for fast access private Map<Object,FormalNode> _binding2formalIn; //from binding object to FormalIn node private Map<Object,FormalNode> _binding2formalOut; //from binding object to FormalOut node private Map<Argument,ActualNode> _arg2actualIn; private Map<Argument,ActualNode> _arg2actualOut; private int _edgeCount[] = new int[5]; private EntryNode _entry; private MethodOrMethodContext _mc; public PDG(MethodOrMethodContext mc){ this._mc = mc; _entry = new EntryNode(_mc); _nodes = new TreeSet<DependenceNode>(NumberableComparator.v()); _binding2formalIn = new HashMap<Object,FormalNode>(); _binding2formalOut = new HashMap<Object,FormalNode>(); //_edgesOut = new HashMap<DependenceNode,Collection<DependenceEdge>>(); //_edgesIn = new HashMap<DependenceNode,Collection<DependenceEdge>>(); //XXX: Use an integer based map to save memory _edgesOut = new NodeMap<Collection<DependenceEdge>>(_entry.getNumber()); _edgesIn = new NodeMap<Collection<DependenceEdge>>(_entry.getNumber()); _obj2node = new HashMap<Object,DependenceNode>(); _arg2actualIn = new HashMap<Argument,ActualNode>(); _arg2actualOut = new HashMap<Argument,ActualNode>(); //build the entry node addNode(_entry); } /** Compact PDG, reduce memory consummation when the PDG becomes stable. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public void compact(){ // Use a compact node set _nodes = new ArrayList<DependenceNode>(_nodes); // compact edge set for non interface nodes if(_edgesIn instanceof NodeMap){ Object[] nodemap = ((NodeMap)_edgesIn).map; int length = nodemap.length; for(int i=0; i<length; i++){ Collection<DependenceEdge> edges = (Collection<DependenceEdge>)nodemap[i]; if(edges!=null){ if(edges.isEmpty()){ nodemap[i] = Collections.EMPTY_LIST; } else{ DependenceNode n = edges.iterator().next().getTo(); if(!(n instanceof ActualNode)){ edges = new ArrayList<DependenceEdge>(edges); nodemap[i] = edges; } } } } } else{ // only ActualNode may update its edges later during SDG construction for(Map.Entry<DependenceNode, Collection<DependenceEdge>> e: _edgesIn.entrySet()){ DependenceNode n = e.getKey(); if(!(n instanceof ActualNode)){ Collection<DependenceEdge> edges = e.getValue(); edges = new ArrayList<DependenceEdge>(edges); e.setValue(edges); } } } if(_edgesOut instanceof NodeMap){ Object[] nodemap = ((NodeMap)_edgesOut).map; int length = nodemap.length; for(int i=0; i<length; i++){ Collection<DependenceEdge> edges = (Collection<DependenceEdge>)nodemap[i]; if(edges!=null){ if(edges.isEmpty()){ nodemap[i] = Collections.EMPTY_LIST; } else{ DependenceNode n = edges.iterator().next().getFrom(); if(!(n instanceof ActualNode)){ edges = new ArrayList<DependenceEdge>(edges); nodemap[i] = edges; } } } } } else{ for(Map.Entry<DependenceNode, Collection<DependenceEdge>> e: _edgesOut.entrySet()){ DependenceNode n = e.getKey(); if(!(n instanceof ActualNode)){ Collection<DependenceEdge> edges = e.getValue(); edges = new ArrayList<DependenceEdge>(edges); e.setValue(edges); } } } } /** Compact edge set representation. */ @SuppressWarnings({ "rawtypes", "unchecked" }) void finalizeEdgeSets(){ // compact edge set for non interface nodes if(_edgesIn instanceof NodeMap){ Object[] nodemap = ((NodeMap)_edgesIn).map; int length = nodemap.length; for(int i=0; i<length; i++){ Collection<DependenceEdge> edges = (Collection<DependenceEdge>)nodemap[i]; if(edges!=null && edges instanceof Set){ edges = new ArrayList<DependenceEdge>(edges); nodemap[i] = edges; } } } else{ // only ActualNode may update its edges later during SDG construction for(Map.Entry<DependenceNode, Collection<DependenceEdge>> e: _edgesIn.entrySet()){ Collection<DependenceEdge> edges = e.getValue(); if(edges instanceof Set){ edges = new ArrayList<DependenceEdge>(edges); e.setValue(edges); } } } if(_edgesOut instanceof NodeMap){ Object[] nodemap = ((NodeMap)_edgesOut).map; int length = nodemap.length; for(int i=0; i<length; i++){ Collection<DependenceEdge> edges = (Collection<DependenceEdge>)nodemap[i]; if(edges!=null && edges instanceof Set){ edges = new ArrayList<DependenceEdge>(edges); nodemap[i] = edges; } } } else{ for(Map.Entry<DependenceNode, Collection<DependenceEdge>> e: _edgesOut.entrySet()){ Collection<DependenceEdge> edges = e.getValue(); if(edges instanceof Set){ edges = new ArrayList<DependenceEdge>(edges); e.setValue(edges); } } } } /**Adds a node to the graph.*/ public boolean addNode(DependenceNode node){ Object binding = node.getBinding(); if(node instanceof FormalIn){ _binding2formalIn.put(binding,(FormalIn)node); } else if(node instanceof FormalOut){ _binding2formalOut.put(binding,(FormalOut)node); } else if(node instanceof ActualIn){ ActualNode actual = (ActualNode)node; Argument arg = new Argument(actual.getCallSite(),actual.getCallee(),binding); _arg2actualIn.put(arg, (ActualIn)node); } else if(node instanceof ActualOut){ //TODO �������̫��Ч,�ڽڵ��Ͻ���Map���������ٶȿ�һЩ ActualNode actual = (ActualNode)node; Argument arg = new Argument(actual.getCallSite(),actual.getCallee(),binding); _arg2actualOut.put(arg, (ActualOut)node); } else{ //FIXME: Can be dangerous for CallNodes _obj2node.put(binding,node); } return _nodes.add(node); } /** Determine whether this is a concrete PDG with modeling to the method body. * Some PDG(s) only have FormalNode(s) to model the interface of the method. */ public boolean containsMethodBody(){ return _obj2node.size()>1; } public DependenceNode entry(){ return _entry; } public Collection<DependenceNode> getNodes(){ return _nodes; } /** * Get the binding DependenceNode of an object. Used for easy access to the PDG. * * @param stmt An unit can bind to a JimpleStmtNode * A Integer line can bind to a JavaStmtNode */ public DependenceNode getStmtBindingNode(Object stmt){ return _obj2node.get(stmt); } /** Get the corresponding formal nodes of the given binding object <code>binding</code>.*/ public FormalNode getBindingFormal(Object binding, boolean isGetFormalIn){ if(isGetFormalIn) return _binding2formalIn.get(binding); else return _binding2formalOut.get(binding); } /** Get the corresponding actual nodes of the given information.*/ public ActualNode getBindingActual(Unit callsite,SootMethod callee,Object loc, boolean isGetActualIn){ Argument arg = new Argument(callsite,callee,loc); if(isGetActualIn) return _arg2actualIn.get(arg); else return _arg2actualOut.get(arg); } private Set<DependenceEdge> createEdgeSet(){ return new HashSet<DependenceEdge>(); } ////////////////// Edges /////////////////// /** * @return true if there is not such edge yet. */ public boolean addEdge(DependenceEdge edge) { DependenceNode from = edge.getFrom(); DependenceNode to = edge.getTo(); boolean exist = true; Collection<DependenceEdge> outs = _edgesOut.get(from); if(outs==null){ outs = createEdgeSet(); _edgesOut.put(from,outs); exist = false; } Collection<DependenceEdge> ins = _edgesIn.get(to); if(ins==null){ ins = createEdgeSet(); _edgesIn.put(to,ins); exist = false; } exist = exist && outs.contains(edge); if(!exist){ outs.add(edge); ins.add(edge); if(edge instanceof CtrlDependenceEdge){ _edgeCount[DependenceEdge.CTRL_DEP]++; }else{ DataDependenceEdge dataEdge = (DataDependenceEdge)edge; Object loc = dataEdge.getReason(); if(loc==null) _edgeCount[DependenceEdge.UNDISTINGUSIHED_DATA_DEP]++; else if(loc instanceof StackLocation || loc instanceof MethodRet) _edgeCount[DependenceEdge.STACK_DEP]++; else if(loc instanceof HeapLocation || loc instanceof SootField || loc instanceof Type) _edgeCount[DependenceEdge.HEAP_DEP]++; else if(loc instanceof GlobalLocation) _edgeCount[DependenceEdge.GLOBAL_DEP]++; else throw new RuntimeException("Stange location"); } } return !exist; } public int getEdgeCount(){ int sum=0; for(int i=0;i<_edgeCount.length;i++) sum += _edgeCount[i]; return sum; } public int getEdgeCount(int type){ return _edgeCount[type]; } public static void updateStatistic(int edgeCount[], PDG pdg){ int count = pdg.getEdgeCount(DependenceEdge.CTRL_DEP); edgeCount[DependenceEdge.CTRL_DEP] += count; count = pdg.getEdgeCount(DependenceEdge.GLOBAL_DEP); edgeCount[DependenceEdge.GLOBAL_DEP] += count; count = pdg.getEdgeCount(DependenceEdge.HEAP_DEP); edgeCount[DependenceEdge.HEAP_DEP] += count; count = pdg.getEdgeCount(DependenceEdge.STACK_DEP); edgeCount[DependenceEdge.STACK_DEP] += count; count = pdg.getEdgeCount(DependenceEdge.UNDISTINGUSIHED_DATA_DEP); edgeCount[DependenceEdge.UNDISTINGUSIHED_DATA_DEP] += count; } public static String statisticsToString(int edgeCount[]){ String ret = "ctrl "+ edgeCount[DependenceEdge.CTRL_DEP] +", stack "+edgeCount[DependenceEdge.STACK_DEP] +", heap "+ edgeCount[DependenceEdge.HEAP_DEP] +", global "+ edgeCount[DependenceEdge.GLOBAL_DEP] +", undistinguished " + edgeCount[DependenceEdge.UNDISTINGUSIHED_DATA_DEP]; return ret; } public String statistcToString(){ return statisticsToString(_edgeCount); } public Collection<DependenceEdge> edgesInto(DependenceNode node){ Collection<DependenceEdge> edges = _edgesIn.get(node); if(edges!=null) return edges; else return Collections.emptySet(); } public Collection<DependenceEdge> edgesOutOf(DependenceNode node){ Collection<DependenceEdge> edges = _edgesOut.get(node); if(edges!=null) return edges; else return Collections.emptySet(); } /////////////////////////////////////////////////////// public PDG toJavaStmtDepGraph(){ return toJavaStmtDepGraph(null); } /** * Change to the PDG with .java line as node. The returned dependence graph is easy for understanding, * but it is not supposed to be used in slicing. * @param old2NewForOutVisiables Map old node to new node. Only the out visible dependence nodes * such as FormalNode, ActualNode, EntryNode, and CallNode are concerned. * This parameter can be null. */ public PDG toJavaStmtDepGraph(Map<DependenceNode,DependenceNode> old2NewForOutVisiables){ PDG depGraph = new PDG(_mc); Map<DependenceNode,DependenceNode> old2New=new HashMap<DependenceNode,DependenceNode>(_nodes.size()*2+1,0.7f); Map<Integer,DependenceNode> line2Node=new HashMap<Integer,DependenceNode>(); //depGraph._entry =( EntryNode)_entry.clone(); //depGraph.addNode(depGraph._entry); old2New.put(_entry, depGraph._entry); //build node map for(DependenceNode node: _nodes){ DependenceNode newNode=null; if(node instanceof JimpleStmtNode){ JimpleStmtNode jmpNode=(JimpleStmtNode)node; Unit unit=jmpNode.getStmt(); if(unit instanceof IdentityStmt || unit instanceof ReturnVoidStmt){ newNode = depGraph.entry(); } else{ newNode = assureJavaNode(depGraph,line2Node,SootUtils.getLine(unit)); } } else{ if(node instanceof FormalNode){ newNode = depGraph._entry; } else if(node instanceof CallNode){ Unit callsite = ((CallNode)node).getCallSite(); newNode = assureJavaNode(depGraph,line2Node,SootUtils.getLine(callsite)); JavaStmtNode javaNode = (JavaStmtNode)newNode; javaNode.setCallSiteFlag(true); } else if(node instanceof ActualNode){ ActualNode actual = (ActualNode)node; Unit callsite = actual.getCallSite(); newNode = assureJavaNode(depGraph,line2Node,SootUtils.getLine(callsite)); } else if(node instanceof EntryNode){ newNode = depGraph.entry(); } else{ newNode = (DependenceNode)node.clone(); depGraph.addNode(newNode); } if(old2NewForOutVisiables!=null){ old2NewForOutVisiables.put(node, newNode); } } old2New.put(node,newNode); } //reconstruct edge for(DependenceNode node: _nodes){ Collection<DependenceEdge> outs = _edgesOut.get(node); if(outs==null) continue; for (DependenceEdge edge: outs) { // map to new edge DependenceNode from = (DependenceNode) old2New.get(node); DependenceNode to = (DependenceNode) old2New.get(edge.getTo()); DependenceEdge newEdge = null; if (edge instanceof DataDependenceEdge) { DataDependenceEdge dataEdge = (DataDependenceEdge) edge; Object reason = dataEdge.getReason(); // ignore data dependence caused by temporal variables if (reason == null || reason.toString().charAt(0) != '$') { if (edge instanceof SummaryEdge) { newEdge = new SummaryEdge(from, to, reason); } else { newEdge = new DataDependenceEdge(from, to, reason); } } } else if (edge instanceof CtrlDependenceEdge && from != to) { newEdge = new CtrlDependenceEdge(from, to); } if (newEdge != null) { depGraph.addEdge(newEdge); } } } return depGraph; } private DependenceNode assureJavaNode(PDG depGraph,Map<Integer,DependenceNode> line2Node,int line){ DependenceNode newNode = line2Node.get(line); if (newNode == null) { newNode = new JavaStmtNode(_mc,line); line2Node.put(line, newNode); depGraph.addNode(newNode); } return newNode; } class PdgDirectedGraph implements DirectedGraph<DependenceNode>{ public List<DependenceNode> getHeads() { List<DependenceNode> list = new ArrayList<DependenceNode>(1); list.add(_entry); return list; } /** No tails at all. */ public List<DependenceNode> getTails() { return Collections.emptyList(); } public List<DependenceNode> getPredsOf(DependenceNode s) { List<DependenceNode> ins = new LinkedList<DependenceNode>(); Collection<DependenceEdge> edges = PDG.this._edgesIn.get(s); for(DependenceEdge e: edges){ ins.add(e.getFrom()); } return ins; } public List<DependenceNode> getSuccsOf(DependenceNode s) { List<DependenceNode> outs = new LinkedList<DependenceNode>(); Collection<DependenceEdge> edges = PDG.this._edgesOut.get(s); if(edges!=null){ for(DependenceEdge e: edges){ outs.add(e.getTo()); } } return outs; } public Iterator<DependenceNode> iterator() { return PDG.this._nodes.iterator(); } public int size() { return PDG.this._nodes.size(); } } /**Get a graph compatible with the DirectedGraph interface*/ public DirectedGraph<DependenceNode> toDirectedGraph(){ return new PdgDirectedGraph(); } public String toString(){ StringBuffer str = new StringBuffer("PDG of "+_mc+"\n"); for(DependenceNode node: _nodes){ str.append("\n\nNode: "+node.toString()); str.append("\nIncomming edges:"); Collection<DependenceEdge> ins = _edgesIn.get(node); if(ins!=null){ str.append("("+ins.size()+")\n"); str.append(CollectionUtils.toString(ins.iterator(),"\n")); } str.append("\nOutcomming edges:"); Collection<DependenceEdge> outs = _edgesOut.get(node); if(outs!=null){ str.append("("+outs.size()+")\n"); str.append(CollectionUtils.toString(outs.iterator(),"\n")); } } return str.toString(); } public Collection<FormalNode> getFormalIns(){ return _binding2formalIn.values(); } public Collection<FormalNode> getFormalOuts(){ return _binding2formalOut.values(); } public void getActualNodes(Collection<ActualNode> out){ out.addAll(_arg2actualIn.values()); out.addAll(_arg2actualOut.values()); } @SuppressWarnings({ "unchecked", "rawtypes" }) public void getActualIns(Collection out,Unit callsite){ for(Map.Entry<Argument,ActualNode> entry: _arg2actualIn.entrySet()){ Argument arg = entry.getKey(); if(arg._callsite==callsite){ out.add(entry.getValue()); } } } @SuppressWarnings({ "unchecked", "rawtypes" }) public void getActualOuts(Collection out,Unit callsite){ for(Map.Entry<Argument,ActualNode> entry: _arg2actualOut.entrySet()){ Argument arg = entry.getKey(); if(arg._callsite==callsite){ out.add(entry.getValue()); } } } public void getActualOutsOfReturnValue(Collection<DependenceNode> out,Unit callsite){ for(Map.Entry<Argument,ActualNode> entry: _arg2actualOut.entrySet()){ Argument arg = entry.getKey(); ActualNode node = entry.getValue(); Object formalBinding = node.getFormalBinding(); if(arg._callsite==callsite && formalBinding instanceof MethodRet){ out.add(node); } } } private static final class Argument{ final Unit _callsite; final SootMethod _callee; final Object _loc; //The argument location can be a Location or even a SootField which standing for a collection of Locations public Argument(Unit callsite,SootMethod callee,Object loc){ this._callsite = callsite; this._callee = callee; this._loc = loc; } public boolean equals(Object obj){ Argument that = (Argument)obj; if(this._callsite==that._callsite && this._callee==that._callee && this._loc==that._loc){ return true; } return false; } public int hashCode(){ int hash = 3*_callsite.hashCode()+7*_callee.getNumber(); if(_loc!=null) hash += 11*_loc.hashCode(); return hash; } } static class NodeMap<N> implements Map<DependenceNode, N>{ static final float INC_FACTOR = (float)1.6; private final int initIndex; private Object[] map; NodeMap(int initIndex){ this.initIndex = initIndex; this.map = new Object[32]; } public int size() { throw new RuntimeException("Non supported"); } public boolean isEmpty() { throw new RuntimeException("Non supported"); } public boolean containsKey(Object key) { throw new RuntimeException("Non supported"); } public boolean containsValue(Object value) { throw new RuntimeException("Non supported"); } private void assureCapacity(int id){ int length = map.length; if(length <= id){ while(length <= id){ length *= INC_FACTOR; } Object[] newmap = new Object[length]; System.arraycopy(map, 0, newmap, 0, map.length); map = newmap; } } @SuppressWarnings("unchecked") public N get(Object key) { DependenceNode n = (DependenceNode)key; int id = n.getNumber() - initIndex; assureCapacity(id); return (N)map[id]; } @SuppressWarnings("unchecked") public N put(DependenceNode key, N value) { DependenceNode n = (DependenceNode)key; int id = n.getNumber() - initIndex; assureCapacity(id); Object old = map[id]; map[id] = value; return (N)old; } public Object[] getMapArray(){ return map; } public N remove(Object key) { throw new RuntimeException("Non supported"); } public void putAll(Map<? extends DependenceNode, ? extends N> m) { throw new RuntimeException("Non supported"); } public void clear() { throw new RuntimeException("Non supported"); } public Set<DependenceNode> keySet() { throw new RuntimeException("Non supported"); } public Collection<N> values() { throw new RuntimeException("Non supported"); } public Set<java.util.Map.Entry<DependenceNode, N>> entrySet() { throw new RuntimeException("Non supported"); } } }