/* Soot - a J*va Optimization Framework * Copyright (C) 2002 Ondrej Lhotak * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ package soot.jimple.spark.pag; import java.util.*; import soot.jimple.*; import soot.*; import soot.jimple.spark.sets.*; import soot.jimple.spark.solver.OnFlyCallGraph; import soot.jimple.spark.internal.*; import soot.jimple.spark.builder.*; import soot.jimple.toolkits.callgraph.Edge; import soot.jimple.toolkits.pointer.util.NativeMethodDriver; import soot.util.*; import soot.util.queue.*; import soot.options.SparkOptions; import soot.tagkit.*; import soot.toolkits.scalar.Pair; /** Pointer assignment graph. * @author Ondrej Lhotak */ public class PAG implements PointsToAnalysis { public PAG( final SparkOptions opts ) { this.opts = opts; if( opts.add_tags() ) { nodeToTag = new HashMap<Node, Tag>(); } typeManager = new TypeManager(this); if( !opts.ignore_types() ) { typeManager.setFastHierarchy( Scene.v().getOrMakeFastHierarchy() ); } switch( opts.set_impl() ) { case SparkOptions.set_impl_hash: setFactory = HashPointsToSet.getFactory(); break; case SparkOptions.set_impl_hybrid: setFactory = HybridPointsToSet.getFactory(); break; case SparkOptions.set_impl_heintze: setFactory = SharedHybridSet.getFactory(); break; case SparkOptions.set_impl_sharedlist: setFactory = SharedListSet.getFactory(); break; case SparkOptions.set_impl_array: setFactory = SortedArraySet.getFactory(); break; case SparkOptions.set_impl_bit: setFactory = BitPointsToSet.getFactory(); break; case SparkOptions.set_impl_double: P2SetFactory oldF; P2SetFactory newF; switch( opts.double_set_old() ) { case SparkOptions.double_set_old_hash: oldF = HashPointsToSet.getFactory(); break; case SparkOptions.double_set_old_hybrid: oldF = HybridPointsToSet.getFactory(); break; case SparkOptions.double_set_old_heintze: oldF = SharedHybridSet.getFactory(); break; case SparkOptions.double_set_old_sharedlist: oldF = SharedListSet.getFactory(); break; case SparkOptions.double_set_old_array: oldF = SortedArraySet.getFactory(); break; case SparkOptions.double_set_old_bit: oldF = BitPointsToSet.getFactory(); break; default: throw new RuntimeException(); } switch( opts.double_set_new() ) { case SparkOptions.double_set_new_hash: newF = HashPointsToSet.getFactory(); break; case SparkOptions.double_set_new_hybrid: newF = HybridPointsToSet.getFactory(); break; case SparkOptions.double_set_new_heintze: newF = SharedHybridSet.getFactory(); break; case SparkOptions.double_set_new_sharedlist: newF = SharedListSet.getFactory(); break; case SparkOptions.double_set_new_array: newF = SortedArraySet.getFactory(); break; case SparkOptions.double_set_new_bit: newF = BitPointsToSet.getFactory(); break; default: throw new RuntimeException(); } setFactory = DoublePointsToSet.getFactory( newF, oldF ); break; default: throw new RuntimeException(); } } /** Returns the set of objects pointed to by variable l. */ public PointsToSet reachingObjects( Local l ) { VarNode n = findLocalVarNode( l ); if( n == null ) { return EmptyPointsToSet.v(); } return n.getP2Set(); } /** Returns the set of objects pointed to by variable l in context c. */ public PointsToSet reachingObjects( Context c, Local l ) { VarNode n = findContextVarNode( l, c ); if( n == null ) { return EmptyPointsToSet.v(); } return n.getP2Set(); } /** Returns the set of objects pointed to by static field f. */ public PointsToSet reachingObjects( SootField f ) { if( !f.isStatic() ) throw new RuntimeException( "The parameter f must be a *static* field." ); VarNode n = findGlobalVarNode( f ); if( n == null ) { return EmptyPointsToSet.v(); } return n.getP2Set(); } /** Returns the set of objects pointed to by instance field f * of the objects in the PointsToSet s. */ public PointsToSet reachingObjects( PointsToSet s, final SootField f ) { if( f.isStatic() ) throw new RuntimeException( "The parameter f must be an *instance* field." ); return reachingObjectsInternal( s, f ); } /** Returns the set of objects pointed to by elements of the arrays * in the PointsToSet s. */ public PointsToSet reachingObjectsOfArrayElement( PointsToSet s ) { return reachingObjectsInternal( s, ArrayElement.v() ); } private PointsToSet reachingObjectsInternal( PointsToSet s, final SparkField f ) { if( getOpts().field_based() || getOpts().vta() ) { VarNode n = findGlobalVarNode( f ); if( n == null ) { return EmptyPointsToSet.v(); } return n.getP2Set(); } if( (getOpts()).propagator() == SparkOptions.propagator_alias ) { throw new RuntimeException( "The alias edge propagator does not compute points-to information for instance fields! Use a different propagator." ); } PointsToSetInternal bases = (PointsToSetInternal) s; final PointsToSetInternal ret = setFactory.newSet( (f instanceof SootField) ? ((SootField)f).getType() : null, this ); bases.forall( new P2SetVisitor() { public final void visit( Node n ) { Node nDotF = ((AllocNode) n).dot( f ); if(nDotF != null) ret.addAll( nDotF.getP2Set(), null ); }} ); return ret; } public P2SetFactory getSetFactory() { return setFactory; } public void cleanUpMerges() { if( opts.verbose() ) { G.v().out.println( "Cleaning up graph for merged nodes" ); } Map[] maps = { simple, alloc, store, load, simpleInv, allocInv, storeInv, loadInv }; for (Map<Object, Object> m : maps) { for (Object object : m.keySet()) { lookup( m, object ); } } somethingMerged = false; if( opts.verbose() ) { G.v().out.println( "Done cleaning up graph for merged nodes" ); } } public boolean doAddSimpleEdge( VarNode from, VarNode to ) { return addToMap( simple, from, to ) | addToMap( simpleInv, to, from ); } public boolean doAddStoreEdge( VarNode from, FieldRefNode to ) { return addToMap( store, from, to ) | addToMap( storeInv, to, from ); } public boolean doAddLoadEdge( FieldRefNode from, VarNode to ) { return addToMap( load, from, to ) | addToMap( loadInv, to, from ); } public boolean doAddAllocEdge( AllocNode from, VarNode to ) { return addToMap( alloc, from, to ) | addToMap( allocInv, to, from ); } /** Node uses this to notify PAG that n2 has been merged into n1. */ void mergedWith( Node n1, Node n2 ) { if( n1.equals( n2 ) ) throw new RuntimeException( "oops" ); somethingMerged = true; if( ofcg() != null ) ofcg().mergedWith( n1, n2 ); Map[] maps = { simple, alloc, store, load, simpleInv, allocInv, storeInv, loadInv }; for (Map<Node, Object> m : maps) { if( !m.keySet().contains( n2 ) ) continue; Object[] os = { m.get( n1 ), m.get( n2 ) }; int size1 = getSize(os[0]); int size2 = getSize(os[1]); if( size1 == 0 ) { if( os[1] != null ) m.put( n1, os[1] ); } else if( size2 == 0 ) { // nothing needed } else if( os[0] instanceof HashSet ) { if( os[1] instanceof HashSet ) { ((HashSet) os[0]).addAll( (HashSet) os[1] ); } else { Node[] ar = (Node[]) os[1]; for (Node element0 : ar) { ( (HashSet<Node>) os[0] ).add( element0 ); } } } else if( os[1] instanceof HashSet ) { Node[] ar = (Node[]) os[0]; for (Node element0 : ar) { ((HashSet<Node>) os[1]).add( element0 ); } m.put( n1, os[1] ); } else if( size1*size2 < 1000 ) { Node[] a1 = (Node[]) os[0]; Node[] a2 = (Node[]) os[1]; Node[] ret = new Node[size1+size2]; System.arraycopy( a1, 0, ret, 0, a1.length ); int j = a1.length; outer: for (Node rep : a2) { for( int k = 0; k < j; k++ ) if( rep == ret[k] ) continue outer; ret[j++] = rep; } Node[] newArray = new Node[j]; System.arraycopy( ret, 0, newArray, 0, j ); m.put( n1, ret = newArray ); } else { HashSet<Node> s = new HashSet<Node>( size1+size2 ); for (Object o : os) { if( o == null ) continue; if( o instanceof Set ) { s.addAll( (Set) o ); } else { Node[] ar = (Node[]) o; for (Node element1 : ar) { s.add( element1 ); } } } m.put( n1, s ); } m.remove( n2 ); } } protected final static Node[] EMPTY_NODE_ARRAY = new Node[0]; protected Node[] lookup( Map<Object, Object> m, Object key ) { Object valueList = m.get( key ); if( valueList == null ) { return EMPTY_NODE_ARRAY; } if( valueList instanceof Set ) { try { m.put( key, valueList = ( (Set) valueList ).toArray( EMPTY_NODE_ARRAY ) ); } catch( Exception e ) { for( Iterator it = ((Set)valueList).iterator(); it.hasNext(); ) { G.v().out.println( ""+it.next() ); } throw new RuntimeException( ""+valueList+e ); } } Node[] ret = (Node[]) valueList; if( somethingMerged ) { for( int i = 0; i < ret.length; i++ ) { Node reti = ret[i]; Node rep = reti.getReplacement(); if( rep != reti || rep == key ) { Set<Node> s; if( ret.length <= 75 ) { int j = i; outer: for( ; i < ret.length; i++ ) { reti = ret[i]; rep = reti.getReplacement(); if( rep == key ) continue; for( int k = 0; k < j; k++ ) if( rep == ret[k] ) continue outer; ret[j++] = rep; } Node[] newArray = new Node[j]; System.arraycopy( ret, 0, newArray, 0, j ); m.put( key, ret = newArray ); } else { s = new HashSet<Node>( ret.length * 2 ); for( int j = 0; j < i; j++ ) s.add( ret[j] ); for( int j = i; j < ret.length; j++ ) { rep = ret[j].getReplacement(); if( rep != key ) { s.add( rep ); } } m.put( key, ret = s.toArray( EMPTY_NODE_ARRAY ) ); } break; } } } return ret; } public Node[] simpleLookup( VarNode key ) { return lookup( simple, key ); } public Node[] simpleInvLookup( VarNode key ) { return lookup( simpleInv, key ); } public Node[] loadLookup( FieldRefNode key ) { return lookup( load, key ); } public Node[] loadInvLookup( VarNode key ) { return lookup( loadInv, key ); } public Node[] storeLookup( VarNode key ) { return lookup( store, key ); } public Node[] storeInvLookup( FieldRefNode key ) { return lookup( storeInv, key ); } public Node[] allocLookup( AllocNode key ) { return lookup( alloc, key ); } public Node[] allocInvLookup( VarNode key ) { return lookup( allocInv, key ); } public Set<Object> simpleSources() { return simple.keySet(); } public Set<Object> allocSources() { return alloc.keySet(); } public Set<Object> storeSources() { return store.keySet(); } public Set<Object> loadSources() { return load.keySet(); } public Set<Object> simpleInvSources() { return simpleInv.keySet(); } public Set<Object> allocInvSources() { return allocInv.keySet(); } public Set<Object> storeInvSources() { return storeInv.keySet(); } public Set<Object> loadInvSources() { return loadInv.keySet(); } public Iterator<Object> simpleSourcesIterator() { return simple.keySet().iterator(); } public Iterator<Object> allocSourcesIterator() { return alloc.keySet().iterator(); } public Iterator<Object> storeSourcesIterator() { return store.keySet().iterator(); } public Iterator<Object> loadSourcesIterator() { return load.keySet().iterator(); } public Iterator<Object> simpleInvSourcesIterator() { return simpleInv.keySet().iterator(); } public Iterator<Object> allocInvSourcesIterator() { return allocInv.keySet().iterator(); } public Iterator<Object> storeInvSourcesIterator() { return storeInv.keySet().iterator(); } public Iterator<Object> loadInvSourcesIterator() { return loadInv.keySet().iterator(); } static private int getSize( Object set ) { if( set instanceof Set ) return ((Set) set).size(); else if( set == null ) return 0; else return ((Object[]) set).length; } protected P2SetFactory setFactory; protected boolean somethingMerged = false; /** Returns the set of objects pointed to by instance field f * of the objects pointed to by l. */ public PointsToSet reachingObjects( Local l, SootField f ) { return reachingObjects( reachingObjects(l), f ); } /** Returns the set of objects pointed to by instance field f * of the objects pointed to by l in context c. */ public PointsToSet reachingObjects( Context c, Local l, SootField f ) { return reachingObjects( reachingObjects(c, l), f ); } private void addNodeTag( Node node, SootMethod m ) { if( nodeToTag != null ) { Tag tag; if( m == null ) { tag = new StringTag( node.toString() ); } else { tag = new LinkTag( node.toString(), m, m.getDeclaringClass().getName() ); } nodeToTag.put( node, tag ); } } public AllocNode makeAllocNode( Object newExpr, Type type, SootMethod m ) { if( opts.types_for_sites() || opts.vta() ) newExpr = type; AllocNode ret = valToAllocNode.get( newExpr ); if( ret == null ) { valToAllocNode.put( newExpr, ret = new AllocNode( this, newExpr, type, m ) ); newAllocNodes.add( ret ); addNodeTag( ret, m ); } else if( !( ret.getType().equals( type ) ) ) { throw new RuntimeException( "NewExpr "+newExpr+" of type "+type+ " previously had type "+ret.getType() ); } return ret; } public AllocNode makeStringConstantNode( String s ) { if( opts.types_for_sites() || opts.vta() ) return makeAllocNode( RefType.v( "java.lang.String" ), RefType.v( "java.lang.String" ), null ); StringConstantNode ret = (StringConstantNode) valToAllocNode.get( s ); if( ret == null ) { valToAllocNode.put( s, ret = new StringConstantNode( this, s ) ); newAllocNodes.add( ret ); addNodeTag( ret, null ); } return ret; } public AllocNode makeClassConstantNode( ClassConstant cc ) { if( opts.types_for_sites() || opts.vta() ) return makeAllocNode( RefType.v( "java.lang.Class" ), RefType.v( "java.lang.Class" ), null ); ClassConstantNode ret = (ClassConstantNode) valToAllocNode.get(cc); if( ret == null ) { valToAllocNode.put(cc, ret = new ClassConstantNode(this, cc)); newAllocNodes.add( ret ); addNodeTag( ret, null ); } return ret; } ChunkedQueue newAllocNodes = new ChunkedQueue(); public QueueReader allocNodeListener() { return newAllocNodes.reader(); } /** Finds the GlobalVarNode for the variable value, or returns null. */ public GlobalVarNode findGlobalVarNode( Object value ) { if( opts.rta() ) { value = null; } return valToGlobalVarNode.get( value ); } /** Finds the LocalVarNode for the variable value, or returns null. */ public LocalVarNode findLocalVarNode( Object value ) { if( opts.rta() ) { value = null; } else if( value instanceof Local ) { return (LocalVarNode) localToNodeMap.get( (Local) value ); } return valToLocalVarNode.get( value ); } /** Finds or creates the GlobalVarNode for the variable value, of type type. */ public GlobalVarNode makeGlobalVarNode( Object value, Type type ) { if( opts.rta() ) { value = null; type = RefType.v("java.lang.Object"); } GlobalVarNode ret = valToGlobalVarNode.get( value ); if( ret == null ) { valToGlobalVarNode.put( value, ret = new GlobalVarNode( this, value, type ) ); addNodeTag( ret, null ); } else if( !( ret.getType().equals( type ) ) ) { throw new RuntimeException( "Value "+value+" of type "+type+ " previously had type "+ret.getType() ); } return ret; } /** Finds or creates the LocalVarNode for the variable value, of type type. */ public LocalVarNode makeLocalVarNode( Object value, Type type, SootMethod method ) { if( opts.rta() ) { value = null; type = RefType.v("java.lang.Object"); method = null; } else if( value instanceof Local ) { Local val = (Local) value; if( val.getNumber() == 0 ) Scene.v().getLocalNumberer().add(val); LocalVarNode ret = (LocalVarNode) localToNodeMap.get( val ); if( ret == null ) { // XXX: Fix bottom_type problem if(type instanceof soot.jimple.toolkits.typing.fast.BottomType){ System.out.println("Find a bottom_type!"); type = RefType.v("java.lang.Object"); } localToNodeMap.put( (Local) value, ret = new LocalVarNode( this, value, type, method ) ); addNodeTag( ret, method ); } else if( !( ret.getType().equals( type ) ) ) { //XXX if(!(type instanceof soot.jimple.toolkits.typing.fast.BottomType)){ throw new RuntimeException( "Value "+value+" of type "+type+ " previously had type "+ret.getType() ); } } return ret; } LocalVarNode ret = valToLocalVarNode.get( value ); if( ret == null ) { valToLocalVarNode.put( value, ret = new LocalVarNode( this, value, type, method ) ); addNodeTag( ret, method ); } else if( !( ret.getType().equals( type ) ) ) { throw new RuntimeException( "Value "+value+" of type "+type+ " previously had type "+ret.getType() ); } return ret; } /** Finds the ContextVarNode for base variable value and context * context, or returns null. */ public ContextVarNode findContextVarNode( Object baseValue, Context context ) { LocalVarNode base = findLocalVarNode( baseValue ); if( base == null ) return null; return base.context( context ); } /** Finds or creates the ContextVarNode for base variable baseValue and context * context, of type type. */ public ContextVarNode makeContextVarNode( Object baseValue, Type baseType, Context context, SootMethod method ) { LocalVarNode base = makeLocalVarNode( baseValue, baseType, method ); return makeContextVarNode( base, context ); } /** Finds or creates the ContextVarNode for base variable base and context * context, of type type. */ public ContextVarNode makeContextVarNode( LocalVarNode base, Context context ) { ContextVarNode ret = base.context( context ); if( ret == null ) { ret = new ContextVarNode( this, base, context ); addNodeTag( ret, base.getMethod() ); } return ret; } /** Finds the FieldRefNode for base variable value and field * field, or returns null. */ public FieldRefNode findLocalFieldRefNode( Object baseValue, SparkField field ) { VarNode base = findLocalVarNode( baseValue ); if( base == null ) return null; return base.dot( field ); } /** Finds the FieldRefNode for base variable value and field * field, or returns null. */ public FieldRefNode findGlobalFieldRefNode( Object baseValue, SparkField field ) { VarNode base = findGlobalVarNode( baseValue ); if( base == null ) return null; return base.dot( field ); } /** Finds or creates the FieldRefNode for base variable baseValue and field * field, of type type. */ public FieldRefNode makeLocalFieldRefNode( Object baseValue, Type baseType, SparkField field, SootMethod method ) { VarNode base = makeLocalVarNode( baseValue, baseType, method ); return makeFieldRefNode( base, field ); } /** Finds or creates the FieldRefNode for base variable baseValue and field * field, of type type. */ public FieldRefNode makeGlobalFieldRefNode( Object baseValue, Type baseType, SparkField field ) { VarNode base = makeGlobalVarNode( baseValue, baseType ); return makeFieldRefNode( base, field ); } /** Finds or creates the FieldRefNode for base variable base and field * field, of type type. */ public FieldRefNode makeFieldRefNode( VarNode base, SparkField field ) { FieldRefNode ret = base.dot( field ); if( ret == null ) { ret = new FieldRefNode( this, base, field ); if( base instanceof LocalVarNode ) { addNodeTag( ret, ((LocalVarNode) base).getMethod() ); } else { addNodeTag( ret, null ); } } return ret; } /** Finds the AllocDotField for base AllocNode an and field * field, or returns null. */ public AllocDotField findAllocDotField( AllocNode an, SparkField field ) { return an.dot( field ); } /** Finds or creates the AllocDotField for base variable baseValue and field * field, of type t. */ public AllocDotField makeAllocDotField( AllocNode an, SparkField field ) { AllocDotField ret = an.dot( field ); if( ret == null ) { ret = new AllocDotField( this, an, field ); } return ret; } public boolean addSimpleEdge( VarNode from, VarNode to ) { boolean ret = false; if( doAddSimpleEdge( from, to ) ) { edgeQueue.add( from ); edgeQueue.add( to ); ret = true; } if( opts.simple_edges_bidirectional() ) { if( doAddSimpleEdge( to, from ) ) { edgeQueue.add( to ); edgeQueue.add( from ); ret = true; } } return ret; } public boolean addStoreEdge( VarNode from, FieldRefNode to ) { if( !opts.rta() ) { if( doAddStoreEdge( from, to ) ) { edgeQueue.add( from ); edgeQueue.add( to ); return true; } } return false; } public boolean addLoadEdge( FieldRefNode from, VarNode to ) { if( !opts.rta() ) { if( doAddLoadEdge( from, to ) ) { edgeQueue.add( from ); edgeQueue.add( to ); return true; } } return false; } public boolean addAllocEdge( AllocNode from, VarNode to ) { FastHierarchy fh = typeManager.getFastHierarchy(); if( fh == null || to.getType() == null || fh.canStoreType( from.getType(), to.getType() ) ) { if( doAddAllocEdge( from, to ) ) { edgeQueue.add( from ); edgeQueue.add( to ); return true; } } return false; } /** Adds an edge to the graph, returning false if it was already there. */ public final boolean addEdge( Node from, Node to ) { from = from.getReplacement(); to = to.getReplacement(); if( from instanceof VarNode ) { if( to instanceof VarNode ) { return addSimpleEdge( (VarNode) from, (VarNode) to ); } else { return addStoreEdge( (VarNode) from, (FieldRefNode) to ); } } else if( from instanceof FieldRefNode ) { return addLoadEdge( (FieldRefNode) from, (VarNode) to ); } else { return addAllocEdge( (AllocNode) from, (VarNode) to ); } } protected ChunkedQueue edgeQueue = new ChunkedQueue(); public QueueReader edgeReader() { return edgeQueue.reader(); } public int getNumAllocNodes() { return allocNodeNumberer.size(); } public TypeManager getTypeManager() { return typeManager; } public void setOnFlyCallGraph( OnFlyCallGraph ofcg ) { this.ofcg = ofcg; } public OnFlyCallGraph getOnFlyCallGraph() { return ofcg; } public OnFlyCallGraph ofcg() { return ofcg; } /** Adds the base of a dereference to the list of dereferenced * variables. */ public void addDereference( VarNode base ) { dereferences.add( base ); } /** Returns list of dereferences variables. */ public List<VarNode> getDereferences() { return dereferences; } public Map<Node, Tag> getNodeTags() { return nodeToTag; } private final ArrayNumberer allocNodeNumberer = new ArrayNumberer(); public ArrayNumberer getAllocNodeNumberer() { return allocNodeNumberer; } private final ArrayNumberer varNodeNumberer = new ArrayNumberer(); public ArrayNumberer getVarNodeNumberer() { return varNodeNumberer; } private final ArrayNumberer fieldRefNodeNumberer = new ArrayNumberer(); public ArrayNumberer getFieldRefNodeNumberer() { return fieldRefNodeNumberer; } private final ArrayNumberer allocDotFieldNodeNumberer = new ArrayNumberer(); public ArrayNumberer getAllocDotFieldNodeNumberer() { return allocDotFieldNodeNumberer; } /** Returns SparkOptions for this graph. */ public SparkOptions getOpts() { return opts; } final public void addCallTarget( Edge e ) { if( !e.passesParameters() ) return; MethodPAG srcmpag = MethodPAG.v( this, e.src() ); MethodPAG tgtmpag = MethodPAG.v( this, e.tgt() ); if( e.isExplicit() || e.kind() == Kind.THREAD ) { addCallTarget( srcmpag, tgtmpag, (Stmt) e.srcUnit(), e.srcCtxt(), e.tgtCtxt() ); } else { if( e.kind() == Kind.PRIVILEGED ) { // Flow from first parameter of doPrivileged() invocation // to this of target, and from return of target to the // return of doPrivileged() InvokeExpr ie = e.srcStmt().getInvokeExpr(); Node parm = srcmpag.nodeFactory().getNode( ie.getArg(0) ); parm = srcmpag.parameterize( parm, e.srcCtxt() ); parm = parm.getReplacement(); Node thiz = tgtmpag.nodeFactory().caseThis(); thiz = tgtmpag.parameterize( thiz, e.tgtCtxt() ); thiz = thiz.getReplacement(); addEdge( parm, thiz ); callAssigns.put(ie, new Pair(parm, thiz)); callToMethod.put(ie, srcmpag.getMethod()); if( e.srcUnit() instanceof AssignStmt ) { AssignStmt as = (AssignStmt) e.srcUnit(); Node ret = tgtmpag.nodeFactory().caseRet(); ret = tgtmpag.parameterize( ret, e.tgtCtxt() ); ret = ret.getReplacement(); Node lhs = srcmpag.nodeFactory().getNode(as.getLeftOp()); lhs = srcmpag.parameterize( lhs, e.srcCtxt() ); lhs = lhs.getReplacement(); addEdge( ret, lhs ); callAssigns.put(ie, new Pair(ret, lhs)); callToMethod.put(ie, srcmpag.getMethod()); } } else if( e.kind() == Kind.FINALIZE ) { Node srcThis = srcmpag.nodeFactory().caseThis(); srcThis = srcmpag.parameterize( srcThis, e.srcCtxt() ); srcThis = srcThis.getReplacement(); Node tgtThis = tgtmpag.nodeFactory().caseThis(); tgtThis = tgtmpag.parameterize( tgtThis, e.tgtCtxt() ); tgtThis = tgtThis.getReplacement(); addEdge( srcThis, tgtThis ); } else if( e.kind() == Kind.NEWINSTANCE ) { Stmt s = (Stmt) e.srcUnit(); InstanceInvokeExpr iie = (InstanceInvokeExpr) s.getInvokeExpr(); Node cls = srcmpag.nodeFactory().getNode( iie.getBase() ); cls = srcmpag.parameterize( cls, e.srcCtxt() ); cls = cls.getReplacement(); Node newObject = nodeFactory.caseNewInstance( (VarNode) cls ); Node initThis = tgtmpag.nodeFactory().caseThis(); initThis = tgtmpag.parameterize( initThis, e.tgtCtxt() ); initThis = initThis.getReplacement(); addEdge( newObject, initThis ); if (s instanceof AssignStmt) { AssignStmt as = (AssignStmt)s; Node asLHS = srcmpag.nodeFactory().getNode(as.getLeftOp()); asLHS = srcmpag.parameterize( asLHS, e.srcCtxt()); asLHS = asLHS.getReplacement(); addEdge( newObject, asLHS); } callAssigns.put(s.getInvokeExpr(), new Pair(newObject, initThis)); callToMethod.put(s.getInvokeExpr(), srcmpag.getMethod()); } else if( e.kind() == Kind.REFL_INVOKE ) { // Flow (1) from first parameter of invoke(..) invocation // to this of target, (2) from the contents of the second (array) parameter // to all parameters of the target, and (3) from return of target to the // return of invoke(..) //(1) InvokeExpr ie = e.srcStmt().getInvokeExpr(); Value arg0 = ie.getArg(0); //if "null" is passed in, omit the edge if(arg0!=NullConstant.v()) { Node parm0 = srcmpag.nodeFactory().getNode( arg0 ); parm0 = srcmpag.parameterize( parm0, e.srcCtxt() ); parm0 = parm0.getReplacement(); Node thiz = tgtmpag.nodeFactory().caseThis(); thiz = tgtmpag.parameterize( thiz, e.tgtCtxt() ); thiz = thiz.getReplacement(); addEdge( parm0, thiz ); callAssigns.put(ie, new Pair(parm0, thiz)); callToMethod.put(ie, srcmpag.getMethod()); } //(2) Value arg1 = ie.getArg(1); SootMethod tgt = e.getTgt().method(); //if "null" is passed in, or target has no parameters, omit the edge if(arg1!=NullConstant.v() && tgt.getParameterCount()>0) { Node parm1 = srcmpag.nodeFactory().getNode( arg1 ); parm1 = srcmpag.parameterize( parm1, e.srcCtxt() ); parm1 = parm1.getReplacement(); FieldRefNode parm1contents = makeFieldRefNode( (VarNode) parm1, ArrayElement.v() ); for(int i=0;i<tgt.getParameterCount(); i++) { //if no reference type, create no edge if(!(tgt.getParameterType(i) instanceof RefLikeType)) continue; Node tgtParmI = tgtmpag.nodeFactory().caseParm( i ); tgtParmI = tgtmpag.parameterize( tgtParmI, e.tgtCtxt() ); tgtParmI = tgtParmI.getReplacement(); addEdge( parm1contents, tgtParmI ); callAssigns.put(ie, new Pair(parm1contents, tgtParmI)); } } //(3) //only create return edge if we are actually assigning the return value and //the return type of the callee is actually a reference type if( e.srcUnit() instanceof AssignStmt && (tgt.getReturnType() instanceof RefLikeType)) { AssignStmt as = (AssignStmt) e.srcUnit(); Node ret = tgtmpag.nodeFactory().caseRet(); ret = tgtmpag.parameterize( ret, e.tgtCtxt() ); ret = ret.getReplacement(); Node lhs = srcmpag.nodeFactory().getNode(as.getLeftOp()); lhs = srcmpag.parameterize( lhs, e.srcCtxt() ); lhs = lhs.getReplacement(); addEdge( ret, lhs ); callAssigns.put(ie, new Pair(ret, lhs)); } } else if( e.kind() == Kind.REFL_CLASS_NEWINSTANCE || e.kind() == Kind.REFL_CONSTR_NEWINSTANCE) { // (1) create a fresh node for the new object // (2) create edge from this object to "this" of the constructor // (3) if this is a call to Constructor.newInstance and not Class.newInstance, // create edges passing the contents of the arguments array of the call // to all possible parameters of the target // (4) if we are inside an assign statement, // assign the fresh object from (1) to the LHS of the assign statement Stmt s = (Stmt) e.srcUnit(); InstanceInvokeExpr iie = (InstanceInvokeExpr) s.getInvokeExpr(); //(1) Node cls = srcmpag.nodeFactory().getNode( iie.getBase() ); cls = srcmpag.parameterize( cls, e.srcCtxt() ); cls = cls.getReplacement(); if( cls instanceof ContextVarNode ) cls = findLocalVarNode( ((VarNode)cls).getVariable() ); VarNode newObject = makeGlobalVarNode( cls, RefType.v( "java.lang.Object" ) ); SootClass tgtClass = e.getTgt().method().getDeclaringClass(); RefType tgtType = tgtClass.getType(); AllocNode site = makeAllocNode( new Pair(cls, tgtClass), tgtType, null ); addEdge( site, newObject ); //(2) Node initThis = tgtmpag.nodeFactory().caseThis(); initThis = tgtmpag.parameterize( initThis, e.tgtCtxt() ); initThis = initThis.getReplacement(); addEdge( newObject, initThis ); //(3) if(e.kind() == Kind.REFL_CONSTR_NEWINSTANCE) { Value arg = iie.getArg(0); SootMethod tgt = e.getTgt().method(); //if "null" is passed in, or target has no parameters, omit the edge if(arg!=NullConstant.v() && tgt.getParameterCount()>0) { Node parm0 = srcmpag.nodeFactory().getNode( arg ); parm0 = srcmpag.parameterize( parm0, e.srcCtxt() ); parm0 = parm0.getReplacement(); FieldRefNode parm1contents = makeFieldRefNode( (VarNode) parm0, ArrayElement.v() ); for(int i=0;i<tgt.getParameterCount(); i++) { //if no reference type, create no edge if(!(tgt.getParameterType(i) instanceof RefLikeType)) continue; Node tgtParmI = tgtmpag.nodeFactory().caseParm( i ); tgtParmI = tgtmpag.parameterize( tgtParmI, e.tgtCtxt() ); tgtParmI = tgtParmI.getReplacement(); addEdge( parm1contents, tgtParmI ); callAssigns.put(iie, new Pair(parm1contents, tgtParmI)); } } } //(4) if (s instanceof AssignStmt) { AssignStmt as = (AssignStmt)s; Node asLHS = srcmpag.nodeFactory().getNode(as.getLeftOp()); asLHS = srcmpag.parameterize( asLHS, e.srcCtxt()); asLHS = asLHS.getReplacement(); addEdge( newObject, asLHS); } callAssigns.put(s.getInvokeExpr(), new Pair(newObject, initThis)); callToMethod.put(s.getInvokeExpr(), srcmpag.getMethod()); } else { throw new RuntimeException( "Unhandled edge "+e ); } } } /** Adds method target as a possible target of the invoke expression in s. * If target is null, only creates the nodes for the call site, * without actually connecting them to any target method. **/ final public void addCallTarget( MethodPAG srcmpag, MethodPAG tgtmpag, Stmt s, Context srcContext, Context tgtContext ) { MethodNodeFactory srcnf = srcmpag.nodeFactory(); MethodNodeFactory tgtnf = tgtmpag.nodeFactory(); InvokeExpr ie = s.getInvokeExpr(); boolean virtualCall = callAssigns.containsKey(ie); int numArgs = ie.getArgCount(); for( int i = 0; i < numArgs; i++ ) { Value arg = ie.getArg( i ); if( !( arg.getType() instanceof RefLikeType ) ) continue; if( arg instanceof NullConstant ) continue; Node argNode = srcnf.getNode( arg ); argNode = srcmpag.parameterize( argNode, srcContext ); argNode = argNode.getReplacement(); Node parm = tgtnf.caseParm( i ); parm = tgtmpag.parameterize( parm, tgtContext ); parm = parm.getReplacement(); addEdge( argNode, parm ); callAssigns.put(ie, new Pair(argNode, parm)); callToMethod.put(ie, srcmpag.getMethod()); } if( ie instanceof InstanceInvokeExpr ) { InstanceInvokeExpr iie = (InstanceInvokeExpr) ie; Node baseNode = srcnf.getNode( iie.getBase() ); baseNode = srcmpag.parameterize( baseNode, srcContext ); baseNode = baseNode.getReplacement(); Node thisRef = tgtnf.caseThis(); thisRef = tgtmpag.parameterize( thisRef, tgtContext ); thisRef = thisRef.getReplacement(); addEdge( baseNode, thisRef ); callAssigns.put(ie, new Pair(baseNode, thisRef)); callToMethod.put(ie, srcmpag.getMethod()); if (virtualCall && !virtualCallsToReceivers.containsKey(ie)) { virtualCallsToReceivers.put(ie, baseNode); } } if( s instanceof AssignStmt ) { Value dest = ( (AssignStmt) s ).getLeftOp(); if( dest.getType() instanceof RefLikeType && !(dest instanceof NullConstant) ) { Node destNode = srcnf.getNode( dest ); destNode = srcmpag.parameterize( destNode, srcContext ); destNode = destNode.getReplacement(); Node retNode = tgtnf.caseRet(); retNode = tgtmpag.parameterize( retNode, tgtContext ); retNode = retNode.getReplacement(); addEdge( retNode, destNode ); callAssigns.put(ie, new Pair(retNode, destNode)); callToMethod.put(ie, srcmpag.getMethod()); } } } /* End of package methods. */ protected SparkOptions opts; protected Map<Object, Object> simple = new HashMap<Object, Object>(); protected Map<Object, Object> load = new HashMap<Object, Object>(); protected Map<Object, Object> store = new HashMap<Object, Object>(); protected Map<Object, Object> alloc = new HashMap<Object, Object>(); protected Map<Object, Object> simpleInv = new HashMap<Object, Object>(); protected Map<Object, Object> loadInv = new HashMap<Object, Object>(); protected Map<Object, Object> storeInv = new HashMap<Object, Object>(); protected Map<Object, Object> allocInv = new HashMap<Object, Object>(); protected boolean addToMap( Map<Object, Object> m, Node key, Node value ) { Object valueList = m.get( key ); if( valueList == null ) { m.put( key, valueList = new HashSet(4) ); } else if( !(valueList instanceof Set) ) { Node[] ar = (Node[]) valueList; HashSet<Node> vl = new HashSet<Node>(ar.length+4); m.put( key, vl ); for (Node element : ar) vl.add( element ); return vl.add( value ); /* Node[] ar = (Node[]) valueList; Node[] newar = new Node[ar.length+1]; for( int i = 0; i < ar.length; i++ ) { Node n = ar[i]; if( n == value ) return false; newar[i] = n; } newar[ar.length] = value; m.put( key, newar ); return true; */ } return ((Set<Node>) valueList).add( value ); } private final Map<Object, LocalVarNode> valToLocalVarNode = new HashMap<Object, LocalVarNode>(1000); private final Map<Object, GlobalVarNode> valToGlobalVarNode = new HashMap<Object, GlobalVarNode>(1000); private final Map<Object, AllocNode> valToAllocNode = new HashMap<Object, AllocNode>(1000); private OnFlyCallGraph ofcg; private final ArrayList<VarNode> dereferences = new ArrayList<VarNode>(); protected TypeManager typeManager; private final LargeNumberedMap localToNodeMap = new LargeNumberedMap( Scene.v().getLocalNumberer() ); public int maxFinishNumber = 0; private Map<Node, Tag> nodeToTag; private final GlobalNodeFactory nodeFactory = new GlobalNodeFactory(this); public GlobalNodeFactory nodeFactory() { return nodeFactory; } public NativeMethodDriver nativeMethodDriver; public HashMultiMap /* InvokeExpr -> Set[Pair] */ callAssigns = new HashMultiMap(); public Map<InvokeExpr, SootMethod> callToMethod = new HashMap<InvokeExpr, SootMethod>(); public Map<InvokeExpr, Node> virtualCallsToReceivers = new HashMap<InvokeExpr, Node>(); /** XXX: Release useless data. */ public void clean(){ //varNodeNumberer = null; //fieldRefNodeNumberer = null; //allocDotFieldNodeNumberer = null; edgeQueue = null; newAllocNodes = null; simple = null; load = null; store = null; alloc = null; simpleInv = null; loadInv = null; storeInv = null; allocInv = null; ofcg = null; nodeToTag = null; //nodeFactory = null; nativeMethodDriver = null; callAssigns = null; callToMethod = null; virtualCallsToReceivers = null; //valToAllocNode.clear(); //dereferences.clear(); } }