All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opalj.br.fpcf.analyses.VirtualMethodPurityAnalysis.scala Maven / Gradle / Ivy

The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package br
package fpcf
package analyses

import org.opalj.fpcf.Entity
import org.opalj.fpcf.EOptionP
import org.opalj.fpcf.InterimResult
import org.opalj.fpcf.ProperPropertyComputationResult
import org.opalj.fpcf.PropertyBounds
import org.opalj.fpcf.PropertyStore
import org.opalj.fpcf.Result
import org.opalj.fpcf.SomeEOptionP
import org.opalj.fpcf.SomeEPS
import org.opalj.fpcf.UBP
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.properties.ClassifiedImpure
import org.opalj.br.fpcf.properties.CompileTimePure
import org.opalj.br.fpcf.properties.Context
import org.opalj.br.fpcf.properties.Purity
import org.opalj.br.fpcf.properties.SimpleContexts
import org.opalj.br.fpcf.properties.SimpleContextsKey
import org.opalj.br.fpcf.properties.VirtualMethodPurity
import org.opalj.br.fpcf.properties.VirtualMethodPurity.VImpureByAnalysis
import org.opalj.br.fpcf.properties.VirtualMethodPurity.VImpureByLackOfInformation

/**
 * Determines the aggregated purity for virtual methods.
 *
 * @author Dominik Helm
 */
class VirtualMethodPurityAnalysis private[analyses] ( final val project: SomeProject) extends FPCFAnalysis {
    private[this] val declaredMethods = project.get(DeclaredMethodsKey)
    private[this] val simpleContexts = project.get(SimpleContextsKey)

    def determinePurity(context: Context): ProperPropertyComputationResult = {
        val dm = context.method
        if (!dm.hasSingleDefinedMethod && !dm.hasMultipleDefinedMethods)
            return Result(dm, VImpureByLackOfInformation);

        var maxPurity: Purity = CompileTimePure
        var dependees: Set[SomeEOptionP] = Set.empty

        val cfo = project.classFile(dm.declaringClassType)
        val methods =
            if (cfo.isDefined && cfo.get.isInterfaceDeclaration)
                project.interfaceCall(
                    dm.declaringClassType,
                    dm.declaringClassType,
                    dm.name,
                    dm.descriptor
                )
            else project.virtualCall(
                dm.declaringClassType,
                dm.declaringClassType,
                dm.name,
                dm.descriptor
            )

        for (method <- methods) {
            propertyStore(simpleContexts(declaredMethods(method)), Purity.key) match {
                case eps @ UBP(ub) =>
                    maxPurity = maxPurity meet ub
                    if (eps.isRefinable) dependees += eps
                case epk => dependees += epk
            }
        }

        def c(eps: SomeEPS): ProperPropertyComputationResult = {
            dependees = dependees.filter { _.e ne eps.e }
            maxPurity = maxPurity meet eps.ub.asInstanceOf[Purity]
            if (eps.isRefinable) {
                dependees += eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]]
            }

            if (dependees.isEmpty || maxPurity.isInstanceOf[ClassifiedImpure]) {
                Result(dm, maxPurity.aggregatedProperty)
            } else {
                InterimResult(
                    dm, VImpureByAnalysis, maxPurity.aggregatedProperty,
                    dependees, c
                )
            }
        }

        if (dependees.isEmpty || maxPurity.isInstanceOf[ClassifiedImpure]) {
            Result(dm, maxPurity.aggregatedProperty)
        } else {
            InterimResult(
                dm, VImpureByAnalysis, maxPurity.aggregatedProperty,
                dependees, c
            )
        }
    }

    /** Called when the analysis is scheduled lazily. */
    def doDeterminePurity(e: Entity): ProperPropertyComputationResult = {
        e match {
            case c: Context => determinePurity(c)
            case _          => throw new IllegalArgumentException(s"$e ist not a DefinedMethod")
        }
    }

}

trait VirtualMethodPurityAnalysisScheduler extends FPCFAnalysisScheduler {

    override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey)

    final override def uses: Set[PropertyBounds] = Set(PropertyBounds.lub(Purity))

    final def derivedProperty: PropertyBounds = PropertyBounds.lub(VirtualMethodPurity)

}

object EagerVirtualMethodPurityAnalysis
    extends VirtualMethodPurityAnalysisScheduler
    with FPCFEagerAnalysisScheduler {

    override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty)

    override def derivesCollaboratively: Set[PropertyBounds] = Set.empty

    override type InitializationData = (DeclaredMethods, SimpleContexts, ConfiguredPurity)

    override def init(
        p: SomeProject, ps: PropertyStore
    ): (DeclaredMethods, SimpleContexts, ConfiguredPurity) = {
        (p.get(DeclaredMethodsKey), p.get(SimpleContextsKey), p.get(ConfiguredPurityKey))
    }

    override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {}

    override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {}

    override def afterPhaseCompletion(
        p:        SomeProject,
        ps:       PropertyStore,
        analysis: FPCFAnalysis
    ): Unit = {}

    override def start(
        p:    SomeProject,
        ps:   PropertyStore,
        data: InitializationData
    ): FPCFAnalysis = {
        val analysis = new VirtualMethodPurityAnalysis(p)
        val (declaredMethods, simpleContexts, configuredPurity) = data

        val dms = declaredMethods.declaredMethods
        val methods = dms.collect {
            case dm if dm.hasSingleDefinedMethod && !configuredPurity.wasSet(dm) =>
                simpleContexts(dm)
        }
        ps.scheduleEagerComputationsForEntities(methods)(analysis.determinePurity)

        analysis
    }
}

object LazyVirtualMethodPurityAnalysis
    extends VirtualMethodPurityAnalysisScheduler
    with BasicFPCFLazyAnalysisScheduler {

    override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty)

    override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = {
        val analysis = new VirtualMethodPurityAnalysis(p)
        ps.registerLazyPropertyComputation(VirtualMethodPurity.key, analysis.doDeterminePurity)
        analysis
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy