package soot.jimple.toolkits.infoflow;
import soot.*;
import java.util.*;
import soot.toolkits.graph.*;
import soot.toolkits.scalar.*;
import soot.jimple.toolkits.callgraph.*;
import soot.jimple.*;
// ClassLocalObjectsAnalysis written by Richard L. Halpert, 2007-02-23
// Finds objects that are local to the given scope.
// NOTE THAT THIS ANALYSIS'S RESULTS DO NOT APPLY TO SUBCLASSES OF THE GIVEN CLASS
public class ClassLocalObjectsAnalysis
{
boolean printdfgs;
LocalObjectsAnalysis loa;
InfoFlowAnalysis dfa;
InfoFlowAnalysis primitiveDfa;
UseFinder uf;
SootClass sootClass;
Map<SootMethod, SmartMethodLocalObjectsAnalysis> methodToMethodLocalObjectsAnalysis;
Map<SootMethod, CallLocalityContext> methodToContext;
List<SootMethod> allMethods;
// methods that are called at least once from outside of this class (ie need to be public, protected, or package-private)
List<SootMethod> externalMethods;
// methods that are only ever called by other methods in this class (ie could be marked private)
List<SootMethod> internalMethods;
// methods that should be used as starting points when determining if a value in a method called from this class is local or shared
// for thread-local objects, this would contain just the run method. For structure-local, it should contain all external methods
List<SootMethod> entryMethods;
List<SootField> allFields;
List<SootField> externalFields;
List<SootField> internalFields;
ArrayList<SootField> localFields;
ArrayList<SootField> sharedFields;
ArrayList<SootField> localInnerFields;
ArrayList<SootField> sharedInnerFields;
public ClassLocalObjectsAnalysis(LocalObjectsAnalysis loa, InfoFlowAnalysis dfa, UseFinder uf, SootClass sootClass)
{
this(loa, dfa, null, uf, sootClass, null);
}
public ClassLocalObjectsAnalysis(LocalObjectsAnalysis loa, InfoFlowAnalysis dfa, InfoFlowAnalysis primitiveDfa, UseFinder uf, SootClass sootClass, List<SootMethod> entryMethods)
{
printdfgs = dfa.printDebug();
this.loa = loa;
this.dfa = dfa;
this.primitiveDfa = primitiveDfa;
this.uf = uf;
this.sootClass = sootClass;
this.methodToMethodLocalObjectsAnalysis = new HashMap<SootMethod, SmartMethodLocalObjectsAnalysis>();
this.methodToContext = null;
this.allMethods = null;
this.externalMethods = null;
this.internalMethods = null;
this.entryMethods = entryMethods;
this.allFields = null;
this.externalFields = null;
this.internalFields = null;
this.localFields = null;
this.sharedFields = null;
this.localInnerFields = null;
this.sharedInnerFields = null;
if(true) // verbose)
{
G.v().out.println("[local-objects] Analyzing local objects for " + sootClass);
G.v().out.println("[local-objects] preparing class " + new Date());
}
prepare();
if(true) // verbose)
{
G.v().out.println("[local-objects] analyzing class " + new Date());
}
doAnalysis();
if(true) // verbose)
{
G.v().out.println("[local-objects] propagating over call graph " + new Date());
}
propagate();
if(true)
{
G.v().out.println("[local-objects] finished at " + new Date());
G.v().out.println("[local-objects] (#analyzed/#encountered): " + SmartMethodInfoFlowAnalysis.counter + "/" + ClassInfoFlowAnalysis.methodCount);
}
}
private void prepare()
{
// Get list of all methods
allMethods = getAllReachableMethods(sootClass);
// Get list of external methods
externalMethods = uf.getExtMethods(sootClass);
SootClass superclass = sootClass;
if(superclass.hasSuperclass())
superclass = superclass.getSuperclass();
while(superclass.hasSuperclass())
{
if(superclass.isApplicationClass())
externalMethods.addAll(uf.getExtMethods(superclass));
superclass = superclass.getSuperclass();
}
// Get list of internal methods
internalMethods = new ArrayList<SootMethod>();
for (SootMethod method : allMethods) {
if(!externalMethods.contains(method))
internalMethods.add(method);
}
// Get list of all fields
allFields = getAllFields(sootClass);
// Get list of external fields
externalFields = uf.getExtFields(sootClass);
superclass = sootClass;
if(superclass.hasSuperclass())
superclass = superclass.getSuperclass();
while(superclass.hasSuperclass())
{
if(superclass.isApplicationClass())
externalFields.addAll(uf.getExtFields(superclass));
superclass = superclass.getSuperclass();
}
// Get list of internal fields
internalFields = new ArrayList<SootField>();
for (SootField field : allFields) {
if(!externalFields.contains(field))
internalFields.add(field);
}
}
// Returns a list of reachable methods in class sc and its superclasses
public static List<SootMethod> getAllReachableMethods(SootClass sc)
{
ReachableMethods rm = Scene.v().getReachableMethods();
// Get list of reachable methods declared in this class
List<SootMethod> allMethods = new ArrayList<SootMethod>();
Iterator methodsIt = sc.methodIterator();
while(methodsIt.hasNext())
{
SootMethod method = (SootMethod) methodsIt.next();
if(rm.contains(method))
allMethods.add(method);
}
// Add reachable methods declared in superclasses
SootClass superclass = sc;
if(superclass.hasSuperclass())
superclass = superclass.getSuperclass();
while(superclass.hasSuperclass()) // we don't want to process Object
{
Iterator scMethodsIt = superclass.methodIterator();
while(scMethodsIt.hasNext())
{
SootMethod scMethod = (SootMethod) scMethodsIt.next();
if(rm.contains(scMethod))
allMethods.add(scMethod);
}
superclass = superclass.getSuperclass();
}
return allMethods;
}
// Returns a list of fields in class sc and its superclasses
public static List<SootField> getAllFields(SootClass sc)
{
// Get list of reachable methods declared in this class
// Also get list of fields declared in this class
List<SootField> allFields = new ArrayList<SootField>();
Iterator fieldsIt = sc.getFields().iterator();
while(fieldsIt.hasNext())
{
SootField field = (SootField) fieldsIt.next();
allFields.add(field);
}
// Add reachable methods and fields declared in superclasses
SootClass superclass = sc;
if(superclass.hasSuperclass())
superclass = superclass.getSuperclass();
while(superclass.hasSuperclass()) // we don't want to process Object
{
Iterator scFieldsIt = superclass.getFields().iterator();
while(scFieldsIt.hasNext())
{
SootField scField = (SootField) scFieldsIt.next();
allFields.add(scField);
}
superclass = superclass.getSuperclass();
}
return allFields;
}
private void doAnalysis()
{
// Combine the DFA results for each of this class's methods, using safe
// approximations for which parameters, fields, and globals are shared
// or local.
// Separate fields into shared and local. Initially fields are known to be
// shared if they have any external accesses, or if they're static.
// Methods are iterated over, moving fields to shared if shared data flows to them.
// This is repeated until no fields move for a complete iteration.
// Populate localFields and sharedFields with fields of this class
localFields = new ArrayList<SootField>();
sharedFields = new ArrayList<SootField>();
Iterator<SootField> fieldsIt = allFields.iterator();
while(fieldsIt.hasNext())
{
SootField field = fieldsIt.next();
if( fieldIsInitiallyLocal(field) )
localFields.add(field);
else
sharedFields.add(field);
}
// Add inner fields to localFields and sharedFields, if present
localInnerFields = new ArrayList<SootField>();
sharedInnerFields = new ArrayList<SootField>();
Iterator<SootMethod> methodsIt = allMethods.iterator();
while(methodsIt.hasNext())
{
SootMethod method = methodsIt.next();
// Get data flow summary
MutableDirectedGraph dataFlowSummary;
if(primitiveDfa != null)
{
dataFlowSummary = primitiveDfa.getMethodInfoFlowSummary(method);
if(printdfgs && method.getDeclaringClass().isApplicationClass())
{
G.v().out.println("Attempting to print graphs (will succeed only if ./dfg/ is a valid path)");
DirectedGraph primitiveGraph = primitiveDfa.getMethodInfoFlowAnalysis(method).getMethodAbbreviatedInfoFlowGraph();
InfoFlowAnalysis.printGraphToDotFile("dfg/" + method.getDeclaringClass().getShortName() + "_" + method.getName() + "_primitive",
primitiveGraph, method.getName() + "_primitive", false);
DirectedGraph nonPrimitiveGraph = dfa.getMethodInfoFlowAnalysis(method).getMethodAbbreviatedInfoFlowGraph();
InfoFlowAnalysis.printGraphToDotFile("dfg/" + method.getDeclaringClass().getShortName() + "_" + method.getName(),
nonPrimitiveGraph, method.getName(), false);
}
}
else
{
dataFlowSummary = dfa.getMethodInfoFlowSummary(method);
if(printdfgs && method.getDeclaringClass().isApplicationClass())
{
G.v().out.println("Attempting to print graph (will succeed only if ./dfg/ is a valid path)");
DirectedGraph nonPrimitiveGraph = dfa.getMethodInfoFlowAnalysis(method).getMethodAbbreviatedInfoFlowGraph();
InfoFlowAnalysis.printGraphToDotFile("dfg/" + method.getDeclaringClass().getShortName() + "_" + method.getName(),
nonPrimitiveGraph, method.getName(), false);
}
}
// Iterate through nodes
Iterator<Object> nodesIt = dataFlowSummary.getNodes().iterator();
while(nodesIt.hasNext())
{
EquivalentValue node = (EquivalentValue) nodesIt.next();
if(node.getValue() instanceof InstanceFieldRef)
{
InstanceFieldRef ifr = (InstanceFieldRef) node.getValue();
if( !localFields.contains(ifr.getField()) && !sharedFields.contains(ifr.getField()) &&
!localInnerFields.contains(ifr.getField()) ) // && !sharedInnerFields.contains(ifr.getField()))
{
// this field is read or written, but is not in the lists of fields!
localInnerFields.add(ifr.getField());
}
}
}
}
// Propagate (aka iterate iterate iterate iterate! hope it's not too slow)
boolean changed = true;
while(changed)
{
changed = false;
// G.v().out.println("Starting iteration:");
methodsIt = allMethods.iterator();
while(methodsIt.hasNext())
{
SootMethod method = methodsIt.next();
// we can't learn anything from non-concrete methods, and statics can't write non-static fields
if(method.isStatic() || !method.isConcrete())
continue;
ListIterator<SootField> localFieldsIt = ((List<SootField>) localFields).listIterator();
while(localFieldsIt.hasNext())
{
SootField localField = localFieldsIt.next();
List sourcesAndSinks = new ArrayList();
MutableDirectedGraph dataFlowSummary;
if(primitiveDfa != null)
dataFlowSummary = primitiveDfa.getMethodInfoFlowSummary(method);
else
dataFlowSummary = dfa.getMethodInfoFlowSummary(method);
EquivalentValue node = InfoFlowAnalysis.getNodeForFieldRef(method, localField);
if(dataFlowSummary.containsNode(node))
{
sourcesAndSinks.addAll(dataFlowSummary.getSuccsOf(node));
sourcesAndSinks.addAll(dataFlowSummary.getPredsOf(node));
}
Iterator sourcesAndSinksIt = sourcesAndSinks.iterator();
if(localField.getDeclaringClass().isApplicationClass() &&
sourcesAndSinksIt.hasNext())
{
// if(!printedMethodHeading)
// {
// G.v().out.println(" Method: " + method.toString());
// printedMethodHeading = true;
// }
// G.v().out.println(" Field: " + localField.toString());
}
while(sourcesAndSinksIt.hasNext())
{
EquivalentValue sourceOrSink = (EquivalentValue) sourcesAndSinksIt.next();
Ref sourceOrSinkRef = (Ref) sourceOrSink.getValue();
boolean fieldBecomesShared = false;
if(sourceOrSinkRef instanceof ParameterRef) // or return ref
{
fieldBecomesShared = !parameterIsLocal(method, sourceOrSink, true);
}
else if(sourceOrSinkRef instanceof ThisRef) // or return ref
{
fieldBecomesShared = !thisIsLocal(method, sourceOrSink);
}
else if(sourceOrSinkRef instanceof InstanceFieldRef)
{
fieldBecomesShared = sharedFields.contains( ((FieldRef)sourceOrSinkRef).getField() ) || sharedInnerFields.contains( ((FieldRef)sourceOrSinkRef).getField() );
}
else if(sourceOrSinkRef instanceof StaticFieldRef)
{
fieldBecomesShared = true;
}
else
{
throw new RuntimeException("Unknown type of Ref in Data Flow Graph:");
}
if(fieldBecomesShared)
{
// if(localField.getDeclaringClass().isApplicationClass())
// G.v().out.println(" Source/Sink: " + sourceOrSinkRef.toString() + " is SHARED");
localFieldsIt.remove();
sharedFields.add(localField);
changed = true;
break; // other sources don't matter now... it only takes one to taint the field
}
else
{
// if(localField.getDeclaringClass().isApplicationClass())
// G.v().out.println(" Source: " + sourceRef.toString() + " is local");
}
}
}
ListIterator<SootField> localInnerFieldsIt = ((List<SootField>) localInnerFields).listIterator();
// boolean printedMethodHeading = false;
while(!changed && localInnerFieldsIt.hasNext())
{
SootField localInnerField = localInnerFieldsIt.next();
List sourcesAndSinks = new ArrayList();
MutableDirectedGraph dataFlowSummary;
if(primitiveDfa != null)
dataFlowSummary = primitiveDfa.getMethodInfoFlowSummary(method);
else
dataFlowSummary = dfa.getMethodInfoFlowSummary(method);
EquivalentValue node = InfoFlowAnalysis.getNodeForFieldRef(method, localInnerField);
if(dataFlowSummary.containsNode(node))
{
sourcesAndSinks.addAll(dataFlowSummary.getSuccsOf(node));
sourcesAndSinks.addAll(dataFlowSummary.getPredsOf(node));
}
Iterator sourcesAndSinksIt = sourcesAndSinks.iterator();
if(localInnerField.getDeclaringClass().isApplicationClass() &&
sourcesAndSinksIt.hasNext())
{
// if(!printedMethodHeading)
// {
// G.v().out.println(" Method: " + method.toString());
// printedMethodHeading = true;
// }
// G.v().out.println(" Field: " + localField.toString());
}
while(sourcesAndSinksIt.hasNext())
{
EquivalentValue sourceOrSink = (EquivalentValue) sourcesAndSinksIt.next();
Ref sourceOrSinkRef = (Ref) sourceOrSink.getValue();
boolean fieldBecomesShared = false;
if(sourceOrSinkRef instanceof ParameterRef) // or return ref
{
fieldBecomesShared = !parameterIsLocal(method, sourceOrSink, true);
}
else if(sourceOrSinkRef instanceof ThisRef) // or return ref
{
fieldBecomesShared = !thisIsLocal(method, sourceOrSink);
}
else if(sourceOrSinkRef instanceof InstanceFieldRef)
{
fieldBecomesShared = sharedFields.contains( ((FieldRef)sourceOrSinkRef).getField() ) || sharedInnerFields.contains( ((FieldRef)sourceOrSinkRef).getField() );
}
else if(sourceOrSinkRef instanceof StaticFieldRef)
{
fieldBecomesShared = true;
}
else
{
throw new RuntimeException("Unknown type of Ref in Data Flow Graph:");
}
if(fieldBecomesShared)
{
// if(localField.getDeclaringClass().isApplicationClass())
// G.v().out.println(" Source/Sink: " + sourceOrSinkRef.toString() + " is SHARED");
localInnerFieldsIt.remove();
sharedInnerFields.add(localInnerField);
changed = true;
break; // other sources don't matter now... it only takes one to taint the field
}
else
{
// if(localField.getDeclaringClass().isApplicationClass())
// G.v().out.println(" Source: " + sourceRef.toString() + " is local");
}
}
}
}
}
// Print debug output
if(dfa.printDebug())
{
G.v().out.println(" Found local/shared fields for " + sootClass.toString());
G.v().out.println(" Local fields: ");
Iterator<SootField> localsToPrintIt = localFields.iterator();
while(localsToPrintIt.hasNext())
{
SootField localToPrint = localsToPrintIt.next();
if(localToPrint.getDeclaringClass().isApplicationClass())
G.v().out.println(" " + localToPrint);
}
G.v().out.println(" Shared fields: ");
Iterator<SootField> sharedsToPrintIt = sharedFields.iterator();
while(sharedsToPrintIt.hasNext())
{
SootField sharedToPrint = sharedsToPrintIt.next();
if(sharedToPrint.getDeclaringClass().isApplicationClass())
G.v().out.println(" " + sharedToPrint);
}
G.v().out.println(" Local inner fields: ");
localsToPrintIt = localInnerFields.iterator();
while(localsToPrintIt.hasNext())
{
SootField localToPrint = localsToPrintIt.next();
if(localToPrint.getDeclaringClass().isApplicationClass())
G.v().out.println(" " + localToPrint);
}
G.v().out.println(" Shared inner fields: ");
sharedsToPrintIt = sharedInnerFields.iterator();
while(sharedsToPrintIt.hasNext())
{
SootField sharedToPrint = sharedsToPrintIt.next();
if(sharedToPrint.getDeclaringClass().isApplicationClass())
G.v().out.println(" " + sharedToPrint);
}
}
}
private void propagate()
{
// Initialize worklist
ArrayList<SootMethod> worklist = new ArrayList<SootMethod>();
worklist.addAll(entryMethods);
// Initialize set of contexts
methodToContext = new HashMap<SootMethod, CallLocalityContext>(); // TODO: add the ability to share a map with another CLOA to save memory (be careful of context-sensitive call graph)
for (SootMethod method : worklist) {
methodToContext.put(method, getContextFor(method));
}
// Propagate
Date start = new Date();
if(dfa.printDebug())
G.v().out.println("CLOA: Starting Propagation at " + start);
while(worklist.size() > 0)
{
ArrayList<SootMethod> newWorklist = new ArrayList<SootMethod>();
for (SootMethod containingMethod : worklist) {
CallLocalityContext containingContext = methodToContext.get(containingMethod);
if(dfa.printDebug())
G.v().out.println(" " + containingMethod.getName() + " " + containingContext.toShortString());
// Calculate the context for each invoke stmt in the containingMethod
Map<Stmt, CallLocalityContext> invokeToContext = new HashMap<Stmt, CallLocalityContext>();
for(Iterator edgesIt = Scene.v().getCallGraph().edgesOutOf(containingMethod); edgesIt.hasNext(); )
{
Edge e = (Edge) edgesIt.next();
if( !e.src().getDeclaringClass().isApplicationClass() || e.srcStmt() == null )
continue;
CallLocalityContext invokeContext;
if( !invokeToContext.containsKey(e.srcStmt()) )
{
invokeContext = getContextFor(e, containingMethod, containingContext);
invokeToContext.put(e.srcStmt(), invokeContext);
}
else
{
invokeContext = invokeToContext.get(e.srcStmt());
}
if( !methodToContext.containsKey(e.tgt()) )
{
methodToContext.put(e.tgt(), invokeContext);
newWorklist.add(e.tgt());
}
else
{
// G.v().out.println(" Merging Contexts for " + e.tgt());
boolean causedChange = methodToContext.get(e.tgt()).merge(invokeContext); // The contexts being merged could be from different DFAs. If so, primitive version might be bigger.
if( causedChange )
newWorklist.add(e.tgt());
}
}
}
worklist = newWorklist;
}
long longTime = ((new Date()).getTime() - start.getTime()) / 100;
float time = (longTime) / 10.0f;
if(dfa.printDebug())
G.v().out.println("CLOA: Ending Propagation after " + time + "s");
}
public CallLocalityContext getMergedContext(SootMethod method)
{
if(methodToContext.containsKey(method))
return methodToContext.get(method);
return null;
}
private CallLocalityContext getContextFor(Edge e, SootMethod containingMethod, CallLocalityContext containingContext)
{
// get new called method and calling context
InvokeExpr ie;
if(e.srcStmt().containsInvokeExpr())
ie = e.srcStmt().getInvokeExpr();
else
ie = null;
SootMethod callingMethod = e.tgt();
CallLocalityContext callingContext = new CallLocalityContext(dfa.getMethodInfoFlowSummary(callingMethod).getNodes()); // just keeps a map from NODE to SHARED/LOCAL
// We will use the containing context that we have to determine if base/args are local
if(callingMethod.isConcrete())
{
Body b = containingMethod.retrieveActiveBody();
// check base
if(ie != null && ie instanceof InstanceInvokeExpr)
{
InstanceInvokeExpr iie = (InstanceInvokeExpr) ie;
if( !containingMethod.isStatic() && iie.getBase().equivTo(b.getThisLocal()) )
{
// calling another method on same object... basically copy the previous context
Iterator<Object> localRefsIt = containingContext.getLocalRefs().iterator();
while(localRefsIt.hasNext())
{
EquivalentValue rEqVal = (EquivalentValue) localRefsIt.next();
Ref r = (Ref) rEqVal.getValue();
if(r instanceof InstanceFieldRef)
{
EquivalentValue newRefEqVal = InfoFlowAnalysis.getNodeForFieldRef(callingMethod, ((FieldRef) r).getFieldRef().resolve());
if(callingContext.containsField(newRefEqVal)) // if not, then we're probably calling a parent class's method, so some fields are missing
callingContext.setFieldLocal(newRefEqVal); // must make a new eqval for the method getting called
}
else if(r instanceof ThisRef)
callingContext.setThisLocal();
}
}
else if( SmartMethodLocalObjectsAnalysis.isObjectLocal(dfa, containingMethod, containingContext, iie.getBase()) )
{
// calling a method on a local object
callingContext.setAllFieldsLocal();
callingContext.setThisLocal();
}
else
{
// calling a method on a shared object
callingContext.setAllFieldsShared();
callingContext.setThisShared();
}
}
else
{
callingContext.setAllFieldsShared();
callingContext.setThisShared();
}
// check args
if(ie == null)
callingContext.setAllParamsShared();
else
{
for(int param = 0; param < ie.getArgCount(); param++)
{
if( SmartMethodLocalObjectsAnalysis.isObjectLocal(dfa, containingMethod, containingContext, ie.getArg(param)) )
callingContext.setParamLocal(param);
else
callingContext.setParamShared(param);
}
}
}
else
{
// The only conservative solution for a bodyless method is to assume everything is shared
callingContext.setAllFieldsShared();
callingContext.setThisShared();
callingContext.setAllParamsShared();
}
return callingContext;
}
public CallLocalityContext getContextFor(SootMethod sm) { return getContextFor(sm, false); }
private CallLocalityContext getContextFor(SootMethod sm, boolean includePrimitiveDataFlowIfAvailable)
{
CallLocalityContext context;
if(includePrimitiveDataFlowIfAvailable)
context = new CallLocalityContext(primitiveDfa.getMethodInfoFlowSummary(sm).getNodes());
else
context = new CallLocalityContext(dfa.getMethodInfoFlowSummary(sm).getNodes());
// Set context for every parameter that is shared
for(int i = 0; i < sm.getParameterCount(); i++) // no need to worry about return value...
{
EquivalentValue paramEqVal = InfoFlowAnalysis.getNodeForParameterRef(sm, i);
if(parameterIsLocal(sm, paramEqVal, includePrimitiveDataFlowIfAvailable))
{
context.setParamLocal(i);
}
else
{
context.setParamShared(i);
}
}
for (SootField sf : getLocalFields()) {
EquivalentValue fieldRefEqVal = InfoFlowAnalysis.getNodeForFieldRef(sm, sf);
context.setFieldLocal(fieldRefEqVal);
}
for (SootField sf : getSharedFields()) {
EquivalentValue fieldRefEqVal = InfoFlowAnalysis.getNodeForFieldRef(sm, sf);
context.setFieldShared(fieldRefEqVal);
}
return context;
}
public boolean isObjectLocal(Value localOrRef, SootMethod sm) { return isObjectLocal(localOrRef, sm, false); }
private boolean isObjectLocal(Value localOrRef, SootMethod sm, boolean includePrimitiveDataFlowIfAvailable)
{
if(localOrRef instanceof StaticFieldRef)
{
return false;
}
if(dfa.printDebug())
G.v().out.println(" CLOA testing if " + localOrRef + " is local in " + sm);
SmartMethodLocalObjectsAnalysis smloa = getMethodLocalObjectsAnalysis(sm, includePrimitiveDataFlowIfAvailable);
if(localOrRef instanceof InstanceFieldRef)
{
InstanceFieldRef ifr = (InstanceFieldRef) localOrRef;
if( ifr.getBase().equivTo(smloa.getThisLocal()) )
return isFieldLocal(ifr.getFieldRef().resolve());
else
{
// if referred object is local, then find out if field is local in that object
if(isObjectLocal(ifr.getBase(), sm, includePrimitiveDataFlowIfAvailable))
{
boolean retval = loa.isFieldLocalToParent(ifr.getFieldRef().resolve());
if(dfa.printDebug())
G.v().out.println(" " + (retval ? "local" : "shared"));
return retval;
}
else
{
if(dfa.printDebug())
G.v().out.println(" shared");
return false;
}
}
}
// TODO Prepare a CallLocalityContext!
CallLocalityContext context = getContextFor(sm);
boolean retval = smloa.isObjectLocal(localOrRef, context);
if(dfa.printDebug())
G.v().out.println(" " + (retval ? "local" : "shared"));
return retval;
}
public SmartMethodLocalObjectsAnalysis getMethodLocalObjectsAnalysis(SootMethod sm) { return getMethodLocalObjectsAnalysis(sm, false); }
private SmartMethodLocalObjectsAnalysis getMethodLocalObjectsAnalysis(SootMethod sm, boolean includePrimitiveDataFlowIfAvailable)
{
if(includePrimitiveDataFlowIfAvailable && primitiveDfa != null)
{
Body b = sm.retrieveActiveBody();
UnitGraph g = new ExceptionalUnitGraph(b);
return new SmartMethodLocalObjectsAnalysis(g, primitiveDfa);
}
else if(!methodToMethodLocalObjectsAnalysis.containsKey(sm))
{
// Analyze this method
Body b = sm.retrieveActiveBody();
UnitGraph g = new ExceptionalUnitGraph(b);
SmartMethodLocalObjectsAnalysis smloa = new SmartMethodLocalObjectsAnalysis(g, dfa);
methodToMethodLocalObjectsAnalysis.put(sm, smloa);
}
return methodToMethodLocalObjectsAnalysis.get(sm);
}
private boolean fieldIsInitiallyLocal(SootField field)
{
if(field.isStatic())
{
// Static fields are always shared
return false;
}
else if(field.isPrivate())
{
// Private fields may be local
return true;
}
else
{
return !externalFields.contains(field);
}
}
protected List<SootField> getSharedFields()
{
return (List<SootField>) sharedFields.clone();
}
protected List<SootField> getLocalFields()
{
return (List<SootField>) localFields.clone();
}
public List<SootField> getInnerSharedFields()
{
return sharedInnerFields;
}
protected boolean isFieldLocal(SootField field)
{
return localFields.contains(field);
}
protected boolean isFieldLocal(EquivalentValue fieldRef)
{
return localFields.contains( ((SootFieldRef) fieldRef.getValue()).resolve() );
}
public boolean parameterIsLocal(SootMethod method, EquivalentValue parameterRef) { return parameterIsLocal(method, parameterRef, false); }
protected boolean parameterIsLocal(SootMethod method, EquivalentValue parameterRef, boolean includePrimitiveDataFlowIfAvailable)
{
if(dfa.printDebug() && method.getDeclaringClass().isApplicationClass())
G.v().out.println(" Checking PARAM " + parameterRef + " for " + method);
// Check if param is primitive or ref type
ParameterRef param = (ParameterRef) parameterRef.getValue();
if( !(param.getType() instanceof RefLikeType) && (!dfa.includesPrimitiveInfoFlow() || method.getName().equals("<init>")) ) // TODO fix
{
if(dfa.printDebug() && method.getDeclaringClass().isApplicationClass())
G.v().out.println(" PARAM is local (primitive)");
return true; // primitive params are always considered local
}
// Check if method is externally called
List extClassCalls = uf.getExtCalls(sootClass);
Iterator extClassCallsIt = extClassCalls.iterator();
while(extClassCallsIt.hasNext())
{
Pair extCall = (Pair) extClassCallsIt.next();
Stmt s = (Stmt) extCall.getO2();
if(s.getInvokeExpr().getMethodRef().resolve() == method)
{
if(dfa.printDebug() && method.getDeclaringClass().isApplicationClass())
G.v().out.println(" PARAM is shared (external access)");
return false; // If so, assume it's params are shared
}
}
// For each internal call, check if arg is local or shared
List intClassCalls = uf.getIntCalls(sootClass);
Iterator intClassCallsIt = intClassCalls.iterator(); // returns all internal accesses
while(intClassCallsIt.hasNext())
{
Pair intCall = (Pair) intClassCallsIt.next();
SootMethod containingMethod = (SootMethod) intCall.getO1();
Stmt s = (Stmt) intCall.getO2();
InvokeExpr ie = s.getInvokeExpr();
if(ie.getMethodRef().resolve() == method)
{
if(((ParameterRef) parameterRef.getValue()).getIndex() >= 0)
{
if(!isObjectLocal( ie.getArg( ((ParameterRef) parameterRef.getValue()).getIndex() ), containingMethod, includePrimitiveDataFlowIfAvailable)) // WORST CASE SCENARIO HERE IS INFINITE RECURSION!
{
if(dfa.printDebug() && method.getDeclaringClass().isApplicationClass())
G.v().out.println(" PARAM is shared (internal propagation)");
return false; // if arg is shared for any internal call, then param is shared
}
}
else
{
if(s instanceof DefinitionStmt)
{
Value obj = ((DefinitionStmt) s).getLeftOp();
if(!isObjectLocal( obj, containingMethod, includePrimitiveDataFlowIfAvailable)) // WORST CASE SCENARIO HERE IS INFINITE RECURSION!
{
if(dfa.printDebug() && method.getDeclaringClass().isApplicationClass())
G.v().out.println(" PARAM is shared (internal propagation)");
return false; // if arg is shared for any internal call, then param is shared
}
}
}
}
}
if(dfa.printDebug() && method.getDeclaringClass().isApplicationClass())
G.v().out.println(" PARAM is local SO FAR (internal propagation)");
return true; // if argument is always local, then parameter is local
}
// TODO: SOUND/UNSOUND???
protected boolean thisIsLocal(SootMethod method, EquivalentValue thisRef)
{
return true;
}
}