package soot.jimple.toolkits.thread.synchronization; /* Soot - a J*va Optimization Framework * Copyright (C) 2003 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. */ import soot.*; import soot.jimple.*; import soot.jimple.toolkits.callgraph.*; import soot.jimple.toolkits.pointer.*; import soot.jimple.toolkits.thread.*; import java.util.*; /** Generates side-effect information from a PointsToAnalysis. * Uses various heuristic rules to filter out side-effects that * are not visible to other threads in a Transactional program. */ class WholeObject { Type type; public WholeObject(Type type) { this.type = type; } public WholeObject() { this.type = null; } public String toString() { return "All Fields" + (type == null ? "" : " (" + type + ")"); } public int hashCode() { if(type == null) return 1; return type.hashCode(); } public boolean equals(Object o) { if(type == null) return true; if(o instanceof WholeObject) { WholeObject other = (WholeObject) o; if(other.type == null) return true; else return (type == other.type); } else if(o instanceof FieldRef) { return type == ((FieldRef)o).getType(); } else if(o instanceof SootFieldRef) { return type == ((SootFieldRef)o).type(); } else if(o instanceof SootField) { return type == ((SootField)o).getType(); } else { return true; } } } public class CriticalSectionAwareSideEffectAnalysis { PointsToAnalysis pa; CallGraph cg; Map<SootMethod, CodeBlockRWSet> methodToNTReadSet = new HashMap<SootMethod, CodeBlockRWSet>(); Map<SootMethod, CodeBlockRWSet> methodToNTWriteSet = new HashMap<SootMethod, CodeBlockRWSet>(); int rwsetcount = 0; CriticalSectionVisibleEdgesPred tve; TransitiveTargets tt; TransitiveTargets normaltt; SideEffectAnalysis normalsea; Collection<CriticalSection> criticalSections; EncapsulatedObjectAnalysis eoa; ThreadLocalObjectsAnalysis tlo; public Vector sigBlacklist; public Vector sigReadGraylist; public Vector sigWriteGraylist; public Vector subSigBlacklist; public void findNTRWSets( SootMethod method ) { if( methodToNTReadSet.containsKey( method ) && methodToNTWriteSet.containsKey( method ) ) return; CodeBlockRWSet read = null; CodeBlockRWSet write = null; for( Iterator sIt = method.retrieveActiveBody().getUnits().iterator(); sIt.hasNext(); ) { final Stmt s = (Stmt) sIt.next(); boolean ignore = false; // Ignore Reads/Writes inside another transaction if(criticalSections != null) { Iterator<CriticalSection> tnIt = criticalSections.iterator(); while(tnIt.hasNext()) { CriticalSection tn = tnIt.next(); if(tn.units.contains(s) || tn.prepStmt == s) { ignore = true; break; } } } if(!ignore) { RWSet ntr = ntReadSet( method, s ); if( ntr != null ) { if( read == null ) read = new CodeBlockRWSet(); read.union( ntr ); } RWSet ntw = ntWriteSet( method, s ); if( ntw != null ) { if( write == null ) write = new CodeBlockRWSet(); write.union( ntw ); } if( s.containsInvokeExpr() ) { InvokeExpr ie = s.getInvokeExpr(); SootMethod calledMethod = ie.getMethod(); // if it's an invoke on certain lib methods if( calledMethod.getDeclaringClass().toString().startsWith("java.util") || calledMethod.getDeclaringClass().toString().startsWith("java.lang") ) { // then it gets approximated Local base = null; if(ie instanceof InstanceInvokeExpr) base = (Local)((InstanceInvokeExpr) ie).getBase(); if(tlo == null || base == null || !tlo.isObjectThreadLocal(base, method) ) { // add its approximated read set to read RWSet r; // String InvokeSig = calledMethod.getSubSignature(); // if( InvokeSig.equals("void notify()") || InvokeSig.equals("void notifyAll()") || // InvokeSig.equals("void wait()") || InvokeSig.equals("void wait(long)") || InvokeSig.equals("void wait(long,int)")) // r = approximatedReadSet(method, s, base, true); // else r = approximatedReadSet(method, s, base, true); if( read == null ) read = new CodeBlockRWSet(); if( r != null ) read.union( r ); // add its approximated write set to write RWSet w; // if( InvokeSig.equals("void notify()") || InvokeSig.equals("void notifyAll()") || // InvokeSig.equals("void wait()") || InvokeSig.equals("void wait(long)") || InvokeSig.equals("void wait(long,int)")) // w = approximatedWriteSet(method, s, base, true); // else w = approximatedWriteSet(method, s, base, true); if( write == null ) write = new CodeBlockRWSet(); if( w != null ) write.union( w ); } } } } } methodToNTReadSet.put( method, read ); methodToNTWriteSet.put( method, write ); } public void setExemptTransaction( CriticalSection tn ) { tve.setExemptTransaction(tn); } public RWSet nonTransitiveReadSet( SootMethod method ) { findNTRWSets( method ); return methodToNTReadSet.get( method ); } public RWSet nonTransitiveWriteSet( SootMethod method ) { findNTRWSets( method ); return methodToNTWriteSet.get( method ); } public CriticalSectionAwareSideEffectAnalysis( PointsToAnalysis pa, CallGraph cg, Collection<CriticalSection> criticalSections, ThreadLocalObjectsAnalysis tlo ) { this.pa = pa; this.cg = cg; this.tve = new CriticalSectionVisibleEdgesPred(criticalSections); this.tt = new TransitiveTargets( cg, new Filter(tve) ); this.normaltt = new TransitiveTargets( cg, null ); this.normalsea = new SideEffectAnalysis( pa, cg ); this.criticalSections = criticalSections; this.eoa = new EncapsulatedObjectAnalysis(); this.tlo = tlo; // can be null sigBlacklist = new Vector(); // Signatures of methods known to have effective read/write sets of size 0 // Math does not have any synchronization risks, we think :-) /* sigBlacklist.add("<java.lang.Math: double abs(double)>"); sigBlacklist.add("<java.lang.Math: double min(double,double)>"); sigBlacklist.add("<java.lang.Math: double sqrt(double)>"); sigBlacklist.add("<java.lang.Math: double pow(double,double)>"); //*/ // sigBlacklist.add(""); sigReadGraylist = new Vector(); // Signatures of methods whose effects must be approximated sigWriteGraylist = new Vector(); /* sigReadGraylist.add("<java.util.Vector: boolean remove(java.lang.Object)>"); sigWriteGraylist.add("<java.util.Vector: boolean remove(java.lang.Object)>"); sigReadGraylist.add("<java.util.Vector: boolean add(java.lang.Object)>"); sigWriteGraylist.add("<java.util.Vector: boolean add(java.lang.Object)>"); sigReadGraylist.add("<java.util.Vector: java.lang.Object clone()>"); // sigWriteGraylist.add("<java.util.Vector: java.lang.Object clone()>"); sigReadGraylist.add("<java.util.Vector: java.lang.Object get(int)>"); // sigWriteGraylist.add("<java.util.Vector: java.lang.Object get(int)>"); sigReadGraylist.add("<java.util.Vector: java.util.List subList(int,int)>"); // sigWriteGraylist.add("<java.util.Vector: java.util.List subList(int,int)>"); sigReadGraylist.add("<java.util.List: void clear()>"); sigWriteGraylist.add("<java.util.List: void clear()>"); //*/ subSigBlacklist = new Vector(); // Subsignatures of methods on all objects known to have read/write sets of size 0 /* subSigBlacklist.add("java.lang.Class class$(java.lang.String)"); subSigBlacklist.add("void notify()"); subSigBlacklist.add("void notifyAll()"); subSigBlacklist.add("void wait()"); subSigBlacklist.add("void <clinit>()"); //*/ } private RWSet ntReadSet( SootMethod method, Stmt stmt ) { if( stmt instanceof AssignStmt ) { AssignStmt a = (AssignStmt) stmt; Value r = a.getRightOp(); if(r instanceof NewExpr) // IGNORE NEW STATEMENTS return null; return addValue( r, method, stmt ); } return null; } private HashMap<Stmt, RWSet> RCache = new HashMap<Stmt, RWSet>(); public RWSet approximatedReadSet( SootMethod method, Stmt stmt, Value specialRead, boolean allFields) {// used for stmts with method calls where the effect of the method call should be approximated by 0 or 1 reads (plus reads of all args) CodeBlockRWSet ret = new CodeBlockRWSet(); if(specialRead != null) { if( specialRead instanceof Local ) { Local vLocal = (Local) specialRead; PointsToSet base = pa.reachingObjects( vLocal ); // Get an RWSet containing all fields // Set possibleTypes = base.possibleTypes(); // for(Iterator pTypeIt = possibleTypes.iterator(); pTypeIt.hasNext(); ) // { Type pType = vLocal.getType(); //(Type) pTypeIt.next(); if(pType instanceof RefType) { SootClass baseTypeClass = ((RefType) pType).getSootClass(); if(!baseTypeClass.isInterface()) { List<SootClass> baseClasses = Scene.v().getActiveHierarchy().getSuperclassesOfIncluding(baseTypeClass); if(!baseClasses.contains(RefType.v("java.lang.Exception").getSootClass())) { for(SootClass baseClass : baseClasses) { for(Iterator baseFieldIt = baseClass.getFields().iterator(); baseFieldIt.hasNext(); ) { SootField baseField = (SootField) baseFieldIt.next(); if(!baseField.isStatic()) ret.addFieldRef( base, baseField ); } } } } } // } // If desired, prune to just actually-read fields if(!allFields) { // Should actually get a list of fields of this object that are read/written // make fake RW set of <base, all fields> (use a special class) // intersect with the REAL RW set of this stmt CodeBlockRWSet allRW = ret; ret = new CodeBlockRWSet(); RWSet normalRW; if(RCache.containsKey(stmt)) { normalRW = RCache.get(stmt); } else { normalRW = normalsea.readSet(method, stmt); RCache.put(stmt, normalRW); } if(normalRW != null) { for(Iterator fieldsIt = normalRW.getFields().iterator(); fieldsIt.hasNext(); ) { Object field = fieldsIt.next(); if(allRW.containsField(field)) { PointsToSet otherBase = normalRW.getBaseForField(field); if(otherBase instanceof FullObjectSet) { ret.addFieldRef(otherBase, field); } else { if(base.hasNonEmptyIntersection(otherBase)) ret.addFieldRef(base, field); // should use intersection of bases!!! } } } } } } else if( specialRead instanceof FieldRef) { ret.union(addValue(specialRead, method, stmt)); } } if(stmt.containsInvokeExpr()) { int argCount = stmt.getInvokeExpr().getArgCount(); for(int i = 0; i < argCount; i++) ret.union(addValue( stmt.getInvokeExpr().getArg(i), method, stmt )); } if( stmt instanceof AssignStmt ) { AssignStmt a = (AssignStmt) stmt; Value r = a.getRightOp(); ret.union(addValue( r, method, stmt )); } return ret; } public RWSet readSet( SootMethod method, Stmt stmt, CriticalSection tn, HashSet uses ) { boolean ignore = false; if(stmt.containsInvokeExpr()) { InvokeExpr ie = stmt.getInvokeExpr(); SootMethod calledMethod = ie.getMethod(); if(ie instanceof StaticInvokeExpr) { // ignore = false; } else if(ie instanceof InstanceInvokeExpr) { if(calledMethod.getSubSignature().startsWith("void <init>") && eoa.isInitMethodPureOnObject(calledMethod)) { ignore = true; } else if(tlo != null && !tlo.hasNonThreadLocalEffects(method, ie)) { ignore = true; } } } boolean inaccessibleUses = false; RWSet ret = new CodeBlockRWSet(); tve.setExemptTransaction(tn); Iterator<MethodOrMethodContext> targets = tt.iterator( stmt ); while( !ignore && targets.hasNext() ) { SootMethod target = (SootMethod) targets.next(); // if( target.isNative() ) { // if( ret == null ) ret = new SiteRWSet(); // ret.setCallsNative(); // } else if( target.isConcrete() ) { // Special treatment for java.util and java.lang... their children are filtered out by the ThreadVisibleEdges filter // An approximation of their behavior must be performed here if( target.getDeclaringClass().toString().startsWith("java.util") || target.getDeclaringClass().toString().startsWith("java.lang") ) { /* RWSet ntr; if(stmt.getInvokeExpr() instanceof InstanceInvokeExpr) { Local base = (Local)((InstanceInvokeExpr)stmt.getInvokeExpr()).getBase(); // Add base object and args to set of possibly contributing uses at this stmt if(!inaccessibleUses) { uses.add(base); int argCount = stmt.getInvokeExpr().getArgCount(); for(int i = 0; i < argCount; i++) { if(addValue( stmt.getInvokeExpr().getArg(i), method, stmt ) != null) uses.add(stmt.getInvokeExpr().getArg(i)); } } // Add base object to read set String InvokeSig = target.getSubSignature(); if( InvokeSig.equals("void notify()") || InvokeSig.equals("void notifyAll()") || InvokeSig.equals("void wait()") || InvokeSig.equals("void wait(long)") || InvokeSig.equals("void wait(long,int)")) { ntr = approximatedReadSet(method, stmt, base, target, true); } else { ntr = approximatedReadSet(method, stmt, base, target, false); } } else { ntr = approximatedReadSet(method, stmt, null, target, false); } ret.union(ntr); */ } else {// note that all library functions have already been filtered out (by name) via the filter // passed to the TransitiveTargets constructor. RWSet ntr = nonTransitiveReadSet(target); if( ntr != null ) { // uses.clear(); // inaccessibleUses = true; ret.union( ntr ); } } } } RWSet ntr = ntReadSet( method, stmt ); if( inaccessibleUses == false && ntr != null && stmt instanceof AssignStmt ) { AssignStmt a = (AssignStmt) stmt; Value r = a.getRightOp(); if(r instanceof InstanceFieldRef) { uses.add( ((InstanceFieldRef)r).getBase() ); } else if(r instanceof StaticFieldRef) { uses.add( r ); } else if(r instanceof ArrayRef) { uses.add( ((ArrayRef)r).getBase() ); } } ret.union( ntr ); if( stmt.containsInvokeExpr() ) { InvokeExpr ie = stmt.getInvokeExpr(); SootMethod calledMethod = ie.getMethod(); // if it's an invoke on certain lib methods if( calledMethod.getDeclaringClass().toString().startsWith("java.util") || calledMethod.getDeclaringClass().toString().startsWith("java.lang") ) { // then it gets approximated as a NTReadSet Local base = null; if(ie instanceof InstanceInvokeExpr) base = (Local)((InstanceInvokeExpr) ie).getBase(); if(tlo == null || base == null || !tlo.isObjectThreadLocal(base, method) ) { // add its approximated read set to read RWSet r; // String InvokeSig = calledMethod.getSubSignature(); // if( InvokeSig.equals("void notify()") || InvokeSig.equals("void notifyAll()") || // InvokeSig.equals("void wait()") || InvokeSig.equals("void wait(long)") || InvokeSig.equals("void wait(long,int)")) // r = approximatedReadSet(method, stmt, base, true); // else r = approximatedReadSet(method, stmt, base, true); if( r != null ) ret.union( r ); int argCount = stmt.getInvokeExpr().getArgCount(); for(int i = 0; i < argCount; i++) uses.add( ie.getArg(i) ); if( base != null ) uses.add( base ); } } } return ret; } private RWSet ntWriteSet( SootMethod method, Stmt stmt ) { if( stmt instanceof AssignStmt ) { AssignStmt a = (AssignStmt) stmt; Value l = a.getLeftOp(); return addValue( l, method, stmt ); } return null; } private HashMap<Stmt, RWSet> WCache = new HashMap<Stmt, RWSet>(); public RWSet approximatedWriteSet( SootMethod method, Stmt stmt, Value v, boolean allFields ) {// used for stmts with method calls where the effect of the method call should be approximated by 0 or 1 writes CodeBlockRWSet ret = new CodeBlockRWSet(); if(v != null) { if( v instanceof Local ) { Local vLocal = (Local) v; PointsToSet base = pa.reachingObjects( vLocal ); // Get an RWSet containing all fields // Set possibleTypes = base.possibleTypes(); // for(Iterator pTypeIt = possibleTypes.iterator(); pTypeIt.hasNext(); ) // { Type pType = vLocal.getType(); //(Type) pTypeIt.next(); if(pType instanceof RefType) { SootClass baseTypeClass = ((RefType) pType).getSootClass(); if(!baseTypeClass.isInterface()) { List<SootClass> baseClasses = Scene.v().getActiveHierarchy().getSuperclassesOfIncluding(baseTypeClass); if(!baseClasses.contains(RefType.v("java.lang.Exception").getSootClass())) { for(SootClass baseClass : baseClasses) { for(Iterator baseFieldIt = baseClass.getFields().iterator(); baseFieldIt.hasNext(); ) { SootField baseField = (SootField) baseFieldIt.next(); if(!baseField.isStatic()) ret.addFieldRef( base, baseField ); } } } } } // } // If desired, prune to just actually-written fields if(!allFields) { // Should actually get a list of fields of this object that are read/written // make fake RW set of <base, all fields> (use a special class) // intersect with the REAL RW set of this stmt CodeBlockRWSet allRW = ret; ret = new CodeBlockRWSet(); RWSet normalRW; if(WCache.containsKey(stmt)) { normalRW = WCache.get(stmt); } else { normalRW = normalsea.writeSet(method, stmt); WCache.put(stmt, normalRW); } if(normalRW != null) { for(Iterator fieldsIt = normalRW.getFields().iterator(); fieldsIt.hasNext(); ) { Object field = fieldsIt.next(); if(allRW.containsField(field)) { PointsToSet otherBase = normalRW.getBaseForField(field); if(otherBase instanceof FullObjectSet) { ret.addFieldRef(otherBase, field); } else { if(base.hasNonEmptyIntersection(otherBase)) ret.addFieldRef(base, field); // should use intersection of bases!!! } } } } } } else if( v instanceof FieldRef) { ret.union(addValue(v, method, stmt)); } } if( stmt instanceof AssignStmt ) { AssignStmt a = (AssignStmt) stmt; Value l = a.getLeftOp(); ret.union(addValue( l, method, stmt )); } return ret; } public RWSet writeSet( SootMethod method, Stmt stmt, CriticalSection tn, Set uses ) { boolean ignore = false; if(stmt.containsInvokeExpr()) { InvokeExpr ie = stmt.getInvokeExpr(); SootMethod calledMethod = ie.getMethod(); if(ie instanceof StaticInvokeExpr) { // ignore = false; } else if(ie instanceof InstanceInvokeExpr) { if(calledMethod.getSubSignature().startsWith("void <init>") && eoa.isInitMethodPureOnObject(calledMethod)) { ignore = true; } else if(tlo != null && !tlo.hasNonThreadLocalEffects(method, ie)) { ignore = true; } } } boolean inaccessibleUses = false; RWSet ret = new CodeBlockRWSet(); tve.setExemptTransaction(tn); Iterator<MethodOrMethodContext> targets = tt.iterator( stmt ); while( !ignore && targets.hasNext() ) { SootMethod target = (SootMethod) targets.next(); // if( target.isNative() ) { // if( ret == null ) ret = new SiteRWSet(); // ret.setCallsNative(); // } else if( target.isConcrete() ) { if( target.getDeclaringClass().toString().startsWith("java.util") || target.getDeclaringClass().toString().startsWith("java.lang") ) { /* RWSet ntw; if(stmt.getInvokeExpr() instanceof InstanceInvokeExpr) { Local base = (Local)((InstanceInvokeExpr)stmt.getInvokeExpr()).getBase(); // Add base object to set of possibly contributing uses at this stmt if(!inaccessibleUses) uses.add(base); // Add base object to write set String InvokeSig = target.getSubSignature(); if( InvokeSig.equals("void notify()") || InvokeSig.equals("void notifyAll()") || InvokeSig.equals("void wait()") || InvokeSig.equals("void wait(long)") || InvokeSig.equals("void wait(long,int)")) { ntw = approximatedWriteSet(method, stmt, base, true); } else { ntw = approximatedWriteSet(method, stmt, base, false); } } else { ntw = approximatedWriteSet(method, stmt, null, false); } ret.union(ntw); */ } else { RWSet ntw = nonTransitiveWriteSet(target); if( ntw != null ) { // inaccessibleUses = true; // uses.clear(); ret.union( ntw ); } } } } RWSet ntw = ntWriteSet( method, stmt ); if( !inaccessibleUses && ntw != null && stmt instanceof AssignStmt ) { AssignStmt a = (AssignStmt) stmt; Value l = a.getLeftOp(); if(l instanceof InstanceFieldRef) { uses.add( ((InstanceFieldRef)l).getBase() ); } else if(l instanceof StaticFieldRef) { uses.add( l ); } else if(l instanceof ArrayRef) { uses.add( ((ArrayRef)l).getBase() ); } } ret.union( ntw ); if( stmt.containsInvokeExpr() ) { InvokeExpr ie = stmt.getInvokeExpr(); SootMethod calledMethod = ie.getMethod(); // if it's an invoke on certain lib methods if( calledMethod.getDeclaringClass().toString().startsWith("java.util") || calledMethod.getDeclaringClass().toString().startsWith("java.lang") ) { // then it gets approximated as a NTReadSet Local base = null; if(ie instanceof InstanceInvokeExpr) base = (Local)((InstanceInvokeExpr) ie).getBase(); if(tlo == null || base == null || !tlo.isObjectThreadLocal(base, method) ) { // add its approximated read set to read RWSet w; // String InvokeSig = calledMethod.getSubSignature(); // if( InvokeSig.equals("void notify()") || InvokeSig.equals("void notifyAll()") || // InvokeSig.equals("void wait()") || InvokeSig.equals("void wait(long)") || InvokeSig.equals("void wait(long,int)")) // w = approximatedWriteSet(method, stmt, base, true); // else w = approximatedWriteSet(method, stmt, base, true); if( w != null ) ret.union( w ); if( base != null ) uses.add( base ); } } } return ret; } public RWSet valueRWSet( Value v, SootMethod m, Stmt s, CriticalSection tn ) { RWSet ret = null; if(tlo != null) { // fields/elements of local objects may be read/written w/o visible // side effects if the base object is local, or if the base is "this" // and the field itself is local (since "this" is always assumed shared) if( v instanceof InstanceFieldRef ) { InstanceFieldRef ifr = (InstanceFieldRef) v; if( m.isConcrete() && !m.isStatic() && m.retrieveActiveBody().getThisLocal().equivTo(ifr.getBase()) && tlo.isObjectThreadLocal(ifr, m) ) return null; else if( tlo.isObjectThreadLocal(ifr.getBase(), m) ) return null; } else if( v instanceof ArrayRef && tlo.isObjectThreadLocal(((ArrayRef)v).getBase(), m) ) return null; } if( v instanceof InstanceFieldRef ) { InstanceFieldRef ifr = (InstanceFieldRef) v; PointsToSet base = pa.reachingObjects( (Local) ifr.getBase() ); ret = new StmtRWSet(); ret.addFieldRef( base, ifr.getField() ); } else if( v instanceof StaticFieldRef ) { StaticFieldRef sfr = (StaticFieldRef) v; ret = new StmtRWSet(); ret.addGlobal( sfr.getField() ); } else if( v instanceof ArrayRef ) { ArrayRef ar = (ArrayRef) v; PointsToSet base = pa.reachingObjects( (Local) ar.getBase() ); ret = new StmtRWSet(); ret.addFieldRef( base, PointsToAnalysis.ARRAY_ELEMENTS_NODE ); } else if( v instanceof Local ) { Local vLocal = (Local) v; PointsToSet base = pa.reachingObjects( vLocal ); ret = new CodeBlockRWSet(); CodeBlockRWSet stmtRW = new CodeBlockRWSet(); RWSet rSet = readSet(m, s, tn, new HashSet()); if(rSet != null) stmtRW.union(rSet); RWSet wSet = writeSet(m, s, tn, new HashSet()); if(wSet != null) stmtRW.union(wSet); // Should actually get a list of fields of this object that are read/written // make fake RW set of <base, all fields> (use a special class) // intersect with the REAL RW set of this stmt for(Iterator fieldsIt = stmtRW.getFields().iterator(); fieldsIt.hasNext(); ) { Object field = fieldsIt.next(); PointsToSet fieldBase = stmtRW.getBaseForField(field); if(base.hasNonEmptyIntersection(fieldBase)) ret.addFieldRef(base, field); // should use intersection of bases!!! } } else { return null; } return ret; } protected RWSet addValue( Value v, SootMethod m, Stmt s ) { RWSet ret = null; if(tlo != null) { // fields/elements of local objects may be read/written w/o visible // side effects if the base object is local, or if the base is "this" // and the field itself is local (since "this" is always assumed shared) if( v instanceof InstanceFieldRef ) { InstanceFieldRef ifr = (InstanceFieldRef) v; if( m.isConcrete() && !m.isStatic() && m.retrieveActiveBody().getThisLocal().equivTo(ifr.getBase()) && tlo.isObjectThreadLocal(ifr, m) ) return null; else if( tlo.isObjectThreadLocal(ifr.getBase(), m) ) return null; } else if( v instanceof ArrayRef && tlo.isObjectThreadLocal(((ArrayRef)v).getBase(), m) ) return null; } // if(tlo != null && // (( v instanceof InstanceFieldRef && tlo.isObjectThreadLocal(((InstanceFieldRef)v).getBase(), m) ) || // ( v instanceof ArrayRef && tlo.isObjectThreadLocal(((ArrayRef)v).getBase(), m) ))) // return null; if( v instanceof InstanceFieldRef ) { InstanceFieldRef ifr = (InstanceFieldRef) v; Local baseLocal = (Local) ifr.getBase(); PointsToSet base = pa.reachingObjects( baseLocal ); if(baseLocal.getType() instanceof RefType) { SootClass baseClass = ((RefType) baseLocal.getType()).getSootClass(); if(Scene.v().getActiveHierarchy().isClassSubclassOfIncluding( baseClass, RefType.v("java.lang.Exception").getSootClass())) return null; } ret = new StmtRWSet(); ret.addFieldRef( base, ifr.getField() ); } else if( v instanceof StaticFieldRef ) { StaticFieldRef sfr = (StaticFieldRef) v; ret = new StmtRWSet(); ret.addGlobal( sfr.getField() ); } else if( v instanceof ArrayRef ) { ArrayRef ar = (ArrayRef) v; PointsToSet base = pa.reachingObjects( (Local) ar.getBase() ); ret = new StmtRWSet(); ret.addFieldRef( base, PointsToAnalysis.ARRAY_ELEMENTS_NODE ); } return ret; } public String toString() { return "TransactionAwareSideEffectAnalysis: PA="+pa+" CG="+cg; } }