/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj package tac package fpcf package analyses package pointsto import scala.collection.immutable.ArraySeq import org.opalj.br.BooleanType import org.opalj.br.ClassType import org.opalj.br.DeclaredMethod import org.opalj.br.IntegerType import org.opalj.br.LongType import org.opalj.br.MethodDescriptor import org.opalj.br.ReferenceType import org.opalj.br.VoidType import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.properties.cg.Callers import org.opalj.br.fpcf.properties.pointsto.AllocationSitePointsToSet import org.opalj.br.fpcf.properties.pointsto.TypeBasedPointsToSet import org.opalj.fpcf.FinalEP import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyComputationResult import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Results import org.opalj.tac.fpcf.properties.TheTACAI /** * Models effects of `sun.misc.Unsafe` to points-to sets. * * @author Dominik Helm */ abstract class UnsafePointsToAnalysis private[pointsto] ( final val project: SomeProject ) extends PointsToAnalysisBase { self => private val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) trait PointsToBase extends AbstractPointsToBasedAnalysis { override protected type ElementType = self.ElementType override protected type PointsToSet = self.PointsToSet override protected type DependerType = self.DependerType override protected val pointsToPropertyKey: PropertyKey[PointsToSet] = self.pointsToPropertyKey override protected def emptyPointsToSet: PointsToSet = self.emptyPointsToSet override protected def createPointsToSet( pc: Int, callContext: ContextType, allocatedType: ReferenceType, isConstant: Boolean, isEmptyArray: Boolean ): PointsToSet = { self.createPointsToSet( pc, callContext.asInstanceOf[self.ContextType], allocatedType, isConstant, isEmptyArray ) } @inline override protected def getTypeOf(element: ElementType): ReferenceType = { self.getTypeOf(element) } @inline override protected def getTypeIdOf(element: ElementType): Int = { self.getTypeIdOf(element) } @inline override protected def isEmptyArray(element: ElementType): Boolean = { self.isEmptyArray(element) } } def getAnalyses(UnsafeT: ClassType): List[APIBasedAnalysis] = { List( new UnsafeGetPointsToAnalysis( p, declaredMethods( UnsafeT, "", UnsafeT, "getObject", MethodDescriptor(ArraySeq(ClassType.Object, LongType), ClassType.Object) ) ) with PointsToBase, new UnsafeGetPointsToAnalysis( p, declaredMethods( UnsafeT, "", UnsafeT, "getObject", MethodDescriptor(ArraySeq(ClassType.Object, IntegerType), ClassType.Object) ) ) with PointsToBase, new UnsafeGetPointsToAnalysis( p, declaredMethods( UnsafeT, "", UnsafeT, "getObjectVolatile", MethodDescriptor(ArraySeq(ClassType.Object, LongType), ClassType.Object) ) ) with PointsToBase, new UnsafePutPointsToAnalysis( p, 2, declaredMethods( UnsafeT, "", UnsafeT, "putObject", MethodDescriptor(ArraySeq(ClassType.Object, LongType, ClassType.Object), VoidType) ) ) with PointsToBase, new UnsafePutPointsToAnalysis( p, 2, declaredMethods( UnsafeT, "", UnsafeT, "putObject", MethodDescriptor(ArraySeq(ClassType.Object, IntegerType, ClassType.Object), VoidType) ) ) with PointsToBase, new UnsafePutPointsToAnalysis( p, 2, declaredMethods( UnsafeT, "", UnsafeT, "putObjectVolatile", MethodDescriptor(ArraySeq(ClassType.Object, LongType, ClassType.Object), VoidType) ) ) with PointsToBase, new UnsafePutPointsToAnalysis( p, 2, declaredMethods( UnsafeT, "", UnsafeT, "putOrderedObject", MethodDescriptor(ArraySeq(ClassType.Object, LongType, ClassType.Object), VoidType) ) ) with PointsToBase, new UnsafePutPointsToAnalysis( p, 3, declaredMethods( UnsafeT, "", UnsafeT, "compareAndSwapObject", MethodDescriptor( ArraySeq(ClassType.Object, LongType, ClassType.Object, ClassType.Object), BooleanType ) ) ) with PointsToBase, new UnsafeGetPointsToAnalysis( p, declaredMethods( UnsafeT, "", UnsafeT, "getAndSetObject", MethodDescriptor(ArraySeq(ClassType.Object, LongType, ClassType.Object), ClassType.Object) ) ) with PointsToBase, new UnsafePutPointsToAnalysis( p, 2, declaredMethods( UnsafeT, "", UnsafeT, "getAndSetObject", MethodDescriptor(ArraySeq(ClassType.Object, LongType, ClassType.Object), ClassType.Object) ) ) with PointsToBase ) } def process(p: SomeProject): PropertyComputationResult = { val analyses = getAnalyses(ClassType.SunUnsafe) ++ getAnalyses(ClassType.JdkUnsafe) Results(analyses.map(_.registerAPIMethod())) } } abstract class UnsafeGetPointsToAnalysis( final val project: SomeProject, override val apiMethod: DeclaredMethod ) extends PointsToAnalysisBase with TACAIBasedAPIBasedAnalysis { def processNewCaller( calleeContext: ContextType, callerContext: ContextType, pc: Int, tac: TACode[TACMethodParameter, V], receiverOption: Option[Expr[V]], params: Seq[Option[Expr[V]]], targetVarOption: Option[V], isDirect: Boolean ): ProperPropertyComputationResult = { implicit val state: State = new PointsToAnalysisState[ElementType, PointsToSet, ContextType]( callerContext, FinalEP(callerContext.method.definedMethod, TheTACAI(tac)) ) val theObject = params.head if (theObject.isDefined) { handleGetField(None, pc, theObject.get.asVar.definedBy) } Results(createResults) } } abstract class UnsafePutPointsToAnalysis( final val project: SomeProject, val index: Int, override val apiMethod: DeclaredMethod ) extends PointsToAnalysisBase with TACAIBasedAPIBasedAnalysis { def processNewCaller( calleeContext: ContextType, callerContext: ContextType, pc: Int, tac: TACode[TACMethodParameter, V], receiverOption: Option[Expr[V]], params: Seq[Option[Expr[V]]], targetVarOption: Option[V], isDirect: Boolean ): ProperPropertyComputationResult = { implicit val state: State = new PointsToAnalysisState[ElementType, PointsToSet, ContextType]( callerContext, FinalEP(callerContext.method.definedMethod, TheTACAI(tac)) ) val baseObject = params.head val storeObject = params(index) if (baseObject.isDefined && storeObject.isDefined) { handlePutField(None, baseObject.get.asVar.definedBy, storeObject.get.asVar.definedBy) } Results(createResults) } } trait UnsafePointsToAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler with PointsToBasedAnalysisScheduler { val propertyKind: PropertyMetaInformation val createAnalysis: SomeProject => UnsafePointsToAnalysis override type InitializationData = Null override def requiredProjectInformation: ProjectInformationKeys = super.requiredProjectInformation :+ DeclaredMethodsKey override def uses: Set[PropertyBounds] = PropertyBounds.ubs(Callers, propertyKind) override def derivesCollaboratively: Set[PropertyBounds] = PropertyBounds.ubs(propertyKind) override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = createAnalysis(p) ps.scheduleEagerComputationForEntity(p)(analysis.process) analysis } override def derivesEagerly: Set[PropertyBounds] = Set.empty } object TypeBasedUnsafePointsToAnalysisScheduler extends UnsafePointsToAnalysisScheduler { override val propertyKind: PropertyMetaInformation = TypeBasedPointsToSet override val createAnalysis: SomeProject => UnsafePointsToAnalysis = new UnsafePointsToAnalysis(_) with TypeBasedAnalysis } object AllocationSiteBasedUnsafePointsToAnalysisScheduler extends UnsafePointsToAnalysisScheduler { override val propertyKind: PropertyMetaInformation = AllocationSitePointsToSet override val createAnalysis: SomeProject => UnsafePointsToAnalysis = new UnsafePointsToAnalysis(_) with AllocationSiteBasedAnalysis }