/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj package tac package fpcf package analyses package escape import org.opalj.br.ClassType import org.opalj.br.DefinedMethod import org.opalj.br.analyses.VirtualFormalParameter import org.opalj.br.fpcf.properties.AtMost import org.opalj.br.fpcf.properties.Context import org.opalj.br.fpcf.properties.EscapeInCallee import org.opalj.br.fpcf.properties.EscapeProperty import org.opalj.br.fpcf.properties.EscapeViaAbnormalReturn import org.opalj.br.fpcf.properties.EscapeViaHeapObject import org.opalj.br.fpcf.properties.EscapeViaParameter import org.opalj.br.fpcf.properties.EscapeViaParameterAndAbnormalReturn import org.opalj.br.fpcf.properties.EscapeViaStaticField import org.opalj.br.fpcf.properties.GlobalEscape import org.opalj.br.fpcf.properties.NoEscape import org.opalj.fpcf.Entity import org.opalj.fpcf.EOptionP import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimUBP import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.Property import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.SomeInterimEP import org.opalj.util.elidedAssert /** * Special handling for constructor calls, as the receiver of a constructor is always an * allocation site. * The constructor of Object does not escape the self reference by definition. For other * constructors, the inter-procedural chain will be processed until it reaches the Object * constructor or escapes. Is this the case, leastRestrictiveProperty will be set to the lower bound * of the current value and the calculated escape state. * * For non constructor calls, [[org.opalj.br.fpcf.properties.AtMost]] * ([[org.opalj.br.fpcf.properties.EscapeInCallee]]) of `e will be ` returned whenever the receiver or * a parameter is a use of defSite. * * @author Florian Kuebler */ trait ConstructorSensitiveEscapeAnalysis extends AbstractEscapeAnalysis { override type AnalysisContext <: AbstractEscapeAnalysisContext & PropertyStoreContainer & VirtualFormalParametersContainer & DeclaredMethodsContainer abstract override protected def handleThisLocalOfConstructor( call: NonVirtualMethodCall[V] )( implicit context: AnalysisContext, state: AnalysisState ): Unit = { elidedAssert(call.name == "", "method is not a constructor") elidedAssert(state.usesDefSite(call.receiver), "call receiver does not use def-site") // the object constructor will not escape the this-local if (call.declaringClass eq ClassType.Object) return; // resolve the constructor val constructor = project.specialCall( context.targetMethodDeclaringClassType, call.declaringClass, call.isInterface, name = "", call.descriptor ) constructor match { case Success(callee) => // check if the this-local escapes in the callee val fp = context.virtualFormalParameters(context.declaredMethods(callee))(0) val fpEntity = ( contextProvider.expandContext(context.entity._1, declaredMethods(callee), call.pc), fp ) if (fpEntity != context.entity) { val escapeState = context.propertyStore(fpEntity, EscapeProperty.key) if (!state.containsDependency(escapeState)) handleEscapeState(escapeState) } case /* unknown method */ _ => state.meetMostRestrictive(AtMost(NoEscape)) } } private def handleEscapeState( eOptionP: EOptionP[Entity, Property] )( implicit state: AnalysisState ): Unit = { eOptionP match { case FinalP(NoEscape) => // NOTHING TO DO case FinalP(GlobalEscape) => state.meetMostRestrictive(GlobalEscape) case FinalP(EscapeViaStaticField) => state.meetMostRestrictive(EscapeViaStaticField) case FinalP(EscapeViaHeapObject) => state.meetMostRestrictive(EscapeViaHeapObject) case FinalP(EscapeInCallee) => state.meetMostRestrictive(EscapeInCallee) case FinalP(AtMost(EscapeInCallee)) => state.meetMostRestrictive(AtMost(EscapeInCallee)) case FinalP(EscapeViaParameter) => state.meetMostRestrictive(AtMost(NoEscape)) case FinalP(EscapeViaAbnormalReturn) => state.meetMostRestrictive(AtMost(NoEscape)) case FinalP(EscapeViaParameterAndAbnormalReturn) => state.meetMostRestrictive(AtMost(NoEscape)) case FinalP(AtMost(NoEscape)) => state.meetMostRestrictive(AtMost(NoEscape)) case FinalP(AtMost(EscapeViaParameter)) => state.meetMostRestrictive(AtMost(NoEscape)) case FinalP(AtMost(EscapeViaAbnormalReturn)) => state.meetMostRestrictive(AtMost(NoEscape)) case FinalP(AtMost(EscapeViaParameterAndAbnormalReturn)) => state.meetMostRestrictive(AtMost(NoEscape)) case FinalP(p) => throw new UnknownError(s"unexpected escape property ($p) for constructors") case ep @ InterimUBP(NoEscape) => state.addDependency(ep) case ep @ InterimUBP(EscapeInCallee) => state.meetMostRestrictive(EscapeInCallee) state.addDependency(ep) case ep @ InterimUBP(AtMost(EscapeInCallee)) => state.meetMostRestrictive(AtMost(EscapeInCallee)) state.addDependency(ep) case ep: SomeInterimEP => state.meetMostRestrictive(AtMost(NoEscape)) state.addDependency(ep) // result not yet finished case epk => state.addDependency(epk) } } abstract override protected def c( someEPS: SomeEPS )( implicit context: AnalysisContext, state: AnalysisState ): ProperPropertyComputationResult = { someEPS.e match { case (_: Context, VirtualFormalParameter(dm: DefinedMethod, -1)) if dm.definedMethod.isConstructor => state.removeDependency(someEPS) handleEscapeState(someEPS) returnResult case _ => super.c(someEPS) } } }