package jqian.sootex.ptsto; import java.util.*; import jqian.Global; import jqian.sootex.Cache; import jqian.util.CollectionUtils; import jqian.util.Utils; import soot.AnySubType; import soot.ArrayType; import soot.Context; import soot.Hierarchy; import soot.Local; import soot.PointsToAnalysis; import soot.PointsToSet; import soot.PrimType; import soot.RefType; import soot.Scene; import soot.SootClass; import soot.SootField; import soot.Type; import soot.jimple.toolkits.callgraph.CallGraphBuilder; import soot.jimple.toolkits.pointer.DumbPointerAnalysis; import soot.options.Options; import soot.util.Numberer; /** * Type-based points-to analysis */ public class TypeBasedPointsToAnalysis implements PointsToAnalysis { private static TypeBasedPointsToAnalysis _instance; protected static void reset(){ _instance = null; } static{ Global.v().regesiterResetableGlobals(TypeBasedPtsToQuery.class); } public static TypeBasedPointsToAnalysis v(boolean allReachable){ if(_instance==null || (!_instance._allReachable && allReachable)){ _instance = new TypeBasedPointsToAnalysis(allReachable); } return _instance; } private final TypeBasedPointsToSet[] _type2ptsto; private final Type OBJECT_TYPE = Scene.v().getObjectType(); private final boolean _allReachable; private Numberer _nodeNumberer; /** * @param allReachable Should all methods considered reachable? */ private TypeBasedPointsToAnalysis(boolean allReachable){ if(allReachable){ //Date startTime = new Date(); //SootUtils.jimplify(); //Date endTime=new Date(); //Global.v().out.println(">> jimplify all classes in " + Utils.getTimeConsumed(startTime,endTime)); // construct a call graph to jimplify all necessary methods, load all necessary types if(!Scene.v().hasCallGraph()){ Options.v().setPhaseOption("cg","verbose:false"); Options.v().setPhaseOption("cg","all-reachable:true"); CallGraphBuilder cg = new CallGraphBuilder( DumbPointerAnalysis.v() ); //CallGraphBuilder cg = new CallGraphBuilder(); cg.build(); Scene.v().setCallGraph(cg.getCallGraph()); } } else{ // parse all reachable methods by building a CHA call graph if(!Scene.v().hasCallGraph() && !Scene.v().hasPointsToAnalysis()){ Date startTime = new Date(); CallGraphBuilder cg = new CallGraphBuilder( DumbPointerAnalysis.v() ); cg.build(); Scene.v().setCallGraph(cg.getCallGraph()); Date endTime=new Date(); Global.v().out.println(">> jimplify reachable classes and build CHA call graph in " + Utils.getTimeConsumed(startTime,endTime)); } } _nodeNumberer = Scene.v().getTypeNumberer(); _type2ptsto = new TypeBasedPointsToSet[_nodeNumberer.size()+1]; _allReachable = allReachable; } public Numberer getObjectNumberer(){ return _nodeNumberer; } /* (non-Javadoc) * @see soot.PointsToAnalysis#reachingObjects(soot.Local) */ public PointsToSet reachingObjects(Local l) { return reachingObjects(l.getType()); } /* (non-Javadoc) * @see soot.PointsToAnalysis#reachingObjects(soot.Context, soot.Local) */ public PointsToSet reachingObjects(Context c, Local l) { return reachingObjects(l.getType()); } /* (non-Javadoc) * @see soot.PointsToAnalysis#reachingObjects(soot.SootField) */ public PointsToSet reachingObjects(SootField f) { return reachingObjects(f.getType()); } /* (non-Javadoc) * @see soot.PointsToAnalysis#reachingObjects(soot.Local, soot.SootField) */ public PointsToSet reachingObjects(Local l, SootField f) { return reachingObjects(f.getType()); } /* (non-Javadoc) * @see soot.PointsToAnalysis#reachingObjects(soot.Context, soot.Local, soot.SootField) */ public PointsToSet reachingObjects(Context c, Local l, SootField f) { return reachingObjects(f.getType()); } /* (non-Javadoc) * @see soot.PointsToAnalysis#reachingObjects(soot.PointsToSet, soot.SootField) */ public PointsToSet reachingObjects(PointsToSet s, SootField f) { return reachingObjects(f.getType()); } /* (non-Javadoc) * @see soot.PointsToAnalysis#reachingObjectsOfArrayElement(soot.PointsToSet) */ public PointsToSet reachingObjectsOfArrayElement(PointsToSet s) { if(s instanceof TypeBasedPointsToSet){ TypeBasedPointsToSet ptsto = new TypeBasedPointsToSet(); for(Type t: (TypeBasedPointsToSet)s){ if(t instanceof ArrayType){ ArrayType at = (ArrayType)t; Type p = at.getElementType(); TypeBasedPointsToSet tgts = reachingObjects(p); ptsto.addAll(tgts); } } return ptsto; } else{ throw new RuntimeException("Parameter of types other than TypeBasedPointsToSet are not acceptable."); } } /**Get a collection of objects that pointer of type <code>type</code> can point to. */ public TypeBasedPointsToSet reachingObjects(Type type){ int tId = type.getNumber(); TypeBasedPointsToSet ptsto = _type2ptsto[tId]; if(ptsto!=null){ return ptsto; } ptsto = toConcreteTypes(type); _type2ptsto[tId] = ptsto; return ptsto; } // Object o = new String[1] private TypeBasedPointsToSet toConcreteTypes(Type type){ TypeBasedPointsToSet typeSet = new TypeBasedPointsToSet(); // Object type can also reference to arrays if(type==OBJECT_TYPE){ Numberer numberer = Scene.v().getTypeNumberer(); int count = numberer.size(); for(int i=1; i<=count; i++){ Type t = (Type)numberer.get(i); if(t instanceof RefType || t instanceof ArrayType){ typeSet.add(t); } else if(t instanceof AnySubType){ } } } else if(type instanceof RefType){ SootClass cls = ((RefType)type).getSootClass(); if(cls.isConcrete()){ typeSet.add(type); } Hierarchy hierarchy = Scene.v().getActiveHierarchy(); Collection<?> classes; if(cls.isInterface()){ classes = hierarchy.getImplementersOf(cls); } else{ classes = hierarchy.getSubclassesOf(cls); } for(Iterator<?> it=classes.iterator();it.hasNext();){ SootClass subCls = (SootClass)it.next(); if(subCls.isConcrete()){ Type subType = subCls.getType(); typeSet.add(subType); } } } else if(type instanceof ArrayType){ //handle the possibility: Object[] array = new String[] Type elmtType = ((ArrayType)type).getArrayElementType(); if(elmtType instanceof PrimType){ typeSet.add(type); } else if(elmtType instanceof RefType){ // The base of an array type does not necessary to be concrete // add the current type typeSet.add(type); // any sub type for the element type SootClass cls = ((RefType)elmtType).getSootClass(); Hierarchy hierarchy = Scene.v().getActiveHierarchy(); Collection<SootClass> subClassesAndInterfaces; if(cls.isInterface()){ List<SootClass> interfaces = hierarchy.getSubinterfacesOf(cls); List<SootClass> implementations = hierarchy.getImplementersOf(cls); subClassesAndInterfaces = new ArrayList<SootClass>(interfaces); subClassesAndInterfaces.addAll(implementations); } else{ subClassesAndInterfaces = hierarchy.getSubclassesOf(cls); } Collection<Type> elementSubTypes = new HashSet<Type>(); for(SootClass c: subClassesAndInterfaces){ elementSubTypes.add(c.getType()); } for(Type subType: elementSubTypes){ // Only existing ArrayType(s) are considered. Avoid creating new types // which may cause type exploding ArrayType at = subType.getArrayType(); if(at!=null) typeSet.add(at); } } else if(elmtType instanceof ArrayType){ TypeBasedPointsToSet concreteTypes = toConcreteTypes(elmtType); for(Type t: concreteTypes){ Type concreteType = t.getArrayType(); if(concreteType!=null){ typeSet.add(concreteType); } } } } return typeSet; } /** Check if type a and type b have a common sub type. */ public boolean mayAliased(Type a, Type b){ TypeBasedPointsToSet pt2setA = reachingObjects(a); TypeBasedPointsToSet pt2setB = reachingObjects(b); return CollectionUtils.hasInterset(pt2setA, pt2setB); } /** * @param assume node should be a concrete type representing an abstract object. * interfaces and abstract classes do not have reaching objects */ public TypeBasedPointsToSet reachingObjectsOfAllFields(Type node){ TypeBasedPointsToSet result = new TypeBasedPointsToSet(); if(node instanceof RefType){ SootClass cls = ((RefType)node).getSootClass(); if(cls.isConcrete()){ Set<SootField> fields = Cache.v().getAllInstanceFields(cls); for(SootField f: fields){ TypeBasedPointsToSet p2set = reachingObjects(f.getType()); result.addAll(p2set); } } } else if(node instanceof ArrayType){ Type elementType = ((ArrayType)node).getArrayElementType(); TypeBasedPointsToSet p2set = reachingObjects(elementType); result.addAll(p2set); } return result; } }