-
Notifications
You must be signed in to change notification settings - Fork 31
Description
The Problem
Currently (even with #229 in place), OPAL will not detect the call to run
for the following example. I want to point out that i'm not trying to construct unnecessarily complex artificial examples here, this is an actual issue that prevents me from analyzing DGMF, specifically this line 😄
class FieldsDemo implements Runnable {
private static Thread theThread;
public static void main(String[] args){
theThread = new Thread(new FieldsDemo());
theThread.start();
}
@Override
public void run(){
System.out.println("Running");
}
}
The reason for this is twofold:
TypeIterator.foreachAllocation
does not consider field reads (neitherGetField
norGetStatic
) as valid allocations.ThreadRelatedCallsAnalysis.handleThreadInit
only processes a thread definition site if it is a direct definition, meaning a constructor invocation. It does not attempt to find possible definition sites for an object when it is retrieved from within a field.
What i thought the solution could be
Honestly, i'm a little unsure as to how a proper solution for this would look like. In a perfect world, i would:
- Look into all field write accesses to the field that is being retrieved
- Find all definition sites for the variables that are assigned to the field via those write accesses
- Recursively invoke
ThreadRelatedCallsAnalysis.handleThreadInit
for each of those actual definition sites
What i've done so far: I implemented a simple solution that solves the example above. It only looks for field write accesse inside the current method, and recursively invokes handleThreadInit
on all definition sites. The issue for me is that in order to analyze possible definition sites in other methods, i need to obtain their TACAI results, and i (think i) would need to obtain their typeIteratorState
as well - i'm not sure if and how that works. My current solution looks something like this (inside ThreadRelatedCallsAnalysis.handleThreadInit
, plus minor fixes for TypeIterator.foreachAllocation
:
case Assignment(_, _, GetStatic(_, declClass, name, declType)) =>
val declFields = project.get(DeclaredFieldsKey)
val theDeclaredField = declFields(declClass, name, declType)
val fieldAccesses = project.get(FieldAccessInformationKey)
val tac = typeIteratorState.tac
fieldAccesses
.writeAccesses(theDeclaredField.definedField)(declFields)
.filter(_._1 == typeIteratorState.callContext.id)
.foreach{ case (_, pc, _, _) =>
val putStatic = stmts(tac.pcToIndex(pc)).asPutStatic
if(putStatic.value.isVar){
putStatic
.value
.asVar
.definedBy
.filter(_ >= 0)
.foreach{ defSite =>
handleThreadInit(callContext, callPC, allocationContext, defSite, stmts, partialAnalysisResults)
}
}
}
Questions
- Do we event want to handle this fully, or is it out of scope for the analysis?
- This adds the
DeclaredFieldsKey
andFieldAccessInformationkey
to the required project information, would this be a performance issue? Is the trade-off worth it?