/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj package br package fpcf package analyses import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.ProjectInformationKey import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.ClassExtensibilityKey import org.opalj.br.fpcf.properties.Purity import org.opalj.fpcf.PropertyStoreKey import pureconfig.* /** * @author Dominik Helm */ class ConfiguredPurity( project: SomeProject, declaredMethods: DeclaredMethods ) { private case class PurityValue( cf: String, m: String, desc: String, p: String, conds: Option[Seq[String]] ) derives ConfigReader private val classExtensibility = project.get(ClassExtensibilityKey) private val toSet = ConfigSource.fromConfig(project.config).at("org.opalj.fpcf.analyses.ConfiguredPurity.purities") .loadOrThrow[Seq[PurityValue]] private val methods: Map[DeclaredMethod, Purity] = ( for { case PurityValue(className, methodName, descriptor, property, conditions) <- toSet.toSet po = Purity(property) if po.isDefined // Purity value only applies if the given types cannot have subtypes if conditions forall { _ forall { typeName => val ct = ClassType(typeName) project.classHierarchy.hasSubtypes(ct).isNo && classExtensibility(ct).isNo } } mdo = if (descriptor == "*") None else Some(MethodDescriptor(descriptor)) ms = if (className == "*") { project.allMethods .filter { m => m.name == methodName && mdo.forall(_ == m.descriptor) } .map(declaredMethods(_)) } else { val classType = ClassType(className) mdo match { case Some(md) => Seq( declaredMethods(classType, classType.packageName, classType, methodName, md) ) case None => project.classFile(classType).map { cf => cf.findMethod(methodName).map(declaredMethods(_)) }.getOrElse(Seq.empty) } } dm <- ms } yield { dm -> po.get } ).toMap def wasSet(dm: DeclaredMethod): Boolean = methods.contains(dm) def purity(dm: DeclaredMethod): Purity = methods(dm) } object ConfiguredPurityKey extends ProjectInformationKey[ConfiguredPurity, Nothing] { override def requirements(project: SomeProject): ProjectInformationKeys = Seq(PropertyStoreKey, DeclaredMethodsKey) override def compute(project: SomeProject): ConfiguredPurity = { val declaredMethods = project.get(DeclaredMethodsKey) new ConfiguredPurity(project, declaredMethods) } }