diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/AbstractCallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/AbstractCallGraphAnalysis.scala index b18aed3e27..e3f2ad1965 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/AbstractCallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/AbstractCallGraphAnalysis.scala @@ -23,6 +23,7 @@ import org.opalj.br.Method import org.opalj.br.MethodDescriptor import org.opalj.br.ObjectType import org.opalj.br.ReferenceType +import org.opalj.br.analyses.cg.IsOverridableMethodKey import org.opalj.tac.fpcf.properties.TACAI trait CGState extends TACAIBasedAnalysisState { @@ -45,6 +46,8 @@ trait CGState extends TACAIBasedAnalysisState { trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { type State <: CGState + private[this] val isMethodOverridable: Method ⇒ Answer = project.get(IsOverridableMethodKey) + def createInitialState(definedMethod: DefinedMethod, tacEP: EPS[Method, TACAI]): State /** @@ -79,7 +82,61 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { specializedDeclaringClassType: ReferenceType, potentialTargets: ForeachRefIterator[ObjectType], calleesAndCallers: DirectCalls - )(implicit state: State): Unit + )(implicit state: State): Unit = { + + for (possibleTgtType ← potentialTargets) { + if (canResolveCall(state)(possibleTgtType)) { + val tgtR = project.instanceCall( + caller.declaringClassType, possibleTgtType, call.name, call.descriptor + ) + + handleCall( + caller, + call.name, + call.descriptor, + call.declaringClass, + pc, + tgtR, + calleesAndCallers + ) + } else { + handleUnresolvedCall(possibleTgtType, call, pc) + } + } + + // Deal with the fact that there may be unknown subtypes of the receiver type that might + // override the method + if (specializedDeclaringClassType.isObjectType) { + val declType = specializedDeclaringClassType.asObjectType + + val mResult = if (classHierarchy.isInterface(declType).isYes) + org.opalj.Result(project.resolveInterfaceMethodReference( + declType, call.name, call.descriptor + )) + else + org.opalj.Result(project.resolveMethodReference( + declType, + call.name, + call.descriptor, + forceLookupInSuperinterfacesOnFailure = true + )) + + if (mResult.isEmpty) { + unknownLibraryCall( + caller, + call.name, + call.descriptor, + call.declaringClass, + declType, + caller.definedMethod.classFile.thisType.packageName, + pc, + calleesAndCallers + ) + } else if (isMethodOverridable(mResult.value).isYesOrUnknown) { + calleesAndCallers.addIncompleteCallSite(pc) + } + } + } protected final def processMethod( state: State, calls: DirectCalls @@ -351,4 +408,18 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { calleesAndCallers ) } + + /** + * Decides whether this call graph implementation can resolve this call immediately. + */ + @inline protected[this] def canResolveCall(implicit state: State): ObjectType ⇒ Boolean + + /** + * Handles a call that is not immediately resolved by this call graph implementation. + */ + @inline protected[this] def handleUnresolvedCall( + possibleTgtType: ObjectType, + call: Call[V] with VirtualCall[V], + pc: Int + )(implicit state: State): Unit } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/CHACallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/CHACallGraphAnalysis.scala index 7c90c1f368..8ac75b043b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/CHACallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/CHACallGraphAnalysis.scala @@ -5,15 +5,12 @@ package fpcf package analyses package cg -import org.opalj.collection.ForeachRefIterator import org.opalj.fpcf.EOptionP import org.opalj.fpcf.EPS import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.br.DefinedMethod import org.opalj.br.ObjectType -import org.opalj.br.ReferenceType -import org.opalj.br.analyses.cg.IsOverridableMethodKey import org.opalj.tac.fpcf.properties.TACAI class CHAState( @@ -36,63 +33,26 @@ class CHACallGraphAnalysis private[analyses] ( ) extends AbstractCallGraphAnalysis { override type State = CHAState - private[this] val isMethodOverridable: Method ⇒ Answer = project.get(IsOverridableMethodKey) - - override def doHandleImpreciseCall( - caller: DefinedMethod, - call: Call[V] with VirtualCall[V], - pc: Int, - specializedDeclaringClassType: ReferenceType, - potentialTargets: ForeachRefIterator[ObjectType], - calleesAndCallers: DirectCalls - )(implicit state: CHAState): Unit = { - for (tgt ← potentialTargets) { - val tgtR = project.instanceCall( - caller.declaringClassType, tgt, call.name, call.descriptor - ) - handleCall( - caller, call.name, call.descriptor, call.declaringClass, pc, tgtR, calleesAndCallers - ) - } - - // TODO: Document what happens here - if (specializedDeclaringClassType.isObjectType) { - val declType = specializedDeclaringClassType.asObjectType - - val mResult = if (classHierarchy.isInterface(declType).isYes) - org.opalj.Result(project.resolveInterfaceMethodReference( - declType, call.name, call.descriptor - )) - else - org.opalj.Result(project.resolveMethodReference( - declType, - call.name, - call.descriptor, - forceLookupInSuperinterfacesOnFailure = true - )) - - if (mResult.isEmpty) { - unknownLibraryCall( - caller, - call.name, - call.descriptor, - call.declaringClass, - declType, - caller.definedMethod.classFile.thisType.packageName, - pc, - calleesAndCallers - ) - } else if (isMethodOverridable(mResult.value).isYesOrUnknown) { - calleesAndCallers.addIncompleteCallSite(pc) - } - } - } - override def createInitialState( definedMethod: DefinedMethod, tacEP: EPS[Method, TACAI] ): CHAState = { new CHAState(definedMethod, tacEP) } + + @inline override protected[this] def canResolveCall( + implicit + state: CHAState + ): ObjectType ⇒ Boolean = { + _ ⇒ true + } + + @inline protected[this] def handleUnresolvedCall( + possibleTgtType: ObjectType, + call: Call[V] with VirtualCall[V], + pc: Int + )(implicit state: CHAState): Unit = { + throw new UnsupportedOperationException() + } } object CHACallGraphAnalysisScheduler extends CallGraphAnalysisScheduler { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/DoPrivilegedPointsToCGAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/DoPrivilegedPointsToCGAnalysis.scala index 574c378443..8fe687fa30 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/DoPrivilegedPointsToCGAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/DoPrivilegedPointsToCGAnalysis.scala @@ -66,7 +66,7 @@ abstract class AbstractDoPrivilegedPointsToCGAnalysis private[cg] ( ) extends TACAIBasedAPIBasedAnalysis with AbstractPointsToBasedAnalysis { override protected[this] type State = PointsToBasedCGState[PointsToSet] - override protected[this] type DependerType = CallSiteT + override protected[this] type DependerType = CallSite override def processNewCaller( caller: DefinedMethod, @@ -86,7 +86,7 @@ abstract class AbstractDoPrivilegedPointsToCGAnalysis private[cg] ( val actualParamDefSites = call.params.head.asVar.definedBy - val callSite = (pc, call.name, call.descriptor, call.declaringClass) + val callSite = CallSite(pc, call.name, call.descriptor, call.declaringClass) val pointsToSets = currentPointsToOfDefSites(callSite, actualParamDefSites) @@ -176,7 +176,7 @@ class DoPrivilegedPointsToCGAnalysis private[cg] ( } override protected[this] type State = PointsToBasedCGState[PointsToSet] - override protected[this] type DependerType = CallSiteT + override protected[this] type DependerType = CallSite trait PointsToBase extends AbstractPointsToBasedAnalysis { override protected[this] type ElementType = self.ElementType diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/package.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/package.scala index 0f7a14ae42..14ff2337be 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/package.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/package.scala @@ -20,12 +20,14 @@ import org.opalj.ai.isImmediateVMException import org.opalj.br.MethodDescriptor import org.opalj.br.ReferenceType +case class CallSite( + pc: Int, methodName: String, methodDescriptor: MethodDescriptor, receiver: ReferenceType +) + package object cg { type V = DUVar[ValueInformation] - type CallSiteT = (Int /*PC*/ , String, MethodDescriptor, ReferenceType) - /** * A persistent representation (using pcs instead of TAC value origins) for a UVar. */ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/pointsto/AbstractPointsToBasedCallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/pointsto/AbstractPointsToBasedCallGraphAnalysis.scala index 6ea5c4bdb6..c5a11fd99a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/pointsto/AbstractPointsToBasedCallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/pointsto/AbstractPointsToBasedCallGraphAnalysis.scala @@ -40,7 +40,7 @@ trait AbstractPointsToBasedCallGraphAnalysis[PointsToSet <: PointsToSetLike[_, _ with AbstractPointsToBasedAnalysis { override type State = PointsToBasedCGState[PointsToSet] - override type DependerType = CallSiteT + override type DependerType = CallSite override protected[this] def handlePreciseCall( calleeType: ObjectType, @@ -68,7 +68,7 @@ trait AbstractPointsToBasedCallGraphAnalysis[PointsToSet <: PointsToSetLike[_, _ calleesAndCallers: DirectCalls )(implicit state: State): Unit = { val callerType = caller.definedMethod.classFile.thisType - val callSite = (pc, call.name, call.descriptor, call.declaringClass) + val callSite = CallSite(pc, call.name, call.descriptor, call.declaringClass) // get the upper bound of the pointsToSet and creates a dependency if needed val currentPointsToSets = currentPointsToOfDefSites(callSite, call.receiver.asVar.definedBy) @@ -122,7 +122,7 @@ trait AbstractPointsToBasedCallGraphAnalysis[PointsToSet <: PointsToSetLike[_, _ if (newType.isObjectType) newType.asObjectType else ObjectType.Object if (typesLeft.contains(theType.id)) { state.removeTypeForCallSite(callSite, theType) - val (pc, name, descriptor, declaredType) = callSite + val CallSite(pc, name, descriptor, declaredType) = callSite val tgtR = project.instanceCall( state.method.declaringClassType, theType, @@ -181,6 +181,21 @@ trait AbstractPointsToBasedCallGraphAnalysis[PointsToSet <: PointsToSetLike[_, _ pointsToUB(p2s) } } + + @inline override protected[this] def canResolveCall( + implicit + state: State + ): ObjectType ⇒ Boolean = { + throw new UnsupportedOperationException() + } + + @inline protected[this] def handleUnresolvedCall( + possibleTgtType: ObjectType, + call: Call[V] with VirtualCall[V], + pc: Int + )(implicit state: State): Unit = { + throw new UnsupportedOperationException() + } } class TypeBasedPointsToBasedCallGraphAnalysis private[pointsto] ( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/pointsto/PointsToBasedCGState.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/pointsto/PointsToBasedCGState.scala index 71027b5038..262cb9ad72 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/pointsto/PointsToBasedCGState.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/pointsto/PointsToBasedCGState.scala @@ -40,15 +40,15 @@ class PointsToBasedCGState[PointsToSet <: PointsToSetLike[_, _, PointsToSet]]( // efficiently perform updates here. // If we get an update for a dependee, we have to update all points-to sets for the // its dependers (_dependeeToDependers(dependee). - private[this] val _dependeeToDependers: mutable.Map[Entity, mutable.Set[CallSiteT]] = { + private[this] val _dependeeToDependers: mutable.Map[Entity, mutable.Set[CallSite]] = { mutable.Map.empty } - private[this] val _dependerToDependees: mutable.Map[CallSiteT, mutable.Set[Entity]] = { + private[this] val _dependerToDependees: mutable.Map[CallSite, mutable.Set[Entity]] = { mutable.Map.empty } final def addPointsToDependency( - depender: CallSiteT, + depender: CallSite, dependee: EOptionP[Entity, PointsToSet] ): Unit = { assert( @@ -90,7 +90,7 @@ class PointsToBasedCGState[PointsToSet <: PointsToSetLike[_, _, PointsToSet]]( throw new RuntimeException(s"failed to remove dependee: $dependee") } - final def removePointsToDepender(depender: CallSiteT): Unit = { + final def removePointsToDepender(depender: CallSite): Unit = { // for every dependee of the given depender: // we have to remove the depender from the set of their dependers for (dependee ← _dependerToDependees(depender)) { @@ -111,7 +111,7 @@ class PointsToBasedCGState[PointsToSet <: PointsToSetLike[_, _, PointsToSet]]( } } - final def removeTypeForCallSite(callSite: CallSiteT, instantiatedType: ObjectType): Unit = { + final def removeTypeForCallSite(callSite: CallSite, instantiatedType: ObjectType): Unit = { if (!_virtualCallSites.contains(callSite)) assert(_virtualCallSites(callSite).contains(instantiatedType.id)) val typesLeft = _virtualCallSites(callSite) - instantiatedType.id @@ -128,7 +128,7 @@ class PointsToBasedCGState[PointsToSet <: PointsToSetLike[_, _, PointsToSet]]( _pointsToDependees.contains(dependee) } - final def hasPointsToDependency(depender: CallSiteT, dependee: Entity): Boolean = { + final def hasPointsToDependency(depender: CallSite, dependee: Entity): Boolean = { _dependerToDependees.contains(depender) && _dependerToDependees(depender).contains(dependee) } @@ -168,19 +168,19 @@ class PointsToBasedCGState[PointsToSet <: PointsToSetLike[_, _, PointsToSet]]( // IMPROVE: In order to be thread-safe, we return an immutable copy of the set. // However, this is very inefficient! // The size of the sets is typically 1 or 2, but there are outliers with up to 100 elements. - final def dependersOf(dependee: Entity): Set[CallSiteT] = { + final def dependersOf(dependee: Entity): Set[CallSite] = { _dependeeToDependers(dependee).toSet } // maps a definition site to the ids of the potential (not yet resolved) objecttypes - private[this] val _virtualCallSites: mutable.Map[CallSiteT, IntTrieSet] = mutable.Map.empty + private[this] val _virtualCallSites: mutable.Map[CallSite, IntTrieSet] = mutable.Map.empty - def typesForCallSite(callSite: CallSiteT): IntTrieSet = { + def typesForCallSite(callSite: CallSite): IntTrieSet = { _virtualCallSites(callSite) } def addPotentialTypesOfCallSite( - callSite: CallSiteT, potentialTypes: IntTrieSet + callSite: CallSite, potentialTypes: IntTrieSet ): Unit = { _virtualCallSites(callSite) = _virtualCallSites.getOrElse(callSite, IntTrieSet.empty) ++ potentialTypes diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/pointsto/PointsToBasedThreadRelatedCallsAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/pointsto/PointsToBasedThreadRelatedCallsAnalysisScheduler.scala index 43f1f2e6e0..95d73dfb42 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/pointsto/PointsToBasedThreadRelatedCallsAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/pointsto/PointsToBasedThreadRelatedCallsAnalysisScheduler.scala @@ -56,7 +56,7 @@ trait PointsToBasedThreadStartAnalysis override val apiMethod: DeclaredMethod = threadStartMethod override type State = PointsToBasedCGState[PointsToSet] - override type DependerType = CallSiteT + override type DependerType = CallSite override def processNewCaller( caller: DefinedMethod, @@ -110,7 +110,7 @@ trait PointsToBasedThreadStartAnalysis val seenTypes = if (oldEOptP.hasUBP) oldEOptP.ub.numTypes else 0 for (cs ← relevantCallSites) { - val pc = cs._1 + val pc = cs.pc val receiver = state.tac.stmts( state.tac.properStmtIndexForPC(pc) ).asInstanceMethodCall.receiver @@ -172,7 +172,7 @@ trait PointsToBasedThreadStartAnalysis indirectCalls ) - val callSite = ( + val callSite = CallSite( pc, "start", MethodDescriptor.NoArgsAndReturnVoid, @@ -378,7 +378,7 @@ trait PointsToBasedThreadStartAnalysis } @inline protected[this] def currentPointsTo( - depender: CallSiteT, + depender: CallSite, dependee: Entity, typeFilter: ReferenceType ⇒ Boolean = PointsToSetLike.noFilter )(implicit state: State): PointsToSet = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala index fdada7307e..70eba962a6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala @@ -8,7 +8,6 @@ package rta import scala.language.existentials -import org.opalj.collection.ForeachRefIterator import org.opalj.fpcf.EPS import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.PropertyBounds @@ -17,7 +16,6 @@ import org.opalj.fpcf.UBP import org.opalj.br.DefinedMethod import org.opalj.br.Method import org.opalj.br.ObjectType -import org.opalj.br.ReferenceType import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.IsOverridableMethodKey import org.opalj.br.analyses.ProjectInformationKeys @@ -43,8 +41,6 @@ class RTACallGraphAnalysis private[analyses] ( // TODO maybe cache results for Object.toString, Iterator.hasNext, Iterator.next - private[this] val isMethodOverridable: Method ⇒ Answer = project.get(IsOverridableMethodKey) - override type State = RTAState override def c(state: RTAState)(eps: SomeEPS): ProperPropertyComputationResult = eps match { @@ -73,72 +69,6 @@ class RTACallGraphAnalysis private[analyses] ( new RTAState(definedMethod, tacEP, instantiatedTypesEOptP) } - override def doHandleImpreciseCall( - caller: DefinedMethod, - call: Call[V] with VirtualCall[V], - pc: Int, - specializedDeclaringClassType: ReferenceType, - potentialTargets: ForeachRefIterator[ObjectType], - calleesAndCallers: DirectCalls - )(implicit state: RTAState): Unit = { - for (possibleTgtType ← potentialTargets) { - if (state.instantiatedTypesUB.contains(possibleTgtType)) { - val tgtR = project.instanceCall( - caller.declaringClassType, - possibleTgtType, - call.name, - call.descriptor - ) - - handleCall( - caller, - call.name, - call.descriptor, - call.declaringClass, - pc, - tgtR, - calleesAndCallers - ) - } else { - state.addVirtualCallSite( - possibleTgtType, (pc, call.name, call.descriptor, call.declaringClass) - ) - } - } - - // TODO: Document what happens here - if (specializedDeclaringClassType.isObjectType) { - val declType = specializedDeclaringClassType.asObjectType - - val mResult = if (classHierarchy.isInterface(declType).isYes) - org.opalj.Result(project.resolveInterfaceMethodReference( - declType, call.name, call.descriptor - )) - else - org.opalj.Result(project.resolveMethodReference( - declType, - call.name, - call.descriptor, - forceLookupInSuperinterfacesOnFailure = true - )) - - if (mResult.isEmpty) { - unknownLibraryCall( - caller, - call.name, - call.descriptor, - call.declaringClass, - declType, - caller.definedMethod.classFile.thisType.packageName, - pc, - calleesAndCallers - ) - } else if (isMethodOverridable(mResult.value).isYesOrUnknown) { - calleesAndCallers.addIncompleteCallSite(pc) - } - } - } - /** * Computes the calls from the given method * ([[org.opalj.br.fpcf.properties.cg.Callees]] property) and updates the @@ -158,7 +88,7 @@ class RTACallGraphAnalysis private[analyses] ( state.newInstantiatedTypes(seenTypes).filter(_.isObjectType).foreach { instantiatedType ⇒ val callSites = state.getVirtualCallSites(instantiatedType.asObjectType) callSites.foreach { callSite ⇒ - val (pc, name, descr, declaringClass) = callSite + val CallSite(pc, name, descr, declaringClass) = callSite val tgtR = project.instanceCall( state.method.definedMethod.classFile.thisType, instantiatedType, @@ -181,6 +111,22 @@ class RTACallGraphAnalysis private[analyses] ( } } + @inline override protected[this] def canResolveCall( + implicit + state: RTAState + ): ObjectType ⇒ Boolean = { + state.instantiatedTypesUB.contains(_) + } + + @inline protected[this] def handleUnresolvedCall( + possibleTgtType: ObjectType, + call: Call[V] with VirtualCall[V], + pc: Int + )(implicit state: RTAState): Unit = { + state.addVirtualCallSite( + possibleTgtType, CallSite(pc, call.name, call.descriptor, call.declaringClass) + ) + } } object RTACallGraphAnalysisScheduler extends CallGraphAnalysisScheduler { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTAState.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTAState.scala index 0febf0728c..b8e706c8f5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTAState.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTAState.scala @@ -29,7 +29,7 @@ class RTAState( override protected[this] var _tacDependee: EOptionP[Method, TACAI], private[this] var _instantiatedTypesDependee: EOptionP[SomeProject, InstantiatedTypes] ) extends CGState { - private[this] val _virtualCallSites: mutable.LongMap[mutable.Set[CallSiteT]] = mutable.LongMap.empty + private[this] val _virtualCallSites: mutable.LongMap[mutable.Set[CallSite]] = mutable.LongMap.empty ///////////////////////////////////////////// // // @@ -73,7 +73,7 @@ class RTAState( override def hasNonFinalCallSite: Boolean = _virtualCallSites.nonEmpty - def addVirtualCallSite(objectType: ObjectType, callSite: CallSiteT): Unit = { + def addVirtualCallSite(objectType: ObjectType, callSite: CallSite): Unit = { val oldValOpt = _virtualCallSites.get(objectType.id.toLong) if (oldValOpt.isDefined) oldValOpt.get += callSite @@ -82,7 +82,7 @@ class RTAState( } } - def getVirtualCallSites(objectType: ObjectType): scala.collection.Set[CallSiteT] = { + def getVirtualCallSites(objectType: ObjectType): scala.collection.Set[CallSite] = { _virtualCallSites.getOrElse(objectType.id.toLong, scala.collection.Set.empty) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/PropagationBasedCGState.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/PropagationBasedCGState.scala index fc75f0f26e..c16ba24ec6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/PropagationBasedCGState.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/PropagationBasedCGState.scala @@ -35,7 +35,7 @@ class PropagationBasedCGState( _instantiatedTypesDependeeMap.update(dependee.e, dependee) } - private[this] val _virtualCallSites: mutable.LongMap[mutable.Set[CallSiteT]] = mutable.LongMap.empty + private[this] val _virtualCallSites: mutable.LongMap[mutable.Set[CallSite]] = mutable.LongMap.empty ///////////////////////////////////////////// // // @@ -78,7 +78,7 @@ class PropagationBasedCGState( override def hasNonFinalCallSite: Boolean = _virtualCallSites.nonEmpty - def addVirtualCallSite(objectType: ObjectType, callSite: CallSiteT): Unit = { + def addVirtualCallSite(objectType: ObjectType, callSite: CallSite): Unit = { val oldValOpt = _virtualCallSites.get(objectType.id.toLong) if (oldValOpt.isDefined) oldValOpt.get += callSite @@ -87,7 +87,7 @@ class PropagationBasedCGState( } } - def getVirtualCallSites(objectType: ObjectType): scala.collection.Set[CallSiteT] = { + def getVirtualCallSites(objectType: ObjectType): scala.collection.Set[CallSite] = { _virtualCallSites.getOrElse(objectType.id.toLong, scala.collection.Set.empty) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/PropagationBasedCallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/PropagationBasedCallGraphAnalysis.scala index 9c8b127d1b..142e717302 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/PropagationBasedCallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/PropagationBasedCallGraphAnalysis.scala @@ -8,7 +8,6 @@ package xta import scala.language.existentials -import org.opalj.collection.ForeachRefIterator import org.opalj.fpcf.EPS import org.opalj.fpcf.EUBP import org.opalj.fpcf.ProperPropertyComputationResult @@ -19,7 +18,6 @@ import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.ReferenceType import org.opalj.br.analyses.SomeProject -import org.opalj.br.analyses.cg.IsOverridableMethodKey import org.opalj.br.fpcf.properties.cg.Callees import org.opalj.br.fpcf.properties.cg.InstantiatedTypes import org.opalj.tac.fpcf.properties.TACAI @@ -42,8 +40,6 @@ class PropagationBasedCallGraphAnalysis private[analyses] ( // TODO maybe cache results for Object.toString, Iterator.hasNext, Iterator.next - private[this] val isMethodOverridable: Method ⇒ Answer = project.get(IsOverridableMethodKey) - override type State = PropagationBasedCGState override def c(state: PropagationBasedCGState)(eps: SomeEPS): ProperPropertyComputationResult = eps match { @@ -82,72 +78,6 @@ class PropagationBasedCallGraphAnalysis private[analyses] ( new PropagationBasedCGState(definedMethod, tacEP, typeSources) } - override def doHandleImpreciseCall( - caller: DefinedMethod, - call: Call[V] with VirtualCall[V], - pc: Int, - specializedDeclaringClassType: ReferenceType, - potentialTargets: ForeachRefIterator[ObjectType], - calleesAndCallers: DirectCalls - )(implicit state: PropagationBasedCGState): Unit = { - for (possibleTgtType ← potentialTargets) { - if (state.instantiatedTypesContains(possibleTgtType)) { - val tgtR = project.instanceCall( - caller.declaringClassType.asObjectType, - possibleTgtType, - call.name, - call.descriptor - ) - - handleCall( - caller, - call.name, - call.descriptor, - call.declaringClass, - pc, - tgtR, - calleesAndCallers - ) - } else { - state.addVirtualCallSite( - possibleTgtType, (pc, call.name, call.descriptor, call.declaringClass) - ) - } - } - - // TODO: Document what happens here - if (specializedDeclaringClassType.isObjectType) { - val declType = specializedDeclaringClassType.asObjectType - - val mResult = if (classHierarchy.isInterface(declType).isYes) - org.opalj.Result(project.resolveInterfaceMethodReference( - declType, call.name, call.descriptor - )) - else - org.opalj.Result(project.resolveMethodReference( - declType, - call.name, - call.descriptor, - forceLookupInSuperinterfacesOnFailure = true - )) - - if (mResult.isEmpty) { - unknownLibraryCall( - caller, - call.name, - call.descriptor, - call.declaringClass, - declType, - caller.definedMethod.classFile.thisType.packageName, - pc, - calleesAndCallers - ) - } else if (isMethodOverridable(mResult.value).isYesOrUnknown) { - calleesAndCallers.addIncompleteCallSite(pc) - } - } - } - /** * Computes the calls from the given method * ([[org.opalj.br.fpcf.properties.cg.Callees]] property) and updates the @@ -167,7 +97,7 @@ class PropagationBasedCallGraphAnalysis private[analyses] ( newTypes.filter(_.isObjectType).foreach { instantiatedType ⇒ val callSites = state.getVirtualCallSites(instantiatedType.asObjectType) callSites.foreach { callSite ⇒ - val (pc, name, descr, declaringClass) = callSite + val CallSite(pc, name, descr, declaringClass) = callSite val tgtR = project.instanceCall( state.method.definedMethod.classFile.thisType, instantiatedType, @@ -189,6 +119,23 @@ class PropagationBasedCallGraphAnalysis private[analyses] ( state.removeCallSite(instantiatedType.asObjectType) } } + + @inline override protected[this] def canResolveCall( + implicit + state: PropagationBasedCGState + ): ObjectType ⇒ Boolean = { + state.instantiatedTypesContains(_) + } + + @inline protected[this] def handleUnresolvedCall( + possibleTgtType: ObjectType, + call: Call[V] with VirtualCall[V], + pc: Int + )(implicit state: PropagationBasedCGState): Unit = { + state.addVirtualCallSite( + possibleTgtType, CallSite(pc, call.name, call.descriptor, call.declaringClass) + ) + } } class PropagationBasedCallGraphAnalysisScheduler(