Skip to content

Commit

Permalink
Refactor reachable method analyses and their state / TAC handling
Browse files Browse the repository at this point in the history
  • Loading branch information
maximilianruesch committed Sep 18, 2024
1 parent 3f8735c commit ec68ef8
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ import org.opalj.fpcf.PropertyStore
import org.opalj.fpcf.Results
import org.opalj.fpcf.UBP
import org.opalj.tac.cg.TypeIteratorKey
import org.opalj.tac.fpcf.analyses.cg.ReachableMethodAnalysis
import org.opalj.tac.fpcf.analyses.cg.DefinedBodyReachableMethodAnalysis
import org.opalj.tac.fpcf.properties.TACAI
import org.opalj.value.ValueInformation

class SystemPropertiesAnalysisScheduler private[analyses] (
final val project: SomeProject
) extends ReachableMethodAnalysis {
) extends DefinedBodyReachableMethodAnalysis {

def processMethod(
callContext: ContextType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,35 +26,22 @@ trait TACAIBasedAnalysisState[TheContextType <: Context]
def callContext: ContextType

protected[this] var _tacDependee: EOptionP[Method, TACAI]
assert((_tacDependee eq null) || (_tacDependee.hasUBP && _tacDependee.ub.tac.isDefined))
assert(_tacDependee.hasUBP && _tacDependee.ub.tac.isDefined)

abstract override def hasOpenDependencies: Boolean = {
(_tacDependee ne null) && _tacDependee.isRefinable || super.hasOpenDependencies
}
final def hasTACDependee: Boolean = _tacDependee.isRefinable
abstract override def hasOpenDependencies: Boolean = _tacDependee.isRefinable || super.hasOpenDependencies
final def isTACDependeeRefinable: Boolean = _tacDependee.isRefinable

/**
* Inherited classes that introduce new dependencies must override this method and call add a
* call to super!
*/
abstract override def dependees: Set[SomeEOptionP] = {
val otherDependees = super.dependees
if ((_tacDependee ne null) && _tacDependee.isRefinable)
otherDependees + _tacDependee
else
otherDependees
}
abstract override def dependees: Set[SomeEOptionP] = super.dependees ++ Some(_tacDependee).filter(_.isRefinable)

final def updateTACDependee(tacDependee: EOptionP[Method, TACAI]): Unit = {
_tacDependee = tacDependee
}

final def tac: TACode[TACMethodParameter, DUVar[ValueInformation]] = {
_tacDependee.ub.tac.get
}

final def tacDependee: EOptionP[Method, TACAI] = {
_tacDependee
}
final def tac: TACode[TACMethodParameter, DUVar[ValueInformation]] = _tacDependee.ub.tac.get

final def tacDependee: EOptionP[Method, TACAI] = _tacDependee
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import org.opalj.tac.fpcf.properties.TACAI
/**
* @author Florian Kuebler
*/
class CGState[ContextType <: Context](
override val callContext: ContextType,
override protected[this] var _tacDependee: EOptionP[Method, TACAI]
) extends BaseAnalysisState with TypeIteratorState with TACAIBasedAnalysisState[ContextType] {
class CGState[TheContextType <: Context](
val callContext: TheContextType
) extends BaseAnalysisState with TypeIteratorState with ContextualAnalysis {

override type ContextType = TheContextType

// maps a definition site to the receiver var
private[this] val _virtualCallSites: mutable.Map[CallSite, (V, Set[ReferenceType])] =
Expand All @@ -38,3 +39,11 @@ class CGState[ContextType <: Context](

def hasNonFinalCallSite: Boolean = _virtualCallSites.nonEmpty
}

/**
* @author Maximilian Rüsch
*/
class TACAIBasedCGState[ContextType <: Context](
override val callContext: ContextType,
override protected[this] var _tacDependee: EOptionP[Method, TACAI]
) extends CGState[ContextType](callContext) with TACAIBasedAnalysisState[ContextType]
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,13 @@ import org.opalj.value.IsSObjectValue
class CallGraphAnalysis private[cg] (
override val project: SomeProject
) extends ReachableMethodAnalysis with TypeConsumerAnalysis {
type LocalTypeInformation

private[this] val isMethodOverridable: Method => Answer = project.get(IsOverridableMethodKey)
private[this] lazy val getCBSTargets = project.get(CallBySignatureKey)
private[this] val resovleCallBySignature =
project.config.getBoolean("org.opalj.br.analyses.cg.callBySignatureResolution")

def c(state: CGState[ContextType])(eps: SomeEPS): ProperPropertyComputationResult = {
def c(state: TACAIBasedCGState[ContextType])(eps: SomeEPS): ProperPropertyComputationResult = {
eps match {
case UBP(tacai: TACAI) if tacai.tac.isDefined =>
state.updateTACDependee(eps.asInstanceOf[EPS[Method, TACAI]])
Expand All @@ -74,7 +73,7 @@ class CallGraphAnalysis private[cg] (
case UBP(_: TACAI) =>
throw new IllegalStateException("there was already a tac defined")

case EPS(e) =>
case EPS(_) =>
val relevantCallSites = state.dependersOf(eps.toEPK).asInstanceOf[Set[CallSite]]

// ensures, that we only add new calls
Expand Down Expand Up @@ -121,25 +120,27 @@ class CallGraphAnalysis private[cg] (

override final def processMethod(
callContext: ContextType,
tacEP: EPS[Method, TACAI]
tacEPOpt: Option[EPS[Method, TACAI]]
): ProperPropertyComputationResult = {
val state = new CGState[ContextType](callContext, tacEP)
if (tacEP ne null)
if (tacEPOpt.isDefined) {
val state = new TACAIBasedCGState[ContextType](callContext, tacEPOpt.get)
processMethod(state, new DirectCalls())
else
returnResult(new DirectCalls(), enforceCalleesResult = true)(state)
} else {
val state = new CGState[ContextType](callContext)
Results {
new DirectCalls().partialResults(state.callContext, enforceCalleesResult = true)
}
}
}

override final val processesMethodsWithoutBody = true

protected[this] def doHandleVirtualCall(
callContext: ContextType,
call: Call[V] with VirtualCall[V],
pc: Int,
specializedDeclaringClassType: ReferenceType,
isPrecise: Boolean,
calleesAndCallers: DirectCalls
)(implicit state: CGState[ContextType]): Unit = {
)(implicit state: TACAIBasedCGState[ContextType]): Unit = {
val callerType = callContext.method.declaringClassType
val callSite = CallSite(pc, call.name, call.descriptor, call.declaringClass)

Expand Down Expand Up @@ -209,7 +210,7 @@ class CallGraphAnalysis private[cg] (
}

protected final def processMethod(
state: CGState[ContextType],
state: TACAIBasedCGState[ContextType],
calls: DirectCalls
): ProperPropertyComputationResult = {
val tac = state.tac
Expand Down Expand Up @@ -294,13 +295,13 @@ class CallGraphAnalysis private[cg] (
case _ => // nothing to do
}

returnResult(calls, true)(state)
returnResult(calls, enforceCalleesResult = true)(state)
}

protected[this] def returnResult(
calleesAndCallers: DirectCalls,
enforceCalleesResult: Boolean = false
)(implicit state: CGState[ContextType]): ProperPropertyComputationResult = {
)(implicit state: TACAIBasedCGState[ContextType]): ProperPropertyComputationResult = {
val results = calleesAndCallers.partialResults(state.callContext, enforceCalleesResult)

// FIXME: This won't work for refinable TACs as state.hasNonFinalCallSite may return false
Expand Down Expand Up @@ -388,7 +389,7 @@ class CallGraphAnalysis private[cg] (
call: Call[V] with VirtualCall[V],
pc: Int,
calleesAndCallers: DirectCalls
)(implicit state: CGState[ContextType]): Unit = {
)(implicit state: TACAIBasedCGState[ContextType]): Unit = {
val rvs = call.receiver.asVar.value.asReferenceValue.allValues
for (rv <- rvs) rv match {
case _: IsSArrayValue =>
Expand Down Expand Up @@ -422,7 +423,7 @@ class CallGraphAnalysis private[cg] (
call: Call[V] with VirtualCall[V],
pc: Int,
calleesAndCallers: DirectCalls
)(implicit state: CGState[ContextType]): Unit = {
)(implicit state: TACAIBasedCGState[ContextType]): Unit = {
doHandleVirtualCall(callContext, call, pc, calleeType, isPrecise = true, calleesAndCallers)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ class DoPrivilegedMethodAnalysis private[cg] (
if (params.nonEmpty && params.head.isDefined) {
val param = params.head.get.asVar

implicit val state: CGState[ContextType] =
new CGState[ContextType](callerContext, FinalEP(callerContext.method.definedMethod, TheTACAI(tac)))
implicit val state: TACAIBasedCGState[ContextType] = new TACAIBasedCGState[ContextType](
callerContext,
FinalEP(callerContext.method.definedMethod, TheTACAI(tac))
)

val thisActual = persistentUVar(param)(state.tac.stmts)

Expand All @@ -93,8 +95,7 @@ class DoPrivilegedMethodAnalysis private[cg] (
thisVar: V,
thisActual: Some[(ValueInformation, IntTrieSet)],
calls: IndirectCalls
)(implicit state: CGState[ContextType]): ProperPropertyComputationResult = {

)(implicit state: TACAIBasedCGState[ContextType]): ProperPropertyComputationResult = {
val partialResults = calls.partialResults(state.callContext)
if (state.hasOpenDependencies)
Results(
Expand Down Expand Up @@ -127,7 +128,7 @@ class DoPrivilegedMethodAnalysis private[cg] (
}

def c(
state: CGState[ContextType],
state: TACAIBasedCGState[ContextType],
thisVar: V,
thisActual: Some[(ValueInformation, IntTrieSet)]
)(eps: SomeEPS): ProperPropertyComputationResult = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,9 @@ trait ReachableMethodAnalysis extends FPCFAnalysis with TypeConsumerAnalysis {
}
}

protected val processesMethodsWithoutBody = false

protected def processMethodWithoutBody(
eOptP: EOptionP[DeclaredMethod, Callers]
): PropertyComputationResult = {
if (processesMethodsWithoutBody) {
processMethod(eOptP, NoCallers, None)
} else
NoResult
}
): PropertyComputationResult = processMethod(eOptP, NoCallers, None)

private[this] def processMethod(
eOptP: EOptionP[DeclaredMethod, Callers],
Expand All @@ -94,18 +87,10 @@ trait ReachableMethodAnalysis extends FPCFAnalysis with TypeConsumerAnalysis {
)
}

/**
* @deprecated Use [[processMethod]](ContextType, Option[_]) instead
*/
def processMethod(
callContext: ContextType,
tacEP: EPS[Method, TACAI]
): ProperPropertyComputationResult

def processMethod(
callContext: ContextType,
tacEPOpt: Option[EPS[Method, TACAI]]
): ProperPropertyComputationResult = processMethod(callContext, tacEPOpt.orNull)
): ProperPropertyComputationResult

protected def continuationForTAC(
declaredMethod: DeclaredMethod
Expand All @@ -132,3 +117,20 @@ trait ReachableMethodAnalysis extends FPCFAnalysis with TypeConsumerAnalysis {
processMethod(newCallers, oldCallers, tacEPOpt)
}
}

trait DefinedBodyReachableMethodAnalysis extends ReachableMethodAnalysis {

override protected final def processMethodWithoutBody(
eOptP: EOptionP[DeclaredMethod, Callers]
): PropertyComputationResult = NoResult

def processMethod(
callContext: ContextType,
tacEP: EPS[Method, TACAI]
): ProperPropertyComputationResult

override final def processMethod(
callContext: ContextType,
tacEPOpt: Option[EPS[Method, TACAI]]
): ProperPropertyComputationResult = processMethod(callContext, tacEPOpt.get)
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class OOSWriteObjectAnalysis private[analyses] (
val receiver = persistentUVar(param)
val parameters = Seq(receiverOption.flatMap(os => persistentUVar(os.asVar)))

implicit val state: CGState[ContextType] = new CGState[ContextType](
implicit val state: TACAIBasedCGState[ContextType] = new TACAIBasedCGState[ContextType](
callerContext,
FinalEP(callerContext.method.definedMethod, TheTACAI(tac))
)
Expand All @@ -101,7 +101,7 @@ class OOSWriteObjectAnalysis private[analyses] (
receiverVar: V,
receiver: Option[(ValueInformation, IntTrieSet)],
parameters: Seq[Option[(ValueInformation, IntTrieSet)]],
state: CGState[ContextType]
state: TACAIBasedCGState[ContextType]
)(eps: SomeEPS): ProperPropertyComputationResult = {
val pc = state.dependersOf(eps.toEPK).head.asInstanceOf[Int]

Expand All @@ -126,7 +126,7 @@ class OOSWriteObjectAnalysis private[analyses] (
receiver: Option[(ValueInformation, IntTrieSet)],
parameters: Seq[Option[(ValueInformation, IntTrieSet)]],
indirectCalls: IndirectCalls
)(implicit state: CGState[ContextType]): ProperPropertyComputationResult = {
)(implicit state: TACAIBasedCGState[ContextType]): ProperPropertyComputationResult = {
val results = indirectCalls.partialResults(state.callContext)
if (state.hasOpenDependencies)
Results(
Expand All @@ -144,7 +144,7 @@ class OOSWriteObjectAnalysis private[analyses] (
receiver: Option[(ValueInformation, IntTrieSet)],
parameters: Seq[Option[(ValueInformation, IntTrieSet)]],
indirectCalls: IndirectCalls
)(implicit state: CGState[ContextType]): Unit = {
)(implicit state: TACAIBasedCGState[ContextType]): Unit = {
typeIterator.foreachType(
param,
typeIterator.typesProperty(param, callContext, callPC.asInstanceOf[Entity], state.tac.stmts)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class ThreadStartAnalysis private[cg] (
isDirect: Boolean
): ProperPropertyComputationResult = {
val partialAnalysisResults = new ThreadStartAnalysisResults()
implicit val state: CGState[ContextType] = new CGState[ContextType](
implicit val state: TACAIBasedCGState[ContextType] = new TACAIBasedCGState[ContextType](
callerContext,
FinalEP(callerContext.method.definedMethod, TheTACAI(tac))
)
Expand All @@ -80,7 +80,7 @@ class ThreadStartAnalysis private[cg] (
def returnResult(
receiver: V,
partialAnalysisResults: ThreadStartAnalysisResults
)(implicit state: CGState[ContextType]): ProperPropertyComputationResult = {
)(implicit state: TACAIBasedCGState[ContextType]): ProperPropertyComputationResult = {
val runnableResults = Results(partialAnalysisResults.partialResults(state.callContext))
if (state.hasOpenDependencies)
Results(
Expand All @@ -91,12 +91,12 @@ class ThreadStartAnalysis private[cg] (
Results(runnableResults)
}

def c(receiver: V, state: CGState[ContextType])(eps: SomeEPS): ProperPropertyComputationResult = {
def c(receiver: V, state: TACAIBasedCGState[ContextType])(eps: SomeEPS): ProperPropertyComputationResult = {
val epk = eps.toEPK

// ensures, that we only add new vm reachable methods
val partialAnalysisResults = new ThreadStartAnalysisResults()
implicit val _state: CGState[ContextType] = state
implicit val _state: TACAIBasedCGState[ContextType] = state

eps.ub match {
case _: TACAI =>
Expand Down Expand Up @@ -178,7 +178,7 @@ class ThreadStartAnalysis private[cg] (
callPC: Int,
receiver: V,
partialAnalysisResults: ThreadStartAnalysisResults
)(implicit state: CGState[ContextType]): Unit = {
)(implicit state: TACAIBasedCGState[ContextType]): Unit = {
// a call to Thread.start will trigger the JVM to later on call Thread.exit()
val exitMethod = project.specialCall(
ObjectType.Thread,
Expand Down Expand Up @@ -480,7 +480,7 @@ class UncaughtExceptionHandlerAnalysis private[analyses] (
): ProperPropertyComputationResult = {
val vmReachableMethods = new VMReachableMethods()

implicit val state: CGState[ContextType] = new CGState[ContextType](
implicit val state: TACAIBasedCGState[ContextType] = new TACAIBasedCGState[ContextType](
callerContext,
FinalEP(callerContext.method.definedMethod, TheTACAI(tac))
)
Expand All @@ -497,7 +497,7 @@ class UncaughtExceptionHandlerAnalysis private[analyses] (

def c(
receiver: V,
state: CGState[ContextType]
state: TACAIBasedCGState[ContextType]
)(eps: SomeEPS): ProperPropertyComputationResult = {
val epk = eps.toEPK
val pc = state.dependersOf(epk).head.asInstanceOf[Int]
Expand All @@ -521,7 +521,7 @@ class UncaughtExceptionHandlerAnalysis private[analyses] (
def returnResult(
receiver: V,
vmReachableMethods: VMReachableMethods
)(implicit state: CGState[ContextType]): ProperPropertyComputationResult = {
)(implicit state: TACAIBasedCGState[ContextType]): ProperPropertyComputationResult = {
val results = vmReachableMethods.partialResults(state.callContext)
if (state.hasOpenDependencies)
Results(
Expand All @@ -545,7 +545,7 @@ class UncaughtExceptionHandlerAnalysis private[analyses] (
receiver: V,
callPC: Int,
vmReachableMethods: VMReachableMethods
)(implicit state: CGState[ContextType]): Unit = {
)(implicit state: TACAIBasedCGState[ContextType]): Unit = {
typeIterator.foreachType(
receiver,
typeIterator.typesProperty(receiver, callContext, callPC.asInstanceOf[Entity], state.tac.stmts)
Expand Down
Loading

0 comments on commit ec68ef8

Please sign in to comment.