package jqian.sootex.sideeffect; import java.util.*; import jqian.sootex.util.*; import jqian.util.Utils; import jqian.*; import soot.*; import soot.jimple.*; import soot.jimple.toolkits.callgraph.CallGraph; import soot.toolkits.graph.DirectedGraph; import soot.toolkits.graph.PseudoTopologicalOrderer; /** * Find what fields a method read and write. * Use a SCC-based approach to improve time and space efficiency. * XXX: The algorithm do not collect information through thread boundaries */ @SuppressWarnings({"rawtypes","unchecked"}) public class FieldScaner{ private static FieldScaner _instance =null; private FieldScaner() { build(); } public static FieldScaner v() { if(_instance==null){ _instance=new FieldScaner(); Global.v().regesiterResetableGlobals(FieldScaner.class); } return _instance; } private Set<SootField>[] _defInstFields; private Set<SootField>[] _useInstFields; private Set<SootField>[] _defStaticFields; private Set<SootField>[] _useStaticFields; private Set<ArrayType>[] _defArrayTypes; private Set<ArrayType>[] _useArrayTypes; /////////////////////////////////////////////////// protected static void reset(){ _instance = null; } private void build(){ Date start = new Date(); Global.v().out.println("[FieldScaner] starting..."); int mthdCount = SootUtils.getMethodCount(); _defInstFields = new Set[mthdCount]; _useInstFields = new Set[mthdCount]; _defStaticFields = new Set[mthdCount]; _useStaticFields = new Set[mthdCount]; _defArrayTypes = new Set[mthdCount]; _useArrayTypes = new Set[mthdCount]; //1. get the collapse call graph, each strong connected commponent into a single graph node CallGraph cg = Scene.v().getCallGraph(); Collection<SootMethod> entries = Scene.v().getEntryPoints(); DirectedGraph graph = SootUtils.getSCCGraph(cg,entries); //2. topological sort PseudoTopologicalOrderer pto = new PseudoTopologicalOrderer(); List order = pto.newList(graph,true); //3. bottom-up phase for(Iterator it=order.iterator();it.hasNext();){ Collection node = (Collection) it.next(); // a strong connected component, can be single method with recursion Set<SootMethod> component = new HashSet(node); // trun to set for fast access findComponentModUse(cg, component); } Date end = new Date(); Global.v().out.println("[FieldScaner] finish in "+Utils.getTimeConsumed(start,end)); } private void findComponentModUse(CallGraph cg,Set<SootMethod> component){ // in this case, all the methods in the recursion share the same side-effect set Set defInstFields = new HashSet(); Set useInstFields = new HashSet(); Set defStaticFields = new HashSet(); Set useStaticFields = new HashSet(); Set defArrayTypes = new HashSet(); Set useArrayTypes = new HashSet(); // a. collect from this strong connected component for(SootMethod m: component){ scanNontransitively(m,defInstFields, useInstFields, defStaticFields, useStaticFields,defArrayTypes, useArrayTypes); } // b. collect from callees Set<SootMethod> computedCallees = new HashSet(); SideEffectHelper.collectComponentCallees(component,cg,computedCallees); mergeCallee(computedCallees,defInstFields,useInstFields, defStaticFields,useStaticFields,defArrayTypes,useArrayTypes); // c. finalized to a compact set defInstFields = SideEffectHelper.compact(defInstFields); useInstFields = SideEffectHelper.compact(useInstFields); defStaticFields = SideEffectHelper.compact(defStaticFields); useStaticFields = SideEffectHelper.compact(useStaticFields); defArrayTypes = SideEffectHelper.compact(defArrayTypes); useArrayTypes = SideEffectHelper.compact(useArrayTypes); for(SootMethod m: component){ int id = m.getNumber(); _defInstFields[id] = defInstFields; _useInstFields[id] = useInstFields; _defStaticFields[id] = defStaticFields; _useStaticFields[id] = useStaticFields; _defArrayTypes[id] = defArrayTypes; _useArrayTypes[id] = useArrayTypes; } } private void mergeCallee(Set<SootMethod> callees,Set defInstFields, Set useInstFields, Set defStaticFields, Set useStaticFields, Set defArrayTypes, Set useArrayTypes){ for(SootMethod tgt: callees){ int tgtId = tgt.getNumber(); defInstFields.addAll(_defInstFields[tgtId]); defStaticFields.addAll(_defStaticFields[tgtId]); defArrayTypes.addAll(_defArrayTypes[tgtId]); useInstFields.addAll(_useInstFields[tgtId]); useStaticFields.addAll(_useStaticFields[tgtId]); useArrayTypes.addAll(_useArrayTypes[tgtId]); } } /** Collect field use intraprocedurally. */ private static void scanNontransitively(SootMethod m,Set defInstFields, Set useInstFields, Set defStaticFields, Set useStaticFields, Set defArrayTypes, Set useArrayTypes){ if(!m.isConcrete()) return; Body body = m.getActiveBody(); for(Iterator it=body.getUnits().iterator();it.hasNext();){ Object obj=it.next(); if(obj instanceof DefinitionStmt){ DefinitionStmt stmt = (DefinitionStmt)obj; Value left = stmt.getLeftOp(); Value right = stmt.getRightOp(); if(left instanceof FieldRef){ FieldRef ref = (FieldRef)left; addFieldRef(ref,defInstFields,defStaticFields); } else if(left instanceof ArrayRef){ ArrayRef ref = (ArrayRef)left; defArrayTypes.add(ref.getBase().getType()); } else if(right instanceof FieldRef){ FieldRef ref = (FieldRef)right; addFieldRef(ref,useInstFields,useStaticFields); } else if(right instanceof ArrayRef){ ArrayRef ref = (ArrayRef)right; useArrayTypes.add(ref.getBase().getType()); } } else if(obj instanceof Stmt){ Stmt s = (Stmt)obj; if(s.containsFieldRef()){ addFieldRef(s.getFieldRef(),useInstFields,useStaticFields); } if(s.containsArrayRef()){ useArrayTypes.add(s.getArrayRef().getBase().getType()); } } } } private static void addFieldRef(FieldRef ref,Set instFields,Set staticFields){ SootField field = ref.getField(); if(field.isStatic()){ staticFields.add(field); }else{ instFields.add(field); } } // ------------------------ Accessors ----------------------------------- public Set<SootField> getModInstanceFields(SootMethod m){ return _defInstFields[m.getNumber()]; } public Set<SootField> getUseInstanceFields(SootMethod m){ return _useInstFields[m.getNumber()]; } public Set<SootField> getModGlobals(SootMethod m){ return _defStaticFields[m.getNumber()]; } public Set<SootField> getUseGlobals(SootMethod m){ return _useStaticFields[m.getNumber()]; } public Set<ArrayType> getModArrayTypes(SootMethod m){ return _defArrayTypes[m.getNumber()]; } public Set<ArrayType> getUseArrayTypes(SootMethod m){ return _useArrayTypes[m.getNumber()]; } public void getAccessedInstanceFields(SootMethod m,Collection out){ int mId = m.getNumber(); out.addAll(_defInstFields[mId]); out.addAll(_useInstFields[mId]); } public void getAccessedGlobals(SootMethod m,Collection out){ int mId = m.getNumber(); out.addAll(_defStaticFields[mId]); out.addAll(_useStaticFields[mId]); } public void getAccessedArrayTypes(SootMethod m,Collection out){ int mId = m.getNumber(); out.addAll(_defArrayTypes[mId]); out.addAll(_useArrayTypes[mId]); } void release(SootMethod m){ int mId = m.getNumber(); _defInstFields[mId] = null; _useInstFields[mId] = null; _defStaticFields[mId] = null; _useStaticFields[mId] = null; _defArrayTypes[mId] = null; _useArrayTypes[mId] = null; } }