diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo.scala new file mode 100644 index 0000000000..af81ba8091 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo.scala @@ -0,0 +1,168 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf +package analyses + +import java.io.BufferedWriter +import java.io.File +import java.io.FileWriter +import java.net.URL +import java.util.Calendar + +import org.opalj.br.ObjectType +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.br.analyses.BasicReport +import org.opalj.br.analyses.Project +import org.opalj.br.analyses.ProjectAnalysisApplication +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.DependentImmutableClass +import org.opalj.br.fpcf.properties.MutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableClass +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds +import java.io.IOException +import org.opalj.br.fpcf.properties.ClassImmutability +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis + +/** + * Runs the EagerL1ClassImmutabilityAnalysis as well as analyses needed for improving the result. + * + * @author Tobias Roth + */ +object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "determines the immutability of classes" + + override def description: String = "identifies classes that are immutable in a shallow or deep way" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + val analysesManager = project.get(FPCFAnalysesManagerKey) + project.get(RTACallGraphKey) + + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, + LazyL3FieldImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, + EagerL1ClassImmutabilityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + + propertyStore.waitOnPhaseCompletion() + + } { t ⇒ + analysisTime = t.toSeconds + } + + val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet + + val groupedResults = propertyStore.entities(ClassImmutability.key). + filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) + + val order = (eps1: EPS[Entity, ClassImmutability], eps2: EPS[Entity, ClassImmutability]) ⇒ + eps1.e.toString < eps2.e.toString + + val mutableClasses = + groupedResults(MutableClass).toSeq.sortWith(order) + + val shallowImmutableClasses = + groupedResults(ShallowImmutableClass).toSeq.sortWith(order) + + val dependentImmutableClasses = + groupedResults(DependentImmutableClass).toSeq.sortWith(order) + + val deepImmutables = groupedResults(DeepImmutableClass).toSeq.sortWith(order) + + val allInterfaces = + project.allProjectClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet + + val deepImmutableClassesInterfaces = deepImmutables + .filter(eps ⇒ allInterfaces.contains(eps.asInstanceOf[ObjectType])).sortWith(order) + + val deepImmutableClasses = + deepImmutables + .filter(eps ⇒ !allInterfaces.contains(eps.asInstanceOf[ObjectType])).sortWith(order) + + val output = + s""" + | Mutable Classes: + | ${mutableClasses.mkString(" | Mutable Class\n")} + | + | Shallow Immutable Classes: + | ${shallowImmutableClasses.mkString(" | Shallow Immutable Class\n")} + | + | Dependent Immutable Classes: + | ${dependentImmutableClasses.mkString(" | Dependent Immutable Class\n")} + | + | Deep Immutable Classes: + | ${deepImmutableClasses.mkString(" | Deep Immutable Classes\n")} + | + | Deep Immutable Class Interfaces: + | ${deepImmutableClassesInterfaces.mkString(" | Deep Immutable Classes Interfaces\n")} + | + | + | Mutable Classes: ${mutableClasses.size} + | Shallow Immutable Classes: ${shallowImmutableClasses.size} + | Dependent Immutable Classes: ${dependentImmutableClasses.size} + | Deep Immutable Classes: ${deepImmutableClasses.size} + | Deep Immutable Classes Interfaces: ${deepImmutableClassesInterfaces.size} + | Deep Immutables: ${deepImmutables.size} + | + | sum: ${ + mutableClasses.size + shallowImmutableClasses.size + dependentImmutableClasses.size + + deepImmutableClasses.size + deepImmutableClassesInterfaces.size + } + | analysis took : $analysisTime seconds + |"""".stripMargin + + val file = new File(s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt") + + val bw = new BufferedWriter(new FileWriter(file)) + + try { + bw.write(output) + bw.close() + } catch { + case e: IOException ⇒ println( + s""" Could not write file: ${file.getName} + | ${e.getMessage} + |""".stripMargin + ) + } finally { + bw.close() + } + + output + } +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo.scala new file mode 100644 index 0000000000..74e675e596 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo.scala @@ -0,0 +1,172 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf +package analyses + +import java.net.URL +import java.io.BufferedWriter +import java.io.File +import java.io.FileWriter +import java.util.Calendar +import java.io.IOException + +import org.opalj.br.Field +import org.opalj.br.analyses.BasicReport +import org.opalj.br.analyses.Project +import org.opalj.br.analyses.ProjectAnalysisApplication +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.DeepImmutableField +import org.opalj.br.fpcf.properties.DependentImmutableField +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.ai.domain +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey + +/** + * Runs the EagerL0FieldImmutabilityAnalysis including all analyses needed for improving the result. + * + * @author Tobias Peter Roth + */ +object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "determines the immutability of (instance/static) fields" + + override def description: String = + "identifies fields that are immutable in a deep or shallow way" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + val analysesManager = project.get(FPCFAnalysesManagerKey) + project.get(RTACallGraphKey) + + project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + Set[Class[_ <: AnyRef]](classOf[domain.l2.DefaultPerformInvocationsDomainWithCFG[URL]]) + } + + time { + propertyStore = analysesManager + .runAll( + LazyL0FieldReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + EagerL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + + val allFieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet + + val groupedResults = propertyStore + .entities(FieldImmutability.key) + .filter(field ⇒ allFieldsInProjectClassFiles.contains(field.e.asInstanceOf[Field])) + .toTraversable + .groupBy(_.toFinalEP.p) + + val order = (eps1: EPS[Entity, FieldImmutability], eps2: EPS[Entity, FieldImmutability]) ⇒ + eps1.e.toString < eps2.e.toString + val mutableFields = groupedResults.getOrElse(MutableField, Seq.empty).toSeq.sortWith(order) + val shallowImmutableFields = + groupedResults.getOrElse(ShallowImmutableField, Seq.empty).toSeq.sortWith(order) + val dependentImmutableFields = + groupedResults.getOrElse(DependentImmutableField, Seq.empty).toSeq.sortWith(order) + val deepImmutableFields = + groupedResults.getOrElse(DeepImmutableField, Seq.empty).toSeq.sortWith(order) + + val output = + /* + | Mutable Fields: + | ${mutableFields.mkString(" | Mutable Field \n")} + | + | Shallow Immutable Fields: + | ${shallowImmutableFields.mkString(" | Shallow Immutable Field \n")} + | + | Dependent Immutable Fields: + | ${dependentImmutableFields.mkString(" | Dependent Immutable Field\n")} + | + | Deep Immutable Fields: + | ${deepImmutableFields.mkString(" | Deep Immutable Field\n")} + |*/ + s""" | + | Mutable Fields: ${mutableFields.size} + | Shallow Immutable Fields: ${shallowImmutableFields.size} + | Dependent Immutable Fields: ${dependentImmutableFields.size} + | Deep Immutable Fields: ${deepImmutableFields.size} + | + | sum: ${ + mutableFields.size + shallowImmutableFields.size + + dependentImmutableFields.size + deepImmutableFields.size + } + | + | took : $analysisTime seconds + | + | level: ${project.getProjectInformationKeyInitializationData(AIDomainFactoryKey)} + |propertyStore: ${propertyStore.getClass} + |""".stripMargin + + import java.text.SimpleDateFormat + + val calender = Calendar.getInstance() + calender.add(Calendar.ALL_STYLES, 1) + val date = calender.getTime() + val simpleDateFormat = new SimpleDateFormat("dd_MM_yyyy_HH_mm_ss") + + val file = new File( + s"demo_${analysis.toString}_${simpleDateFormat.format(date)}.txt" + ) + + println(s"filepath: ${file.getAbsolutePath}") + val bw = new BufferedWriter(new FileWriter(file)) + + try { + bw.write(output) + bw.close() + } catch { + case e: IOException ⇒ + println( + s""" Could not write file: ${file.getName} + | ${e.getMessage} + |""".stripMargin + ) + } finally { + bw.close() + } + + output + } +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldReferenceImmutabilityAnalysisDemo.scala new file mode 100644 index 0000000000..3b1dc50b15 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldReferenceImmutabilityAnalysisDemo.scala @@ -0,0 +1,161 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf +package analyses + +import java.net.URL +import org.opalj.br.analyses.BasicReport +import org.opalj.br.analyses.Project +import org.opalj.br.analyses.ProjectAnalysisApplication +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds +import java.util.Calendar +import org.opalj.br.Field +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.EagerL0FieldReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import java.io.IOException +import java.io.BufferedWriter +import java.io.FileWriter +import java.io.File +import org.opalj.br.fpcf.properties.FieldReferenceImmutability +import org.opalj.br.fpcf.properties.ImmutableFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeFieldReference +import org.opalj.br.fpcf.properties.MutableFieldReference + +/** + * Runs the EagerL0FieldReferenceImmutabilityAnalysis including all analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object FieldReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "determines the immutability of (static/instance) field references" + + override def description: String = + "identifies immutable field references" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + val analysesManager = project.get(FPCFAnalysesManagerKey) + project.get(RTACallGraphKey) + + time { + propertyStore = analysesManager + .runAll( + EagerL0FieldReferenceImmutabilityAnalysis, + LazyL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + + propertyStore.waitOnPhaseCompletion() + + } { t ⇒ + analysisTime = t.toSeconds + } + + val allFieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet + + val groupedResults = propertyStore.entities(FieldReferenceImmutability.key). + filter(field ⇒ allFieldsInProjectClassFiles.contains(field.asInstanceOf[Field])). + toTraversable.groupBy(_.toFinalEP.p) + + val order = (eps1: EPS[Entity, FieldReferenceImmutability], eps2: EPS[Entity, FieldReferenceImmutability]) ⇒ + eps1.e.toString < eps2.e.toString + val mutableReferences = groupedResults(MutableFieldReference).toSeq.sortWith(order) + val notThreadSafeLazyInitializedFieldReferences = groupedResults(LazyInitializedNotThreadSafeFieldReference). + toSeq.sortWith(order) + val lazyInitializedReferencesNotThreadSafeButDeterministic = + groupedResults(LazyInitializedNotThreadSafeButDeterministicFieldReference).toSeq.sortWith(order) + val threadSafeLazyInitializedFieldReferences = groupedResults(LazyInitializedThreadSafeFieldReference).toSeq. + sortWith(order) + val immutableReferences = groupedResults(ImmutableFieldReference).toSeq.sortWith(order) + val output = + s""" + | Mutable Field References: + | ${mutableReferences.mkString(" | Mutable Field Reference \n")} + | + | Lazy Initialized Not Thread Safe Field References: + | ${notThreadSafeLazyInitializedFieldReferences.mkString(" | Not Thread Safe Lazy Initialization \n")} + | + | Lazy Initialized Not Thread Safe But Deterministic References: + | ${ + lazyInitializedReferencesNotThreadSafeButDeterministic. + mkString(" | Lazy Initialized Not Thread Safe But Deterministic Field Reference \n") + } + | + | lazy Initialized Thread Safe References: + | ${threadSafeLazyInitializedFieldReferences.mkString(" | Lazy initialized thread safe field reference \n")} + | + | Immutable Field References: + | ${immutableReferences.mkString(" | Immutable Field Reference \n")} + | + | + | Mutable References: ${mutableReferences.size} + Lazy Initialized References Not Thread : ${notThreadSafeLazyInitializedFieldReferences.size} + | Lazy Initialized References Not Thread Safe But Deterministic: ${ + lazyInitializedReferencesNotThreadSafeButDeterministic.size + } + | Lazy Initialized Thread Safe References: ${threadSafeLazyInitializedFieldReferences.size} + | Immutable References: ${immutableReferences.size} + | + | sum: ${ + mutableReferences.size + notThreadSafeLazyInitializedFieldReferences.size + + lazyInitializedReferencesNotThreadSafeButDeterministic.size + threadSafeLazyInitializedFieldReferences.size + + immutableReferences.size + } + | took : $analysisTime seconds + |""".stripMargin + + val file = new File(s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt") + + val bw = new BufferedWriter(new FileWriter(file)) + + try { + bw.write(output) + bw.close() + } catch { + case e: IOException ⇒ println( + s""" Could not write file: ${file.getName} + | ${e.getMessage} + |""".stripMargin + ) + } finally { + bw.close() + } + + output + } +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo.scala index ec0f165a99..a6db4c2ac6 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo.scala @@ -12,13 +12,13 @@ import org.opalj.br.ClassFile import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.analyses.Project +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.fpcf.analyses.EagerL0ClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0TypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.properties.ClassImmutability -import org.opalj.br.fpcf.properties.FieldMutability +import org.opalj.br.fpcf.properties.FieldImmutability import org.opalj.br.fpcf.properties.TypeImmutability -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.fpcf.analyses.EagerClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyL0FieldMutabilityAnalysis /** * Determines the immutability of the classes of a project. @@ -82,12 +82,13 @@ object ImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val propertyStore = project.get(PropertyStoreKey) time { + propertyStore.setupPhase(Set[PropertyKind]( - FieldMutability.key, ClassImmutability.key, TypeImmutability.key + FieldImmutability.key, ClassImmutability.key, TypeImmutability.key )) - LazyL0FieldMutabilityAnalysis.register(project, propertyStore, null) - EagerClassImmutabilityAnalysis.start(project, propertyStore, null) - EagerTypeImmutabilityAnalysis.start(project, propertyStore, null) + LazyL0FieldImmutabilityAnalysis.register(project, propertyStore, null) + EagerL0ClassImmutabilityAnalysis.start(project, propertyStore, null) + EagerL0TypeImmutabilityAnalysis.start(project, propertyStore, null) propertyStore.waitOnPhaseCompletion() } { r ⇒ analysisTime = r } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PurityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PurityAnalysisDemo.scala index d2a2927db0..5abd38d4ef 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PurityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PurityAnalysisDemo.scala @@ -16,7 +16,7 @@ import org.opalj.br.Field import org.opalj.br.DefinedMethod import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis -import org.opalj.br.fpcf.analyses.LazyL0FieldMutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.Purity @@ -88,7 +88,7 @@ object PurityAnalysisDemo extends ProjectAnalysisApplication { } { r ⇒ setupTime = r } time { - LazyL0FieldMutabilityAnalysis.register(project, propertyStore, null) + LazyL0FieldImmutabilityAnalysis.register(project, propertyStore, null) EagerL0PurityAnalysis.start(project, propertyStore, null) propertyStore.waitOnPhaseCompletion() } { r ⇒ analysisTime = r } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo.scala new file mode 100644 index 0000000000..bafa739dfc --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo.scala @@ -0,0 +1,152 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.analyses + +import java.io.BufferedWriter +import java.io.File +import java.io.FileWriter +import java.net.URL +import java.util.Calendar + +import org.opalj.br.ObjectType +import org.opalj.br.analyses.BasicReport +import org.opalj.br.analyses.Project +import org.opalj.br.analyses.ProjectAnalysisApplication +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.DependentImmutableType +import org.opalj.br.fpcf.properties.MutableType +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import java.io.IOException +import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.fpcf.EPS +import org.opalj.fpcf.Entity + +/** + * Runs the EagerL1TypeImmutabilityAnalysis as well as all analyses needed for improving the result + * + * @author Tobias Roth + */ +object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "determines the immutability of types respecting all possible subtypes" + + override def description: String = "identifies types that are immutable in a shallow or deep way respecting all"+ + "possible subtypes" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + + var propertyStore: PropertyStore = null + + var analysisTime: Seconds = Seconds.None + + val analysesManager = project.get(FPCFAnalysesManagerKey) + + project.get(RTACallGraphKey) + + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, + LazyL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + EagerL1TypeImmutabilityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + + propertyStore.waitOnPhaseCompletion(); + + } { t ⇒ + analysisTime = t.toSeconds + } + + val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet + + val groupedResults = propertyStore.entities(TypeImmutability.key). + filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) + + val order = (eps1: EPS[Entity, TypeImmutability], eps2: EPS[Entity, TypeImmutability]) ⇒ + eps1.e.toString < eps2.e.toString + + val mutableTypes = groupedResults(MutableType).toSeq.sortWith(order) + + val shallowImmutableTypes = groupedResults(ShallowImmutableType).toSeq.sortWith(order) + + val dependentImmutableTypes = groupedResults(DependentImmutableType).toSeq.sortWith(order) + + val deepImmutableTypes = groupedResults(DeepImmutableType).toSeq.sortWith(order) + + val output = + s""" + | Mutable Types: + | ${mutableTypes.mkString(" | Mutable Type\n")} + | + | Shallow Immutable Types: + | ${shallowImmutableTypes.mkString(" | Shallow Immutable Type\n")} + | + | Dependent Immutable Types: + | ${dependentImmutableTypes.mkString(" | Dependent Immutable Type\n")} + | + | Deep Immutable Types: + | ${deepImmutableTypes.mkString(" | Deep Immutable Type\n")} + | + | + | Mutable Types: ${mutableTypes.size} + | Shallow Immutable Types: ${shallowImmutableTypes.size} + | Dependent Immutable Types: ${dependentImmutableTypes.size} + | Deep Immutable Types: ${deepImmutableTypes.size} + | + | sum: ${mutableTypes.size + shallowImmutableTypes.size + dependentImmutableTypes.size + deepImmutableTypes.size} + | took : $analysisTime seconds + |""".stripMargin + + val file = new File(s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt") + + val bw = new BufferedWriter(new FileWriter(file)) + + try { + bw.write(output) + bw.close() + } catch { + case e: IOException ⇒ println( + s""" Could not write file: ${file.getName} + | ${e.getMessage} + |""".stripMargin + ) + } finally { + bw.close() + } + + output + } +} diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/CallGraph.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/CallGraph.scala index 578856e376..a3f6948ed8 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/CallGraph.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/CallGraph.scala @@ -10,7 +10,9 @@ import java.net.URL import java.util.Calendar import scala.collection.JavaConverters._ + import com.typesafe.config.ConfigValueFactory + import org.opalj.log.LogContext import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -35,9 +37,6 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.ai.Domain import org.opalj.ai.domain.RecordDefUse import org.opalj.fpcf.seq.PKESequentialPropertyStore -import org.opalj.log.DevNullLogger -import org.opalj.log.GlobalLogContext -import org.opalj.log.OPALLogger import org.opalj.tac.cg.AllocationSiteBasedPointsToCallGraphKey import org.opalj.tac.cg.CallGraphSerializer import org.opalj.tac.cg.CHACallGraphKey @@ -58,7 +57,7 @@ import org.opalj.tac.fpcf.analyses.pointsto.TamiFlexKey * -algorithm=PointsTo for a points-to based call graph * The default algorithm is RTA. * - * Please also specify whether the target (-cp=) is an application or a library using "-projectConf=". + * Please also specify whether the target (-cp=) is an application or a library using "-projectConfig=". * Predefined configurations `ApplicationProject.conf` or `LibraryProject.conf` can be used here. * * Furthermore, it can be used to print the callees or callers of specific methods. @@ -69,7 +68,7 @@ import org.opalj.tac.fpcf.analyses.pointsto.TamiFlexKey */ object CallGraph extends ProjectAnalysisApplication { - OPALLogger.updateLogger(GlobalLogContext, DevNullLogger) + //OPALLogger.updateLogger(GlobalLogContext, DevNullLogger) override def title: String = "Call Graph Analysis" @@ -144,8 +143,7 @@ object CallGraph extends ProjectAnalysisApplication { if (threads == 0) { org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) } else { - org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = threads - // FIXME: The PKECPropertyStore is broken + org.opalj.fpcf.par.PKECPropertyStore.MaxThreads = threads org.opalj.fpcf.par.PKECPropertyStore(context: _*) } } @@ -457,4 +455,4 @@ object CallGraph extends ProjectAnalysisApplication { projectTime ) } -} +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/FieldMutability.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/FieldMutability.scala index 33572629c8..d590f0a2e2 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/FieldMutability.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/FieldMutability.scala @@ -18,7 +18,7 @@ import org.opalj.br.fpcf.properties.NonFinalFieldByLackOfInformation import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldImmutabilityAnalysis /** * Computes the field mutability; see [[org.opalj.br.fpcf.properties.FieldMutability]] for details. @@ -43,7 +43,7 @@ object FieldMutability extends ProjectAnalysisApplication { LazyUnsoundPrematurelyReadFieldsAnalysis, LazyInterProceduralEscapeAnalysis, LazyL2PurityAnalysis, - EagerL1FieldMutabilityAnalysis + EagerL1FieldImmutabilityAnalysis ) val declaredFinal = ps.finalEntities(DeclaredFinalField).toSeq diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Immutability.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Immutability.scala new file mode 100644 index 0000000000..1f8ccab2eb --- /dev/null +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Immutability.scala @@ -0,0 +1,853 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package support +package info + +import java.nio.file.Path +import java.io.IOException +import java.io.File +import java.io.BufferedWriter +import java.io.FileWriter +import java.util.Calendar +import java.nio.file.FileSystems +import java.net.URL +import com.typesafe.config.ConfigValueFactory +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory + +import org.opalj.br.analyses.BasicReport +import org.opalj.br.analyses.Project.JavaClassFileReader +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.EagerL0FieldReferenceImmutabilityAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.br.ObjectType +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.br.Field +import org.opalj.br.fpcf.properties.DeepImmutableField +import org.opalj.br.fpcf.properties.DependentImmutableField +import org.opalj.br.fpcf.properties.ImmutableFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeFieldReference +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.MutableFieldReference +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.fpcf.Entity +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.DependentImmutableClass +import org.opalj.br.fpcf.properties.DependentImmutableType +import org.opalj.br.fpcf.properties.MutableClass +import org.opalj.br.fpcf.properties.MutableType +import org.opalj.br.fpcf.properties.ShallowImmutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.tac.fpcf.analyses.immutability.LazyL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1TypeImmutabilityAnalysis +import org.opalj.bytecode.JRELibraryFolder +import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.tac.fpcf.analyses.immutability.EagerL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis +import org.opalj.br.analyses.Project +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.PropertyStoreContext +import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis +import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater +import org.opalj.log.LogContext +import org.opalj.tac.cg.AllocationSiteBasedPointsToCallGraphKey +import org.opalj.tac.cg.CHACallGraphKey +import org.opalj.tac.cg.AbstractCallGraphKey +import org.opalj.br.fpcf.properties.ClassImmutability +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.FieldReferenceImmutability +import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.fpcf.EPS +import org.opalj.ai.domain +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.fpcf.OrderedProperty + +/** + * Determines the immutability of field references, fields, classes and types and gives several setting options for the + * evaluation. + * + * @author Tobias Roth + */ +object Immutability { + + sealed trait Analyses + case object FieldReferences extends Analyses + case object Fields extends Analyses + case object Classes extends Analyses + case object Types extends Analyses + case object All extends Analyses + + def evaluate( + cp: File, + analysis: Analyses, + numThreads: Int, + projectDir: Option[String], + libDir: Option[String], + resultsFolder: Path, + withoutJDK: Boolean, + isLibrary: Boolean, + closedWorldAssumption: Boolean, + callGraphKey: AbstractCallGraphKey, + level: Int, + withoutConsiderGenericity: Boolean, + withoutConsiderLazyInitialization: Boolean + ): BasicReport = { + + val classFiles = projectDir match { + case Some(dir) ⇒ JavaClassFileReader().ClassFiles(cp.toPath.resolve(dir).toFile) + case None ⇒ JavaClassFileReader().ClassFiles(cp) + } + + val libFiles = libDir match { + case Some(dir) ⇒ JavaClassFileReader().ClassFiles(cp.toPath.resolve(dir).toFile) + case None ⇒ Traversable.empty + } + + val JDKFiles = + if (withoutJDK) Traversable.empty + else JavaClassFileReader().ClassFiles(JRELibraryFolder) + + // TODO: use variables for the constants + implicit var config: Config = + if (isLibrary) + ConfigFactory.load("LibraryProject.conf") + else + ConfigFactory.load("CommandLineProject.conf") + + // TODO: in case of application this value is already set + if (closedWorldAssumption) { + config = config.withValue( + "org.opalj.br.analyses.cg.ClassExtensibilityKey.analysis", + ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.ClassHierarchyIsNotExtensible") + ) + } + + config = config.withValue( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity", + ConfigValueFactory.fromAnyRef(!withoutConsiderGenericity) + ) + + config = config.withValue( + "org.opalj.fpcf.analyses.L0FieldReferenceImmutabilityAnalysis.considerLazyInitialization", + ConfigValueFactory.fromAnyRef(!withoutConsiderLazyInitialization) + ) + + var projectTime: Seconds = Seconds.None + var analysisTime: Seconds = Seconds.None + var callGraphTime: Seconds = Seconds.None + + val project = time { + Project( + classFiles, + libFiles ++ JDKFiles, + libraryClassFilesAreInterfacesOnly = false, + Traversable.empty + ) + } { t ⇒ + projectTime = t.toSeconds + } + + val fieldReferenceDependencies: List[FPCFAnalysisScheduler] = List( + EagerL0FieldReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + + val fieldDependencies: List[FPCFAnalysisScheduler] = List( + LazyL0FieldReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + EagerL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + + val classDependencies: List[FPCFAnalysisScheduler] = List( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, + LazyL3FieldImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, + EagerL1ClassImmutabilityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + + val typeDependencies: List[FPCFAnalysisScheduler] = List( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, + LazyL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + EagerL1TypeImmutabilityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + + val allImmAnalysisDependencies: List[FPCFAnalysisScheduler] = + List( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + EagerL0FieldReferenceImmutabilityAnalysis, + EagerL3FieldImmutabilityAnalysis, + EagerL1ClassImmutabilityAnalysis, + EagerL1TypeImmutabilityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + + val dependencies = analysis match { + case FieldReferences ⇒ fieldReferenceDependencies + case Fields ⇒ fieldDependencies + case Classes ⇒ classDependencies + case Types ⇒ typeDependencies + case All ⇒ allImmAnalysisDependencies + } + + L2PurityAnalysis.setRater(Some(SystemOutLoggingAllExceptionRater)) + + project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + if (level == 0) + Set[Class[_ <: AnyRef]](classOf[domain.l0.BaseDomainWithDefUse[URL]]) + else if (level == 1) + Set[Class[_ <: AnyRef]](classOf[domain.l1.DefaultDomainWithCFGAndDefUse[URL]]) + else if (level == 2) + Set[Class[_ <: AnyRef]](classOf[domain.l2.DefaultPerformInvocationsDomainWithCFG[URL]]) + else + throw new Exception(s"The level $level does not exist") + } + + project.getOrCreateProjectInformationKeyInitializationData( + PropertyStoreKey, + (context: List[PropertyStoreContext[AnyRef]]) ⇒ { + implicit val lg: LogContext = project.logContext + if (numThreads == 0) { + org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) + } else { + org.opalj.fpcf.par.PKECPropertyStore.MaxThreads = numThreads + org.opalj.fpcf.par.PKECPropertyStore(context: _*) + } + } + ) + time { project.get(callGraphKey) } { t ⇒ + callGraphTime = t.toSeconds + } + var propertyStore: PropertyStore = null + + val analysesManager = project.get(FPCFAnalysesManagerKey) + + time { + propertyStore = analysesManager.runAll(dependencies)._1 + propertyStore.waitOnPhaseCompletion() + } { t ⇒ + analysisTime = t.toSeconds + } + + val stringBuilderResults: StringBuilder = new StringBuilder() + + val allProjectClassTypes = project.allProjectClassFiles.toIterator.map(_.thisType).toSet + + val allFieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet + + val fieldReferenceGroupedResults = propertyStore + .entities(FieldReferenceImmutability.key) + .filter(field ⇒ allFieldsInProjectClassFiles.contains(field.e.asInstanceOf[Field])) + .toTraversable + .groupBy(_.asFinal.p) + + def unpackFieldEPS(eps: EPS[Entity, OrderedProperty]): String = { + if (!eps.e.isInstanceOf[Field]) + throw new Exception(s"${eps.e} is not a field") + val field = eps.e.asInstanceOf[Field] + val fieldName = field.name + val packageName = field.classFile.thisType.packageName.replace("/", ".") + val className = field.classFile.thisType.simpleName + packageName+"."+className+"."+fieldName + } + + val mutableFieldReferences = + fieldReferenceGroupedResults + .getOrElse(MutableFieldReference, Iterator.empty) + .map(unpackFieldEPS) + .toSeq + .sortWith(_ < _) + + val notThreadSafeLazyInitializedFieldReferences = + fieldReferenceGroupedResults + .getOrElse(LazyInitializedNotThreadSafeFieldReference, Iterator.empty) + .toSeq + .map(unpackFieldEPS) + .sortWith(_ < _) + + val lazyInitializedReferencesNotThreadSafeButDeterministic = + fieldReferenceGroupedResults + .getOrElse(LazyInitializedNotThreadSafeButDeterministicFieldReference, Iterator.empty) + .toSeq + .map(unpackFieldEPS) + .sortWith(_ < _) + + val threadSafeLazyInitializedFieldReferences = + fieldReferenceGroupedResults + .getOrElse(LazyInitializedThreadSafeFieldReference, Iterator.empty) + .toSeq + .map(unpackFieldEPS) + .sortWith(_ < _) + + val immutableReferences = fieldReferenceGroupedResults + .getOrElse(ImmutableFieldReference, Iterator.empty) + .toSeq + .map(unpackFieldEPS) + .sortWith(_ < _) + + if (analysis == All || analysis == FieldReferences) { + stringBuilderResults.append( + s""" + | Mutable References: + | ${mutableFieldReferences.map(_+" | Mutable Reference ").mkString("\n")} + | + | Lazy Initialized Not Thread Safe And Not Deterministic Field References: + | ${ + notThreadSafeLazyInitializedFieldReferences + .map(_+" | Lazy Initialized Not Thread Safe And Not Deterministic Field Reference") + .mkString("\n") + } + | + | Lazy Initialized Not Thread Safe But Deterministic Field References: + | ${ + lazyInitializedReferencesNotThreadSafeButDeterministic + .map(_+" | Lazy Initialized Not Thread Safe But Deterministic Field Reference") + .mkString("\n") + } + | + | Lazy Initialized Thread Safe References: + | ${ + threadSafeLazyInitializedFieldReferences + .map(_+" | Lazy Initialized Thread Safe Field Reference") + .mkString("\n") + } + | + | Immutable References: + | ${ + immutableReferences + .map(_+" | immutable field Reference") + .mkString("\n") + } + | + |""".stripMargin + ) + } + + val fieldGroupedResults = propertyStore + .entities(FieldImmutability.key) + .filter(eps ⇒ allFieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field])) + .toTraversable + .groupBy(_.asFinal.p) + + val mutableFields = fieldGroupedResults + .getOrElse(MutableField, Iterator.empty) + .toSeq + .map(unpackFieldEPS) + .sortWith(_ < _) + + val shallowImmutableFields = fieldGroupedResults + .getOrElse(ShallowImmutableField, Iterator.empty) + .toSeq + .map(unpackFieldEPS) + .sortWith(_ < _) + + val dependentImmutableFields = fieldGroupedResults + .getOrElse(DependentImmutableField, Iterator.empty) + .toSeq + .map(unpackFieldEPS) + .sortWith(_ < _) + + val deepImmutableFields = fieldGroupedResults + .getOrElse(DeepImmutableField, Iterator.empty) + .toSeq + .map(unpackFieldEPS) + .sortWith(_ < _) + + if (analysis == All || analysis == Fields) { + stringBuilderResults.append( + s""" + | Mutable Fields: + | ${mutableFields.map(_+" | Mutable Field ").mkString("\n")} + | + | Shallow Immutable Fields: + | ${shallowImmutableFields.map(_+" | Shallow Immutable Field ").mkString("\n")} + | + | Dependent Immutable Fields: + | ${ + dependentImmutableFields + .map(_+" | Dependent Immutable Field ") + .mkString("\n") + } + | + | Deep Immutable Fields: + | ${deepImmutableFields.map(_+" | Deep Immutable Field ").mkString("\n")} + | + |""".stripMargin + ) + } + + val classGroupedResults = propertyStore + .entities(ClassImmutability.key) + .filter(eps ⇒ allProjectClassTypes.contains(eps.e.asInstanceOf[ObjectType])) + .toTraversable + .groupBy(_.asFinal.p) + + def unpackClass(eps: EPS[Entity, OrderedProperty]): String = { + val classFile = eps.e.asInstanceOf[ObjectType] + val className = classFile.simpleName + s"${classFile.packageName.replace("/", ".")}.$className" + } + + val mutableClasses = + classGroupedResults + .getOrElse(MutableClass, Iterator.empty) + .toSeq + .map(unpackClass) + .sortWith(_ < _) + + val shallowImmutableClasses = + classGroupedResults + .getOrElse(ShallowImmutableClass, Iterator.empty) + .toSeq + .map(unpackClass) + .sortWith(_ < _) + + val dependentImmutableClasses = + classGroupedResults + .getOrElse(DependentImmutableClass, Iterator.empty) + .toSeq + .map(unpackClass) + .sortWith(_ < _) + + val deepImmutables = classGroupedResults.getOrElse(DeepImmutableClass, Iterator.empty) + + val allInterfaces = + project.allProjectClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet + + val deepImmutableClassesInterfaces = deepImmutables + .filter(eps ⇒ allInterfaces.contains(eps.e.asInstanceOf[ObjectType])) + .toSeq + .map(unpackClass) + .sortWith(_ < _) + + val deepImmutableClasses = deepImmutables + .filter(eps ⇒ !allInterfaces.contains(eps.e.asInstanceOf[ObjectType])) + .toSeq + .map(unpackClass) + .sortWith(_ < _) + + if (analysis == All || analysis == Classes) { + stringBuilderResults.append( + s""" + | Mutable Classes: + | ${mutableClasses.map(_+" | Mutable Class ").mkString("\n")} + | + | Shallow Immutable Classes: + | ${shallowImmutableClasses.map(_+" | Shallow Immutable Class ").mkString("\n")} + | + | Dependent Immutable Classes: + | ${ + dependentImmutableClasses + .map(_+" | Dependent Immutable Class ") + .mkString("\n") + } + | + | Deep Immutable Classes: + | ${deepImmutableClasses.map(_+" | Deep Immutable Classes ").mkString("\n")} + | + | Deep Immutable Interfaces: + | ${ + deepImmutableClassesInterfaces + .map(_+" | Deep Immutable Interfaces ") + .mkString("\n") + } + |""".stripMargin + ) + } + + val typeGroupedResults = propertyStore + .entities(TypeImmutability.key) + .filter(eps ⇒ allProjectClassTypes.contains(eps.e.asInstanceOf[ObjectType])) + .toTraversable + .groupBy(_.asFinal.p) + + val mutableTypes = typeGroupedResults + .getOrElse(MutableType, Iterator.empty) + .toSeq + .map(unpackClass) + .sortWith(_ < _) + + val shallowImmutableTypes = typeGroupedResults + .getOrElse(ShallowImmutableType, Iterator.empty) + .toSeq + .map(unpackClass) + .sortWith(_ < _) + + val dependentImmutableTypes = typeGroupedResults + .getOrElse(DependentImmutableType, Iterator.empty) + .toSeq + .map(unpackClass) + .sortWith(_ < _) + + val deepImmutableTypes = typeGroupedResults + .getOrElse(DeepImmutableType, Iterator.empty) + .toSeq + .map(unpackClass) + .sortWith(_ < _) + + if (analysis == All || analysis == Types) { + stringBuilderResults.append( + s""" + | Mutable Types: + | ${mutableTypes.map(_+" | Mutable Type ").mkString("\n")} + | + | Shallow Immutable Types: + | ${shallowImmutableTypes.map(_+" | Shallow Immutable Types ").mkString("\n")} + | + | Dependent Immutable Types: + | ${dependentImmutableTypes.map(_+" | Dependent Immutable Types ").mkString("\n")} + | + | Deep Immutable Types: + | ${deepImmutableTypes.map(_+" | Deep Immutable Types ").mkString("\n")} + |""".stripMargin + ) + } + + val stringBuilderNumber: StringBuilder = new StringBuilder + + if (analysis == FieldReferences || analysis == All) { + stringBuilderNumber.append( + s""" + | Mutable References: ${mutableFieldReferences.size} + | Lazy Initialized Not Thread Safe Field References: ${notThreadSafeLazyInitializedFieldReferences.size} + | Lazy Initialized Not Thread Safe But Deterministic Field References: + ${lazyInitializedReferencesNotThreadSafeButDeterministic.size} + | Lazy Initialized Thread Safe Field Reference: ${threadSafeLazyInitializedFieldReferences.size} + | Immutable Field References: ${immutableReferences.size} + | Field References: ${allFieldsInProjectClassFiles.size} + |""".stripMargin + ) + } + + if (analysis == Fields || analysis == All) { + stringBuilderNumber.append( + s""" + | Mutable Fields: ${mutableFields.size} + | Shallow Immutable Fields: ${shallowImmutableFields.size} + | Dependent Immutable Fields: ${dependentImmutableFields.size} + | Deep Immutable Fields: ${deepImmutableFields.size} + | Fields: ${allFieldsInProjectClassFiles.size} + |""".stripMargin + ) + } + + if (analysis == Classes || analysis == All) { + stringBuilderNumber.append( + s""" + | Mutable Classes: ${mutableClasses.size} + | Shallow Immutable Classes: ${shallowImmutableClasses.size} + | Dependent Immutable Classes: ${dependentImmutableClasses.size} + | Deep Immutable Classes: ${deepImmutableClasses.size} + | Deep Immutable Interfaces: ${deepImmutableClassesInterfaces.size} + | Classes: ${allProjectClassTypes.size} + | + |""".stripMargin + ) + } + + if (analysis == Types || analysis == All) + stringBuilderNumber.append( + s""" + | Mutable Types: ${mutableTypes.size} + | Shallow Immutable Types: ${shallowImmutableTypes.size} + | Dependent Immutable Types: ${dependentImmutableTypes.size} + | Deep immutable Types: ${deepImmutableTypes.size} + | Types: ${allProjectClassTypes.size} + |""".stripMargin + ) + + val totalTime = projectTime + callGraphTime + analysisTime + + stringBuilderNumber.append( + s""" + | running ${analysis.toString} analysis + | took: + | $totalTime seconds total time + | $projectTime seconds project time + | $callGraphTime seconds callgraph time + | $analysisTime seconds analysis time + |""".stripMargin + ) + + println( + s""" + | + | ${stringBuilderNumber.toString()} + | + | time results: + | + | $numThreads Threads :: took $analysisTime seconds analysis time + | + | results folder: $resultsFolder + | + | level: ${project.getProjectInformationKeyInitializationData(AIDomainFactoryKey)} + | + | consider escape: ${ + project.config.atKey( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape" + ) + } + | consider genericity: ${ + project.config.atKey( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity" + ) + } + | consider lazy initialization: ${ + project.config.atKey( + "org.opalj.fpcf.analyses.L0FieldReferenceImmutabilityAnalysis.considerLazyInitialization" + ) + } + | + |propertyStore: ${propertyStore.getClass} + | + |""".stripMargin + ) + + val fileNameExtension = { + { + if (withoutConsiderGenericity) { + println("withoutConsiderGenericity") + "_withoutConsiderGenericity" + } else "" + } + { + if (withoutConsiderLazyInitialization) { + println("withoutConsiderLazyInitialization") + "_withoutConsiderLazyInitialization" + } else "" + } + { + if (closedWorldAssumption) { + println("closed world assumption") + "_closedWorldAssumption_" + } else "" + } + { + if (numThreads == 0) "" + else s"_${numThreads}threads" + } + s"_l$level" + + } + + if (resultsFolder != null) { + import java.text.SimpleDateFormat + + val calender = Calendar.getInstance() + calender.add(Calendar.ALL_STYLES, 1) + val date = calender.getTime() + val simpleDateFormat = new SimpleDateFormat("dd_MM_yyyy_HH_mm_ss") + + val file = new File( + s"$resultsFolder/${analysis.toString}_${simpleDateFormat.format(date)}_$fileNameExtension.txt" + ) + + println(s"filepath: ${file.getAbsolutePath}") + val bw = new BufferedWriter(new FileWriter(file)) + + try { + bw.write( + s""" ${stringBuilderResults.toString()} + | + | ${stringBuilderNumber.toString()} + | + | level: $level + | + | jdk folder: $JRELibraryFolder + | + |""".stripMargin + ) + + bw.close() + } catch { + case _: IOException ⇒ println(s"could not write file: ${file.getName}") + } finally { + bw.close() + } + + } + + println(s"propertyStore: ${propertyStore.getClass.toString}") + + println(s"jdk folder: $JRELibraryFolder") + + BasicReport(stringBuilderNumber.toString()) + } + + def main(args: Array[String]): Unit = { + + def usage: String = { + s""" + | Usage: java …ImmutabilityAnalysisEvaluation + | -cp OR -JDK + | -projectDir + | -libDir + | -isLibrary + | [-JDK] (running with the JDK) + | [-analysis ] + | [-threads ] + | [-resultFolder ] + | [-closedWorld] (uses closed world assumption, i.e. no class can be extended) + | [-noJDK] (running without the JDK) + | [-callGraph (Default: RTA) + | [-level] <0|1|2> (domain level Default: 2) + | [-ReImInferComparison] (without transient fields and with eager L2 purity analysis) + | [-withoutConsiderGenericity] + | [-withoutConsiderLazyInitialization] + | [-times <0...n>] (times of execution. n is a natural number) + |""".stripMargin + } + + var i = 0 + var cp: File = null + var resultFolder: Path = null + var numThreads = 0 + var timeEvaluation: Boolean = false + var threadEvaluation: Boolean = false + var projectDir: Option[String] = None + var libDir: Option[String] = None + var withoutJDK: Boolean = false + var closedWorldAssumption = false + var isLibrary = false + var callGraphName: Option[String] = None + var level = 2 + var withoutConsiderLazyInitialization = false + var withoutConsiderGenericity = false + var times = 1 + + def readNextArg(): String = { + i = i + 1 + if (i < args.length) { + args(i) + } else { + println(usage) + throw new IllegalArgumentException(s"missing argument: ${args(i - 1)}") + } + } + + var analysis: Analyses = All + + while (i < args.length) { + args(i) match { + + case "-analysis" ⇒ + val result = readNextArg() + if (result == "All") + analysis = All + else if (result == "FieldReferences") + analysis = FieldReferences + else if (result == "Fields") + analysis = Fields + else if (result == "Classes") + analysis = Classes + else if (result == "Types") + analysis = Types + else { + println(usage) + throw new IllegalArgumentException(s"unknown parameter: $result") + } + + case "-threads" ⇒ numThreads = readNextArg().toInt + case "-cp" ⇒ cp = new File(readNextArg()) + case "-resultFolder" ⇒ resultFolder = FileSystems.getDefault.getPath(readNextArg()) + case "-timeEvaluation" ⇒ timeEvaluation = true + case "-threadEvaluation" ⇒ threadEvaluation = true + case "-projectDir" ⇒ projectDir = Some(readNextArg()) + case "-libDir" ⇒ libDir = Some(readNextArg()) + case "-closedWorld" ⇒ closedWorldAssumption = true + case "-isLibrary" ⇒ isLibrary = true + case "-noJDK" ⇒ withoutJDK = true + case "-callGraph" ⇒ callGraphName = Some(readNextArg()) + case "-level" ⇒ level = Integer.parseInt(readNextArg()) + case "-times" ⇒ times = Integer.parseInt(readNextArg()) + case "-withoutConsiderGenericity" ⇒ withoutConsiderGenericity = true + case "-withoutConsiderLazyInitialization" ⇒ withoutConsiderLazyInitialization = true + + case "-JDK" ⇒ + cp = JRELibraryFolder + withoutJDK = true + + case unknown ⇒ + println(usage) + throw new IllegalArgumentException(s"unknown parameter: $unknown") + } + i += 1 + } + if (!(0 <= level && level <= 2)) + throw new Exception(s"not a domain level: $level") + + val callGraphKey = callGraphName match { + case Some("CHA") ⇒ CHACallGraphKey + case Some("PointsTo") ⇒ AllocationSiteBasedPointsToCallGraphKey + case Some("RTA") | None ⇒ RTACallGraphKey + case Some(a) ⇒ + Console.println(s"unknown call graph analysis: $a") + Console.println(usage) + return ; + } + var nIndex = 1 + while (nIndex <= times) { + println(s"start $nIndex") + nIndex = nIndex + 1 + evaluate( + cp, + analysis, + numThreads, + projectDir, + libDir, + resultFolder, + withoutJDK, + isLibrary, + closedWorldAssumption, + callGraphKey, + level, + withoutConsiderGenericity, + withoutConsiderLazyInitialization + ) + } + } +} diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ImmutabilityAnalysis.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ImmutabilityAnalysis.scala index 46eb6bd799..d72559318b 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ImmutabilityAnalysis.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ImmutabilityAnalysis.scala @@ -10,13 +10,11 @@ import org.opalj.util.Seconds import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis -import org.opalj.br.fpcf.properties.ClassImmutability -import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.br.fpcf.analyses.EagerL0TypeImmutabilityAnalysis import org.opalj.br.ObjectType import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.fpcf.analyses.EagerClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerL0FieldMutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0ClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis /** * Determines the immutability of the classes of a project. @@ -35,6 +33,8 @@ object ImmutabilityAnalysis extends ProjectAnalysisApplication { isInterrupted: () ⇒ Boolean ): BasicReport = { + import org.opalj.br.fpcf.properties.ClassImmutability + import org.opalj.br.fpcf.properties.TypeImmutability import project.get // The following measurements (t) are done such that the results are comparable with the @@ -43,13 +43,13 @@ object ImmutabilityAnalysis extends ProjectAnalysisApplication { val ps = time { val ps = get(PropertyStoreKey) val derivedPKs = Set.empty ++ - EagerL0FieldMutabilityAnalysis.derives.map(_.pk) ++ - EagerClassImmutabilityAnalysis.derives.map(_.pk) ++ - EagerTypeImmutabilityAnalysis.derives.map(_.pk) + EagerL0FieldImmutabilityAnalysis.derives.map(_.pk) ++ + EagerL0ClassImmutabilityAnalysis.derives.map(_.pk) ++ + EagerL0TypeImmutabilityAnalysis.derives.map(_.pk) ps.setupPhase(derivedPKs) - EagerL0FieldMutabilityAnalysis.start(project, ps, null) - EagerClassImmutabilityAnalysis.start(project, ps, null) - EagerTypeImmutabilityAnalysis.start(project, ps, null) + EagerL0FieldImmutabilityAnalysis.start(project, ps, null) + EagerL0ClassImmutabilityAnalysis.start(project, ps, null) + EagerL0TypeImmutabilityAnalysis.start(project, ps, null) ps.waitOnPhaseCompletion() ps } { r ⇒ t = r.toSeconds } diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/PureVoidMethods.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/PureVoidMethods.scala index 11eadf58bd..607f11914b 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/PureVoidMethods.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/PureVoidMethods.scala @@ -12,17 +12,17 @@ import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0ClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0TypeImmutabilityAnalysis import org.opalj.br.fpcf.properties.CompileTimePure import org.opalj.br.fpcf.properties.Pure import org.opalj.br.fpcf.properties.SideEffectFree import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.purity.EagerL2PurityAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL1FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis @@ -53,9 +53,9 @@ object PureVoidMethods extends ProjectAnalysisApplication { LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis, + LazyL1FieldImmutabilityAnalysis, + LazyL0ClassImmutabilityAnalysis, + LazyL0TypeImmutabilityAnalysis, EagerL2PurityAnalysis ) diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Purity.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Purity.scala index 9db09ca681..922a8bdbe6 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Purity.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Purity.scala @@ -25,12 +25,12 @@ import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0ClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis -import org.opalj.br.fpcf.analyses.LazyL0FieldMutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0PurityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0TypeImmutabilityAnalysis import org.opalj.br.fpcf.properties.CompileTimePure import org.opalj.br.fpcf.properties.ContextuallyPure import org.opalj.br.fpcf.properties.ContextuallySideEffectFree @@ -47,9 +47,9 @@ import org.opalj.br.DefinedMethod import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.Project import org.opalj.br.analyses.Project.JavaClassFileReader -import org.opalj.br.fpcf.analyses.EagerClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerL0FieldMutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0ClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0TypeImmutabilityAnalysis import org.opalj.br.fpcf.properties.cg.Callers import org.opalj.br.fpcf.properties.cg.NoCallers import org.opalj.ai.Domain @@ -60,16 +60,13 @@ import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.fpcf.PropertyStoreContext import org.opalj.fpcf.seq.PKESequentialPropertyStore -import org.opalj.log.DevNullLogger -import org.opalj.log.GlobalLogContext import org.opalj.log.LogContext -import org.opalj.log.OPALLogger import org.opalj.tac.cg.AbstractCallGraphKey import org.opalj.tac.cg.AllocationSiteBasedPointsToCallGraphKey import org.opalj.tac.cg.CHACallGraphKey import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL1FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis @@ -79,9 +76,14 @@ import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL1PurityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL2FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1TypeImmutabilityAnalysis /** * Executes a purity analysis (L2 by default) along with necessary supporting analysis. @@ -90,7 +92,7 @@ import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis */ object Purity { - OPALLogger.updateLogger(GlobalLogContext, DevNullLogger) + //OPALLogger.updateLogger(GlobalLogContext, DevNullLogger) def usage: String = { "Usage: java …PurityAnalysisEvaluation \n"+ @@ -146,8 +148,9 @@ object Purity { case None ⇒ Traversable.empty } - val JDKFiles = if (withoutJDK) Traversable.empty - else JavaClassFileReader().ClassFiles(JRELibraryFolder) + val JDKFiles = + if (withoutJDK) Traversable.empty + else JavaClassFileReader().ClassFiles(JRELibraryFolder) val dirName = if (cp eq JRELibraryFolder) "JDK" else cp.getName val projectEvalDir = evaluationDir.map(new File(_, dirName)) @@ -159,10 +162,11 @@ object Purity { var callGraphTime: Seconds = Seconds.None // TODO: use variables for the constants - implicit var config: Config = if (isLibrary) - ConfigFactory.load("LibraryProject.conf") - else - ConfigFactory.load("ApplicationProject.conf") + implicit var config: Config = + if (isLibrary) + ConfigFactory.load("LibraryProject.conf") + else + ConfigFactory.load("CommandLineProject.conf") // TODO: in case of application this value is already set if (closedWorldAssumption) { @@ -186,7 +190,9 @@ object Purity { libraryClassFilesAreInterfacesOnly = false, Traversable.empty ) - } { t ⇒ projectTime = t.toSeconds } + } { t ⇒ + projectTime = t.toSeconds + } project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { case None ⇒ Set(domain) @@ -200,15 +206,16 @@ object Purity { if (numThreads == 0) { org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) } else { - org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = numThreads - // FIXME: this property store is broken + org.opalj.fpcf.par.PKECPropertyStore.MaxThreads = numThreads org.opalj.fpcf.par.PKECPropertyStore(context: _*) } } ) PropertyStore.updateDebug(debug) - val ps = time { project.get(PropertyStoreKey) } { t ⇒ propertyStoreTime = t.toSeconds } + val ps = time { project.get(PropertyStoreKey) } { t ⇒ + propertyStoreTime = t.toSeconds + } analysis match { case LazyL0PurityAnalysis ⇒ @@ -226,12 +233,16 @@ object Purity { time { project.get(callGraphKey) - } { t ⇒ callGraphTime = t.toSeconds } + } { t ⇒ + callGraphTime = t.toSeconds + } val reachableMethods = - ps.entities(Callers.key).collect { - case FinalEP(e: DeclaredMethod, c: Callers) if c ne NoCallers ⇒ e - }.toSet + ps.entities(Callers.key) + .collect { + case FinalEP(e: DeclaredMethod, c: Callers) if c ne NoCallers ⇒ e + } + .toSet val analyzedMethods = projMethods.filter(reachableMethods.contains) @@ -239,15 +250,18 @@ object Purity { val analyses = analysis :: support manager.runAll( - analyses, - { css: Chain[ComputationSpecification[FPCFAnalysis]] ⇒ - if (css.contains(analysis)) { - analyzedMethods.foreach { dm ⇒ ps.force(dm, br.fpcf.properties.Purity.key) } + analyses, { css: Chain[ComputationSpecification[FPCFAnalysis]] ⇒ + if (css.contains(analysis)) { + analyzedMethods.foreach { dm ⇒ + ps.force(dm, br.fpcf.properties.Purity.key) } } + } ) - } { t ⇒ analysisTime = t.toSeconds } + } { t ⇒ + analysisTime = t.toSeconds + } ps.shutdown() val purityEs = ps(analyzedMethods, br.fpcf.properties.Purity.key).filter { @@ -262,16 +276,32 @@ object Purity { val compileTimePure = purityEs.collect { case FinalEP(m: DefinedMethod, CompileTimePure) ⇒ m } val pure = purityEs.collect { case FinalEP(m: DefinedMethod, Pure) ⇒ m } val sideEffectFree = purityEs.collect { case FinalEP(m: DefinedMethod, SideEffectFree) ⇒ m } - val externallyPure = purityEs.collect { case FinalEP(m: DefinedMethod, ContextuallyPure(p)) if isExternal(m, p) ⇒ m } - val externallySideEffectFree = purityEs.collect { case FinalEP(m: DefinedMethod, ContextuallySideEffectFree(p)) if isExternal(m, p) ⇒ m } - val contextuallyPure = purityEs.collect { case FinalEP(m: DefinedMethod, ContextuallyPure(p)) if !isExternal(m, p) ⇒ (m, p) } - val contextuallySideEffectFree = purityEs.collect { case FinalEP(m: DefinedMethod, ContextuallySideEffectFree(p)) if !isExternal(m, p) ⇒ (m, p) } + val externallyPure = purityEs.collect { + case FinalEP(m: DefinedMethod, ContextuallyPure(p)) if isExternal(m, p) ⇒ m + } + val externallySideEffectFree = purityEs.collect { + case FinalEP(m: DefinedMethod, ContextuallySideEffectFree(p)) if isExternal(m, p) ⇒ m + } + val contextuallyPure = purityEs.collect { + case FinalEP(m: DefinedMethod, ContextuallyPure(p)) if !isExternal(m, p) ⇒ (m, p) + } + val contextuallySideEffectFree = purityEs.collect { + case FinalEP(m: DefinedMethod, ContextuallySideEffectFree(p)) if !isExternal(m, p) ⇒ (m, p) + } val dPure = purityEs.collect { case FinalEP(m: DefinedMethod, DPure) ⇒ m } val dSideEffectFree = purityEs.collect { case FinalEP(m: DefinedMethod, DSideEffectFree) ⇒ m } - val dExternallyPure = purityEs.collect { case FinalEP(m: DefinedMethod, DContextuallyPure(p)) if isExternal(m, p) ⇒ m } - val dExternallySideEffectFree = purityEs.collect { case FinalEP(m: DefinedMethod, DContextuallySideEffectFree(p)) if isExternal(m, p) ⇒ m } - val dContextuallyPure = purityEs.collect { case FinalEP(m: DefinedMethod, DContextuallyPure(p)) if !isExternal(m, p) ⇒ (m, p) } - val dContextuallySideEffectFree = purityEs.collect { case FinalEP(m: DefinedMethod, DContextuallySideEffectFree(p)) if !isExternal(m, p) ⇒ (m, p) } + val dExternallyPure = purityEs.collect { + case FinalEP(m: DefinedMethod, DContextuallyPure(p)) if isExternal(m, p) ⇒ m + } + val dExternallySideEffectFree = purityEs.collect { + case FinalEP(m: DefinedMethod, DContextuallySideEffectFree(p)) if isExternal(m, p) ⇒ m + } + val dContextuallyPure = purityEs.collect { + case FinalEP(m: DefinedMethod, DContextuallyPure(p)) if !isExternal(m, p) ⇒ (m, p) + } + val dContextuallySideEffectFree = purityEs.collect { + case FinalEP(m: DefinedMethod, DContextuallySideEffectFree(p)) if !isExternal(m, p) ⇒ (m, p) + } val lbImpure = purityEs.collect { case FinalEP(m: DefinedMethod, ImpureByAnalysis) ⇒ m } if (projectEvalDir.isDefined) { @@ -324,7 +354,9 @@ object Purity { try { if (runtimeNew) { runtime.createNewFile() - runtimeWriter.println("analysisName;project time;propertyStore time;callGraph time;analysis time; total time;") + runtimeWriter.println( + "analysisName;project time;propertyStore time;callGraph time;analysis time; total time;" + ) } runtimeWriter.println(s"$projectTime;$propertyStoreTime;$callGraphTime;$analysisTime") } finally { @@ -340,13 +372,15 @@ object Purity { if (resultsNew) { results.createNewFile() if (!individual) - resultsWriter.println("compile time pure;pure;domain-specific pure;"+ - "side-effect free;domain-specific side-effect free;"+ - "externally pure;domain-specific externally pure;"+ - "externally side-effect free; domain-specific externally side-effect "+ - "free;contextually pure;domain-specific contextually pure;"+ - "contextually side-effect free;domain-specific contextually "+ - "side-effect free;impure;count") + resultsWriter.println( + "compile time pure;pure;domain-specific pure;"+ + "side-effect free;domain-specific side-effect free;"+ + "externally pure;domain-specific externally pure;"+ + "externally side-effect free; domain-specific externally side-effect "+ + "free;contextually pure;domain-specific contextually pure;"+ + "contextually side-effect free;domain-specific contextually "+ + "side-effect free;impure;count" + ) } if (!individual) { @@ -387,19 +421,25 @@ object Purity { resultsWriter.println(s"${m.definedMethod.toJava} => externally side-effect free") } for (m ← dExternallySideEffectFree) { - resultsWriter.println(s"${m.definedMethod.toJava} => domain-specific externally side-effect free") + resultsWriter.println( + s"${m.definedMethod.toJava} => domain-specific externally side-effect free" + ) } for ((m, p) ← contextuallyPure) { resultsWriter.println(s"${m.definedMethod.toJava} => contextually pure: $p") } for ((m, p) ← dContextuallyPure) { - resultsWriter.println(s"${m.definedMethod.toJava} => domain-specific contextually pure: $p") + resultsWriter.println( + s"${m.definedMethod.toJava} => domain-specific contextually pure: $p" + ) } for ((m, p) ← contextuallySideEffectFree) { resultsWriter.println(s"${m.definedMethod.toJava} => contextually side-effect free: $p") } for ((m, p) ← dContextuallySideEffectFree) { - resultsWriter.println(s"${m.definedMethod.toJava} => domain-specific contextually side-effect free: $p") + resultsWriter.println( + s"${m.definedMethod.toJava} => domain-specific contextually side-effect free: $p" + ) } for (m ← lbImpure) { resultsWriter.println(s"${m.definedMethod.toJava} => impure") @@ -531,13 +571,14 @@ object Purity { Console.println(usage) return ; } - - if (eager) { - support ::= EagerClassImmutabilityAnalysis - support ::= EagerTypeImmutabilityAnalysis - } else { - support ::= LazyClassImmutabilityAnalysis - support ::= LazyTypeImmutabilityAnalysis + if (!fieldMutabilityAnalysisName.contains("L3")) { + if (eager) { + support ::= EagerL0ClassImmutabilityAnalysis + support ::= EagerL0TypeImmutabilityAnalysis + } else { + support ::= LazyL0ClassImmutabilityAnalysis + support ::= LazyL0TypeImmutabilityAnalysis + } } escapeAnalysisName match { @@ -548,7 +589,6 @@ object Purity { support ::= LazyInterProceduralEscapeAnalysis case Some("none") ⇒ - case Some(a) ⇒ Console.println(s"unknown escape analysis: $a") Console.println(usage) @@ -556,29 +596,41 @@ object Purity { } fieldMutabilityAnalysisName match { - case Some("L0") if eager ⇒ support ::= EagerL0FieldMutabilityAnalysis + case Some("L0") if eager ⇒ support ::= EagerL0FieldImmutabilityAnalysis - case Some("L0") ⇒ support ::= LazyL0FieldMutabilityAnalysis + case Some("L0") ⇒ support ::= LazyL0FieldImmutabilityAnalysis - case Some("L1") if eager ⇒ support ::= EagerL1FieldMutabilityAnalysis + case Some("L1") if eager ⇒ support ::= EagerL1FieldImmutabilityAnalysis - case Some("L1") ⇒ support ::= LazyL1FieldMutabilityAnalysis + case Some("L1") ⇒ support ::= LazyL1FieldImmutabilityAnalysis case Some("L2") if eager ⇒ - support ::= EagerL2FieldMutabilityAnalysis + support ::= EagerL2FieldImmutabilityAnalysis support ::= EagerUnsoundPrematurelyReadFieldsAnalysis case Some("L2") ⇒ - support ::= LazyL2FieldMutabilityAnalysis + support ::= LazyL2FieldImmutabilityAnalysis support ::= LazyUnsoundPrematurelyReadFieldsAnalysis - case Some("none") ⇒ + case Some("L3") ⇒ + support ::= LazyL3FieldImmutabilityAnalysis + support ::= LazyUnsoundPrematurelyReadFieldsAnalysis - case None ⇒ analysis match { - case LazyL0PurityAnalysis ⇒ LazyL0FieldMutabilityAnalysis - case LazyL1PurityAnalysis ⇒ LazyL1FieldMutabilityAnalysis - case LazyL2PurityAnalysis ⇒ LazyL1FieldMutabilityAnalysis - } + if (eager) { + support ::= EagerL1ClassImmutabilityAnalysis + support ::= EagerL1TypeImmutabilityAnalysis + } else { + support ::= LazyL1ClassImmutabilityAnalysis + support ::= LazyL1TypeImmutabilityAnalysis + } + + case Some("none") ⇒ + case None ⇒ + analysis match { + case LazyL0PurityAnalysis ⇒ LazyL0FieldImmutabilityAnalysis + case LazyL1PurityAnalysis ⇒ LazyL1FieldImmutabilityAnalysis + case LazyL2PurityAnalysis ⇒ LazyL1FieldImmutabilityAnalysis + } case Some(a) ⇒ Console.println(s"unknown field mutability analysis: $a") diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/UnusedResults.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/UnusedResults.scala index eda6cb272e..9c8db20658 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/UnusedResults.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/UnusedResults.scala @@ -27,8 +27,8 @@ import org.opalj.br.fpcf.properties.VirtualMethodPurity.VPure import org.opalj.br.fpcf.properties.VirtualMethodPurity.VSideEffectFree import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0ClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0TypeImmutabilityAnalysis import org.opalj.br.fpcf.properties.CompileTimePure import org.opalj.br.fpcf.properties.Pure import org.opalj.br.fpcf.properties.SideEffectFree @@ -44,7 +44,7 @@ import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL1FieldImmutabilityAnalysis import org.opalj.tac.fpcf.properties.TACAI /** @@ -80,9 +80,9 @@ object UnusedResults extends ProjectAnalysisApplication { LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis, + LazyL1FieldImmutabilityAnalysis, + LazyL0ClassImmutabilityAnalysis, + LazyL0TypeImmutabilityAnalysis, EagerL2PurityAnalysis ) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayClasses.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayClasses.java new file mode 100644 index 0000000000..d48ec6b566 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayClasses.java @@ -0,0 +1,137 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.arrays; + +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("") +@MutableClass("") +public class ArrayClasses { + + @ShallowImmutableField("The elements of the array are manipulated after initialization and can escape.") + @ImmutableFieldReference("The array is eager initialized.") + private Object[] b = new Object[]{1, 2, 3, 4, 5}; + + public Object[] getB() { + b[2] = 2; + return b; + } + + @MutableField("Array has a mutable reference.") + @MutableFieldReference("The array is initalized always when the InitC function is called") + private Object[] c; + + public void InitC() { + c = new Object[]{1, 2, 3}; + } + + @ShallowImmutableField("The elements of the array can escape.") + @ImmutableFieldReference("The array is eager initialized.") + private Object[] d = new Object[]{1, 2, 3, 4, 5,}; + + public Object[] getD() { + return d; + } + + @ShallowImmutableField("The elements of the array can escape.") + @LazyInitializedThreadSafeFieldReference("The array is initialized lazily.") + private Object[] e; + + public synchronized Object[] getE() { + Object[] tmp; + if (e == null) { + tmp = new Object[3]; + for (int i = 0; i < 3; i++) + tmp[i] = i; + this.e = tmp; + } + return this.e; + } + + @MutableField("The reference is seen as mutable.") + @LazyInitializedNotThreadSafeFieldReference("The array is initialized lazily but not thread safe.") + private Object[] f; + + public void getF() { + if (f == null) { + f = new Object[]{1, 2, 3}; + } + } + + + @ShallowImmutableField("One element of the array is written after initialization.") + @LazyInitializedThreadSafeFieldReference("The array is initialized lazily and thread safe.") + private Object[] g; + + public synchronized void getG() { + if (g == null) + g = new Object[]{1, 2, 4, 5}; + g[2] = 2; + } + + @LazyInitializedNotThreadSafeFieldReference("The array is not thread-safe lazily initialized.") + private int[] m; + + public int[] getM() { + if(m==null) + m = new int[]{1,2,3}; + return m; + } + + + @LazyInitializedNotThreadSafeFieldReference("The array is not thread-safe lazily initialized.") + private Object[] n; + + public Object[] getN(){ //Object[] + if(n==null) + n = new Object[]{new Object(), new Object(), new Object()}; + n[2] = new Object(); + return n; + } + + @ShallowImmutableField("The elements of the array can escape.") + @LazyInitializedThreadSafeFieldReference("The array is initialized thread safe and lazily.") + private Object[] o; + + public synchronized Object[] getO(){ + if(o==null) + o = new Object[]{new Object(), new Object(), new Object()}; + o[2] = new Object(); + return o; + } + + @ShallowImmutableField("One element of the array can escape") + @LazyInitializedThreadSafeFieldReference("The array is thread safe lazily intialized.") + private Object[] p; + public synchronized Object getP(){ + if(p==null) + p = new Object[]{new Object(), new Object(), new Object()}; + return p[2]; + } + + + @ShallowImmutableField("escaping") + private String[] stringArray; + + @ShallowImmutableField("escaping") + private int[] intArray; + + @ShallowImmutableField("escaping") + private Object[] oArr; + + @ShallowImmutableField("escaping") + private T[] tArray; + + ArrayClasses(String[] stringArray, int[] intArray, Object[] oArr, T[] tArray) { + this.stringArray = stringArray; + this.intArray = intArray; + this.oArr = oArr; + this.tArray = tArray; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayDeepImmutability.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayDeepImmutability.java new file mode 100644 index 0000000000..dfd57bd8ce --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayDeepImmutability.java @@ -0,0 +1,76 @@ +package org.opalj.fpcf.fixtures.benchmark.arrays; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.types.DeepImmutableType; + +//@Immutable +@DeepImmutableType("") +@DeepImmutableClass("") +public class ArrayDeepImmutability { + + @DeepImmutableField("The elements of the array can not escape") + @ImmutableFieldReference("Array is eager initialized") + private Object[] zzz = new Object[]{1, 2, 3}; + + @DeepImmutableField("The elements of the array can not escape") + @ImmutableFieldReference("Array is initialized in the constructor") + private Object[] a; + + public ArrayDeepImmutability() { + a = new Object[]{5, 6, 7, 8}; + } + + @DeepImmutableField("The elements of the array can not escape.") + @LazyInitializedThreadSafeFieldReference("The array is initialized thread safen and eagerly.") + private Object[] j; + + public synchronized void getJ(){ + if(j==null) + j = new Object[]{new Object(), new Object(), new Object()}; + } + + + @DeepImmutableField("The elements of the array can not escape") + @ImmutableFieldReference("The array is not initialized.") + private Object[] k; + + + @DeepImmutableField("The elements of the array can not escape.") + @ImmutableFieldReference("The array is initialized eagerly.") + private Object[] h = new Object[]{new Object(), new Object(), new Object()}; + + + @DeepImmutableField("The elements of the array can escape, but have a deep immutable reference.") + @LazyInitializedThreadSafeFieldReference("The array is thread safe lazily intialized.") + private Integer[] q; + public synchronized Integer getQ(){ + if(q==null) + q = new Integer[]{new Integer(1), new Integer(2), new Integer(3)}; + return q[2]; + } + + @DeepImmutableField("The elements of the array can escape.") + @LazyInitializedThreadSafeFieldReference("The array is thread-safely lazily initialized") + private Object[] arr; + + public synchronized Object[] getI(){ + if(arr==null) + arr = new Object[]{new Object(), new Object(), new Object()}; + return arr; + } + + @DeepImmutableField("") + @ImmutableFieldReference("Reference is only initialized once") + private Object[] array1 = new Object[]{new Object(), new Object(), new Object()}; //TODO + + + @DeepImmutableField("") + @ImmutableFieldReference("") + private Object[] clonedArray = new Object[]{new Object(), new Object(), new Object()}; + + public Object[] getClonedArray(){ return clonedArray.clone(); } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayInitialization.java new file mode 100644 index 0000000000..3ceacf1907 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayInitialization.java @@ -0,0 +1,45 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.arrays; + +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("") +@MutableClass("") +public class ArrayInitialization { + + @MutableField("") + @MutableFieldReference("") + private Object[] array; + + public Object[] getArray(int n) { + if (array == null || array.length < n) { + this.array = new Object[n]; + } + return array; + } + + @MutableField("") + @MutableFieldReference("") + private Object[] b; + + public Object[] getB(boolean flag) throws Exception { + if(b!=null) + return b; + else if(flag) + return b; //throw new Exception(""); + else { + this.b = new Object[5]; + return b; + } + } +} + + + + + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayWithOneEscapingObject.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayWithOneEscapingObject.java new file mode 100644 index 0000000000..a3144cddcf --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayWithOneEscapingObject.java @@ -0,0 +1,50 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.arrays; + +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("It has a mutable state") +@MutableClass("It has a mutable state") +public class ArrayWithOneEscapingObject { + + @MutableField("Reference of the field is mutable") + @MutableFieldReference("Field is public") + public Object o = new Object(); + + @ShallowImmutableField("Field is initialized with an Shallow immutable field") + @ImmutableFieldReference("Field is only initialized once.") + private Object[] array2; + + public ArrayWithOneEscapingObject() { + array2 = new Object[]{o}; + } + + @ShallowImmutableField("Field is initialized with a shallow immutable field.") + @LazyInitializedThreadSafeFieldReference("Synchronized method with a guard-statement around the write") + private Object[] array3; + + public synchronized void initArray3(Object o){ + if(array3==null) + array3 = new Object[]{o}; + } + + @ShallowImmutableField("An array element escapes") + @LazyInitializedThreadSafeFieldReference("Synchronized method, with guarding if-statement.") + private Object[] array4; + + public synchronized Object initArray4(Object o){ + + Object tmp0 = new Object(); + + if(array4==null) + array4 = new Object[]{tmp0}; + + return tmp0; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/PrivateFinalArrayEscapesViaConstructor.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/PrivateFinalArrayEscapesViaConstructor.java new file mode 100644 index 0000000000..680c63c9d9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/PrivateFinalArrayEscapesViaConstructor.java @@ -0,0 +1,41 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.arrays; + +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.ShallowImmutableType; + +@ShallowImmutableType("") +@ShallowImmutableClass("") +public final class PrivateFinalArrayEscapesViaConstructor { + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private final char[] charArray; + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private final byte[] byteArray; + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private final int[] intArray; + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private final long[] longArray; + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private final Object[] objectArray; + + public PrivateFinalArrayEscapesViaConstructor(char[] charArray, byte[] byteArray, int[] intArray, + long[] longArray, Object[] objectArray) { + this.charArray = charArray; + this.byteArray = byteArray; + this.intArray = intArray; + this.longArray = longArray; + this.objectArray = objectArray; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/CloneAssignable.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/CloneAssignable.java new file mode 100644 index 0000000000..a466fcaa5b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/CloneAssignable.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.assignability; + +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("") +@MutableClass("") +public class CloneAssignable { + + @MutableField("") + @MutableFieldReference("") + int i; + + @MutableField("") + @MutableFieldReference("") + CloneAssignable instance; + + public CloneAssignable clone(){ + CloneAssignable c = new CloneAssignable(); + c.i = 5; + c.i = i; + instance = c; + c.i = 6; + return c; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/CloneNonAssignable.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/CloneNonAssignable.java new file mode 100644 index 0000000000..82f6a13eea --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/CloneNonAssignable.java @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.assignability; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.DeepImmutableType; + +//@Immutable +@DeepImmutableType("") +@DeepImmutableClass("") +public final class CloneNonAssignable { + + @DeepImmutableField("") + @ImmutableFieldReference("") + int i; + + public CloneNonAssignable clone(){ + CloneNonAssignable c = new CloneNonAssignable(); + c.i = i; + return c; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/DifferentAssignmentPossibilitiesOfEffectivelyNonAssignableField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/DifferentAssignmentPossibilitiesOfEffectivelyNonAssignableField.java new file mode 100644 index 0000000000..c7ac118508 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/DifferentAssignmentPossibilitiesOfEffectivelyNonAssignableField.java @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.assignability; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.fixtures.benchmark.generals.EmptyClass; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +//@Immutable +@MutableType("") +@DeepImmutableClass("") +public class DifferentAssignmentPossibilitiesOfEffectivelyNonAssignableField { + + @DeepImmutableField("") + @ImmutableFieldReference("") + private Object o; + + public DifferentAssignmentPossibilitiesOfEffectivelyNonAssignableField() { + this.o = new EmptyClass(); + } + + public DifferentAssignmentPossibilitiesOfEffectivelyNonAssignableField(int n) { + this.o = new Object(); + } + + public Object getO(){ + return this.o; + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/DifferentAssignmentPossibilitiesOfNonAssignableField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/DifferentAssignmentPossibilitiesOfNonAssignableField.java new file mode 100644 index 0000000000..1eb9dbe8e3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/DifferentAssignmentPossibilitiesOfNonAssignableField.java @@ -0,0 +1,32 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.assignability; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.fixtures.benchmark.generals.EmptyClass; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +//@Immutable +@MutableType("") +@DeepImmutableClass("") +public class DifferentAssignmentPossibilitiesOfNonAssignableField { + + + @DeepImmutableField("") + @ImmutableFieldReference("") + private final Object o; + + public DifferentAssignmentPossibilitiesOfNonAssignableField() { + this.o = new EmptyClass(); + } + + public DifferentAssignmentPossibilitiesOfNonAssignableField(int n) { + this.o = new Object(); + } + + public Object getO(){ + return this.o; + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/NonAssignability.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/NonAssignability.java new file mode 100644 index 0000000000..5a6769244a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/NonAssignability.java @@ -0,0 +1,48 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.assignability; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +//@Immutable +@MutableType("") +@DeepImmutableClass("") +public class NonAssignability { + + @DeepImmutableField("") + @ImmutableFieldReference("") + public final Object publicObject = new Object(); + + @DeepImmutableField("") + @ImmutableFieldReference("") + protected final Object protectedObject = new Object(); + + @DeepImmutableField("") + @ImmutableFieldReference("") + final Object packagePrivateObject = new Object(); + + @DeepImmutableField("") + @ImmutableFieldReference("Initialized directly") + private final int a = 1; + + @DeepImmutableField("") + @ImmutableFieldReference("Initialized through instance initializer") + private final int b; + + @DeepImmutableField("") + @ImmutableFieldReference("Initialized through constructor") + private final int c; + + public NonAssignability() { + c=1; + } + + // Instance initializer! + { + b = 1; + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/Singleton.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/Singleton.java new file mode 100644 index 0000000000..a213101e8f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/Singleton.java @@ -0,0 +1,46 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.assignability; + +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("") +@MutableClass("") +public class Singleton { + + @MutableField("") + @MutableFieldReference("written by static initializer after the field becomes (indirectly) readable") + private String name; + + @DeepImmutableField("") + @ImmutableFieldReference("only initialized once by the constructor") + private Object mutex = new Object(); + + private Singleton() { + this.name = ""; + } + + public String getName() { + synchronized (mutex) { + return name; + } + } + + // STATIC FUNCTIONALITY + @ImmutableFieldReference("only set in the static initializer") + private static Singleton theInstance; + + static { + theInstance = new Singleton(); + theInstance.name = "The Singleton Instance"; + } + + public static Singleton getInstance() { + return theInstance; + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/TriviallyEffectivelyNonAssignable.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/TriviallyEffectivelyNonAssignable.java new file mode 100644 index 0000000000..f90c8462a8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/TriviallyEffectivelyNonAssignable.java @@ -0,0 +1,37 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.assignability; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.ShallowImmutableType; + +//@Immutable +@ShallowImmutableType("") +@ShallowImmutableClass("") +public final class TriviallyEffectivelyNonAssignable { + + + @DeepImmutableField("") + @ImmutableFieldReference("") + private int n = 5; + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private Object escapingViaConstructor; + + @DeepImmutableField("") + @ImmutableFieldReference("") + private Object escapingViaGetter = new Object(); + + + public TriviallyEffectivelyNonAssignable(Object o) { + this.escapingViaConstructor = o; + } + + public Object getObject(){ + return escapingViaGetter; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/concrete_class_type_is_known/ConcreteAssignedInstanceTypeUnknown.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/concrete_class_type_is_known/ConcreteAssignedInstanceTypeUnknown.java new file mode 100644 index 0000000000..bcb4649ca4 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/concrete_class_type_is_known/ConcreteAssignedInstanceTypeUnknown.java @@ -0,0 +1,20 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.concrete_class_type_is_known; + +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.ShallowImmutableType; + +@ShallowImmutableType("") +@ShallowImmutableClass("") +final class ConcreteAssignedInstanceTypeUnknown { + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private Object object; + + public ConcreteAssignedInstanceTypeUnknown(Object object) { + this.object = object; + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/concrete_class_type_is_known/ConcreteObjectInstanceAssigned.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/concrete_class_type_is_known/ConcreteObjectInstanceAssigned.java new file mode 100644 index 0000000000..3b3380df68 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/concrete_class_type_is_known/ConcreteObjectInstanceAssigned.java @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.concrete_class_type_is_known; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +//@Immutable +@MutableType("") +@DeepImmutableClass("") +class ConcreteObjectInstanceAssigned { + + @DeepImmutableField(value = "concrete object is known") + @ImmutableFieldReference("") + private Object object = new Object(); + + public Object getObject() { + return this.object; + } + +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/ClassWithMutableField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/ClassWithMutableField.java new file mode 100644 index 0000000000..ca50f3dc4d --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/ClassWithMutableField.java @@ -0,0 +1,11 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generals; + +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("") +@MutableClass("") +public class ClassWithMutableField { + public int n = 5; +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/ClassWithNotDeepImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/ClassWithNotDeepImmutableFields.java new file mode 100644 index 0000000000..692a6d7389 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/ClassWithNotDeepImmutableFields.java @@ -0,0 +1,38 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generals; + +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("") +@MutableClass("") +public class ClassWithNotDeepImmutableFields { + + @ShallowImmutableField("field has an immutable field reference and mutable type") + @ImmutableFieldReference("declared final reference") + private final ClassWithNotDeepImmutableFields cwpf = new ClassWithNotDeepImmutableFields(new ClassWithMutableField()); + + public ClassWithNotDeepImmutableFields getTmc() { + return cwpf; + } + + @ShallowImmutableField("immutable field reference and mutable type ClassWithPublicFields") + @ImmutableFieldReference("declared final field") + private final ClassWithMutableField tmc; + + public ClassWithNotDeepImmutableFields(ClassWithMutableField tmc){ + this.tmc = tmc; + } + + @MutableField(value = "field is public") + @MutableFieldReference(value = "field is public") + public int n = 0; + + @MutableField(value = "field is public") + @MutableFieldReference(value = "field is public") + public String name = "name"; +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/ClassWithProtectedFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/ClassWithProtectedFields.java new file mode 100644 index 0000000000..17d2364ad1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/ClassWithProtectedFields.java @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generals; + +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("") +@MutableClass("") +class ClassWithProtectedFields { + + @MutableField("the field has a mutable field reference") + @MutableFieldReference("the field is protected") + protected FinalEmptyClass fec1 = new FinalEmptyClass(); + + @MutableField("Because of Mutable Reference") + @MutableFieldReference("Because it is declared as protected") + protected ClassWithNotDeepImmutableFields cwpf1 = new ClassWithNotDeepImmutableFields(new ClassWithMutableField()); + + @ShallowImmutableField("field has an immutable reference and mutable type") + @ImmutableFieldReference("Declared final Field") + private final ClassWithNotDeepImmutableFields cwpf2 = new ClassWithNotDeepImmutableFields(new ClassWithMutableField()); + + @DeepImmutableField("immutable reference and deep immutable field type") + @ImmutableFieldReference("Declared final Field") + private final FinalEmptyClass fec2 = new FinalEmptyClass(); +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/DeepImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/DeepImmutableFields.java new file mode 100644 index 0000000000..a19da05bc5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/DeepImmutableFields.java @@ -0,0 +1,40 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generals; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.DeepImmutableType; + +//@Immutable +@DeepImmutableType("") +@DeepImmutableClass("") +public final class DeepImmutableFields { + + @DeepImmutableField("Immutable Reference and Immutable Field Type") + @ImmutableFieldReference("declared final field") + private final FinalEmptyClass fec = new FinalEmptyClass(); + + public FinalEmptyClass getFec() { + return fec; + } + + @DeepImmutableField("Immutable Reference and Immutable Field Type") + @ImmutableFieldReference("effective immutable field") + private FinalEmptyClass name = new FinalEmptyClass(); + + @DeepImmutableField("immutable reference and deep immutable field type") + @ImmutableFieldReference(value = "declared final field reference") + private final FinalEmptyClass fec1; + + public DeepImmutableFields(FinalEmptyClass fec) { + this.fec1 = fec; + } + +} + + + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/DifferentModifier.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/DifferentModifier.java new file mode 100644 index 0000000000..f81af27f2f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/DifferentModifier.java @@ -0,0 +1,109 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generals; + +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +/** + * This testclass tests that different modifiers like transient, volatile or static + * does not have an impact of mutability. + * + * @author Tobias Roth + * + */ +@MutableType("") +@MutableClass("") +public class DifferentModifier { + + @MutableField(value = "field has a mutable field reference") + @MutableFieldReference(value = "field is public") + public int mutableInt = 5; + + //@Immutable + @DeepImmutableField("") + @ImmutableFieldReference("") + private int immutableInt = 3; + + @MutableField(value = "field has a mutable field reference") + @MutableFieldReference(value = "field is public") + public transient int mutableTransientInt = 5; + + //@Immutable + @DeepImmutableField("") + @ImmutableFieldReference("") + private transient int immutableTransientInt = 5; + + @MutableField(value = "field has a mutable field reference") + @MutableFieldReference(value = "field is public") + public volatile int mutableVolatileInt = 5; + + //@Immutable + @DeepImmutableField("") + @ImmutableFieldReference("") + private volatile int immutableVolatileInt = 5; + + @MutableField(value = "field has a mutable field reference") + @MutableFieldReference(value = "field is public") + public volatile long mutableVolatileLong; + + //@Immutable + @DeepImmutableField("") + @ImmutableFieldReference("") + private volatile long immutableVolatileLong = 0L; + + DifferentModifier(long l){ + this.mutableVolatileLong = l; + } + + static final class InnerClass { + + @MutableField(value = "field has a mutable field reference") + @MutableFieldReference(value = "field is public") + public static int mutableInnerStaticInt = 1; + + //@Immutable + @DeepImmutableField("") + @ImmutableFieldReference("") + private static int immutableInnerStaticInt = 1; + + @MutableField(value = "field has a mutable field reference") + @MutableFieldReference(value = "field is public") + public int mutableInnerInt = 5; + + //@Immutable + @DeepImmutableField("") + @ImmutableFieldReference("") + private int immutableInnerInt = 5; + + @MutableField(value = "field has a mutable field reference") + @MutableFieldReference(value = "field is public") + public transient int mutableInnerTransientInt = 5; + + //@Immutable + @DeepImmutableField("") + @ImmutableFieldReference("") + private transient int immutableInnerTransientInt = 5; + + @MutableField(value = "field has a mutable field reference") + @MutableFieldReference(value = "field is public") + public volatile int mutableInnerVolatileInt = 5; + + //@Immutable + @DeepImmutableField("") + @ImmutableFieldReference("") + private volatile int immutableInnerVolatileInt = 5; + + @MutableField(value = "field has a mutable field reference") + @MutableFieldReference(value = "field is public") + public volatile transient int mutableInnerVolatileTransientInt = 5; + + //@Immutable + @DeepImmutableField("") + @ImmutableFieldReference("") + private volatile transient int immutableInnerVolatileTransientInt = 5; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/EmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/EmptyClass.java new file mode 100644 index 0000000000..a2b23ffc49 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/EmptyClass.java @@ -0,0 +1,12 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generals; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +//@Immutable +@MutableType("") +@DeepImmutableClass("Class has no fields and, thus, no state") +public class EmptyClass { +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/FinalEmptyClass.java new file mode 100644 index 0000000000..8cc2e1368b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/FinalEmptyClass.java @@ -0,0 +1,12 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generals; + +//import edu.cmu.cs.glacier.qual.Immutable; + +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.types.DeepImmutableType; + +//@Immutable +@DeepImmutableType("") +@DeepImmutableClass("") +public final class FinalEmptyClass {} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/Interface.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/Interface.java new file mode 100644 index 0000000000..9271d8bc80 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/Interface.java @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generals; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.MutableClass; + +//@Immutable +@MutableClass("") +@DeepImmutableClass("As an interface has no state, it is deeply immutable.") +public interface Interface { + + public void run(); + + public void stop(); + + public void reset(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/Mutability.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/Mutability.java new file mode 100644 index 0000000000..02ed86c4da --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/Mutability.java @@ -0,0 +1,39 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generals; + +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("") +@MutableClass("") +public class Mutability { + @MutableFieldReference("") + private Object o = new Object(); + + public void setO(){ + this.o = o; + } + + @MutableField("") + @MutableFieldReference("The field can be incremented") + private int i; + + @MutableField("") + @MutableFieldReference("") + private int n = 5; + + public void setN(int n){ + this.n = n; + } + + public void nop(){ + } + + public static void updateI(Mutability s) { + if (s != null) { + s.i += 1; + } + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/StaticFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/StaticFields.java new file mode 100644 index 0000000000..aec01ce5dc --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/StaticFields.java @@ -0,0 +1,46 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generals; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +//@Immutable +@MutableType("") +@DeepImmutableClass("") +public class StaticFields { + + @MutableFieldReference("") + public static String name = "Class with static fields"; + + @ImmutableFieldReference("") + private static Object[] shallowArray = new Object[5]; + + @DeepImmutableField("") + @ImmutableFieldReference("") + private static String deepImmutableString = "string"; + + @MutableFieldReference("") + private static int manualIncrementingCounter; + + @MutableFieldReference("") + private static int manualCounter; + + @MutableFieldReference("") + private static int instanceCounter; + + StaticFields() { + instanceCounter = instanceCounter + 1; + } + + public void incrementCounter() { + manualIncrementingCounter = manualIncrementingCounter + 1; + } + + public void setCounter(int n){ + manualCounter = n; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/Generic.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/Generic.java new file mode 100644 index 0000000000..90b3640884 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/Generic.java @@ -0,0 +1,19 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generic; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DependentImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +//@Immutable +@MutableType("") +@DependentImmutableClass("") +public class Generic { + + @DependentImmutableField("") + @ImmutableFieldReference("") + T t; + public Generic(T t){this.t = t;} +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericCounterExampleDeep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericCounterExampleDeep.java new file mode 100644 index 0000000000..15b406788a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericCounterExampleDeep.java @@ -0,0 +1,11 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generic; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; + +//@Immutable +@DeepImmutableClass("") +class GenericCounterExampleDeep { + private int n = 5; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericCounterExampleMutable.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericCounterExampleMutable.java new file mode 100644 index 0000000000..5a0a5b1095 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericCounterExampleMutable.java @@ -0,0 +1,9 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generic; + +import org.opalj.fpcf.properties.immutability.classes.MutableClass; + +@MutableClass("") +class GenericCounterExampleMutable { + public int n = 5; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericFields.java new file mode 100644 index 0000000000..3df66280c8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericFields.java @@ -0,0 +1,33 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generic; + +import org.opalj.fpcf.fixtures.benchmark.generals.ClassWithMutableField; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("") +@ShallowImmutableClass("") +class GenericFields { + + @DependentImmutableField("") + private Generic generic; + + @ShallowImmutableField("") + private Generic mutable = new Generic(new ClassWithMutableField()); + + @DependentImmutableField("") + private Generic> nestedDependent; + + @ShallowImmutableField("") + private Generic> nestedShallow = + new Generic<>(new Generic<>(new ClassWithMutableField())); + + public GenericFields(T t){ + this.generic = new Generic<>(t); + this.nestedDependent = new Generic<>(new Generic<>(t)); + } + +} + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericFieldsDeep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericFieldsDeep.java new file mode 100644 index 0000000000..e5b6d3ad6e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericFieldsDeep.java @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generic; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +//@Immutable +@MutableType("") +@DeepImmutableClass("") +class GenericFieldsDeep { + + @DeepImmutableField("") + private Generic> nestedDeep = new Generic<>(new Generic<>(new FinalEmptyClass())); + + @DeepImmutableField("") + Generic fecG = new Generic<>(new FinalEmptyClass()); + +} + +final class FinalEmptyClass {} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/LowerUpperBounds.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/LowerUpperBounds.java new file mode 100644 index 0000000000..2a958fe2c8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/LowerUpperBounds.java @@ -0,0 +1,17 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.generic; + +import org.opalj.fpcf.fixtures.benchmark.generals.ClassWithMutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; + +class LowerUpperBounds { + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private T t; + + public LowerUpperBounds(T t){ + this.t = t; + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLocking.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLocking.java new file mode 100644 index 0000000000..cfa644f822 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLocking.java @@ -0,0 +1,511 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; + +import java.util.Random; + +/** + * This classes encompasses different variation of double checked locking as a form of lazy initialization. + * + * @author Tobias Roth + * + */ +public class DoubleCheckedLocking { + + //@Immutable + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("") + private Object o; + + public synchronized Object getO(){ + if(this.o==null) + this.o = new Object(); + return this.o; + } + + @LazyInitializedThreadSafeFieldReference("standard double checked locked initialized int field") + private int doubleCheckedLockedIntField; + + public int getDoubleCheckedLockedIntField() { + if(doubleCheckedLockedIntField==0){ + synchronized(this) { + if(doubleCheckedLockedIntField==0){ + doubleCheckedLockedIntField = 42; + } + } + } + return doubleCheckedLockedIntField; + } + + @LazyInitializedThreadSafeFieldReference("field write is not deterministic but only once written due to dcl") + private int initializedWithDCLRandomWrite; + + public int getInitializedWithDCLRandomWrite(){ + if(initializedWithDCLRandomWrite==0){ + synchronized(this){ + if(initializedWithDCLRandomWrite==0){ + initializedWithDCLRandomWrite = new Random().nextInt(); + } + } + } + return initializedWithDCLRandomWrite; + } + + @LazyInitializedNotThreadSafeFieldReference("The field is not thread safe and not deterministic written") + private int notThreadSafeRandomWrite; + + public int getNotThreadSafeRandomWrite(){ + if(notThreadSafeRandomWrite==0){ + notThreadSafeRandomWrite = new Random().nextInt(); + } + return notThreadSafeRandomWrite; + } + + @LazyInitializedThreadSafeFieldReference("dcl is implemented with early return") + private Object dclWithEarlyReturn; + public Object getDclWithEarlyReturn(){ + if(dclWithEarlyReturn!=null) + return dclWithEarlyReturn; + synchronized(this) { + if(dclWithEarlyReturn==null) + dclWithEarlyReturn = new Object(); + } + return dclWithEarlyReturn; + } + + + @LazyInitializedThreadSafeFieldReference("write within a dcl and try catch") + private Object dclWithTryCatch; + + public Object DCL5(){ + if(dclWithTryCatch==null){ + synchronized(this){ + if(dclWithTryCatch==null){ + try{ + dclWithTryCatch = new Object(); + } + catch (Exception e) + { + throw e; + } + } + } + } + return dclWithTryCatch; + } + + @MutableFieldReference("no correct lazy initialization because all exceptions are caught in the inner guard") + Object noDCLAllExceptionsCaughtInsidInnerGuard; + + public Object getNoDCL(){ + if(noDCLAllExceptionsCaughtInsidInnerGuard==null){ + synchronized(this){ + if(noDCLAllExceptionsCaughtInsidInnerGuard==null){ + try{ + noDCLAllExceptionsCaughtInsidInnerGuard = new Object(); + } + catch(Exception e){ + + } + } + } + } + return noDCLAllExceptionsCaughtInsidInnerGuard; + } + + @MutableFieldReference("No correct lazy initialization because all exceptions are caught in the complete dcl") + Object noDCLAllExceptionsAreCaughtInTheCompleteDCL; + + public Object getNoDCLAllExceptionsAreCaughtInTheCompleteDCL(){ + try{ + if(noDCLAllExceptionsAreCaughtInTheCompleteDCL ==null){ + synchronized(this){ + if(noDCLAllExceptionsAreCaughtInTheCompleteDCL ==null){ + noDCLAllExceptionsAreCaughtInTheCompleteDCL = new Object(); + } + + } + } + } + catch(Exception e){ + } + return noDCLAllExceptionsAreCaughtInTheCompleteDCL; + } + + @MutableFieldReference("no correct dcl pattern because all exceptions are caught in the outer guard") + Object instance; + + public Object NoDCL1(){ + + if(instance==null){ + try{ + synchronized(this){ + if(instance==null){ + + instance = new Object(); + } + }}catch(Exception e){}} + return instance; + } + + + @MutableFieldReference("no correct dcl, because the two try-catch-blocks") + Object noDCLTwoTryCatchBlocks; + + public Object getNoDCLTwoTryCatchBlocks(){ + + if(noDCLTwoTryCatchBlocks==null){ + try{ + synchronized(this){ + if(noDCLTwoTryCatchBlocks==null){ + try{ + noDCLTwoTryCatchBlocks = new Object(); + } + catch (Exception e) + { + throw e; + } + } + } + } + catch(Exception e){ + + } + } + return noDCLTwoTryCatchBlocks; + } + + @MutableFieldReference("no correct dcl because wrong exception forwarding") + Object noDCLWrongExceptionForwarding; + + public Object getNoDCLWrongExceptionForwarding() throws IndexOutOfBoundsException{ + if(noDCLWrongExceptionForwarding==null){ + synchronized(this){ + if(noDCLWrongExceptionForwarding==null){ + try{ + noDCLWrongExceptionForwarding = new Object(); + } + catch (Exception e) + { + throw new IndexOutOfBoundsException(); + } + } + } + } + return noDCLWrongExceptionForwarding; + } + + + @LazyInitializedThreadSafeFieldReference("standard dcl initialization of array reference") + private Object[] lazyInitializedArrayReference; + + public Object[] getLazyInitializedArrayReference() { + if(lazyInitializedArrayReference==null){ + synchronized(this) { + if(lazyInitializedArrayReference==null){ + lazyInitializedArrayReference = new Object[10]; + } + } + } + return lazyInitializedArrayReference; + } + + @LazyInitializedNotThreadSafeFieldReference("The field is not thread safe lazy initialized but within a guard") + private int[] notThreadSafeLazyInitializedArray; + + public int[] getValues(){ + if(notThreadSafeLazyInitializedArray==null){ + notThreadSafeLazyInitializedArray = new int[] {1,2,3,4,5,6,7,8,9,10}; + } + return notThreadSafeLazyInitializedArray; + } + + @LazyInitializedThreadSafeFieldReference("the field is guarded initialized within a synchronized method") + + private int[] synchronizedArrayInitialization; + + public synchronized int[] getSynchronizedArrayInitialization(){ + if(synchronizedArrayInitialization==null){ + synchronizedArrayInitialization = new int[] {1,2,3,4,5,6,7,8,9,10}; + } + return synchronizedArrayInitialization; + } + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized method") + private Integer synchronizedSimpleLazyInitializedIntegerField; + + public synchronized void initNO2(){ + if(synchronizedSimpleLazyInitializedIntegerField==0) + synchronizedSimpleLazyInitializedIntegerField = 5; + } + + @LazyInitializedNotThreadSafeFieldReference("not synchronized") + private Object notSynchronized; + + public Object getNotSynchronized() { + if(notSynchronized==null) + notSynchronized = new Object(); + return notSynchronized; + } + + + @LazyInitializedThreadSafeFieldReference(value = "standard double checked locking") + private Object standardDCL; + public Object getStandardDCL() { + if(standardDCL ==null){ + synchronized(this) { + if(standardDCL ==null){ + standardDCL = new Object(); + } + } + } + return standardDCL; + } + + + @LazyInitializedNotThreadSafeFieldReference("the field write is not synchronized guarded") + private Object noSynchronizedGuard; + public Object getNoSynchronizedGuard() { + if(noSynchronizedGuard ==null){ + synchronized(Object.class) { + noSynchronizedGuard = new Object(); + } + } + return noSynchronizedGuard; + } + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("a simple variation of double checked locking without the outer guard") + private Object onlyOneGuardWithinTheSynchronization; + + public Object getOnlyOneGuardWithinTheSynchronization() { + synchronized(Object.class) { + if(onlyOneGuardWithinTheSynchronization ==null){ + onlyOneGuardWithinTheSynchronization = new Object(); + } + } + return onlyOneGuardWithinTheSynchronization; + } + + @LazyInitializedNotThreadSafeFieldReference(value = "the field write is not synchronized guarded two times") + private Object twoNotSynchronizedGuards; + public Object getTwoNotSynchronizedGuards() { + if(twoNotSynchronizedGuards ==null){ + if(twoNotSynchronizedGuards ==null){ + twoNotSynchronizedGuards = new Object(); + } + } + return twoNotSynchronizedGuards; + } + + @LazyInitializedNotThreadSafeFieldReference("the field is written outside the synchronized block") + private Object writeOutsideTheSynchronizedGuard; + public Object getWriteOutsideTheSynchronizedGuard() { + if(writeOutsideTheSynchronizedGuard ==null){ + synchronized(this) { + if(writeOutsideTheSynchronizedGuard ==null){ + + } + writeOutsideTheSynchronizedGuard = new Object(); + } + + } + return writeOutsideTheSynchronizedGuard; + } + + @MutableFieldReference("no valid lazy initialization") + private Object multipleWritesWithinTheDCLPattern; + public Object getMultipleWritesWithinTheDCLPattern() { + if(multipleWritesWithinTheDCLPattern ==null){ + synchronized(this) { + if(multipleWritesWithinTheDCLPattern ==null){ + multipleWritesWithinTheDCLPattern = new Object(); + } + } + multipleWritesWithinTheDCLPattern = new Object(); + } + return multipleWritesWithinTheDCLPattern; + } + + @MutableFieldReference("no valid lazy initialization") + private Object alsoWrittenOutsideTheDCLPattern; + public Object getAlsoWrittenOutsideTheDCLPattern() { + if(alsoWrittenOutsideTheDCLPattern ==null){ + synchronized(this) { + if(alsoWrittenOutsideTheDCLPattern ==null){ + alsoWrittenOutsideTheDCLPattern = new Object(); + } + } + } + alsoWrittenOutsideTheDCLPattern = new Object(); + return alsoWrittenOutsideTheDCLPattern; + } + + @MutableFieldReference(value = "no lazy initialization due to wrong guard statements") + private Object wrongGuardStatement; + + public Object getWrongGuardStatement() { + if (wrongGuardStatement != null) { + synchronized (this) { + if (wrongGuardStatement != null) { + wrongGuardStatement = new Object(); + } + } + } + return wrongGuardStatement; + } + + @MutableFieldReference(value = "no valid lazy initialization") + private Object writeOutsideDCL; + + public Object getWriteOutsideDCL() { + if (writeOutsideDCL == null) { + synchronized (this) { + if (writeOutsideDCL == null) { + } + } + } + writeOutsideDCL = new Object(); + return writeOutsideDCL; + } + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("dcl pattern with loops in it") + private Object loopsInDCLPattern; + public Object getLoopsInDCLPattern() { + for(int i=0; i<1000; i++){} + if(loopsInDCLPattern ==null){ + for(int i=0; i<1000; i++){} + synchronized(this) { + for(int i=0; i<1000; i++){} + if(loopsInDCLPattern ==null){ + for(int i=0; i<1000; i++){} + loopsInDCLPattern = new Object(); + } + } + } + return loopsInDCLPattern; + } + + @LazyInitializedThreadSafeFieldReference("no correct complecte dcl pattern but sufficient for thread " + + "safety due to a correct guard in a synchronized block") + private Object outerGuardEndsBeforeSynchronization; + public Object getOuterGuardEndsBeforeSynchronization() { + if(outerGuardEndsBeforeSynchronization ==null){ + } + synchronized(this) { + if(outerGuardEndsBeforeSynchronization ==null){ + outerGuardEndsBeforeSynchronization = new Object(); + } + } + return outerGuardEndsBeforeSynchronization; + } + + @LazyInitializedNotThreadSafeFieldReference(value = "only a guard around the field write") + private Object notNestedDCLWriteOnlyInGuard; + public Object getNotNestedDCLWriteOnlyInGuard() { + if(notNestedDCLWriteOnlyInGuard ==null){ + } + synchronized(this) { + } + if(notNestedDCLWriteOnlyInGuard ==null){ + notNestedDCLWriteOnlyInGuard = new Object(); + } + return notNestedDCLWriteOnlyInGuard; + } + + @MutableFieldReference("field write outside guards and synchronized blocks") + private Object notNestedDCLWriteOutside; + public Object getNotNestedDCLWriteOutside() { + if(notNestedDCLWriteOutside ==null){ + } + synchronized(this) { + } + if(notNestedDCLWriteOutside ==null){ + + } + notNestedDCLWriteOutside = new Object(); + return notNestedDCLWriteOutside; + } + + + @LazyInitializedThreadSafeFieldReference("") + private Object multipleGuardsInDCLPattern; + public Object getMultipleGuardsInDCLPattern() { + if(multipleGuardsInDCLPattern ==null){ + if(multipleGuardsInDCLPattern ==null) { + if (multipleGuardsInDCLPattern == null) { + synchronized (this) { + if (multipleGuardsInDCLPattern == null) { + if (multipleGuardsInDCLPattern == null) { + multipleGuardsInDCLPattern = new Object(); + } + } + } + } + } + } + return multipleGuardsInDCLPattern; + } + + @MutableFieldReference("guard not only dependent on field value") + private Object dclWithLockAnd; + + public Object getDclWithLockAnd(boolean lock) { + if(dclWithLockAnd ==null && lock){ + synchronized(this) { + if(dclWithLockAnd ==null && lock){ + dclWithLockAnd = new Object(); + } + } + } + return dclWithLockAnd; + } + + @MutableFieldReference("guard not only dependent on field value") + private Object dclWithLockOr; + + public Object getDclWithLockOr(boolean lock) { + if(dclWithLockOr ==null && lock){ + synchronized(this) { + if(dclWithLockOr ==null && lock){ + dclWithLockOr = new Object(); + } + } + } + return dclWithLockOr; + } + + @LazyInitializedNotThreadSafeFieldReference(value = "the field read for the guard is outside the synchronized block") + private Object fieldReadOutsideSynchronizedBlock; + + public Object getFieldReadOutsideSynchronizedBlock(){ + Object tmpo1 = fieldReadOutsideSynchronizedBlock; + synchronized (this){ + if(tmpo1==null) + fieldReadOutsideSynchronizedBlock = tmpo1 = new Object(); + } + return fieldReadOutsideSynchronizedBlock; + } + + @LazyInitializedNotThreadSafeFieldReference(value = "the field read for the guard is outside the synchronized block") + private Object fieldReadOutsideSynchronizedBlockEarlyReturn; + + public Object getFieldReadOutsideSynchronizedBlockEarlyReturn(){ + Object tmpo2 = fieldReadOutsideSynchronizedBlockEarlyReturn; + if(tmpo2!=null) + return fieldReadOutsideSynchronizedBlockEarlyReturn; + synchronized (this){ + if(tmpo2==null) + fieldReadOutsideSynchronizedBlockEarlyReturn = new Object(); + } + return fieldReadOutsideSynchronizedBlockEarlyReturn; + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingClassWithStaticFieldsDeep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingClassWithStaticFieldsDeep.java new file mode 100644 index 0000000000..f001a6c5d9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingClassWithStaticFieldsDeep.java @@ -0,0 +1,27 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +//@Immutable +@MutableType("") +@DeepImmutableClass("") +public class DoubleCheckedLockingClassWithStaticFieldsDeep { + + @LazyInitializedThreadSafeFieldReference("standard dcl pattern within a static method") + private static Object instance; + + public static Object getInstance() { + if (instance == null) { + synchronized (Object.class) { + if (instance == null) { + instance = new Object(); + } + } + } + return instance; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingDeep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingDeep.java new file mode 100644 index 0000000000..1882829cea --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingDeep.java @@ -0,0 +1,186 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; + +import java.util.Random; + +/** + * This classes encompasses different variation of double checked locking as a form of lazy initialization. + * + * @author Tobias Roth + * + */ +//@Immutable +public class DoubleCheckedLockingDeep { + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("") + private Object o; + + public synchronized Object getO(){ + if(this.o==null) + this.o = new Object(); + return this.o; + } + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("standard double checked locked initialized int field") + private int doubleCheckedLockedIntField; + + public int getDoubleCheckedLockedIntField() { + if(doubleCheckedLockedIntField==0){ + synchronized(this) { + if(doubleCheckedLockedIntField==0){ + doubleCheckedLockedIntField = 42; + } + } + } + return doubleCheckedLockedIntField; + } + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("field write is not deterministic but only once written due to dcl") + private int initializedWithDCLRandomWrite; + + public int getInitializedWithDCLRandomWrite(){ + if(initializedWithDCLRandomWrite==0){ + synchronized(this){ + if(initializedWithDCLRandomWrite==0){ + initializedWithDCLRandomWrite = new Random().nextInt(); + } + } + } + return initializedWithDCLRandomWrite; + } + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("dcl is implemented with early return") + private Object dclWithEarlyReturn; + public Object getDclWithEarlyReturn(){ + if(dclWithEarlyReturn!=null) + return dclWithEarlyReturn; + synchronized(this) { + if(dclWithEarlyReturn==null) + dclWithEarlyReturn = new Object(); + } + return dclWithEarlyReturn; + } + + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("write within a dcl and try catch") + private Object dclWithTryCatch; + + public Object DCL5(){ + if(dclWithTryCatch==null){ + synchronized(this){ + if(dclWithTryCatch==null){ + try{ + dclWithTryCatch = new Object(); + } + catch (Exception e) + { + throw e; + } + } + } + } + return dclWithTryCatch; + } + + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized method") + private Integer synchronizedSimpleLazyInitializedIntegerField; + + public synchronized void initNO2(){ + if(synchronizedSimpleLazyInitializedIntegerField==0) + synchronizedSimpleLazyInitializedIntegerField = 5; + } + + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference(value = "standard double checked locking") + private Object standardDCL; + public Object getStandardDCL() { + if(standardDCL ==null){ + synchronized(this) { + if(standardDCL ==null){ + standardDCL = new Object(); + } + } + } + return standardDCL; + } + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("a simple variation of double checked locking without the outer guard") + private Object onlyOneGuardWithinTheSynchronization; + + public Object getOnlyOneGuardWithinTheSynchronization() { + synchronized(Object.class) { + if(onlyOneGuardWithinTheSynchronization ==null){ + onlyOneGuardWithinTheSynchronization = new Object(); + } + } + return onlyOneGuardWithinTheSynchronization; + } + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("dcl pattern with loops in it") + private Object loopsInDCLPattern; + public Object getLoopsInDCLPattern() { + for(int i=0; i<1000; i++){} + if(loopsInDCLPattern ==null){ + for(int i=0; i<1000; i++){} + synchronized(this) { + for(int i=0; i<1000; i++){} + if(loopsInDCLPattern ==null){ + for(int i=0; i<1000; i++){} + loopsInDCLPattern = new Object(); + } + } + } + return loopsInDCLPattern; + } + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("no correct complecte dcl pattern but sufficient for thread " + + "safety due to a correct guard in a synchronized block") + private Object outerGuardEndsBeforeSynchronization; + public Object getOuterGuardEndsBeforeSynchronization() { + if(outerGuardEndsBeforeSynchronization ==null){ + } + synchronized(this) { + if(outerGuardEndsBeforeSynchronization ==null){ + outerGuardEndsBeforeSynchronization = new Object(); + } + } + return outerGuardEndsBeforeSynchronization; + } + + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("") + private Object multipleGuardsInDCLPattern; + public Object getMultipleGuardsInDCLPattern() { + if(multipleGuardsInDCLPattern ==null){ + if(multipleGuardsInDCLPattern ==null) { + if (multipleGuardsInDCLPattern == null) { + synchronized (this) { + if (multipleGuardsInDCLPattern == null) { + if (multipleGuardsInDCLPattern == null) { + multipleGuardsInDCLPattern = new Object(); + } + } + } + } + } + } + return multipleGuardsInDCLPattern; + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/EffectivelyImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/EffectivelyImmutableFields.java new file mode 100644 index 0000000000..fdd4330435 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/EffectivelyImmutableFields.java @@ -0,0 +1,455 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; + +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; + +import java.util.*; + +public class EffectivelyImmutableFields { + + @DeepImmutableField("field value has a primitive type and an immutable field reference") + @ImmutableFieldReference("field is not written after initialization") + private int simpleInitializedFieldWithPrimitiveType = 5; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private int lazyInitializedFieldWithPrimitiveType; + + public synchronized void initLazyInitializedFieldWithPrimitiveType(){ + if(lazyInitializedFieldWithPrimitiveType == 0) + lazyInitializedFieldWithPrimitiveType = 5; + } + + @DeepImmutableField("Lazy initialized field with primitive type.") + @LazyInitializedThreadSafeFieldReference("field is thread safely lazy initialized") + private int inTheGetterLazyInitializedFieldWithPrimitiveType; + + public synchronized int getInTheGetterLazyInitializedFieldWithPrimitiveType(){ + if(inTheGetterLazyInitializedFieldWithPrimitiveType ==0) + inTheGetterLazyInitializedFieldWithPrimitiveType = 5; + return inTheGetterLazyInitializedFieldWithPrimitiveType; + } + + @DeepImmutableField(value = "immutable reference and deep immutable type") + @ImmutableFieldReference(value = "effective immutable field") + private Integer effectiveImmutableIntegerField = 5; + + @LazyInitializedNotThreadSafeFieldReference(value = "write of reference objects is not atomic") + private Integer simpleLazyInitializedIntegerField; + + public void initSimpleLazyInitializedIntegerField(){ + if(simpleLazyInitializedIntegerField==0) + simpleLazyInitializedIntegerField = 5; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized method") + private Integer synchronizedSimpleLazyInitializedIntegerField; + + public synchronized void initNO2(){ + if(synchronizedSimpleLazyInitializedIntegerField==0) + synchronizedSimpleLazyInitializedIntegerField = 5; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private Integer inGetterSynchronizedSimpleLazyInitializedIntegerField; + + public synchronized Integer getInGetterSynchronizedSimpleLazyInitializedIntegerField(){ + if(inGetterSynchronizedSimpleLazyInitializedIntegerField==0) + inGetterSynchronizedSimpleLazyInitializedIntegerField = 5; + return inGetterSynchronizedSimpleLazyInitializedIntegerField; + } + + @DeepImmutableField("field value has a primitive type and an immutable field reference") + @ImmutableFieldReference("field is effective immutable") + private double effectiveImmutableDoubleField = 5d; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private double synchronizedLazyInitializedDoubleField; + + public synchronized void initD2(){ + if(synchronizedLazyInitializedDoubleField ==0d) + synchronizedLazyInitializedDoubleField = 5d; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private double inGetterSynchronizedLazyInitializedDoubleField; + + public synchronized double getD3(){ + if(inGetterSynchronizedLazyInitializedDoubleField==0d) + inGetterSynchronizedLazyInitializedDoubleField = 5; + return inGetterSynchronizedLazyInitializedDoubleField; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @ImmutableFieldReference("field is effective immutable") + private Double effectiveImmutableObjectDoubleField = 5d; + + @DeepImmutableField("field has an immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private Double lazyInitializedObjectDoubleField; + + public synchronized void initLazyInitializedObjectDoubleField(){ + if(lazyInitializedObjectDoubleField==0) + lazyInitializedObjectDoubleField = 5d; + } + + @DeepImmutableField("field has an immutable reference and a deep immutable type") + @LazyInitializedThreadSafeFieldReference("field is in a synchronized getter lazy initialized") + private Double inAGetterLazyInitializedObjectDoubleField; + + public synchronized Double getInAGetterLazyInitializedObjectDoubleField(){ + if(inAGetterLazyInitializedObjectDoubleField==0) + inAGetterLazyInitializedObjectDoubleField = 5d; + return inAGetterLazyInitializedObjectDoubleField; + } + + @DeepImmutableField("field value has a primitive type and an immutable field reference") + @ImmutableFieldReference(value = "field is not written after initialization") + private float effectiveImmutableFloatField = 5; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private float synchronizedLazyInitializedFloatField; + + public synchronized void initF2(){ + if(synchronizedLazyInitializedFloatField ==0) + synchronizedLazyInitializedFloatField = 5f; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private float inGetterSynchronizedLazyInitializedFloatField; + + public synchronized float getf3(){ + if(inGetterSynchronizedLazyInitializedFloatField==0) + inGetterSynchronizedLazyInitializedFloatField = 5f; + return inGetterSynchronizedLazyInitializedFloatField; + } + + @DeepImmutableField("field has an immutable field reference and a deep immutable type") + @ImmutableFieldReference("the field reference is effective immutable") + private Float effectiveImmutableFloatObjectField = 5f; + + @DeepImmutableField("field has an immutable field reference and a deep immutable type") + @LazyInitializedThreadSafeFieldReference("the field is thread safely lazy initialized") + private Float lazyInitializedFloatObjectField; + + public synchronized void initFO2(){ + if(lazyInitializedFloatObjectField ==0) + lazyInitializedFloatObjectField = 5f; + } + + @DeepImmutableField("field has an immutable field reference and a deep immutable type") + @LazyInitializedThreadSafeFieldReference("the field is in a getter thread safely lazy initialized") + private float inAGetterLazyInitializedFloatObjectField; + + public synchronized Float getInAGetterLazyInitializedFloatObjectField(){ + if(inAGetterLazyInitializedFloatObjectField==0) + inAGetterLazyInitializedFloatObjectField = 5f; + return inAGetterLazyInitializedFloatObjectField; + } + + @DeepImmutableField("field value has a primitive type and an immutable field reference") + @ImmutableFieldReference("field is effective immutable") + private byte effectiveImmutableByteField = 5; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safely lazy initialized") + private byte synchronizedLazyInitializedByteField; + + public synchronized void initB2(){ + if(synchronizedLazyInitializedByteField ==0) + synchronizedLazyInitializedByteField = 5; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private byte inGetterSynchronizedLazyInitializedByteField; + + public synchronized byte getInGetterSynchronizedLazyInitializedByteField(){ + if(inGetterSynchronizedLazyInitializedByteField==0) + inGetterSynchronizedLazyInitializedByteField = 5; + return inGetterSynchronizedLazyInitializedByteField; + } + + @DeepImmutableField("field value has a primitive type and an immutable field reference") + @ImmutableFieldReference("field is effective immutable") + private Byte effectiveImmutableByteObjectField = 5; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safely lazy initialized") + private Byte lazyInitializedByteObjectField; + + public synchronized void initBO2(){ + if(lazyInitializedByteObjectField ==0) + lazyInitializedByteObjectField = 5; + } + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safely lazy initialized in a getter") + private Byte inAGetterLazyInitializedByteObjectField; + + public synchronized Byte getInAGetterLazyInitializedByteObjectField(){ + if(inAGetterLazyInitializedByteObjectField==0) + inAGetterLazyInitializedByteObjectField = 5; + return inAGetterLazyInitializedByteObjectField; + } + + @DeepImmutableField("field value has a primitive type and an immutable field reference") + @ImmutableFieldReference(value = "field is effective immutable") + private char c = 'a'; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private char synchronizedLazyInitializedCharField; + + public synchronized void initC2(){ + if(synchronizedLazyInitializedCharField == '\u0000') + synchronizedLazyInitializedCharField = 'a'; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private char inGetterSynchronizedLazyInitializedCharField; + + public synchronized char c3(){ + if(inGetterSynchronizedLazyInitializedCharField == '\u0000') + inGetterSynchronizedLazyInitializedCharField = 5; + return inGetterSynchronizedLazyInitializedCharField; + } + + @DeepImmutableField("field value has a primitive type and an immutable field reference") + @ImmutableFieldReference(value = "field is not written after initialization") + private long effectiveImmutableLongField = 5; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private long sychronizedLazyInitializedLongField; + + public synchronized void initL2(){ + if(sychronizedLazyInitializedLongField == 0l) + sychronizedLazyInitializedLongField = 5l; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private long inGetterSynchronizedLazyInitializedLongField; + + public synchronized long getInGetterSynchronizedLazyInitializedLongField(){ + if(inGetterSynchronizedLazyInitializedLongField == 0l) + inGetterSynchronizedLazyInitializedLongField = 5; + return inGetterSynchronizedLazyInitializedLongField; + } + + @DeepImmutableField("") + @ImmutableFieldReference("") + private Long lO = 5l; + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("") + private Long lO2; + + public synchronized void initLO2(){ + if(lO2 == 0l) + lO2 = 5l; + } + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("") + private Long lO3; + + public synchronized Long lO3(){ + if(lO3 == 0l) + lO3 = 5l; + return lO3; + } + + @DeepImmutableField("The concrete assigned object is known to be deep immutable") + @ImmutableFieldReference("The field is effective immutable") + private String effectiveImmutableString = "abc"; + + @DeepImmutableField("The concrete type of the object that is assigned is known") + @LazyInitializedThreadSafeFieldReference("lazy initialized within a synchronized method") + private String lazyInitializedString; + + public synchronized void initLazyInitializedString(){ + if(lazyInitializedString == null) + lazyInitializedString = "abc"; + } + + @DeepImmutableField("The concrete type of the object that is assigned is known") + @LazyInitializedThreadSafeFieldReference("lazy initialized within a synchronized method") + private String inAGetterLazyInitializedString; + + public synchronized String getInAGetterLazyInitializedString(){ + if(inAGetterLazyInitializedString == null) + inAGetterLazyInitializedString = "abc"; + return inAGetterLazyInitializedString; + } + + @DeepImmutableField("The concrete assigned object is known to be deep immutable") + @ImmutableFieldReference("The field is effective immutable") + private Object effectiveImmutableObjectReference = new Object(); + + @DeepImmutableField("The concrete type of the object that is assigned is known") + @LazyInitializedThreadSafeFieldReference("lazy initialized within a synchronized method") + private Object lazyInitializedObjectReference; + + public synchronized void initLazyInitializedObjectReference(){ + if(lazyInitializedObjectReference == null) + lazyInitializedObjectReference = new Object(); + } + + @DeepImmutableField("The concrete type of the object that is assigned is known") + @LazyInitializedThreadSafeFieldReference("lazy initialized within a synchronized method") + private Object inAGetterLazyInitializedObjectReference; + + public synchronized Object getInAGetterLazyInitializedObjectReference(){ + if(inAGetterLazyInitializedObjectReference == null) + inAGetterLazyInitializedObjectReference = new Object(); + return inAGetterLazyInitializedObjectReference; + } + + + + @ShallowImmutableField("") + @ImmutableFieldReference("effective immutable reference") + private List effectiveImmutableLinkedList = new LinkedList(); + + @ShallowImmutableField("") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private List lazyInitializedLinkedList; + + public synchronized void initLinkedList2(){ + if(lazyInitializedLinkedList == null) + lazyInitializedLinkedList = new LinkedList(); + } + + @ShallowImmutableField("concrete assigned object is known but manipulation of the referenced object with .add") + @LazyInitializedThreadSafeFieldReference("thread safe lazy initialization due to synchronized method") + private List lazyInitializedLinkedListWithManipulationAfterwards; + + public synchronized void initLinkedList3(){ + if(lazyInitializedLinkedListWithManipulationAfterwards == null) + lazyInitializedLinkedListWithManipulationAfterwards = new LinkedList(); + lazyInitializedLinkedListWithManipulationAfterwards.add(new Object()); + } + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private Object inTheGetterLazyInitializedObject; + + public synchronized Object getInTheGetterLazyInitializedObject(){ + if(inTheGetterLazyInitializedObject == null) + inTheGetterLazyInitializedObject = new Object(); + return inTheGetterLazyInitializedObject; + } + + @ShallowImmutableField("") + @ImmutableFieldReference("effective immutable reference") + private List effectiveImmutableArrayList = new ArrayList(); + + @ShallowImmutableField("") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private List lazyInitializedArrayList; + + public synchronized void initLazyInitializedArrayList(){ + if(lazyInitializedArrayList == null) + lazyInitializedArrayList = new ArrayList(); + } + + @ShallowImmutableField("concrete assigned object is known but manipulation of the referenced object with .add") + @LazyInitializedThreadSafeFieldReference("thread safe lazy initialization due to synchronized method") + private List lazyInitializedArrayListWithManipulationAfterwards; + + public synchronized void initLazyInitializedArrayListWithManipulationAfterwards(){ + if(lazyInitializedArrayListWithManipulationAfterwards == null) + lazyInitializedArrayListWithManipulationAfterwards = new ArrayList(); + lazyInitializedArrayListWithManipulationAfterwards.add(new Object()); + } + + @ShallowImmutableField("immutable reference but assigned object escapes via getter") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private List inTheGetterLazyInitializedArrayList; + + public synchronized List getInTheGetterLazyInitializedArrayList(){ + if(inTheGetterLazyInitializedArrayList == null) + inTheGetterLazyInitializedArrayList = new ArrayList(); + return inTheGetterLazyInitializedArrayList; + } + + @ShallowImmutableField("") + @ImmutableFieldReference("effective immutable reference") + private Set effectiveImmutableSet = new HashSet(); + + @ShallowImmutableField("") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private Set lazyInitializedSet; + + public synchronized void initLazyInitializedSet(){ + if(lazyInitializedSet == null) + lazyInitializedSet = new HashSet(); + } + + @ShallowImmutableField("concrete assigned object is known but manipulation of the referenced object with .add") + @LazyInitializedThreadSafeFieldReference("thread safe lazy initialization due to synchronized method") + private Set lazyInitializedSetWithManipulationAfterwards; + + public synchronized void initSet3(){ + if(lazyInitializedSetWithManipulationAfterwards == null) + lazyInitializedSetWithManipulationAfterwards = new HashSet(); + lazyInitializedSetWithManipulationAfterwards.add(new Object()); + } + + @ShallowImmutableField("immutable reference but assigned object escapes via getter") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private Set inTheGetterLazyInitializedSet; + + public synchronized Set getInTheGetterLazyInitializedSet(){ + if(inTheGetterLazyInitializedSet == null) + inTheGetterLazyInitializedSet = new HashSet(); + return inTheGetterLazyInitializedSet; + } + + @ShallowImmutableField("") + @ImmutableFieldReference("effective immutable reference") + private HashMap effectiveImmutableHashMap = new HashMap(); + + @ShallowImmutableField("") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private HashMap lazyInitializedHashMap; + + public synchronized void initLazyInitializedHashMap(){ + if(lazyInitializedHashMap == null) + lazyInitializedHashMap = new HashMap(); + } + + @ShallowImmutableField("concrete assigned object is known but manipulation of the referenced object with .put") + @LazyInitializedThreadSafeFieldReference("thread safe lazy initialization due to synchronized method") + private HashMap lazyInitializedHashMapWithManipulationAfterwards; + + public synchronized void initHashMap3(){ + if(lazyInitializedHashMapWithManipulationAfterwards == null) + lazyInitializedHashMapWithManipulationAfterwards = new HashMap(); + lazyInitializedHashMapWithManipulationAfterwards.put(new Object(), new Object()); + } + + @ShallowImmutableField("immutable reference but assigned object escapes via getter") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private HashMap inTheGetterLazyInitializedHashMap; + + public synchronized HashMap getInTheGetterLazyInitializedHashMap(){ + if(inTheGetterLazyInitializedHashMap == null) + inTheGetterLazyInitializedHashMap = new HashMap(); + return inTheGetterLazyInitializedHashMap; + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/LazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/LazyInitialization.java new file mode 100644 index 0000000000..4cca12fc13 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/LazyInitialization.java @@ -0,0 +1,344 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; + +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +/** + * Test classes for simple lazy initialization patterns and anti-patterns regarding reference immutability analysis. + * + * @author Dominik Helm + * @author Tobias Roth + */ + +class Simple { + + @LazyInitializedNotThreadSafeFieldReference("Simple lazy initialization") + private int x; + + public int init() { + if (x == 0) { + x = 5; + } + return x; + } +} + +class Local { + + @LazyInitializedNotThreadSafeFieldReference("Lazy initialization with local") + private int x; + + public int init() { + int y = this.x; + if (y == 0) { + x = y = 5; + } + return y; + } +} + +class LocalWrong { + + @MutableField("") + @MutableFieldReference(value = "Incorrect lazy initialization with local") + private int x; + + public int init() { + int y = this.x; + if (y == 0) { + x = 5; + } + return y; + } +} + +class LocalReversed { + + @LazyInitializedNotThreadSafeFieldReference(value = "Lazy initialization with local (reversed)") + private int x; + + public int init() { + int y = this.x; + if (y == 0) { + y = x = 5; + } + return y; + } +} + +class LocalReload { + + @LazyInitializedNotThreadSafeFieldReference("Lazy initialization with local (reloading the field's value after the write)") + private int x; + + public int init() { + int y = this.x; + if (y == 0) { + x = 5; + y = x; + } + return y; + } +} + +class SimpleReversed { + + @LazyInitializedNotThreadSafeFieldReference("Simple lazy initialization (reversed)") + private int x; + + public int init() { + if (x != 0) + return x; + x = 5; + return x; + } +} + + +class WrongDefault { + + @MutableField("") + @MutableFieldReference(value = "Not lazily initialized because of two different default values") + private int x; + + public WrongDefault() { + } + + public WrongDefault(int a) { + this(); + x = -1; + } + + public int init() { + if (x == -1) { + x = 5; + } + return x; + } +} + +class DeterministicCall { + + //FIXME: Iusse with Java11 @LazyInitialized("Lazy initialization with call to deterministic method") + //FIXME: Issue with Java11 @NonFinal(value = "Analysis doesn't recognize lazy initialization", + // analyses = { L0FieldMutabilityAnalysis.class, L1FieldMutabilityAnalysis.class }) + @LazyInitializedNotThreadSafeFieldReference("Lazy initialization with call to deterministic method") + private int x; + + public int init() { + if (x == 0) { + x = this.sum(5, 8); + } + return x; + } + + private final int sum(int a, int b) { + return a + b; + } +} + +class DeterministicCallWithParam { + + @LazyInitializedNotThreadSafeFieldReference(value = "Lazy initialization is not the same for different invocations") + private int x; + + public int init(int z) { + if (x == 0) { + x = this.sum(z, 8); + } + return x; + } + + private final int sum(int a, int b) { + return a + b; + } +} + +class DeterministicCallOnFinalField { + + @LazyInitializedNotThreadSafeFieldReference(value = "Lazy initialization with call to deterministic method ") + private int x; + + @ImmutableFieldReference(value = "Declared final field") + private final Inner inner; + + public DeterministicCallOnFinalField(int v) { + inner = new Inner(v); + } + + private final class Inner { + + @DeepImmutableField("immutable reference with base type") + final int val; + + public Inner(int v) { + val = v; + } + + public final int hashCode() { + return val; + } + } + + public int init() { + if (x == 0) { + x = inner.hashCode(); + } + return x; + } +} + +class DeterministicCallOnNonFinalField { + + @LazyInitializedNotThreadSafeFieldReference(value = "Wrong lazy initialization with call to non-deterministic method on final field") + private int x; + + @MutableFieldReference("Non final field") + private Inner inner; + + public void createInner(int v) { + inner = new Inner(v); + } + + private final class Inner { + + final int val; + + public Inner(int v) { + val = v; + } + + public final int hashCode() { + return val; + } + } + + public int init() { + if (x == 0) { + x = inner.hashCode(); + } + return x; + } +} + +class NondeterministicCall { + + @LazyInitializedNotThreadSafeFieldReference(value = "Wrong lazy initialization with call to non-deterministic method") + private int x; + + private final Object object = new Object(); + + public int init() { + if (x == 0) { + x = object.hashCode(); + } + return x; + } +} + +class DoubleLocalAssignment { + + @LazyInitializedNotThreadSafeFieldReference(value = "Lazy initialization with a local that is updated twice") + private int x; + + public int init() { + if (x == 0) { + int y = 5; + y ^= -1; + x = y; + } + return x; + } +} + +class DoubleAssignment { + + @MutableFieldReference(value ="Field can be observed partially updated") + private int x; + + public int init() { + if (x == 0) { + x = 5; + x ^= -1; + } + return x; + } +} + +class VisibleInitialization { + + @MutableFieldReference(value = "Incorrect because lazy initialization is visible") + private int x; + + public int init() { + int y = this.x; + int z; + if (y == 0) { + y = x = 5; + z = 3; + } else { + z = 2; + } + System.out.println(z); + return y; + } +} + +class ExceptionInInitialization { + + /** + * @note As the field write is dead, this field is really 'effectively final' as it will never + * be different from the default value. + */ + //FIXME: Issue with Java11 @EffectivelyFinal(value = "Field is never initialized, so it stays on its default value", + // analyses = { L1FieldMutabilityAnalysis.class, L2FieldMutabilityAnalysis.class }) + //FIXME: Issue with Java11 @NonFinal(value = "Instance field not considered by analysis", + // analyses = L0FieldMutabilityAnalysis.class) + @LazyInitializedNotThreadSafeFieldReference(value = "L1 Domain can not recognize this exception", //Field is never initialized, so it stays on its default value", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + private int getZero() { + return 0; + } + + public int init() { + int y = this.x; + if (y == 0) { + int z = 10 / getZero(); + y = x = 5; + } + return y; + } +} + + +class CaughtExceptionInInitialization { + + //TODO reasoning + @MutableField(value = "Incorrect because lazy initialization is may not happen due to exception") + @MutableFieldReference(value = "Incorrect because lazy initialization is may not happen due to exception") + private int x; + + public int init(int i) { + int y = this.x; + try { + if (y == 0) { + int z = 10 / i; + y = x = 5; + } + return y; + } catch (Exception e) { + return 0; + } + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/MethodCalls.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/MethodCalls.java new file mode 100644 index 0000000000..6c90ad67c1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/MethodCalls.java @@ -0,0 +1,94 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; + +import org.opalj.fpcf.fixtures.benchmark.generals.Mutability; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; + +public class MethodCalls { + @ShallowImmutableField("") + @LazyInitializedThreadSafeFieldReference("") + private Mutability tm1; + + public synchronized void getTM1(){ + if(tm1==null){ + tm1= new Mutability(); + } + tm1.nop(); + } + + @ShallowImmutableField("") + @LazyInitializedThreadSafeFieldReference("") + private Mutability tm2; + + public synchronized Mutability getTM2(){ + if(tm2==null){ + tm2= new Mutability(); + } + return tm2; + } + + @MutableField("") + @LazyInitializedNotThreadSafeFieldReference("") + private Mutability tm3; + + public void getTm3() { + if(tm3==null){ + tm3 = new Mutability(); + } + } + + @MutableFieldReference("") + @MutableField("") + private Mutability tm4; + + public synchronized Mutability getTm4() { + if(tm4==null){ + tm4 = new Mutability(); + } + return tm4; + } + + public synchronized Mutability getTm42() { + if(tm4==null){ + tm4 = new Mutability(); + } + return tm4; + } + + @ShallowImmutableField("") + private Mutability tm5; + + public synchronized void getTm5() { + if(tm5==null){ + tm5 = new Mutability(); + } + tm5.nop(); + } + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private Mutability tm6 = new Mutability(); + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private Mutability tm7 = new Mutability(); + + public void foo(){ + tm7.nop(); + } + + + + + + + + +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/SimpleLazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/SimpleLazyInitialization.java new file mode 100644 index 0000000000..f5e2aea1b0 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/SimpleLazyInitialization.java @@ -0,0 +1,22 @@ +package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; + +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; + +class SimpleLazyInitialization { + @LazyInitializedNotThreadSafeFieldReference("deterministic write due to guarded primitive type") + private int i = 0; + public int hashcode() { + if(i==0) + i = 5; + return i; + } + @LazyInitializedNotThreadSafeFieldReference("the write to the object reference simpleLazyInitialization is not atomic") + private static Object simpleLazyInitialization; + + public static Object init() { + if (simpleLazyInitialization == null) + simpleLazyInitialization = new Object(); + return simpleLazyInitialization; + } + +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/SimpleStringModel.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/SimpleStringModel.java new file mode 100644 index 0000000000..304947a657 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/SimpleStringModel.java @@ -0,0 +1,38 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; + +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; + +/** + * This class represents a simple model of the string class. + */ +public class SimpleStringModel { + + @ImmutableFieldReference("final field") + private final char value[]; + + public char[] getValue(){ + return value.clone(); + } + + + @LazyInitializedThreadSafeFieldReference("") + private int hash; // Default value 0 + + public SimpleStringModel(SimpleStringModel original) { + this.value = original.value; + } + + public int hashCode() { + int h = 0; + if (hash == 0) { + char val[] = value; + for (int i = 0; i < value.length; i++) { + h = 31 * h + val[i]; + } + hash = h; + } + return hash; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/Template.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/Template.java new file mode 100644 index 0000000000..7b36b3a6bb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/Template.java @@ -0,0 +1,34 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; + +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; + +public class Template { + + @LazyInitializedNotThreadSafeFieldReference("") + private Template _template; + + @ImmutableFieldReference("") + private Template _parent; + + public Template(Template parent) { + _parent = parent; + } + + protected final Template getParent() { + return _parent; + } + + protected Template getTemplate() { + + if (_template == null) { + Template parent = this; + while (parent != null) + parent = parent.getParent(); + _template = parent; + } + return _template; + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/escape/EscapesOfExceptions.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/escape/EscapesOfExceptions.java index ad57703bc2..2270ac1b48 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/escape/EscapesOfExceptions.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/escape/EscapesOfExceptions.java @@ -2,11 +2,11 @@ package org.opalj.fpcf.fixtures.escape; import org.opalj.fpcf.properties.escape.*; -import org.opalj.fpcf.properties.field_mutability.NonFinal; +import org.opalj.fpcf.properties.immutability.fields.MutableField; public class EscapesOfExceptions { - @NonFinal("the field is global and public and gets modified") + @MutableField("the field is global and public and gets modified") public static Exception global; public static void directThrowException() { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/DeclaredFinalFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/DeclaredFinalFields.java index 82cf7f5cdb..20c4b4aada 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/DeclaredFinalFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/DeclaredFinalFields.java @@ -1,35 +1,8 @@ -/* BSD 2-Clause License: - * Copyright (c) 2009 - 2017 - * Software Technology Group - * Department of Computer Science - * Technische Universität Darmstadt - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.field_mutability; -import org.opalj.fpcf.properties.field_mutability.DeclaredFinal; -import org.opalj.fpcf.properties.field_mutability.NonFinal; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; /** * Base class for tests below that calls a virtual method in its constructor that makes declared @@ -53,19 +26,19 @@ public Super(){ */ public class DeclaredFinalFields extends Super { - @DeclaredFinal("Initialized directly") + @ShallowImmutableField("Initialized directly") private final int a = 1; - @DeclaredFinal("Initialized through instance initializer") + @ShallowImmutableField("Initialized through instance initializer") private final int b; - @DeclaredFinal("Initialized through constructor") + @ShallowImmutableField("Initialized through constructor") private final int c; - @NonFinal(value = "Prematurely read through super constructor", prematurelyRead = true) + @MutableField(value = "Prematurely read through super constructor", prematurelyRead = true) private final int d; - @NonFinal(value = "Prematurely read through own constructor", prematurelyRead = true) + @MutableField(value = "Prematurely read through own constructor", prematurelyRead = true) private final int e; public DeclaredFinalFields() { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/LazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/LazyInitialization.java index acfae09888..806726939e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/LazyInitialization.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/LazyInitialization.java @@ -28,13 +28,11 @@ */ package org.opalj.fpcf.fixtures.field_mutability; -import org.opalj.br.fpcf.analyses.L0FieldMutabilityAnalysis; -import org.opalj.fpcf.properties.field_mutability.DeclaredFinal; -import org.opalj.fpcf.properties.field_mutability.EffectivelyFinal; -import org.opalj.fpcf.properties.field_mutability.LazyInitialized; -import org.opalj.fpcf.properties.field_mutability.NonFinal; -import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; -import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; /** * Test classes for simple lazy initialization patterns and anti-patterns. @@ -44,9 +42,9 @@ class Simple { - @LazyInitialized("Simple lazy initialization") - @NonFinal(value = "Analysis doesn't recognize lazy initialization", - analyses = { L0FieldMutabilityAnalysis.class, L1FieldMutabilityAnalysis.class }) + @ShallowImmutableField(value = "Simple lazy initialization", analyses = L2FieldImmutabilityAnalysis.class) + @MutableField(value = "Analysis doesn't recognize lazy initialization", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) private int x; public int init() { @@ -59,9 +57,9 @@ public int init() { class Local { - @LazyInitialized("Lazy initialization with local") - @NonFinal(value = "Analysis doesn't recognize lazy initialization", - analyses = { L0FieldMutabilityAnalysis.class, L1FieldMutabilityAnalysis.class }) + @ShallowImmutableField("Lazy initialization with local") + @MutableField(value = "Analysis doesn't recognize lazy initialization", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) private int x; public int init() { @@ -75,7 +73,7 @@ public int init() { class LocalWrong { - @NonFinal("Incorrect lazy initialization with local") + @MutableField("Incorrect lazy initialization with local") private int x; public int init() { @@ -89,9 +87,9 @@ public int init() { class LocalReversed { - @LazyInitialized("Lazy initialization with local (reversed)") - @NonFinal(value = "Analysis doesn't recognize lazy initialization", - analyses = { L0FieldMutabilityAnalysis.class, L1FieldMutabilityAnalysis.class }) + @ShallowImmutableField("Lazy initialization with local (reversed)") + @MutableField(value = "Analysis doesn't recognize lazy initialization", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) private int x; public int init() { @@ -105,9 +103,9 @@ public int init() { class LocalReload { - @LazyInitialized("Lazy initialization with local (reloading the field's value after the write)") - @NonFinal(value = "Analysis doesn't recognize lazy initialization", - analyses = { L0FieldMutabilityAnalysis.class, L1FieldMutabilityAnalysis.class }) + @ShallowImmutableField("Lazy initialization with local (reloading the field's value after the write)") + @MutableField(value = "Analysis doesn't recognize lazy initialization", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) private int x; public int init() { @@ -122,9 +120,9 @@ public int init() { class SimpleReversed { - @LazyInitialized("Simple lazy initialization (reversed)") - @NonFinal(value = "Analysis doesn't recognize lazy initialization", - analyses = { L0FieldMutabilityAnalysis.class, L1FieldMutabilityAnalysis.class }) + @ShallowImmutableField("Simple lazy initialization (reversed)") + @MutableField(value = "Analysis doesn't recognize lazy initialization", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) private int x; public int init() { @@ -137,9 +135,9 @@ public int init() { class SimpleWithDifferentDefault { - @LazyInitialized(value = "Simple lazy initialization, but different default value", + @ShallowImmutableField(value = "Simple lazy initialization, but different default value", analyses = {}) - @NonFinal(value = "Analysis doesn't recognize lazy initialization with different default") + @MutableField(value = "Analysis doesn't recognize lazy initialization with different default") private int x; public SimpleWithDifferentDefault() { @@ -160,7 +158,7 @@ public int init() { class WrongDefault { - @NonFinal("Not lazily initialized because of two different default values") + @MutableField("Not lazily initialized because of two different default values") private int x; public WrongDefault() { @@ -200,7 +198,7 @@ private final int sum(int a, int b) { class DeterministicCallWithParam { - @NonFinal("Lazy initialization is not the same for different invocations") + @MutableField("Lazy initialization is not the same for different invocations") private int x; public int init(int z) { @@ -217,12 +215,12 @@ private final int sum(int a, int b) { class DeterministicCallOnFinalField { - @LazyInitialized("Lazy initialization with call to deterministic method on final field") - @NonFinal(value = "Analysis doesn't recognize lazy initialization", - analyses = { L0FieldMutabilityAnalysis.class, L1FieldMutabilityAnalysis.class }) + @ShallowImmutableField("Lazy initialization with call to deterministic method on final field") + @MutableField(value = "Analysis doesn't recognize lazy initialization", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) private int x; - @DeclaredFinal("Declared final field") + @ShallowImmutableField("Declared final field") private final Inner inner; public DeterministicCallOnFinalField(int v) { @@ -252,10 +250,10 @@ public int init() { class DeterministicCallOnNonFinalField { - @NonFinal("Wrong lazy initialization with call to non-deterministic method on final field") + @MutableField("Wrong lazy initialization with call to non-deterministic method on final field") private int x; - @NonFinal("Non final field") + @MutableField("Non final field") private Inner inner; public void createInner(int v) { @@ -285,7 +283,7 @@ public int init() { class NondeterministicCall { - @NonFinal("Wrong lazy initialization with call to non-deterministic method") + @MutableField("Wrong lazy initialization with call to non-deterministic method") private int x; private final Object object = new Object(); @@ -300,9 +298,9 @@ public int init() { class DoubleLocalAssignment { - @LazyInitialized("Lazy initialization with a local that is updated twice") - @NonFinal(value = "Analysis doesn't recognize lazy initialization", - analyses = { L0FieldMutabilityAnalysis.class, L1FieldMutabilityAnalysis.class }) + @ShallowImmutableField("Lazy initialization with a local that is updated twice") + @MutableField(value = "Analysis doesn't recognize lazy initialization", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) private int x; public int init() { @@ -317,7 +315,7 @@ public int init() { class DoubleAssignment { - @NonFinal("Field can be observed partially updated") + @MutableField("Field can be observed partially updated") private int x; public int init() { @@ -331,7 +329,7 @@ public int init() { class VisibleInitialization { - @NonFinal("Incorrect because lazy initialization is visible") + @MutableField("Incorrect because lazy initialization is visible") private int x; public int init() { @@ -376,7 +374,7 @@ public int init() { class PossibleExceptionInInitialization { - @NonFinal("Incorrect because lazy initialization is may not happen due to exception") + @MutableField("Incorrect because lazy initialization is may not happen due to exception") private int x; public int init(int i) { @@ -391,7 +389,7 @@ public int init(int i) { class CaughtExceptionInInitialization { - @NonFinal("Incorrect because lazy initialization is may not happen due to exception") + @MutableField("Incorrect because lazy initialization is may not happen due to exception") private int x; public int init(int i) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/PrivateFieldUpdater.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/PrivateFieldUpdater.java index 2962c09992..cfb0c858b2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/PrivateFieldUpdater.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/PrivateFieldUpdater.java @@ -1,26 +1,26 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.field_mutability; -import org.opalj.br.fpcf.analyses.L0FieldMutabilityAnalysis; -import org.opalj.fpcf.properties.field_mutability.EffectivelyFinal; -import org.opalj.fpcf.properties.field_mutability.NonFinal; -import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; -import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; /** * Simple demo class which updates the private field of another instance of this class. */ public class PrivateFieldUpdater { - @EffectivelyFinal( + @ShallowImmutableField( value = "only initialized by the constructor", - analyses = { L1FieldMutabilityAnalysis.class, L2FieldMutabilityAnalysis.class } + analyses = { L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class } ) - @NonFinal(value = "instance field not recognized by analysis", - analyses = L0FieldMutabilityAnalysis.class) + @MutableField(value = "instance field not recognized by analysis", + analyses = L0FieldImmutabilityAnalysis.class) private String name; - @NonFinal("incremented whenever `this` object is passed to another `NonFinal` object") + @MutableField("incremented whenever `this` object is passed to another `NonFinal` object") private int i; private PrivateFieldUpdater(PrivateFieldUpdater s) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/Singleton.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/Singleton.java index 6552e464ba..735d44024f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/Singleton.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_mutability/Singleton.java @@ -1,23 +1,23 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.field_mutability; -import org.opalj.br.fpcf.analyses.L0FieldMutabilityAnalysis; -import org.opalj.fpcf.properties.field_mutability.EffectivelyFinal; -import org.opalj.fpcf.properties.field_mutability.NonFinal; -import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; -import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; public class Singleton { - @NonFinal("written by static initializer after the field becomes (indirectly) readable") + @MutableField("written by static initializer after the field becomes (indirectly) readable") private String name; - @EffectivelyFinal( + @ShallowImmutableField( value = "only initialized once by the constructor", - analyses = { L1FieldMutabilityAnalysis.class, L2FieldMutabilityAnalysis.class } + analyses = { L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class } ) - @NonFinal(value = "instance field not recognized by analysis", - analyses = L0FieldMutabilityAnalysis.class) + @MutableField(value = "instance field not recognized by analysis", + analyses = L0FieldImmutabilityAnalysis.class) private Object mutex = new Object(); private Singleton() { @@ -32,7 +32,7 @@ public String getName() { // STATIC FUNCTIONALITY - @EffectivelyFinal("only set in the static initializer") + @ShallowImmutableField("only set in the static initializer") private static Singleton theInstance; static { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField.java new file mode 100644 index 0000000000..cc01cfcb87 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField.java @@ -0,0 +1,389 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.classes.generic; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.DependentImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.DeepImmutableType; +import org.opalj.fpcf.properties.immutability.types.DependentImmutableType; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.fpcf.properties.immutability.types.ShallowImmutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + + +@DependentImmutableType("") +@DependentImmutableClass("") +public final class DependentClassWithGenericField { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable field reference with generic type T", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable reference", analyses = L3FieldImmutabilityAnalysis.class) + private T t; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "dep imm field", analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private SimpleGenericClass gc; + + public DependentClassWithGenericField(T t) { + this.t = t; + gc = new SimpleGenericClass<>(t, new FinalEmptyClass(), new FinalEmptyClass()); + } +} + +@DeepImmutableType(value = "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +@DeepImmutableClass(value = "has only deep immutable fields", analyses = L1ClassImmutabilityAnalysis.class) +final class DependentClassWithGenericField_deep01 { + + @DeepImmutableField(value = "immutable field reference with deep immutable type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private FinalEmptyClass fec; + + @DeepImmutableField(value = "the genericity was conretised with deep immutable types", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L3FieldImmutabilityAnalysis.class) + private SimpleGenericClass gc; + + public DependentClassWithGenericField_deep01(FinalEmptyClass fec) { + this.fec = fec; + gc = new SimpleGenericClass<>(fec, new FinalEmptyClass(), new FinalEmptyClass()); + } +} + +@DependentImmutableType(value = "has only dependent immutable fields and is not extensible", + analyses = L1TypeImmutabilityAnalysis.class) +@DependentImmutableClass(value = "has only dependent immutable fields and is not extensible", +analyses = L1ClassImmutabilityAnalysis.class) +final class DependentClassWithGenericFieldWithOneLeftGenericParameter { + + @DependentImmutableField(value = "has one left generic parameter T and no shallow or mutable types", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private SimpleGenericClass sgc; + + public DependentClassWithGenericFieldWithOneLeftGenericParameter(T t) { + sgc = new SimpleGenericClass<>(t, new FinalEmptyClass(), new FinalEmptyClass()); + } + +} + + + +@MutableType("") +@ShallowImmutableClass("") +class DependentClassWithMutableGenericTypeArgument { + + @ShallowImmutableField(value = "mutable type", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private final SimpleGenericClass sgc; + + DependentClassWithMutableGenericTypeArgument(SimpleGenericClass sgc) { + this.sgc = sgc; + } +} + +@DeepImmutableType(value = "class is not extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "class has no fields and is final", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +final class FinalEmptyClass { + +} + +@DependentImmutableType(value = "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +@DependentImmutableClass(value = "class has only dependent immutable fields", + analyses = L1ClassImmutabilityAnalysis.class) +final class SimpleGenericClass { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T1 t1; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T2 t2; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T3 t3; + + public SimpleGenericClass(T1 t1, T2 t2, T3 t3){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + } +} + +@DependentImmutableType("") +@DependentImmutableClass("") +final class GenericAndDeepImmutableFields { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T1 t1; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T2 t2; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DeepImmutableField(value = "immutable reference with deep immutable type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle deep immutability", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class} ) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private FinalEmptyClass fec; + + GenericAndDeepImmutableFields(T1 t1, T2 t2, FinalEmptyClass fec){ + this.t1 = t1; + this.t2 = t2; + this.fec = fec; + } +} + +@MutableType(value = "class has a mutable field", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "class has a mutable field", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class ClassWithGenericAndMutableFields { + + @MutableField("has a mutable field reference") + @MutableFieldReference("field is public") + public T1 t1; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T2 t2; + + ClassWithGenericAndMutableFields(T1 t1, T2 t2){ + this.t1 = t1; + this.t2 = t2; + } +} + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@ShallowImmutableClass(value="upper bound is a shallow immutable field", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class GenericAndShallowImmutableFields { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T1 t1; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T2 t2; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "immutable field reference, with mutable type that escapes over the constructor", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, + L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private SimpleMutableClass smc; + + GenericAndShallowImmutableFields(T1 t1, T2 t2, SimpleMutableClass smc){ + this.t1 = t1; + this.t2 = t2; + this.smc = smc; + } +} + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "has only a deep immutable field", + analyses = {L1ClassImmutabilityAnalysis.class, L0ClassImmutabilityAnalysis.class}) +class GenericClassWithDeepImmParam { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class}) + @DeepImmutableField(value = "immutable field reference with generic type inheriting a final deep immutable type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value="effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private A a; + + GenericClassWithDeepImmParam(A a) { + this.a = a; + } +} + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@ShallowImmutableClass(value = "has only a shallow immutable instance field", +analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class GenericClassWithExtendingFinalMutableType { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "immutable reference with a generic types that inherits a mutable type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private A a; + + GenericClassWithExtendingFinalMutableType(A a){ + this.a = a; + } +} + +@MutableType("class has a mutable field") +@MutableClass("class has a mutable field") +final class FinalMutableClass{ + + @MutableField(value="mutable field reference", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "public field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public int n = 0; +} + +@ShallowImmutableType(value = "class is not extensible", analyses = L0TypeImmutabilityAnalysis.class) +@ShallowImmutableClass(value = "can not handle generics", analyses = L0ClassImmutabilityAnalysis.class) +@DeepImmutableType(value = "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +@DeepImmutableClass(value = "has only deep immutable fields", analyses = L1ClassImmutabilityAnalysis.class) +final class ClassWithGenericField_deep { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DeepImmutableField(value = "deep imm field", analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private SimpleGenericClass gc = + new SimpleGenericClass<>(new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); +} + +@MutableType(value = "has only mutable fields", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "has only mutable fields", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +final class MutableClassWithGenericField { + + @MutableField(value = "field is public", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public SimpleGenericClass gc = + new SimpleGenericClass<>(new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); +} + +//@DeepImmutableType(value = "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +//@DeepImmutableClass(value = "has only deep immutable fields", analyses = L1ClassImmutabilityAnalysis.class) +@ShallowImmutableType(value = "class is not extensible", analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@ShallowImmutableClass(value = "can not handle generics", analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +final class ClassWithGenericField_shallow { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value="the generic type is concretised with mutable types", + analyses = {L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "field is effectively final", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private SimpleGenericClass gc = + new SimpleGenericClass + (new SimpleMutableClass(), new FinalEmptyClass(), new FinalEmptyClass()); +} + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "class extends a mutable class", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class DependentClassInheritingMutableOne extends SimpleMutableClass { + + @DependentImmutableField(value="effective final field with generic type T", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value= "can not handle generic type", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "effective final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private final T field; + + public DependentClassInheritingMutableOne(T field) { + this.field = field; + } +} + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "class has only a mutable field", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class SimpleMutableClass{ + + @MutableField(value = "field is public", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value="field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public int n = 0; +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Nested.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Nested.java new file mode 100644 index 0000000000..8d6e1ebd3e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Nested.java @@ -0,0 +1,106 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.classes.generic; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.DependentImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.DependentImmutableType; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.fpcf.properties.immutability.types.ShallowImmutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +/** + * Headline class + */ +public class Nested{ + +} + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value="Class has no instance fields", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class Simple{ + + @MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) + @ShallowImmutableClass(value= "has shallow immutable fields", analyses = L1ClassImmutabilityAnalysis.class) + class Inner{ + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DependentImmutableField(value= "effective final with generic type T", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value="can not handle generic types", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "effective final field", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T t; + + public Inner(T t){ + this.t = t; + } + } + +} +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value="Class has no instance fields", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class Complex{ + + @MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) + @ShallowImmutableClass(value= "has shallow immutable fields", analyses = L1ClassImmutabilityAnalysis.class) + class Inner { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value="can not work with generic type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value= "effective final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private GenericClass gc; + + public Inner(GenericClass gc){ + this.gc = gc; + } + } +} + +@DependentImmutableType(value= "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +@ShallowImmutableType(value = "class is not extensible", analyses = L0TypeImmutabilityAnalysis.class) +@DependentImmutableClass(value="has only one dependent immutable field", analyses = L1ClassImmutabilityAnalysis.class) +@ShallowImmutableClass(value="can not work with dependent immutable fields", + analyses = L0ClassImmutabilityAnalysis.class) +final class GenericClass { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value="can not work with generic type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value="effective final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T t; + + public GenericClass(T t){ + this.t = t; + } +} + + + + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClass.java new file mode 100644 index 0000000000..b2a6da6568 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClass.java @@ -0,0 +1,53 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.classes.inheriting; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "Class has no fields", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +public class EmptyClass { + +} + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "empty class inheriting empty class", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class EmptyClassInheritingEmptyClass extends EmptyClass{ + +} + +@MutableType(value = "Because of mutable class", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "Because of extending mutable class", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +final class EmptyClassInheritingMutableClass extends ClassWithMutableField { + +} + +@MutableType(value = "Because of mutable class", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "Because of mutable field", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class ClassWithMutableField { + + @MutableField(value = "Mutable reference", analyses = {L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "public field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public int n= 0; +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/EmptyInterface.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/EmptyInterface.java new file mode 100644 index 0000000000..8a1a3eb2b1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/EmptyInterface.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.classes.interfaces; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; + +@MutableType(value = "interface is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "empty interface", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +public interface EmptyInterface { +} + +@MutableType(value = "interface is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "interface", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +interface TrivialInterfaceWithMethods { + + String getName(); + String getAge(); + void setName(); + void setAge(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericBaseClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericBaseClass.java new file mode 100644 index 0000000000..3621c4aef8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericBaseClass.java @@ -0,0 +1,379 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.DependentImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.DeepImmutableType; +import org.opalj.fpcf.properties.immutability.types.DependentImmutableType; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.fpcf.properties.immutability.types.ShallowImmutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +@ShallowImmutableType(value = "class is not extensible", analyses = L0TypeImmutabilityAnalysis.class) +@ShallowImmutableClass(value = "can not handle generics", analyses = L0ClassImmutabilityAnalysis.class) +@DependentImmutableType(value = "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +@DependentImmutableClass(value = "has only dependent immutable fields", analyses = L1ClassImmutabilityAnalysis.class) +public final class GenericBaseClass { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value="can not work with generic type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value="effective final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T1 t1; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value="can not work with generic type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value="effective final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T2 t2; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value="can not work with generic type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value="effective final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T3 t3; + + public GenericBaseClass(T1 t1, T2 t2, T3 t3){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + } + +} + +@ShallowImmutableType(value = "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +@ShallowImmutableClass(value = "class has only shallow immutable fields", analyses = L1ClassImmutabilityAnalysis.class) +final class GenericClassWithMutableFinalParameter { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics; " + + "immutable reference with generic type inheriting final mutable type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, + L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T1 t1; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DeepImmutableField(value = "immutable reference with generic type inheriting finale deep immutable type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T2 t2; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T3 t3; + + public GenericClassWithMutableFinalParameter(T1 t1, T2 t2, T3 t3){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + } +} + +@DependentImmutableType(value = "class has only dependent immutable fields and is not extensible", + analyses = L1TypeImmutabilityAnalysis.class) +@DependentImmutableClass(value = "class has only dependent immutable fields", + analyses = L1ClassImmutabilityAnalysis.class) +final class GenericClassLevel2 { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T t; + + @DependentImmutableField(value = "one generic parameter left and no mutable types", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private GenericBaseClass gc; + + public GenericClassLevel2(T t1, FinalClassWithoutFields fec1, FinalClassWithoutFields fec2){ + this.t = t; + gc = new GenericBaseClass(fec1, fec2, t1); + } +} + +@DependentImmutableType(value = "class has only dependent immutable fields and is not extensible", + analyses = L1TypeImmutabilityAnalysis.class) +@DependentImmutableClass(value = "class has only dependent immutable fields", +analyses = L1ClassImmutabilityAnalysis.class) +final class GenericClassLevel3 { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T t; + + @DependentImmutableField(value = "generic type", analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private GenericClassLevel2 gc; + + public GenericClassLevel3(T t, FinalClassWithoutFields fec){ + this.t = t; + gc = new GenericClassLevel2(t, fec, fec); + } +} + +@DeepImmutableType(value = "only deep immutable fields and not extensible class", + analyses = L1TypeImmutabilityAnalysis.class) +@DeepImmutableClass(value = "only deep immutable fields", analyses = L1ClassImmutabilityAnalysis.class) +final class GenericClassLevel4Deep { + + @ImmutableFieldReference(value = "effective immutable field", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + @DeepImmutableField(value = "only dee immutable types", analyses = L3FieldImmutabilityAnalysis.class) + private GenericClassLevel3 gc; + + public GenericClassLevel4Deep(FinalClassWithoutFields fec1, FinalClassWithoutFields fec2){ + gc = new GenericClassLevel3(fec1, fec2); + } +} + +@ShallowImmutableType(value = "has one shallow immutable field and is not extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@ShallowImmutableClass(value = "has one shallow immutable field and is not extensible", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +final class GenericClassLevel4Shallow { + + @ImmutableFieldReference(value = "field is effective immutable", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + @ShallowImmutableField(value = "has a mutable type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, + L3FieldImmutabilityAnalysis.class}) + private GenericClassLevel3 gc; + + public GenericClassLevel4Shallow(ClassWithOneMutableField tmc1, FinalClassWithoutFields fec){ + gc = new GenericClassLevel3(tmc1, fec); + } +} + +@DeepImmutableType(value = "has only one deep immutable field and is not extensible", + analyses = L1TypeImmutabilityAnalysis.class) +@DeepImmutableClass(value = "has only one deep immutable field", analyses = L1ClassImmutabilityAnalysis.class) +@ShallowImmutableType(value = "class is not extensible", analyses = L0TypeImmutabilityAnalysis.class) +@ShallowImmutableClass(value = "has only shallow immutable fields", analyses = L0ClassImmutabilityAnalysis.class) +final class DeepGeneric { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DeepImmutableField(value = "only deep immutable types in generics", analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private GenericBaseClass gc1; + + public DeepGeneric(GenericBaseClass gc1){ + this.gc1 = gc1; + } + +} + +@MutableType(value = "class has mutable fields", analyses = {L0TypeImmutabilityAnalysis.class, + L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "class has mutable fields", analyses = {L0ClassImmutabilityAnalysis.class, + L1ClassImmutabilityAnalysis.class}) +class One { + + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private A a; + + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private B b; + + @MutableField(value = "field is public", analyses = { + L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class + }) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public ClassWithOneMutableField tmc; + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private GenericBaseClass gc1; + public One(A a, B b, ClassWithOneMutableField tmc){ + this.a = a; + this.b = b; + this.tmc = tmc; + this.gc1 = new GenericBaseClass(this.a,this.b, this.tmc); + } +} + +@MutableType(value = "class has mutable fields", analyses = {L0TypeImmutabilityAnalysis.class, +L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "class has mutable fields", analyses = {L0ClassImmutabilityAnalysis.class, +L1ClassImmutabilityAnalysis.class}) +class OneVirgin { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private A a; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private B b; + + @MutableField(value = "field is public", analyses = { + L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class + }) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public ClassWithOneMutableField tmc; + + GenericBaseClass gc1; + public OneVirgin(A a, B b, ClassWithOneMutableField tmc){ + this.a = a; + this.b = b; + this.tmc = tmc; + this.gc1 = new GenericBaseClass(this.a, this.b, this.tmc); + } +} + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@ShallowImmutableClass(value="has only one shallow immutable field", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class Two { + + @ShallowImmutableField(value = "There is a mutable type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, + L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "field is effective immutabe", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private GenericBaseClass, B, ClassWithOneMutableField> gc1; + + public Two(A a, B b, ClassWithOneMutableField tmc, GenericBaseClass gc1) { + this.gc1 = new GenericBaseClass, B, ClassWithOneMutableField>(gc1, b, tmc); + } +} + +@MutableType(value = "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +@ShallowImmutableClass(value = "class has only dependent immutable fields", + analyses = L1ClassImmutabilityAnalysis.class) +class TwoVirgin { + + @ShallowImmutableField(value="field has generic parameter", analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "field is effective immutable", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private GenericBaseClass, B, C> gc1; + + public TwoVirgin(A a, B b, C c, GenericBaseClass gc1) { + this.gc1 = new GenericBaseClass, B, C>(gc1,b,c); + } +} + +@DependentImmutableType(value = "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +@DependentImmutableClass(value = "class has only dependent immutable fields", + analyses = L1ClassImmutabilityAnalysis.class) +final class TestTest { + + + private GenericBaseClass t1; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DeepImmutableField(value = "immutable reference with generic type inheriting final deep immutable", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T2 t2; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T3 t3; + + + public TestTest(GenericBaseClass t1, T2 t2, T3 t3){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + } +} + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "class has only a mutable field", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class ClassWithOneMutableField { + @MutableField(value = "field is public", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value="field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public int n = 0; +} + +@DeepImmutableType(value = "class is not extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "class has no fields and is final", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +final class FinalClassWithoutFields { + +} + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericEscapesTransitive.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericEscapesTransitive.java new file mode 100644 index 0000000000..279f64d171 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericEscapesTransitive.java @@ -0,0 +1,93 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.DependentImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.DeepImmutableType; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.fpcf.properties.immutability.types.ShallowImmutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +@ShallowImmutableType(value = "has only deep immutable fields and is not extensible", +analyses = L1TypeImmutabilityAnalysis.class) +@ShallowImmutableClass(value = "has only deep immutable fields", analyses = L1ClassImmutabilityAnalysis.class) +final class ClassWithGenericField { + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private SimpleGenericClass gc = + new SimpleGenericClass + (new SimpleMutableClass(), new FinalEmptyClass(), new FinalEmptyClass()); +} + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DependentImmutableClass(value = "has only dependent immutable fields", analyses = L1ClassImmutabilityAnalysis.class) +class SimpleGenericClass { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DependentImmutableField(value = "field has an immutable field reference and a generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "field is effectively final", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "field is effectively final", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private A a; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DependentImmutableField(value = "field has an immutable field reference and a generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "field is effectively final", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "field is effectively final", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private B b; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DependentImmutableField(value = "field has an immutable field reference and a generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "field is effectively final", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "field is effectively final", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private C c; + + SimpleGenericClass(A a, B b, C c){ + this.a = a; + this.b = b; + this.c = c; + } +} + +@DeepImmutableClass(value = "class has no fields", analyses = L3FieldImmutabilityAnalysis.class) +class FinalEmptyClass{ + +} + +@MutableType(value = "class is mutable", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "class has a mutable instance field", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class SimpleMutableClass{ + + @MutableField(value = "field is public", analyses = {L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value= "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public int n = 10; +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericExt2Deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericExt2Deep.java new file mode 100644 index 0000000000..b7d08424f6 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericExt2Deep.java @@ -0,0 +1,14 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +//TODO @DeepImmutableClassAnnotation("") +public class GenericExt2Deep { + /* //TODO @DeepImmutableFieldAnnotation("") + private GenericClass gc; + GenericExt2Deep(GenericClass gc) { + this.gc = gc; + }*/ +} + +final class EmptyClass{ +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericExt2Shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericExt2Shallow.java new file mode 100644 index 0000000000..9d79040990 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericExt2Shallow.java @@ -0,0 +1,15 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +//TODO @ShallowImmutableClassAnnotation("") +public class GenericExt2Shallow { + /* //TODO @ShallowImmutableFieldAnnotation("") + private GenericClass gc; + GenericExt2Shallow(GenericClass gc) { + this.gc = gc; + }*/ +} + +final class FinalMutableClass{ + public int n = 0; +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/MultipleNestedInnerClasses.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/MultipleNestedInnerClasses.java new file mode 100644 index 0000000000..2cb811eadf --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/MultipleNestedInnerClasses.java @@ -0,0 +1,116 @@ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; + +public class MultipleNestedInnerClasses { +} +class LevelZero{ + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + T t; + public LevelZero(T t){ + this.t = t; + } +} + +class LevelOne { + + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + private T t; + + public LevelOne(T t){ + this.t = t; + } +} + +class LevelTwo { + class InnerOne { + @DependentImmutableField("") + private T t; + public InnerOne(T t){ + this.t = t; + } + } +} + +class LevelThree { + class InnerOne { + class InnerTwo { + @DependentImmutableField("") + private A a; + + @DependentImmutableField("") + private B b; + + @DependentImmutableField("") + private C c; + + public InnerTwo(A a, B b, C c){ + this.a = a; + this.b = b; + this.c = c; + } + } + } +} + +class LevelFour { + class InnerOne { + class InnerTwo { +class InnerThree{ + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + private A a; + + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + private B b; + + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + private C c; + + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + private D d; + + public InnerThree(A a, B b, C c, D d){ + this.a = a; + this.b = b; + this.c = c; + this.d = d; + } +} + } + } +} + +class LevelFive { + class InnerOne { + class InnerTwo { + class InnerThree{ +class InnerFour{ + + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + private A a; + + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + private B b; + + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + private C c; + + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + private D d; + + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + private E e; + + public InnerFour(A a, B b, C c, D d, E e){ + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + } +} + } + } + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/Container.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/Container.java new file mode 100644 index 0000000000..fbe779c2fc --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/Container.java @@ -0,0 +1,99 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.classes.trivials; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.fpcf.properties.immutability.types.ShallowImmutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +public class Container { + + @DeepImmutableClass(value = "Tree has no fields", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) + @MutableType(value = "Tree is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) + private static abstract class Tree { + + protected abstract void write(Object o); + + @ShallowImmutableClass(value = "The body is of ImmutableContainerType", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) + @ShallowImmutableType(value = "The body is of ImmutableContainerType", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) + private static final class Repeated extends Tree { + + @ShallowImmutableField(value="final field with mutable type", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value="final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private final Tree body; + + private Repeated(Tree body) { + this.body = body; + } + + @Override + protected void write(Object o) { + body.write(o); + } + } + + @ShallowImmutableClass(value = "The body is of ImmutableContainerType", + analyses = L0ClassImmutabilityAnalysis.class) + @ShallowImmutableType(value = "The body is of ImmutableContainerType", + analyses = L0TypeImmutabilityAnalysis.class) + private static final class Optional extends Tree { + + @ShallowImmutableField(value="final field with mutable type", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value="final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private final Tree body; + + private Optional(Tree body) { + this.body = body; + } + + @Override + protected void write(Object o) { + body.write(o); + } + } + + @ShallowImmutableClass(value = "Arrays are treated as immutable", analyses = L0ClassImmutabilityAnalysis.class) + @ShallowImmutableType(value = "Arrays are treated as immutable", analyses = L0TypeImmutabilityAnalysis.class) + private static final class Group extends Tree { + + @ShallowImmutableField(value=" ", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value=" ", analyses={L0FieldReferenceImmutabilityAnalysis.class}) + private final Tree[] children; + + private Group(Tree[] children) { + this.children = children; + } + + @Override + protected void write(Object o) { + for (Tree child : children) { + child.write(o); + } + } + } + } +} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/TrivialShallowImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/TrivialShallowImmutableClass.java new file mode 100644 index 0000000000..b1cad1c4da --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/TrivialShallowImmutableClass.java @@ -0,0 +1,99 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.classes.trivials; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.DependentImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.DeepImmutableType; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +@MutableType("Because of not final class") +@ShallowImmutableClass("has shallow immutable field") +public class TrivialShallowImmutableClass { + + @ShallowImmutableField(value = "Because object can not escape", analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference("Because it is private") + private TrivialMutableClass mutableClass = new TrivialMutableClass(); +} + +@MutableType("Because of not final mutable class") +@MutableClass("Because of mutable field") +class TrivialMutableClass { + + @MutableField("Because of mutable reference") + @MutableFieldReference("Because of public field") + public int n = 0; +} + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "class has no fields", analyses = L1ClassImmutabilityAnalysis.class) +class EmptyClass { +} + +@DeepImmutableType(value = "class has no fields and is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +@DeepImmutableClass(value = "class has no fields", analyses = L1ClassImmutabilityAnalysis.class) +final class FinalEmptyClass { +} + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DependentImmutableClass(value = "class has dependent immutable field", analyses = L1ClassImmutabilityAnalysis.class) +class TrivialDependentImmutableClass { + + @DependentImmutableField(value = "Because of type generic type T", analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "Private effectively final field", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T t; + + public TrivialDependentImmutableClass(T t){ + this.t = t; + } +} + +@MutableType(value = "Because of not final class", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "Generic Type is not used as field Type", analyses = L1ClassImmutabilityAnalysis.class) +class GenericTypeIsNotUsedAsFieldType { + + @DeepImmutableField(value="effective immutable field with primitive type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value="effective immutable field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int n = 0; + + GenericTypeIsNotUsedAsFieldType(T t){ + String s = t.toString(); + } +} + +@MutableType(value = "Because of mutable not final class", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "Generic class but public field", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class ClassWithGenericPublicField { + + @MutableField(value = "Because of mutable reference", analyses = {L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "Field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public T t; + + ClassWithGenericPublicField(T t){ + this.t = t; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/DeclaredFinalFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/DeclaredFinalFields.java new file mode 100644 index 0000000000..e3445003ea --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/DeclaredFinalFields.java @@ -0,0 +1,88 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references; + +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; + +/** + * Base class for tests below that calls a virtual method in its constructor that makes declared + * final field visible in uninitialized state. + * + * @author Dominik Helm + */ +abstract class Super{ + + public Super(){ + System.out.println(getD()); + } + + public abstract int getD(); +} + +/** + * Tests for references that are declared final. Some of them are not strictly final because they can + * be observed uninitialized. + * + * @author Dominik Helm + * @author Tobias Roth + */ +public class DeclaredFinalFields extends Super { + + @DeepImmutableField(value = "Initialized directly", analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "Initialized directly", analyses = {L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference("Initialized directly") + private final int a = 1; + + @DeepImmutableField(value = "Initialized through instance initializer", analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "Initialized through instance initializer", analyses = {L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference("Initialized through instance initializer") + private final int b; + + @DeepImmutableField(value = "Initialized through constructor", analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "Initialized through constructor", analyses = {L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference("Initialized through constructor") + private final int c; + + @MutableField(value = "Prematurely read through super constructor", analyses = {L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}, + prematurelyRead = true) + @MutableFieldReference(value = "Prematurely read through super constructor", prematurelyRead = true) + private final int d; + + @MutableField(value = "Prematurely read through own constructor", analyses = {L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}, + prematurelyRead = true) + @MutableFieldReference(value = "Prematurely read through own constructor", prematurelyRead = true) + private final int e; + + public DeclaredFinalFields() { + super(); + c=1; + d=1; + System.out.println(getE()); + e=1; + } + + public int getD(){ + return d; + } + + public int getE(){ + return e; + } + + // Instance initializer! + { + b = 1; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/LazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/LazyInitialization.java new file mode 100644 index 0000000000..4b681e3340 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/LazyInitialization.java @@ -0,0 +1,443 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references; + +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeButDeterministicReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +/** + * Test classes for simple lazy initialization patterns and anti-patterns regarding reference immutability analysis. + * + * @author Dominik Helm + * @author Tobias Roth + */ + +class Simple { + + /*@DeepImmutableField(value="Simple Lazy Initialization and primitive field type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "Simple lazy initialization", analyses = L2FieldImmutabilityAnalysis.class) */ + @MutableField(value = "Analysis doesn't recognize lazy initialization") + @LazyInitializedNotThreadSafeFieldReference("Simple lazy initialization") + private int x; + + public int init() { + if (x == 0) { + x = 5; + } + return x; + } +} + +class Local { + + /*@DeepImmutableField(value = "Lazy initialized and primitive type", analyses = {L3FieldImmutabilityAnalysis.class}) + @ShallowImmutableField(value = "Lazy initialization with local", analyses = {L2FieldImmutabilityAnalysis.class}) */ + @MutableField(value = "Analysis doesn't recognize lazy initialization") + @LazyInitializedNotThreadSafeFieldReference("Lazy initialization with local") + private int x; + + public int init() { + int y = this.x; + if (y == 0) { + x = y = 5; + } + return y; + } +} + +class LocalWrong { + + @MutableField(value = "Incorrect lazy initialization with local", analyses = {L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "Incorrect lazy initialization with local", + analyses = L3FieldImmutabilityAnalysis.class) + private int x; + + public int init() { + int y = this.x; + if (y == 0) { + x = 5; + } + return y; + } +} + +class LocalReversed { + + /*@DeepImmutableField(value="Lazy initializatio with primitive type", analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "Lazy initialization with local (reversed)", + analyses = L2FieldImmutabilityAnalysis.class) */ + @MutableField(value = "Not thread safe lazy initialized field reference is seen as mutable") + @LazyInitializedNotThreadSafeFieldReference(value = "Lazy initialization with local (reversed)", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + public int init() { + int y = this.x; + if (y == 0) { + y = x = 5; + } + return y; + } +} + +class LocalReload { + + //@DeepImmutableField(value = "Lazy initialization with primitive type", + //analyses = L3FieldImmutabilityAnalysis.class) + //@ShallowImmutableField(value = "Lazy initialization with local (reloading the field's value after the write)", + //analyses = L2FieldImmutabilityAnalysis.class) + @MutableField(value = "Analysis doesn't recognize lazy initialization", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class , + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class }) + @LazyInitializedNotThreadSafeFieldReference( + value = "Lazy initialization with local (reloading the field's value after the write)") + private int x; + + public int init() { + int y = this.x; + if (y == 0) { + x = 5; + y = x; + } + return y; + } +} + +class SimpleReversed { + + /* @DeepImmutableField(value = "Lazy initialization with primitive type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "Simple lazy initialization (reversed)", + analyses = L2FieldImmutabilityAnalysis.class) */ + @MutableField(value = "Analysis doesn't recognize lazy initialization") + @LazyInitializedNotThreadSafeFieldReference(value = "Simple lazy initialization (reversed)", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + public int init() { + if (x != 0) + return x; + x = 5; + return x; + } +} + +class SimpleWithDifferentDefault { + + @ShallowImmutableField(value = "Simple lazy initialization, but different default value", + analyses = {}) + @MutableField(value = "Analysis doesn't recognize lazy initialization with different default", analyses = + {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "Analysis doesn't recognize lazy initialization with different default", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + public SimpleWithDifferentDefault() { + x = -1; + } + + public SimpleWithDifferentDefault(int a) { + this(); + } + + public int init() { + if (x == -1) { + x = 5; + } + return x; + } +} + +class WrongDefault { + + @MutableField(value = "Not lazily initialized because of two different default values", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "Not lazily initialized because of two different default values", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + public WrongDefault() { + } + + public WrongDefault(int a) { + this(); + x = -1; + } + + public int init() { + if (x == -1) { + x = 5; + } + return x; + } +} + +class DeterministicCall { + + //FIXME: Iusse with Java11 @LazyInitialized("Lazy initialization with call to deterministic method") + //FIXME: Issue with Java11 @NonFinal(value = "Analysis doesn't recognize lazy initialization", + // analyses = { L0FieldMutabilityAnalysis.class, L1FieldMutabilityAnalysis.class }) + @LazyInitializedNotThreadSafeFieldReference("Lazy initialization with call to deterministic method") + private int x; + + public int init() { + if (x == 0) { + x = this.sum(5, 8); + } + return x; + } + + private final int sum(int a, int b) { + return a + b; + } +} + +class DeterministicCallWithParam { + + @MutableField(value = "Lazy initialization is not the same for different invocations", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @LazyInitializedNotThreadSafeFieldReference(value = "Lazy initialization is not the same for different invocations", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + public int init(int z) { + if (x == 0) { + x = this.sum(z, 8); + } + return x; + } + + private final int sum(int a, int b) { + return a + b; + } +} + +class DeterministicCallOnFinalField { + + /*@DeepImmutableField(value = "Lazy initialization with call to deterministic " + + "method on final field with primitive type", analyses = L3FieldImmutabilityAnalysis.class + ) + @ShallowImmutableField(value = "Lazy initialization with call to deterministic method on final field", + analyses = L2FieldImmutabilityAnalysis.class) */ + @MutableField(value="Not thread safe lazy initialized field reference is seen as mutable") + @LazyInitializedNotThreadSafeFieldReference(value = "Lazy initialization with call to deterministic method " + + "on final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + @ShallowImmutableField(value = "Declared final field", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "Declared final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private final Inner inner; + + public DeterministicCallOnFinalField(int v) { + inner = new Inner(v); + } + + private final class Inner { + + @DeepImmutableField(value="immutable reference with base type", + analyses = L3FieldImmutabilityAnalysis.class) + final int val; + + public Inner(int v) { + val = v; + } + + public final int hashCode() { + return val; + } + } + + public int init() { + if (x == 0) { + x = inner.hashCode(); + } + return x; + } +} + +class DeterministicCallOnNonFinalField { + + @MutableField(value = "Wrong lazy initialization with call to non-deterministic method on final field", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @LazyInitializedNotThreadSafeFieldReference(value = "Wrong lazy initialization with call to non-deterministic method on final field", + analyses = L3FieldImmutabilityAnalysis.class) + private int x; + + + @MutableField(value = "Non final field", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, + L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference("Non final field") + private Inner inner; + + public void createInner(int v) { + inner = new Inner(v); + } + + private final class Inner { + + final int val; + + public Inner(int v) { + val = v; + } + + public final int hashCode() { + return val; + } + } + + public int init() { + if (x == 0) { + x = inner.hashCode(); + } + return x; + } +} + +class NondeterministicCall { + + @MutableField(value = "Wrong lazy initialization with call to non-deterministic method", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @LazyInitializedNotThreadSafeFieldReference(value = "Wrong lazy initialization with call to non-deterministic method", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + private final Object object = new Object(); + + public int init() { + if (x == 0) { + x = object.hashCode(); + } + return x; + } +} + +class DoubleLocalAssignment { + /*@DeepImmutableField(value = "Lazy initialization with a local that is updated twice and primitive type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "Lazy initialization with a local that is updated twice", + analyses = L2FieldImmutabilityAnalysis.class) */ + @MutableField(value = "Analysis doesn't recognize lazy initialization") + @LazyInitializedNotThreadSafeFieldReference(value = "Lazy initialization with a local that is updated twice", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + public int init() { + if (x == 0) { + int y = 5; + y ^= -1; + x = y; + } + return x; + } +} + +class DoubleAssignment { + @MutableField(value = "Field can be observed partially updated", analyses = {L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value ="Field can be observed partially updated", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + public int init() { + if (x == 0) { + x = 5; + x ^= -1; + } + return x; + } +} + +class VisibleInitialization { + + @MutableField(value= "Incorrect because lazy initialization is visible", analyses = {L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "Incorrect because lazy initialization is visible", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + public int init() { + int y = this.x; + int z; + if (y == 0) { + y = x = 5; + z = 3; + } else { + z = 2; + } + System.out.println(z); + return y; + } +} + +class ExceptionInInitialization { + + /** + * @note As the field write is dead, this field is really 'effectively final' as it will never + * be different from the default value. + */ + //FIXME: Issue with Java11 @EffectivelyFinal(value = "Field is never initialized, so it stays on its default value", + // analyses = { L1FieldMutabilityAnalysis.class, L2FieldMutabilityAnalysis.class }) + //FIXME: Issue with Java11 @NonFinal(value = "Instance field not considered by analysis", + // analyses = L0FieldMutabilityAnalysis.class) + @LazyInitializedNotThreadSafeFieldReference(value = "L1 Domain can not recognize this exception", //Field is never initialized, so it stays on its default value", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + private int getZero() { + return 0; + } + + public int init() { + int y = this.x; + if (y == 0) { + int z = 10 / getZero(); + y = x = 5; + } + return y; + } +} + + + +class CaughtExceptionInInitialization { + //TODO reasoning + @MutableField(value = "Incorrect because lazy initialization is may not happen due to exception", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "Incorrect because lazy initialization is may not happen due to exception", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int x; + + public int init(int i) { + int y = this.x; + try { + if (y == 0) { + int z = 10 / i; + y = x = 5; + } + return y; + } catch (Exception e) { + return 0; + } + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/NativeFunctionCalls.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/NativeFunctionCalls.java new file mode 100644 index 0000000000..1968d1dfb6 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/NativeFunctionCalls.java @@ -0,0 +1,18 @@ +package org.opalj.fpcf.fixtures.immutability.field_references; + +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; + +public class NativeFunctionCalls { + + @MutableField("concrete class type is deep immutable") + @MutableFieldReference("field is final") + private Object finalObjectField = new Object(); + + @MutableField("The field objectField has a non final reassignable reference") + @MutableFieldReference("There is a native function in that class") + private Object objectField = new Object(); + + native void hulului(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/PrivateFieldUpdater.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/PrivateFieldUpdater.java new file mode 100644 index 0000000000..2ac0c35c27 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/PrivateFieldUpdater.java @@ -0,0 +1,53 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references; + +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +/** + * Simple demo class which updates the private field of another instance of this class. + */ +public class PrivateFieldUpdater { +@DeepImmutableField(value = "only initialized by the constructor", analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField( + value = "only initialized by the constructor", + analyses = { L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class } + ) + @ImmutableFieldReference("only initialized by the constructor") +@MutableField(value = "instance field not recognized by analysis", + analyses = L0FieldImmutabilityAnalysis.class) + private String name; + + @MutableField(value = "incremented whenever `this` object is passed to another `NonFinal` object", analyses = { + L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, + L3FieldImmutabilityAnalysis.class + }) + @MutableFieldReference(value = "incremented whenever `this` object is passed to another `NonFinal` object", + analyses = {L0FieldReferenceImmutabilityAnalysis.class}) + private int i; + + private PrivateFieldUpdater(PrivateFieldUpdater s) { + if (s != null) { + s.i += 1; + this.i = s.i; + this.name = s.name + s.i; + } + } + + public String getName() { + return name; + } + + public int getI() { + return i; + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/PrivateFinalArrayEscapesViaConstructor.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/PrivateFinalArrayEscapesViaConstructor.java new file mode 100644 index 0000000000..27faaf2e33 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/PrivateFinalArrayEscapesViaConstructor.java @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references; + +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; + +public class PrivateFinalArrayEscapesViaConstructor { + + @ImmutableFieldReference("") + private final char[] charArray; + + @ImmutableFieldReference("") + private final byte[] byteArray; + + @ImmutableFieldReference("") + private final int[] intArray; + + @ImmutableFieldReference("") + private final long[] longArray; + + @ImmutableFieldReference("") + private final Object[] objectArray; + + public PrivateFinalArrayEscapesViaConstructor(char[] charArray, byte[] byteArray, int[] intArray, + long[] longArray, Object[] objectArray) { + this.charArray = charArray; + this.byteArray = byteArray; + this.intArray = intArray; + this.longArray = longArray; + this.objectArray = objectArray; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/Singleton.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/Singleton.java new file mode 100644 index 0000000000..37cb26eea1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/Singleton.java @@ -0,0 +1,57 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references; + +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; + +public class Singleton { + + @MutableField(value = "written by static initializer after the field becomes (indirectly) readable", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference("written by static initializer after the field becomes (indirectly) readable") + private String name; + + @ShallowImmutableField( + value = "only initialized once by the constructor", + analyses = { L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class , + L3FieldImmutabilityAnalysis.class} + ) + @MutableField(value = "instance field not recognized by analysis", + analyses = L0FieldImmutabilityAnalysis.class) + @ImmutableFieldReference("only initialized once by the constructor") + private Object mutex = new Object(); + + private Singleton() { + this.name = ""; + } + + public String getName() { + synchronized (mutex) { + return name; + } + } + + // STATIC FUNCTIONALITY + @ShallowImmutableField(value = "only set in the static initializer", analyses = {L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference("only set in the static initializer") + private static Singleton theInstance; + + static { + theInstance = new Singleton(); + theInstance.name = "The Singleton Instance"; + } + + public static Singleton getInstance() { + return theInstance; + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/StaticFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/StaticFields.java new file mode 100644 index 0000000000..83751d037d --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/StaticFields.java @@ -0,0 +1,20 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "the class has only static fields and thus no state", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +public class StaticFields { + + private static String a = "a"; + + static String b = "b"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/Template.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/Template.java new file mode 100644 index 0000000000..a9467c001e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/Template.java @@ -0,0 +1,45 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references; + +//import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; + +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +public class Template { + + //@LazyInitializedNotThreadSafeReferenceAnnotation("") + @MutableField(value = "mutable field reference", analyses = L3FieldImmutabilityAnalysis.class) + @MutableFieldReference(value = "can not handle this kind of lazy initialization", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Template _template; + + + @ShallowImmutableField(value = "immutable reference and mutable type", + analyses = L3FieldImmutabilityAnalysis.class) + private Template _parent; + + public Template(Template parent) { + _parent = parent; + } + + protected final Template getParent() { + return _parent; + } + + protected Template getTemplate() { + + if (_template == null) { + Template parent = this; + while (parent != null) + parent = parent.getParent(); + _template = parent; + } + return _template; + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/Test.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/Test.java new file mode 100644 index 0000000000..3fb5f7a174 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/Test.java @@ -0,0 +1,15 @@ +package org.opalj.fpcf.fixtures.immutability.field_references; + +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; + +public class Test { + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized method") + private Integer synchronizedSimpleLazyInitializedIntegerField; + + public synchronized void initNO2(){ + if(synchronizedSimpleLazyInitializedIntegerField==0) + synchronizedSimpleLazyInitializedIntegerField = 5; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/TypeCastsInLazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/TypeCastsInLazyInitialization.java new file mode 100644 index 0000000000..b4cb034e64 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/TypeCastsInLazyInitialization.java @@ -0,0 +1,34 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references; + +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; + +public class TypeCastsInLazyInitialization { + + @MutableFieldReference("Analysis couldn't handle typecasts") + private Integer iF; + + public synchronized Integer getIF(){ + if(iF==0F) + iF = 5; + return iF; + } + + @MutableFieldReference("Analysis couldn't handle typecasts") + private Integer iD; + + public synchronized Integer getiD(){ + if(iD==0D) + iD = 5; + return iD; + } + + @MutableFieldReference("Analysis couldn't handle typecasts") + private Integer iL; + + public synchronized Integer getiL(){ + if(iL==0L) + iL = 5; + return iL; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/DCL.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/DCL.java new file mode 100644 index 0000000000..39ce10b75f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/DCL.java @@ -0,0 +1,261 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references.lazy_initialized_field_references; + +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +import java.util.Random; + +/** + * This class encompasses variations of double checked locking pattern and + * counter examples. + */ +public class DCL { + + @LazyInitializedThreadSafeFieldReference(value = "standard double checked locked initialized int field", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int doubleCheckedLockedIntField; + + public int getDoubleCheckedLockedIntField() { + if(doubleCheckedLockedIntField==0){ + synchronized(this) { + if(doubleCheckedLockedIntField==0){ + doubleCheckedLockedIntField = 42; + } + } + } + return doubleCheckedLockedIntField; + } + + @LazyInitializedThreadSafeFieldReference(value = "field write is not deterministic but only once written due to dcl", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int initializedWithDCLRandomWrite; + + public int getInitializedWithDCLRandomWrite(){ + if(initializedWithDCLRandomWrite==0){ + synchronized(this){ + if(initializedWithDCLRandomWrite==0){ + initializedWithDCLRandomWrite = new Random().nextInt(); + } + } + } + return initializedWithDCLRandomWrite; + } + + @LazyInitializedNotThreadSafeFieldReference(value = "The field is not thread safe and not deterministic written", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int notThreadSafeRandomWrite; + + public int getNotThreadSafeRandomWrite(){ + if(notThreadSafeRandomWrite==0){ + notThreadSafeRandomWrite = new Random().nextInt(); + } + return notThreadSafeRandomWrite; + } + + @LazyInitializedThreadSafeFieldReference(value = "dcl is implemented with early return", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object dclWithEarlyReturn; + public Object getInstance(){ + if(dclWithEarlyReturn!=null) + return dclWithEarlyReturn; + synchronized(this) { + if(dclWithEarlyReturn==null) + dclWithEarlyReturn = new Object(); + } + return dclWithEarlyReturn; + } + + + @LazyInitializedThreadSafeFieldReference(value = "write within a dcl and try catch", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object dclWithTryCatch; + + public Object DCL5(){ + if(dclWithTryCatch==null){ + synchronized(this){ + if(dclWithTryCatch==null){ + try{ + dclWithTryCatch = new Object(); + } + catch (Exception e) + { + throw e; + } + } + } + } + return dclWithTryCatch; + } + + @MutableFieldReference(value = "no correct lazy initialization because " + + "all exceptions are caught in the inner guard", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + Object noDCLAllExceptionsCaughtInsidInnerGuard; + + public Object getNoDCL(){ + if(noDCLAllExceptionsCaughtInsidInnerGuard==null){ + synchronized(this){ + if(noDCLAllExceptionsCaughtInsidInnerGuard==null){ + try{ + noDCLAllExceptionsCaughtInsidInnerGuard = new Object(); + } + catch(Exception e){ + + } + } + } + } + return noDCLAllExceptionsCaughtInsidInnerGuard; + } + + @MutableFieldReference(value = "No correct lazy initialization because all exceptions are caught in the complete dcl", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + Object noDCLAllExceptionsAreCaughtInTheCompleteDCL; + + public Object getNoDCLAllExceptionsAreCaughtInTheCompleteDCL(){ + try{ + if(noDCLAllExceptionsAreCaughtInTheCompleteDCL ==null){ + synchronized(this){ + if(noDCLAllExceptionsAreCaughtInTheCompleteDCL ==null){ + noDCLAllExceptionsAreCaughtInTheCompleteDCL = new Object(); + } + + } + } + } + catch(Exception e){ + } + return noDCLAllExceptionsAreCaughtInTheCompleteDCL; + } + + @MutableFieldReference(value = "no correct dcl pattern because all exceptions are caught in the outer guard", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + Object instance; + + public Object NoDCL1(){ + + if(instance==null){ + try{ + synchronized(this){ + if(instance==null){ + + instance = new Object(); + } + }}catch(Exception e){}} + return instance; + } + + + @MutableFieldReference(value = "no correct dcl, because the two try-catch-blocks", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + Object noDCLTwoTryCatchBlocks; + + public Object getNoDCLTwoTryCatchBlocks(){ + + if(noDCLTwoTryCatchBlocks==null){ + try{ + synchronized(this){ + if(noDCLTwoTryCatchBlocks==null){ + try{ + noDCLTwoTryCatchBlocks = new Object(); + } + catch (Exception e) + { + throw e; + } + } + } + } + catch(Exception e){ + + } + } + return noDCLTwoTryCatchBlocks; + } + + @MutableFieldReference(value = "no correct dcl because wrong exception forwarding", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + Object noDCLWrongExceptionForwarding; + + public Object getNoDCLWrongExceptionForwarding() throws IndexOutOfBoundsException{ + if(noDCLWrongExceptionForwarding==null){ + synchronized(this){ + if(noDCLWrongExceptionForwarding==null){ + try{ + noDCLWrongExceptionForwarding = new Object(); + } + catch (Exception e) + { + throw new IndexOutOfBoundsException(); + } + } + } + } + return noDCLWrongExceptionForwarding; + } + + + @LazyInitializedThreadSafeFieldReference(value = "standard dcl initialization of array reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object[] lazyInitializedArrayReference; + + public Object[] getLazyInitializedArrayReference() { + if(lazyInitializedArrayReference==null){ + synchronized(this) { + if(lazyInitializedArrayReference==null){ + lazyInitializedArrayReference = new Object[10]; + } + } + } + return lazyInitializedArrayReference; + } + + + + + + @LazyInitializedNotThreadSafeFieldReference(value = "The field is not thread safe lazy initialized but within a guard", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int[] notThreadSafeLazyInitializedArray; + + public int[] getValues(){ + if(notThreadSafeLazyInitializedArray==null){ + notThreadSafeLazyInitializedArray = new int[] {1,2,3,4,5,6,7,8,9,10}; + } + return notThreadSafeLazyInitializedArray; + } + + + @LazyInitializedThreadSafeFieldReference(value = "the field is guarded initialized within a synchronized method", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + + private int[] synchronizedArrayInitialization; + + public synchronized int[] getSynchronizedArrayInitialization(){ + if(synchronizedArrayInitialization==null){ + synchronizedArrayInitialization = new int[] {1,2,3,4,5,6,7,8,9,10}; + } + return synchronizedArrayInitialization; + } +} + +class DoubleCheckedLockingClassWithStaticFields { + + @LazyInitializedThreadSafeFieldReference(value = "standard dcl pattern within a static method", + analyses = L3FieldImmutabilityAnalysis.class) + private static DoubleCheckedLockingClassWithStaticFields instance; + public static DoubleCheckedLockingClassWithStaticFields getInstance() { + if(instance==null){ + synchronized(DoubleCheckedLockingClassWithStaticFields.class) { + if(instance==null){ + instance = new DoubleCheckedLockingClassWithStaticFields(); + } + } + } + return instance; + } + } \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/DoubleCheckedLocking.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/DoubleCheckedLocking.java new file mode 100644 index 0000000000..3080fb4509 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/DoubleCheckedLocking.java @@ -0,0 +1,298 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references.lazy_initialized_field_references; + +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +/** + * This classes encompasses different variation of double checked locking as a form of lazy initialization. + * + * @author Tobias Roth + * + */ +public class DoubleCheckedLocking { + + @LazyInitializedThreadSafeFieldReference(value = "standard double checked locking", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object standardDCL; + public Object getStandardDCL() { + if(standardDCL ==null){ + synchronized(this) { + if(standardDCL ==null){ + standardDCL = new Object(); + } + } + } + return standardDCL; + } + + + @LazyInitializedNotThreadSafeFieldReference(value = "the field write is not synchronized guarded", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object noSynchronizedGuard; + public Object getNoSynchronizedGuard() { + if(noSynchronizedGuard ==null){ + synchronized(Object.class) { + noSynchronizedGuard = new Object(); + } + } + return noSynchronizedGuard; + } + + @LazyInitializedThreadSafeFieldReference(value = "a simple variation of double checked locking without the outer guard", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object onlyOneGuardWithinTheSynchronization; + public Object getOnlyOneGuardWithinTheSynchronization() { + synchronized(Object.class) { + if(onlyOneGuardWithinTheSynchronization ==null){ + onlyOneGuardWithinTheSynchronization = new Object(); + } + } + return onlyOneGuardWithinTheSynchronization; + } + + @LazyInitializedNotThreadSafeFieldReference(value = "the field write is not synchronized guarded two times", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object twoNotSynchronizedGuards; + public Object getTwoNotSynchronizedGuards() { + if(twoNotSynchronizedGuards ==null){ + if(twoNotSynchronizedGuards ==null){ + twoNotSynchronizedGuards = new Object(); + } + } + return twoNotSynchronizedGuards; + } + + @LazyInitializedNotThreadSafeFieldReference(value = "the field is written outside the synchronized block", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object writeOutsideTheSynchronizedGuard; + public Object getWriteOutsideTheSynchronizedGuard() { + if(writeOutsideTheSynchronizedGuard ==null){ + synchronized(this) { + if(writeOutsideTheSynchronizedGuard ==null){ + + } + writeOutsideTheSynchronizedGuard = new Object(); + } + + } + return writeOutsideTheSynchronizedGuard; + } + + @MutableFieldReference(value = "no valid lazy initialization", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object multipleWritesWithinTheDCLPattern; + public Object getMultipleWritesWithinTheDCLPattern() { + if(multipleWritesWithinTheDCLPattern ==null){ + synchronized(this) { + if(multipleWritesWithinTheDCLPattern ==null){ + multipleWritesWithinTheDCLPattern = new Object(); + } + } + multipleWritesWithinTheDCLPattern = new Object(); + } + return multipleWritesWithinTheDCLPattern; + } + + @MutableFieldReference(value = "no valid lazy initialization", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object alsoWrittenOutsideTheDCLPattern; + public Object getAlsoWrittenOutsideTheDCLPattern() { + if(alsoWrittenOutsideTheDCLPattern ==null){ + synchronized(this) { + if(alsoWrittenOutsideTheDCLPattern ==null){ + alsoWrittenOutsideTheDCLPattern = new Object(); + } + } + } + alsoWrittenOutsideTheDCLPattern = new Object(); + return alsoWrittenOutsideTheDCLPattern; + } + + @MutableFieldReference(value = "no lazy initialization due to wrong guard statements", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object wrongGuardStatement; + + public Object getWrongGuardStatement() { + if (wrongGuardStatement != null) { + synchronized (this) { + if (wrongGuardStatement != null) { + wrongGuardStatement = new Object(); + } + } + } + return wrongGuardStatement; + } + + @MutableFieldReference(value = "no valid lazy initialization", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object writeOutsideDCL; + + public Object getWriteOutsideDCL() { + if (writeOutsideDCL == null) { + synchronized (this) { + if (writeOutsideDCL == null) { + } + } + } + writeOutsideDCL = new Object(); + return writeOutsideDCL; + } + + @LazyInitializedThreadSafeFieldReference(value = "dcl pattern with loops in it", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object loopsInDCLPattern; + public Object getLoopsInDCLPattern() { + for(int i=0; i<1000; i++){} + if(loopsInDCLPattern ==null){ + for(int i=0; i<1000; i++){} + synchronized(this) { + for(int i=0; i<1000; i++){} + if(loopsInDCLPattern ==null){ + for(int i=0; i<1000; i++){} + loopsInDCLPattern = new Object(); + } + } + } + return loopsInDCLPattern; + } + + @LazyInitializedThreadSafeFieldReference(value = "no correct complecte dcl pattern but sufficient for thread " + + "safety due to a correct guard in a synchronized block", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object outerGuardEndsBeforeSynchronization; + public Object getOuterGuardEndsBeforeSynchronization() { + if(outerGuardEndsBeforeSynchronization ==null){ + } + synchronized(this) { + if(outerGuardEndsBeforeSynchronization ==null){ + outerGuardEndsBeforeSynchronization = new Object(); + } + } + return outerGuardEndsBeforeSynchronization; + } + + @LazyInitializedNotThreadSafeFieldReference(value = "only a guard around the field write", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object notNestedDCLWriteOnlyInGuard; + public Object getNotNestedDCLWriteOnlyInGuard() { + if(notNestedDCLWriteOnlyInGuard ==null){ + } + synchronized(this) { + } + if(notNestedDCLWriteOnlyInGuard ==null){ + notNestedDCLWriteOnlyInGuard = new Object(); + } + return notNestedDCLWriteOnlyInGuard; + } + + @MutableFieldReference(value = "field write outside guards and synchronized blocks", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object notNestedDCLWriteOutside; + public Object getNotNestedDCLWriteOutside() { + if(notNestedDCLWriteOutside ==null){ + } + synchronized(this) { + } + if(notNestedDCLWriteOutside ==null){ + + } + notNestedDCLWriteOutside = new Object(); + return notNestedDCLWriteOutside; + } + + + @LazyInitializedThreadSafeFieldReference("") + private Object multipleGuardsInDCLPattern; + public Object getMultipleGuardsInDCLPattern() { + if(multipleGuardsInDCLPattern ==null){ + if(multipleGuardsInDCLPattern ==null) { + if (multipleGuardsInDCLPattern == null) { + synchronized (this) { + if (multipleGuardsInDCLPattern == null) { + if (multipleGuardsInDCLPattern == null) { + multipleGuardsInDCLPattern = new Object(); + } + } + } + } + } + } + return multipleGuardsInDCLPattern; + } + + //@LazyInitializedThreadSafeReference("") + @MutableFieldReference(value = "guard not only dependent on field value", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object dclWithLockAnd; + //private boolean lock = true; + public Object getDclWithLockAnd(boolean lock) { + if(dclWithLockAnd ==null && lock){ + synchronized(this) { + if(dclWithLockAnd ==null && lock){ + dclWithLockAnd = new Object(); + } + } + } + return dclWithLockAnd; + } + + //@LazyInitializedThreadSafeReference("") + @MutableFieldReference(value = "guard not only dependent on field value", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Object dclWithLockOr; + //private boolean lock = true; + public Object getDclWithLockOr(boolean lock) { + if(dclWithLockOr ==null && lock){ + synchronized(this) { + if(dclWithLockOr ==null && lock){ + dclWithLockOr = new Object(); + } + } + } + return dclWithLockOr; + } + + @LazyInitializedNotThreadSafeFieldReference(value = "the field read for the guard is outside the synchronized block", + analyses = L0FieldImmutabilityAnalysis.class) + private Object fieldReadOutsideSynchronizedBlock; + + public Object getFieldReadOutsideSynchronizedBlock(){ + Object tmpo1 = fieldReadOutsideSynchronizedBlock; + synchronized (this){ + if(tmpo1==null) + fieldReadOutsideSynchronizedBlock = tmpo1 = new Object(); + } + return fieldReadOutsideSynchronizedBlock; + } + + @LazyInitializedNotThreadSafeFieldReference(value = "the field read for the guard is outside the synchronized block", analyses = L0FieldImmutabilityAnalysis.class) + private Object fieldReadOutsideSynchronizedBlockEarlyReturn; + + public Object getFieldReadOutsideSynchronizedBlockEarlyReturn(){ + Object tmpo2 = fieldReadOutsideSynchronizedBlockEarlyReturn; + if(tmpo2!=null) + return fieldReadOutsideSynchronizedBlockEarlyReturn; + synchronized (this){ + if(tmpo2==null) + fieldReadOutsideSynchronizedBlockEarlyReturn = new Object(); + } + return fieldReadOutsideSynchronizedBlockEarlyReturn; + } + +} + + + + + + + + + + + + + + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/SimpleLazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/SimpleLazyInitialization.java new file mode 100644 index 0000000000..963d8ffa5f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/SimpleLazyInitialization.java @@ -0,0 +1,49 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references.lazy_initialized_field_references; + +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeButDeterministicReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +/** + * This class represents lazy initialization in the simplest way + * with one guard and no synchronization + * + * @author Tobias Roth + * + */ +public class SimpleLazyInitialization { + @LazyInitializedNotThreadSafeFieldReference(value = "the write to the object reference simpleLazyInitialization" + + " is not atomic", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private static SimpleLazyInitialization simpleLazyInitialization; + + public static SimpleLazyInitialization init() { + if(simpleLazyInitialization ==null) + simpleLazyInitialization = new SimpleLazyInitialization(); + return simpleLazyInitialization; + } + + /*@ShallowImmutableField(value = "can not handle transitive immutability", + analyses = L2FieldImmutabilityAnalysis.class) + @DeepImmutableField(value = "field has immutable field reference an primitive type", + analyses = L3FieldImmutabilityAnalysis.class) */ + @MutableField(value = "can not handle effective immutability and lazy initialization") + @LazyInitializedNotThreadSafeFieldReference(value = "deterministic write due to guarded primitive type", + analyses = {L0FieldReferenceImmutabilityAnalysis.class}) + private int i = 0; + public int hashcode() { + if(i==0) + i = 5; + return i; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/SimpleStringModel.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/SimpleStringModel.java new file mode 100644 index 0000000000..4408eb3993 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/SimpleStringModel.java @@ -0,0 +1,54 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references.lazy_initialized_field_references; + +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeButDeterministicReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +/** + * This class represents a simple model of the string class. + */ +public class SimpleStringModel { + + @ShallowImmutableField(value= "field has immutable reference and array type char[]", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private final char value[]; + + char[] getValue(){ + return value; + } + + //TODO @DeepImmutableField + //TODO @LazyInitializedNotThreadSafeButDeterministicReference + @MutableField("Mutable field reference") + @LazyInitializedNotThreadSafeFieldReference(value="The analysis state this for performance issues as being not thread safe") //The two def sites of h are conservatively handled as mutable") + private int hash; // Default value 0 + + + public SimpleStringModel(SimpleStringModel original) { + this.value = original.value; + } + + public int hashCode() { + int h = 0; + if (hash == 0) { + char val[] = value; + for (int i = 0; i < value.length; i++) { + h = 31 * h + val[i]; + } + hash = h; + } + return hash; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayAndString.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayAndString.java new file mode 100644 index 0000000000..435440e406 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayAndString.java @@ -0,0 +1,37 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; + +public class ArrayAndString { + + @ShallowImmutableField("") + private String[] stringArray; + + @ShallowImmutableField("") + private int[] intArray; + + @DeepImmutableField("") + private String string; + + @DeepImmutableField("") + private int i; + + @ShallowImmutableField("") + private ClassWithPublicFields[] tmc; + + @ShallowImmutableField("") + private T[] tArray; + + ArrayAndString(String[] stringArray, int[] intArray, String string, int i, + ClassWithPublicFields[] tmc, T[] tArray) { + this.stringArray = stringArray; + this.intArray = intArray; + this.string = string; + this.i = i; + this.tmc = tmc; + this.tArray = tArray; + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayClasses.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayClasses.java new file mode 100644 index 0000000000..b9301c0b29 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayClasses.java @@ -0,0 +1,177 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; + +public class ArrayClasses { +/* + @DeepImmutableField("The elements of the array can not escape") + @ImmutableFieldReference("Array is eager initialized") + private Object[] zzz = new Object[]{1, 2, 3}; + + @DeepImmutableField("The elements of the array can not escape") + @ImmutableFieldReference("Array is initialized in the constructor") + private Object[] a; + + public ArrayClasses() { + a = new Object[]{5, 6, 7, 8}; + } + + @ShallowImmutableField("The elements of the array are manipulated after initialization and can escape.") + @ImmutableFieldReference("The array is eager initialized.") + private Object[] b = new Object[]{1, 2, 3, 4, 5}; + + public Object[] getB() { + b[2] = 2; + return b; + } + + @MutableField("Array has a mutable reference.") + @MutableFieldReference("The array is initalized always when the InitC function is called") + private Object[] c; + + public void InitC() { + c = new Object[]{1, 2, 3}; + } + + + @ShallowImmutableField("The elements of the array can escape.") + @ImmutableFieldReference("The array is eager initialized.") + private Object[] d = new Object[]{1, 2, 3, 4, 5,}; + + public Object[] getD() { + return d; + } + + + @ShallowImmutableField("The elements of the array can escape.") + @LazyInitializedThreadSafeFieldReference("The array is initialized lazily.") + private Object[] e; + + public synchronized Object[] getE() { + Object[] tmp; + if (e == null) { + tmp = new Object[3]; + for (int i = 0; i < 3; i++) + tmp[i] = i; + this.e = tmp; + } + return this.e; + } + + + @MutableField("The reference is seen as mutable.") + @LazyInitializedNotThreadSafeFieldReference("The array is initialized lazily but not thread safe.") + private Object[] f; + + public void getF() { + if (f == null) { + f = new Object[]{1, 2, 3}; + } + } + + + @ShallowImmutableField("One element of the array is written after initialization.") + @LazyInitializedThreadSafeFieldReference("The array is initialized lazily and thread safe.") + private Object[] g; + + public synchronized void getG() { + if (g == null) + g = new Object[]{1, 2, 4, 5}; + g[2] = 2; + //return g; + } + + + @DeepImmutableField("The elements of the array can not escape.") + @ImmutableFieldReference("The array is initialized eagerly.") + private Object[] h = new Object[]{new Object(), new Object(), new Object()}; + + + @ShallowImmutableField("The elements of the array can escape.") + @LazyInitializedThreadSafeFieldReference("The array is initialized thread safe and eagerly.") + private Object[] i; + + public synchronized Object[] getI(){ + if(i==null) + i = new Object[]{new Object(), new Object(), new Object()}; + return i; + } + + + @DeepImmutableField("The elements of the array can not escape.") + @LazyInitializedThreadSafeFieldReference("The array is initialized thread safen and eagerly.") + private Object[] j; + + public synchronized void getJ(){ //Object[] + if(j==null) + j = new Object[]{new Object(), new Object(), new Object()}; + //j[2] = new Object(); + // return j; + } + + + @DeepImmutableField("The elements of the array can not escape") + @ImmutableFieldReference("The array is not initialized.") + private Object[] k; + + + @MutableField("The reference is seen as mutable.") + @LazyInitializedNotThreadSafeFieldReference("The array is initialized lazily but not thread-safe.") + private int[] m; + + public int[] getM() { + if(m==null) + m = new int[]{1,2,3}; + return m; + } + + + @MutableField("The reference is seen as mutable.") + @LazyInitializedNotThreadSafeFieldReference("The array is initialized lazily but not thread-safe.") + private Object[] n; + + public Object[] getN(){ //Object[] + if(n==null) + n = new Object[]{new Object(), new Object(), new Object()}; + n[2] = new Object(); + return n; + } + + + @ShallowImmutableField("The elements of the array can escape.") + @LazyInitializedThreadSafeFieldReference("The array is initialized thread safe and lazily.") + private Object[] o; + + public synchronized Object[] getO(){ + if(o==null) + o = new Object[]{new Object(), new Object(), new Object()}; + o[2] = new Object(); + return o; + } + + @ShallowImmutableField("One element of the array can escape") + @LazyInitializedThreadSafeFieldReference("The array is thread safe lazily intialized.") + private Object[] p; + public synchronized Object getP(){ + if(p==null) + p = new Object[]{new Object(), new Object(), new Object()}; + return p[2]; + } + + //TODO @DeepImmutableField("The elements of the array can escape, but have a deep immutable reference.") + //TODO @LazyInitializedThreadSafeFieldReference("The array is thread safe lazily intialized.") + private Integer[] q; + public synchronized Integer getQ(){ + if(q==null) + q = new Integer[]{new Integer(1), new Integer(2), new Integer(3)}; + return q[2]; + } +*/ +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayInitialization.java new file mode 100644 index 0000000000..82ac43ff8e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayInitialization.java @@ -0,0 +1,105 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; + +public class ArrayInitialization { + + @MutableFieldReference("") + private Object[] array; + + public Object[] getArray(int n) { + if (array == null || array.length < n) { + this.array = new Object[n]; + } + return array; + } + + @MutableFieldReference("") + private Object[] b; + + public Object[] getB(boolean flag) throws Exception { + if(b!=null) + return b; + else if(flag) + return b; //throw new Exception(""); + else { + this.b = new Object[5]; + return b; + } + } +} + + +class SimpleLazyObjectsInstantiation{ + + @LazyInitializedNotThreadSafeFieldReference("") + private SimpleLazyObjectsInstantiation instance; + + public SimpleLazyObjectsInstantiation getInstance() { + if(instance==null) + instance = new SimpleLazyObjectsInstantiation(); + return instance; + } +} + +class EscapingObjectDeep { + //TODO + @ShallowImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + private Object o; + + public synchronized Object getO(){ + if(this.o==null) + this.o = new Object(); + return this.o; + } +} + +class EscapingObjectThatIsShallow { + + @ShallowImmutableField(value = "there are more than one object possibly assigned", + analyses = L3FieldImmutabilityAnalysis.class) + private final Object o; + + public EscapingObjectThatIsShallow() { + this.o = new EmptyClass(); + } + + public EscapingObjectThatIsShallow(int n) { + this.o = new Object(); + } + + public Object getO(){ + return this.o; + } +} + +class ClassUsingEmptyClass { + + //TODO @DeepImmutableField(value = "concrete object is known", analyses = L3FieldImmutabilityAnalysis.class) + private EmptyClass emptyClass = new EmptyClass(); + + public EmptyClass getEmptyClass() { + return this.emptyClass; + } + +} + +class ClassUsingEmptyClassExtensible { + + @ShallowImmutableField(value = "all the concrete object that can be assigned are not known", + analyses = L3FieldImmutabilityAnalysis.class) + private EmptyClass emptyClass = new EmptyClass(); + + public ClassUsingEmptyClassExtensible(EmptyClass emptyClass) { + this.emptyClass = emptyClass; + } +} + +class EmptyClass { + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayWithOneEscapingObject.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayWithOneEscapingObject.java new file mode 100644 index 0000000000..f6234ad095 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayWithOneEscapingObject.java @@ -0,0 +1,55 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("It has a mutable state") +@MutableClass("It has a mutable state") +public class ArrayWithOneEscapingObject { + + @MutableField("Reference of the field is mutable") + @MutableFieldReference("Field is public") + public Object o = new Object(); + + @ShallowImmutableField("") + @ImmutableFieldReference("Reference is only initialized once") + private Object[] array1 = new Object[]{o, new Object(), new Object()}; //TODO + + + @ShallowImmutableField("Field is initialized with an Shallow immutable field") + @ImmutableFieldReference("Field is only initialized once.") + private Object[] array2; + + public ArrayWithOneEscapingObject() { + array2 = new Object[]{o}; + } + + @ShallowImmutableField("Field is initialized with a shallow immutable field.") + @LazyInitializedThreadSafeFieldReference("Synchronized method with a guard-statement around the write") + private Object[] array3; + + public synchronized void initArray3(Object o){ + if(array3==null) + array3 = new Object[]{o}; + } + + @ShallowImmutableField("An array element escapes") + @LazyInitializedThreadSafeFieldReference("Synchronized method, with guarding if-statement.") + private Object[] array4; + + public synchronized Object initArray4(Object o){ + + Object tmp0 = new Object(); + + if(array4==null) + array4 = new Object[]{tmp0}; + + return tmp0; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithADeepImmutableFieldWhichIsSetInTheConstructor.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithADeepImmutableFieldWhichIsSetInTheConstructor.java new file mode 100644 index 0000000000..d01e5c7828 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithADeepImmutableFieldWhichIsSetInTheConstructor.java @@ -0,0 +1,38 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +@MutableType(value = "Class is extensible", analyses = {L0TypeImmutabilityAnalysis.class, + L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "class has only the deep immutable field fec", + analyses = {L1ClassImmutabilityAnalysis.class, L0ClassImmutabilityAnalysis.class}) +public class ClassWithADeepImmutableFieldWhichIsSetInTheConstructor { + + @ShallowImmutableField(value = "can not handle transitive immutability", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class}) + @DeepImmutableField(value = "immutable reference and deep immutable field type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "declared final field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private final FinalEmptyClass fec; + + public ClassWithADeepImmutableFieldWhichIsSetInTheConstructor(FinalEmptyClass fec) { + this.fec = fec; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithDeepImmutableFieldWhereTheObjectCanEscape.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithDeepImmutableFieldWhereTheObjectCanEscape.java new file mode 100644 index 0000000000..b7a936e7b5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithDeepImmutableFieldWhereTheObjectCanEscape.java @@ -0,0 +1,26 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "class has only the deep immutable field fec", +analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +public class ClassWithDeepImmutableFieldWhereTheObjectCanEscape { + + @DeepImmutableField("immutable reference and immutable field type FinalEmptyClass") + @ImmutableFieldReference("field is effective immutable") + private FinalEmptyClass fec = new FinalEmptyClass(); + + public FinalEmptyClass getFec() { + return fec; + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithDeepImmutableFieldWithGetter.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithDeepImmutableFieldWithGetter.java new file mode 100644 index 0000000000..109a8075f2 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithDeepImmutableFieldWithGetter.java @@ -0,0 +1,35 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +@MutableType(value="class is extensible", analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "class has only the deep immutable field fec", + analyses = {L1ClassImmutabilityAnalysis.class, L0ClassImmutabilityAnalysis.class}) +public class ClassWithDeepImmutableFieldWithGetter { + @DeepImmutableField(value = "Immutable Reference and Immutable Field Type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle transitive immutability", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "declared final field", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private final FinalEmptyClass fec = new FinalEmptyClass(); + + public FinalEmptyClass getFec() { + return fec; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithEffectiveDeepImmutableField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithEffectiveDeepImmutableField.java new file mode 100644 index 0000000000..1d1bc50b4e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithEffectiveDeepImmutableField.java @@ -0,0 +1,39 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +//@DeepImmutableClass(value = "class has only the deep immutable field tmc", +// analyses = L1ClassImmutabilityAnalysis.class) +@ShallowImmutableClass(value = "class has only the shallow immutable field tmc", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +public class ClassWithEffectiveDeepImmutableField { + + @MutableField(value="can not handle effective immutability", + analyses = L0FieldImmutabilityAnalysis.class) + // @DeepImmutableField(value = "immutable reference and mutable object that can not escape", + // analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle transitive immutability", + analyses = {L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "effectively immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private ClassWithPublicFields tmc = new ClassWithPublicFields(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithFieldReferringEscapingObject.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithFieldReferringEscapingObject.java new file mode 100644 index 0000000000..fb77aec250 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithFieldReferringEscapingObject.java @@ -0,0 +1,42 @@ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.fixtures.immutability.classes.inheriting.EmptyClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@ShallowImmutableClass(value = "class has the shallow immutable field o", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +public class ClassWithFieldReferringEscapingObject { + + @ShallowImmutableField(value = "mutable referenced object can escape", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "final field", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private final Object o; + + public ClassWithFieldReferringEscapingObject() { + this.o = new EmptyClass(); + } + + public ClassWithFieldReferringEscapingObject(int n) { + this.o = new Object(); + } + + public Object getO(){ + return this.o; + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithPrivateFinalFieldAndMutableTypeThatIsSetInTheConstructor.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithPrivateFinalFieldAndMutableTypeThatIsSetInTheConstructor.java new file mode 100644 index 0000000000..aa9b8e1314 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithPrivateFinalFieldAndMutableTypeThatIsSetInTheConstructor.java @@ -0,0 +1,33 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@ShallowImmutableClass(value = "class has only the shallow immutable field tmc", +analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +public class ClassWithPrivateFinalFieldAndMutableTypeThatIsSetInTheConstructor { + + @ShallowImmutableField(value = "immutable field reference and mutable type ClassWithPublicFields", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "declared final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private final ClassWithPublicFields tmc; + + public ClassWithPrivateFinalFieldAndMutableTypeThatIsSetInTheConstructor(ClassWithPublicFields tmc) { + this.tmc = tmc; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithPrivateNotEffectiveImmutableField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithPrivateNotEffectiveImmutableField.java new file mode 100644 index 0000000000..6341d35a80 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithPrivateNotEffectiveImmutableField.java @@ -0,0 +1,33 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "class has the mutable field tmc", + analyses = {L0ClassImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +public class ClassWithPrivateNotEffectiveImmutableField { + + @MutableField(value = "field has mutable reference", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "referenced object can be changed via setter", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private ClassWithPublicFields tmc = new ClassWithPublicFields(); + + public void setTmc(ClassWithPublicFields tmc) { + this.tmc = tmc; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithPrivateNotEffectiveImmutableFieldWithDeepImmutableType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithPrivateNotEffectiveImmutableFieldWithDeepImmutableType.java new file mode 100644 index 0000000000..7702908ff6 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithPrivateNotEffectiveImmutableFieldWithDeepImmutableType.java @@ -0,0 +1,23 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass("Because it has Mutable Fields") +public class ClassWithPrivateNotEffectiveImmutableFieldWithDeepImmutableType { + + public void setFec(FinalEmptyClass fec) { + this.fec = fec; + } + + @MutableField("Because of Mutable Reference") + @MutableFieldReference("Not final field could be set via setter") + private FinalEmptyClass fec = new FinalEmptyClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithProtectedFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithProtectedFields.java new file mode 100644 index 0000000000..d3f1d90be3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithProtectedFields.java @@ -0,0 +1,68 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +public class ClassWithProtectedFields { + @MutableField(value = "the field has a mutable field reference", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "the field is protected", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + protected FinalEmptyClass fec1 = new FinalEmptyClass(); + + @MutableField("Because of Mutable Reference") + @MutableFieldReference(value = "Because it is declared as protected", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + protected ClassWithPublicFields cwpf1 = new ClassWithPublicFields(); + + @ShallowImmutableField(value = "field has an immutable reference and mutable type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "Declared final Field", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private final ClassWithPublicFields cwpf2 = new ClassWithPublicFields(); + + @DeepImmutableField(value = "immutable reference and deep immutable field type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "Declared final Field", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private final FinalEmptyClass fec2 = new FinalEmptyClass(); +} + +@MutableType(value = "class has mutable fields n and name", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "class has mutable fields n and name", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class ClassWithPublicFields { + + @MutableField(value = "field is public", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + public int n = 0; + + @MutableField(value = "field is public", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + public String name = "name"; +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithShallowImmutableField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithShallowImmutableField.java new file mode 100644 index 0000000000..512228ca11 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithShallowImmutableField.java @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; + +@ShallowImmutableClass("class has only the shallow immutable field tmc") +public class ClassWithShallowImmutableField { + + @ShallowImmutableField("field has an immutable field reference and mutable type") + @ImmutableFieldReference("declared final reference") + private final ClassWithPublicFields tmc = new ClassWithPublicFields(); + + public ClassWithPublicFields getTmc() { + return tmc; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithShallowImmutableFieldWhereTheObjectCanEscapeViaGetter.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithShallowImmutableFieldWhereTheObjectCanEscapeViaGetter.java new file mode 100644 index 0000000000..860da075fa --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithShallowImmutableFieldWhereTheObjectCanEscapeViaGetter.java @@ -0,0 +1,27 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@ShallowImmutableClass(value = "class has only the shallow immutable field tmc", +analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +public class ClassWithShallowImmutableFieldWhereTheObjectCanEscapeViaGetter { + + @ShallowImmutableField("Because of Immutable Reference and Mutable Field Type") + @ImmutableFieldReference(value = "effective immutable field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private ClassWithPublicFields tmc = new ClassWithPublicFields(); + + public ClassWithPublicFields getTmc() { + return tmc; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithStaticFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithStaticFields.java new file mode 100644 index 0000000000..2606b03db2 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithStaticFields.java @@ -0,0 +1,27 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("") +@DeepImmutableClass("") +public class ClassWithStaticFields { + + @MutableField("") + @MutableFieldReference("") + public static String name = "Class with static fields"; + + // @ShallowImmutableFieldAnnotation("") + // @ImmutableReferenceAnnotation("") + private static int counter; + ClassWithStaticFields() { + counter++; + } + + public void setCounter(int n) { + counter = n; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithdirectlySetDeepImmutableField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithdirectlySetDeepImmutableField.java new file mode 100644 index 0000000000..da631cbd38 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithdirectlySetDeepImmutableField.java @@ -0,0 +1,33 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; + +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "Because it has only Deep Immutable Field Types", + analyses = L1ClassImmutabilityAnalysis.class) +public class ClassWithdirectlySetDeepImmutableField { + + @DeepImmutableField(value = "Immutable Reference and Immutable Field Type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle transitive immutability", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @MutableField(value = "can not handle effective immutability", + analyses = L0FieldImmutabilityAnalysis.class) + @ImmutableFieldReference("effective immutable field") + private FinalEmptyClass name = new FinalEmptyClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ConstructorWithEscapingParameters.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ConstructorWithEscapingParameters.java new file mode 100644 index 0000000000..83f28ccf8f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ConstructorWithEscapingParameters.java @@ -0,0 +1,32 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; + +public class ConstructorWithEscapingParameters { + + @ShallowImmutableField("The field is init") + @ImmutableFieldReference("The field is only assigned in the constructor.") + private TrivialClass tc1; + + //TODO @DeepImmutableField("The construtor pararameter of the assigned object not escape") + @ImmutableFieldReference("The field is only assigned in the constructor.") + private TrivialClass tc2; + + ConstructorWithEscapingParameters(Object o1, Object o2){ + tc1 = new TrivialClass(o1, o2); + tc2 = new TrivialClass(new Object(), new Object()); + } + +} + +class TrivialClass{ + private Object o1; + private Object o2; + public TrivialClass(Object o1, Object o2){ + this.o1 = o1; + this.o2 = 02; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/DifferentModifier.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/DifferentModifier.java new file mode 100644 index 0000000000..9ba8721660 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/DifferentModifier.java @@ -0,0 +1,91 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +/** + * This testclass tests that different modifiers like transient, volatile or static + * does not have an impact of mutability. + * + * @author Tobias Roth + * + */ +@MutableType(value= "class has different mutable fields", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "class has different mutable fields", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +public class DifferentModifier { + + @MutableField(value = "field has a mutable field reference", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public int a = 5; + + @MutableField(value = "field has a mutable field reference", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public transient int b = 5; + + @MutableField(value = "field has a mutable field reference", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public volatile int c = 5; + + @MutableField(value = "field has a mutable field reference", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public volatile long ctl; + + DifferentModifier(long ctl){ + this.ctl = ctl; + } + + static final class InnerClass { + + @MutableField(value = "field has a mutable field reference", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public static int LEFT = 1; + + @MutableField(value = "field has a mutable field reference", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public int c = 5; + + @MutableField(value = "field has a mutable field reference", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public transient int d = 5; + + @MutableField(value = "field has a mutable field reference", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public volatile int e = 5; + + @MutableField(value = "field has a mutable field reference", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public volatile transient int f = 5; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/EffectivelyImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/EffectivelyImmutableFields.java new file mode 100644 index 0000000000..f14ae82653 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/EffectivelyImmutableFields.java @@ -0,0 +1,509 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import java.util.List; +import java.util.LinkedList; +import java.util.Set; +import java.util.HashSet; +import java.util.HashMap; +import java.util.ArrayList; + +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +public class EffectivelyImmutableFields { +/* + @DeepImmutableField(value = "field value has a primitive type and an immutable field reference", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle transitive immutability", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @MutableField(value = "can not handle effective immutabiltiy", analyses = L0FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "field is not written after initialization", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private int simpleInitializedFieldWithPrimitiveType = 5; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private int lazyInitializedFieldWithPrimitiveType; + + public synchronized void initLazyInitializedFieldWithPrimitiveType(){ + if(lazyInitializedFieldWithPrimitiveType == 0) + lazyInitializedFieldWithPrimitiveType = 5; + } + + @DeepImmutableField("Lazy initialized field with primitive type.") + @LazyInitializedThreadSafeFieldReference("field is thread safely lazy initialized") + private int inTheGetterLazyInitializedFieldWithPrimitiveType; + + public synchronized int getInTheGetterLazyInitializedFieldWithPrimitiveType(){ + if(inTheGetterLazyInitializedFieldWithPrimitiveType ==0) + inTheGetterLazyInitializedFieldWithPrimitiveType = 5; + return inTheGetterLazyInitializedFieldWithPrimitiveType; + } + + @DeepImmutableField(value = "immutable reference and deep immutable type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective immutable field", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Integer effectiveImmutableIntegerField = 5; + + + @MutableField(value = "due to mutable field reference", + analyses = L3FieldImmutabilityAnalysis.class) + @LazyInitializedNotThreadSafeFieldReference(value = "write of reference objects is not atomic", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Integer simpleLazyInitializedIntegerField; + + public void initSimpleLazyInitializedIntegerField(){ + if(simpleLazyInitializedIntegerField==0) + simpleLazyInitializedIntegerField = 5; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized method") + private Integer synchronizedSimpleLazyInitializedIntegerField; + + public synchronized void initNO2(){ + if(synchronizedSimpleLazyInitializedIntegerField==0) + synchronizedSimpleLazyInitializedIntegerField = 5; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private Integer inGetterSynchronizedSimpleLazyInitializedIntegerField; + + public synchronized Integer getInGetterSynchronizedSimpleLazyInitializedIntegerField(){ + if(inGetterSynchronizedSimpleLazyInitializedIntegerField==0) + inGetterSynchronizedSimpleLazyInitializedIntegerField = 5; + return inGetterSynchronizedSimpleLazyInitializedIntegerField; + } + + @DeepImmutableField(value = "field value has a primitive type and an immutable field reference", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle transitive immutability", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @MutableField(value = "can not handle effective immutabiltiy", analyses = L0FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "field is effective immutable", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private double effectiveImmutableDoubleField = 5d; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private double synchronizedLazyInitializedDoubleField; + + public synchronized void initD2(){ + if(synchronizedLazyInitializedDoubleField ==0d) + synchronizedLazyInitializedDoubleField = 5d; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private double inGetterSynchronizedLazyInitializedDoubleField; + + public synchronized double getD3(){ + if(inGetterSynchronizedLazyInitializedDoubleField==0d) + inGetterSynchronizedLazyInitializedDoubleField = 5; + return inGetterSynchronizedLazyInitializedDoubleField; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @ImmutableFieldReference("field is effective immutable") + private Double effectiveImmutableObjectDoubleField = 5d; + + @DeepImmutableField("field has an immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private Double lazyInitializedObjectDoubleField; + + public synchronized void initLazyInitializedObjectDoubleField(){ + if(lazyInitializedObjectDoubleField==0) + lazyInitializedObjectDoubleField = 5d; + } + + @DeepImmutableField("field has an immutable reference and a deep immutable type") + @LazyInitializedThreadSafeFieldReference("field is in a synchronized getter lazy initialized") + private Double inAGetterLazyInitializedObjectDoubleField; + + public synchronized Double getInAGetterLazyInitializedObjectDoubleField(){ + if(inAGetterLazyInitializedObjectDoubleField==0) + inAGetterLazyInitializedObjectDoubleField = 5d; + return inAGetterLazyInitializedObjectDoubleField; + } + + @DeepImmutableField(value = "field value has a primitive type and an immutable field reference", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle transitive immutability", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @MutableField(value = "can not handle effective immutabiltiy", analyses = L0FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "field is not written after initialization", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private float effectiveImmutableFloatField = 5; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private float synchronizedLazyInitializedFloatField; + + public synchronized void initF2(){ + if(synchronizedLazyInitializedFloatField ==0) + synchronizedLazyInitializedFloatField = 5f; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private float inGetterSynchronizedLazyInitializedFloatField; + + public synchronized float getf3(){ + if(inGetterSynchronizedLazyInitializedFloatField==0) + inGetterSynchronizedLazyInitializedFloatField = 5f; + return inGetterSynchronizedLazyInitializedFloatField; + } + + @DeepImmutableField("field has an immutable field reference and a deep immutable type") + @ImmutableFieldReference("the field reference is effective immutable") + private Float effectiveImmutableFloatObjectField = 5f; + + @DeepImmutableField("field has an immutable field reference and a deep immutable type") + @LazyInitializedThreadSafeFieldReference("the field is thread safely lazy initialized") + private Float lazyInitializedFloatObjectField; + + public synchronized void initFO2(){ + if(lazyInitializedFloatObjectField ==0) + lazyInitializedFloatObjectField = 5f; + } + + @DeepImmutableField("field has an immutable field reference and a deep immutable type") + @LazyInitializedThreadSafeFieldReference("the field is in a getter thread safely lazy initialized") + private float inAGetterLazyInitializedFloatObjectField; + + public synchronized Float getInAGetterLazyInitializedFloatObjectField(){ + if(inAGetterLazyInitializedFloatObjectField==0) + inAGetterLazyInitializedFloatObjectField = 5f; + return inAGetterLazyInitializedFloatObjectField; + } + + @DeepImmutableField(value = "field value has a primitive type and an immutable field reference", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle transitive immutability", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @MutableField(value = "can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "field is effective immutable", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private byte effectiveImmutableByteField = 5; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safely lazy initialized") + private byte synchronizedLazyInitializedByteField; + + public synchronized void initB2(){ + if(synchronizedLazyInitializedByteField ==0) + synchronizedLazyInitializedByteField = 5; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private byte inGetterSynchronizedLazyInitializedByteField; + + public synchronized byte getInGetterSynchronizedLazyInitializedByteField(){ + if(inGetterSynchronizedLazyInitializedByteField==0) + inGetterSynchronizedLazyInitializedByteField = 5; + return inGetterSynchronizedLazyInitializedByteField; + } + + @DeepImmutableField(value = "field value has a primitive type and an immutable field reference", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle transitive immutability", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @MutableField(value = "can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "field is effective immutable", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Byte effectiveImmutableByteObjectField = 5; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safely lazy initialized") + private Byte lazyInitializedByteObjectField; + + public synchronized void initBO2(){ + if(lazyInitializedByteObjectField ==0) + lazyInitializedByteObjectField = 5; + } + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safely lazy initialized in a getter") + private Byte inAGetterLazyInitializedByteObjectField; + + public synchronized Byte getInAGetterLazyInitializedByteObjectField(){ + if(inAGetterLazyInitializedByteObjectField==0) + inAGetterLazyInitializedByteObjectField = 5; + return inAGetterLazyInitializedByteObjectField; + } + + @DeepImmutableField(value = "field value has a primitive type and an immutable field reference", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle transitive immutability", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @MutableField(value = "can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "field is effective immutable", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private char c = 'a'; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private char synchronizedLazyInitializedCharField; + + public synchronized void initC2(){ + if(synchronizedLazyInitializedCharField == '\u0000') + synchronizedLazyInitializedCharField = 'a'; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private char inGetterSynchronizedLazyInitializedCharField; + + public synchronized char c3(){ + if(inGetterSynchronizedLazyInitializedCharField == '\u0000') + inGetterSynchronizedLazyInitializedCharField = 5; + return inGetterSynchronizedLazyInitializedCharField; + } + + @DeepImmutableField(value = "field value has a primitive type and an immutable field reference", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle transitive immutability", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @MutableField(value = "can not handle effective immutabiltiy", analyses = L0FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "field is not written after initialization", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private long effectiveImmutableLongField = 5; + + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private long sychronizedLazyInitializedLongField; + + public synchronized void initL2(){ + if(sychronizedLazyInitializedLongField == 0l) + sychronizedLazyInitializedLongField = 5l; + } + + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private long inGetterSynchronizedLazyInitializedLongField; + + public synchronized long getInGetterSynchronizedLazyInitializedLongField(){ + if(inGetterSynchronizedLazyInitializedLongField == 0l) + inGetterSynchronizedLazyInitializedLongField = 5; + return inGetterSynchronizedLazyInitializedLongField; + } + + @DeepImmutableField("") + @ImmutableFieldReference("") + private Long lO = 5l; + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("") + private Long lO2; + + public synchronized void initLO2(){ + if(lO2 == 0l) + lO2 = 5l; + } + + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("") + private Long lO3; + + public synchronized Long lO3(){ + if(lO3 == 0l) + lO3 = 5l; + return lO3; + } + + @DeepImmutableField("The concrete assigned object is known to be deep immutable") + @ImmutableFieldReference("The field is effective immutable") + private String effectiveImmutableString = "abc"; + + @DeepImmutableField("The concrete type of the object that is assigned is known") + @LazyInitializedThreadSafeFieldReference("lazy initialized within a synchronized method") + private String lazyInitializedString; + + public synchronized void initLazyInitializedString(){ + if(lazyInitializedString == null) + lazyInitializedString = "abc"; + } + + @DeepImmutableField("The concrete type of the object that is assigned is known") + @LazyInitializedThreadSafeFieldReference("lazy initialized within a synchronized method") + private String inAGetterLazyInitializedString; + + public synchronized String getInAGetterLazyInitializedString(){ + if(inAGetterLazyInitializedString == null) + inAGetterLazyInitializedString = "abc"; + return inAGetterLazyInitializedString; + } + + @DeepImmutableField("The concrete assigned object is known to be deep immutable") + @ImmutableFieldReference("The field is effective immutable") + private Object effectiveImmutableObjectReference = new Object(); + + @DeepImmutableField("The concrete type of the object that is assigned is known") + @LazyInitializedThreadSafeFieldReference("lazy initialized within a synchronized method") + private Object lazyInitializedObjectReference; + + public synchronized void initLazyInitializedObjectReference(){ + if(lazyInitializedObjectReference == null) + lazyInitializedObjectReference = new Object(); + } + + @DeepImmutableField("The concrete type of the object that is assigned is known") + @LazyInitializedThreadSafeFieldReference("lazy initialized within a synchronized method") + private Object inAGetterLazyInitializedObjectReference; + + public synchronized Object getInAGetterLazyInitializedObjectReference(){ + if(inAGetterLazyInitializedObjectReference == null) + inAGetterLazyInitializedObjectReference = new Object(); + return inAGetterLazyInitializedObjectReference; + } + + + + @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @ImmutableFieldReference("effective immutable reference") + private List effectiveImmutableLinkedList = new LinkedList(); + + @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private List lazyInitializedLinkedList; + + public synchronized void initLinkedList2(){ + if(lazyInitializedLinkedList == null) + lazyInitializedLinkedList = new LinkedList(); + } + + @ShallowImmutableField("concrete assigned object is known but manipulation of the referenced object with .add") + @LazyInitializedThreadSafeFieldReference("thread safe lazy initialization due to synchronized method") + private List lazyInitializedLinkedListWithManipulationAfterwards; + + public synchronized void initLinkedList3(){ + if(lazyInitializedLinkedListWithManipulationAfterwards == null) + lazyInitializedLinkedListWithManipulationAfterwards = new LinkedList(); + lazyInitializedLinkedListWithManipulationAfterwards.add(new Object()); + } + + @DeepImmutableField("The concrete type of the object that is assigned is known " + + "and no manipulation after assignment") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private Object inTheGetterLazyInitializedlinkedList; + + public synchronized Object getInTheGetterLazyInitializedlinkedList(){ + if(inTheGetterLazyInitializedlinkedList == null) + inTheGetterLazyInitializedlinkedList = new Object(); + return inTheGetterLazyInitializedlinkedList; + } + + @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @ImmutableFieldReference("effective immutable reference") + private List effectiveImmutableArrayList = new ArrayList(); + + @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private List lazyInitializedArrayList; + + public synchronized void initLazyInitializedArrayList(){ + if(lazyInitializedArrayList == null) + lazyInitializedArrayList = new ArrayList(); + } + + @ShallowImmutableField("concrete assigned object is known but manipulation of the referenced object with .add") + @LazyInitializedThreadSafeFieldReference("thread safe lazy initialization due to synchronized method") + private List lazyInitializedArrayListWithManipulationAfterwards; + + public synchronized void initLazyInitializedArrayListWithManipulationAfterwards(){ + if(lazyInitializedArrayListWithManipulationAfterwards == null) + lazyInitializedArrayListWithManipulationAfterwards = new ArrayList(); + lazyInitializedArrayListWithManipulationAfterwards.add(new Object()); + } + + @ShallowImmutableField("immutable reference but assigned object escapes via getter") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private List inTheGetterLazyInitializedArrayList; + + public synchronized List getInTheGetterLazyInitializedArrayList(){ + if(inTheGetterLazyInitializedArrayList == null) + inTheGetterLazyInitializedArrayList = new ArrayList(); + return inTheGetterLazyInitializedArrayList; + } + + @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @ImmutableFieldReference("effective immutable reference") + private Set effectiveImmutableSet = new HashSet(); + + @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private Set lazyInitializedSet; + + public synchronized void initLazyInitializedSet(){ + if(lazyInitializedSet == null) + lazyInitializedSet = new HashSet(); + } + + @ShallowImmutableField("concrete assigned object is known but manipulation of the referenced object with .add") + @LazyInitializedThreadSafeFieldReference("thread safe lazy initialization due to synchronized method") + private Set lazyInitializedSetWithManipulationAfterwards; + + public synchronized void initSet3(){ + if(lazyInitializedSetWithManipulationAfterwards == null) + lazyInitializedSetWithManipulationAfterwards = new HashSet(); + lazyInitializedSetWithManipulationAfterwards.add(new Object()); + } + + @ShallowImmutableField("immutable reference but assigned object escapes via getter") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private Set inTheGetterLazyInitializedSet; + + public synchronized Set getInTheGetterLazyInitializedSet(){ + if(inTheGetterLazyInitializedSet == null) + inTheGetterLazyInitializedSet = new HashSet(); + return inTheGetterLazyInitializedSet; + } + + @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @ImmutableFieldReference("effective immutable reference") + private HashMap effectiveImmutableHashMap = new HashMap(); + + @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private HashMap lazyInitializedHashMap; + + public synchronized void initLazyInitializedHashMap(){ + if(lazyInitializedHashMap == null) + lazyInitializedHashMap = new HashMap(); + } + + @ShallowImmutableField("concrete assigned object is known but manipulation of the referenced object with .put") + @LazyInitializedThreadSafeFieldReference("thread safe lazy initialization due to synchronized method") + private HashMap lazyInitializedHashMapWithManipulationAfterwards; + + public synchronized void initHashMap3(){ + if(lazyInitializedHashMapWithManipulationAfterwards == null) + lazyInitializedHashMapWithManipulationAfterwards = new HashMap(); + lazyInitializedHashMapWithManipulationAfterwards.put(new Object(), new Object()); + } + + @ShallowImmutableField("immutable reference but assigned object escapes via getter") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private HashMap inTheGetterLazyInitializedHashMap; + + public synchronized HashMap getInTheGetterLazyInitializedHashMap(){ + if(inTheGetterLazyInitializedHashMap == null) + inTheGetterLazyInitializedHashMap = new HashMap(); + return inTheGetterLazyInitializedHashMap; + } +*/ +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/Escapers.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/Escapers.java new file mode 100644 index 0000000000..97f5c708d7 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/Escapers.java @@ -0,0 +1,146 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DependentImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +public class Escapers{ + +} + +class TransitiveEscape1 { + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private ClassWithPublicFields tmc = new ClassWithPublicFields(); + + public void printTMC(){ + System.out.println(tmc.name); + } + + public ClassWithPublicFields get(){ + ClassWithPublicFields tmc1 = this.tmc; + return tmc1; + } +} + +class TransitiveEscape2 { + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private ClassWithPublicFields tmc = new ClassWithPublicFields(); + + public void printTMC(){ + System.out.println(tmc.name); + } + + public ClassWithPublicFields get(){ + ClassWithPublicFields tmc1 = this.tmc; + ClassWithPublicFields tmc2 = tmc1; + return tmc2; + } +} +class OneThatNotEscapesAndOneWithDCL { + @ShallowImmutableField(value = "immutable field reference and mutable type", + analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "field is only written once", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private ClassWithPublicFields tmc1 = new ClassWithPublicFields(); + + @MutableField(value = "mutable reference", analyses = { + L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class + }) + @LazyInitializedNotThreadSafeFieldReference("") + private ClassWithPublicFields tmc2; + + public ClassWithPublicFields set() { + ClassWithPublicFields tmc22 = tmc2; + if (tmc22 == null) { + synchronized (this) { + if (tmc22 == null) { + tmc2 = new ClassWithPublicFields(); + } + } + } + return tmc2; + } +} +class GenericEscapes { + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private SimpleGenericClass sgc; + + public GenericEscapes(ClassWithPublicFields tmc){ + sgc = new SimpleGenericClass(tmc); + } +} + + +@ShallowImmutableClass("") +class GenericEscapesTransitive { + @ShallowImmutableField("") + @ImmutableFieldReference("") + private SimpleGenericClass gc1; + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private ClassWithPublicFields tmc; + + public GenericEscapesTransitive(ClassWithPublicFields tmc){ + this.tmc = tmc; + gc1 = new SimpleGenericClass(this.tmc); + } +} + +class GenericNotEscapesMutualEscapeDependencyNotAbleToResolve{ + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private ClassWithPublicFields tmc = new ClassWithPublicFields(); + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private SimpleGenericClass sgc; + + public GenericNotEscapesMutualEscapeDependencyNotAbleToResolve() { + + this.sgc = new SimpleGenericClass(this.tmc); + } +} + +class GenericNotEscapesMutualEscapeDependencyAbleToResolve{ + + @DeepImmutableField("") + @ImmutableFieldReference("") + private FinalEmptyClass fec = new FinalEmptyClass(); + + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference("") + private SimpleGenericClass sgc; + + public GenericNotEscapesMutualEscapeDependencyAbleToResolve() { + this.sgc = new SimpleGenericClass(this.fec); + } +} + +@DependentImmutableClass("") +final class SimpleGenericClass { + @DependentImmutableField(value = "") + private T t; + SimpleGenericClass(T t){ + this.t = t; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/FinalEmptyClass.java new file mode 100644 index 0000000000..57236095a6 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/FinalEmptyClass.java @@ -0,0 +1,16 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.types.DeepImmutableType; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; + +@DeepImmutableType(value = "class is not extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "class has no instance fields", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +public final class FinalEmptyClass { +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/MethodCalls.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/MethodCalls.java new file mode 100644 index 0000000000..f789708f99 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/MethodCalls.java @@ -0,0 +1,103 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; + +public class MethodCalls { + @ShallowImmutableField("") + @LazyInitializedThreadSafeFieldReference("") + private TestMutable tm1; + + public synchronized void getTM1(){ + if(tm1==null){ + tm1= new TestMutable(); + } + tm1.nop(); + } + + @ShallowImmutableField("") + @LazyInitializedThreadSafeFieldReference("") + private TestMutable tm2; + + public synchronized TestMutable getTM2(){ + if(tm2==null){ + tm2= new TestMutable(); + } + return tm2; + } + + @MutableField("") + @LazyInitializedNotThreadSafeFieldReference("") + private TestMutable tm3; + + public void getTm3() { + if(tm3==null){ + tm3 = new TestMutable(); + } + } + + @MutableFieldReference("") + @MutableField("") + private TestMutable tm4; + + public synchronized TestMutable getTm4() { + if(tm4==null){ + tm4 = new TestMutable(); + } + return tm4; + } + + public synchronized TestMutable getTm42() { + if(tm4==null){ + tm4 = new TestMutable(); + } + return tm4; + } + + @ShallowImmutableField("") + private TestMutable tm5; + + public synchronized void getTm5() { + if(tm5==null){ + tm5 = new TestMutable(); + } + tm5.nop(); + } + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private TestMutable tm6 = new TestMutable(); + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private TestMutable tm7 = new TestMutable(); + + public void foo(){ + tm7.nop(); + } + + + + + + + + +} + +class TestMutable{ + private int n = 5; + + public void setN(int n){ + this.n = n; + } + + public void nop(){ + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox15/NonVirtualMethodCallFalsePositive.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox15/NonVirtualMethodCallFalsePositive.java new file mode 100644 index 0000000000..ee4ac2fdbc --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox15/NonVirtualMethodCallFalsePositive.java @@ -0,0 +1,36 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox15; + +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; + +public class NonVirtualMethodCallFalsePositive { + + public float n = 5; + + private float byteValue(){ + return n; + } + + //@ImmutableFieldReference("") + private NonVirtualMethodCallFalsePositive instance; + public synchronized NonVirtualMethodCallFalsePositive getInstance(){ + NonVirtualMethodCallFalsePositive tmpInstance = this.instance; + boolean b = tmpInstance.byteValue() == 0; + if(b==true){ + instance = tmpInstance = new NonVirtualMethodCallFalsePositive(); + } + return instance; + } + +} + +class Test{ + int n; + public Test(int n){ + this.n = n; + } + /*sun.util.locale.provider.DictionaryBasedBreakIterator + sun.util.calendar.ZoneInfoFile.ruleArray + com.sun.media.sound.SoftEnvelopeGenerator.on + java.awt.Container.EMPTY_ARRAY */ + //sun.awt.AppContext.threadAppContext +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox20/StaticEffectiveDeepImmutablFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox20/StaticEffectiveDeepImmutablFields.java new file mode 100644 index 0000000000..32319a6c79 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox20/StaticEffectiveDeepImmutablFields.java @@ -0,0 +1,56 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox20; + +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; + +import java.io.IOException; + +public class StaticEffectiveDeepImmutablFields { + //@DeepImmutableField("") + //private static Object o = new Object(); + + //@DeepImmutableField("") + //private static Object[] objectArray = new ; + + @ShallowImmutableField("") + private static MutableClass mc = new MutableClass(); + + + @ShallowImmutableField("") + private final int[] terminationLock = new int[0]; + + /*static { + objectArray = new Object[]{new Object(), new Object()}; + }*/ + int n = 10; + + public Object get(){ + synchronized (this.terminationLock){ + n = 5; + } + return identity(mc); + } + + //javax.security.auth.login.LoginContext.PARAMS + //com.sun.jmx.remote.internal.ServerNotifForwarder + //sun.misc.SoftCache.entrySet + + public Object identity(Object o){ + return o; + } + + private int checkState() throws IOException { + int n = 10; + synchronized(this.terminationLock) { + n = 5; + } + return n; + } + +} + +class MutableClass { + public int n = 5; +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox21/Test.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox21/Test.java new file mode 100644 index 0000000000..70c4c49035 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox21/Test.java @@ -0,0 +1,32 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox21; + + +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; + +public class Test { + + @LazyInitializedNotThreadSafeFieldReference("") +private Object o = null; + public void getO() { + if (this.o == null) { + this.o = new Object(); + } + //javax.swing.text.DefaultEditorKit.defaultActions + //java.net.InetAddress.serialPersistentFields + //java.beans.PropertyChangeSupport.serialPersistentFields + //com.sun.corba.se.impl.io.ObjectStreamClass.noArgsList + //com.sun.java.swing.plaf.gtk.GTKColorType.HLS_COLORS + //com.sun.xml.internal.bind.v2.ClassFactory.emptyClass + //com.sun.beans.editors.EnumEditor.tags + //com.sun.beans.finder.InstanceFinder.EMPTY + //com.sun.crypto.provider.AESCrypt.S + /*com.sun.beans.finder.InstanceFinder.EMPTY + com.sun.jmx.mbeanserver.ClassLoaderRepositorySupport.EMPTY_LOADER_ARRAY + com.sun.jndi.ldap.LdapDnsProviderService.LOCK + sun.text.normalizer.UBiDiProps.jgArray + sun.util.calendar.ZoneInfoFile.ruleArray + sun.util.calendar.ZoneInfoFile.regions + sun.nio.cs.ext.SJIS_0213 $Decoder.cc + com.sun.media.sound.SoftChannel.controller */ + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox22/EscapeFailureTest.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox22/EscapeFailureTest.java new file mode 100644 index 0000000000..595643a1d2 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox22/EscapeFailureTest.java @@ -0,0 +1,18 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox22; + +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; + +public class EscapeFailureTest { + + @ShallowImmutableField("") + private static final Object[] o; + + static { + o = new Object[]{new MutableClass()}; + } +} + + +class MutableClass { + public int n = 5; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox25/TestClass5.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox25/TestClass5.java new file mode 100644 index 0000000000..7f145474d9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox25/TestClass5.java @@ -0,0 +1,16 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox25; + +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; + +public class TestClass5 { + @ShallowImmutableField("") + private static final Object[] o = new Object[0]; + + public Object identity(Object o){ + return o; + } + + public Object getO(){ + return this.o; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox26/DependentClassWithGenericFieldWithOneLeftGenericParameter.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox26/DependentClassWithGenericFieldWithOneLeftGenericParameter.java new file mode 100644 index 0000000000..6d2aecc4c7 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox26/DependentClassWithGenericFieldWithOneLeftGenericParameter.java @@ -0,0 +1,86 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox26; + +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; + +final class DependentClassWithGenericFieldWithOneLeftGenericParameter { + + @DependentImmutableField(value = "has one left generic parameter T and no shallow or mutable types", + analyses = L3FieldImmutabilityAnalysis.class) + private SimpleGenericClass sgc; + + public DependentClassWithGenericFieldWithOneLeftGenericParameter(T t) { + sgc = new SimpleGenericClass<>(t, new FinalEmptyClass(), new FinalEmptyClass()); + } + +} + +final class SimpleGenericClass { + + private T1 t1; + + private T2 t2; + + private T3 t3; + + public SimpleGenericClass(T1 t1, T2 t2, T3 t3){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + } + /*com.oracle.webservices.internal.api.databinding.DatabindingModeFeature + com.oracle.webservices.internal.api.databinding.ExternalMetadataFeature + com.oracle.webservices.internal.api.message.ContentType + com.oracle.xmlns.internal.webservices.jaxws_databinding.XmlWebServiceRef.lookup + com.sun.activation.registries.MailcapTokenizer.currentToken + com.oracle.webservices.internal.api.message.BasePropertySet $PropertyMap.cachedEntries + com.sun.imageio.plugins.bmp.BMPImageWriter + com.sun.java.swing.plaf.windows.WindowsFileChooserUI + com.sun.java.swing.plaf.windows.WindowsIconFactory + com.sun.nio.zipfs.ZipPath + com.sun.corba.se.impl.corba.AnyImplHelper.__typeCode + com.sun.corba.se.impl.corba.TypeCodeImplHelper.__typeCode + com.sun.corba.se.impl.encoding.CachedCodeBase.bases + // com.sun.corba.se.impl.encoding.CachedCodeBase. + com.sun.corba.se.impl.encoding.CachedCodeBase.implementations + com.sun.corba.se.impl.naming.namingutil.INSURLHandler.insURLHandler + com.sun.corba.se.impl.oa.toa.TOAFactory.toa + com.sun.corba.se.impl.orb.ORBSingleton.fullORB + com.sun.corba.se.impl.protocol.giopmsgheaders.TargetAddressHelper.__typeCode + com.sun.corba.se.spi.activation.ActivatorHelper.__typeCode + jdk.nashorn.internal.objects.Global.builtinArrayBuffer + sun.util.locale.provider.LocaleServiceProviderPool.availableLocales + sun.util.locale.provider.JRELocaleProviderAdapter.localeData + sun.text.normalizer.UnicodeSet.INCLUSIONS + sun.text.normalizer.UBiDiProps.gBdpDummy + sun.nio.fs.UnixDirectoryStream + sun.nio.fs.UnixFileAttributes + com.oracle.net.Sdp $1.val$o + com.sun.activation.registries.MimeTypeEntry.type + com.sun.activation.registries.MailcapTokenizer.START_TOKEN*/ + /*javax.management.monitor.CounterMonitor.notifsInfo + javax.management.monitor.GaugeMonitor.notifsInfo + javax.management.monitor.StringMonitor.notifsInfo + com.sun.xml.internal.bind.v2.runtime.property.SingleMapNodeProperty $1.map + javax.management.monitor.CounterMonitor.notifsInfo + javax.management.monitor.GaugeMonitor.notifsInfo + javax.management.monitor.StringMonitor.notifsInfo + sun.awt.ExtendedKeyCodes.extendedKeyCodesSet + com.sun.jndi.ldap.Connection.pauseLock + com.sun.corba.se.impl.util.Utility.CACHE_MISS + com.sun.xml.internal.bind.v2.model.impl.ElementInfoImpl.adapter + com.sun.xml.internal.txw2.output.IndentingXMLStreamWriter.SEEN_ELEMENT + com.sun.xml.internal.ws.resources.PolicyMessages.messageFactory + com.sun.xml.internal.ws.wsdl.writer.WSDLPatcher.SCHEMA_REDEFINE_QNAME + com.sun.xml.internal.bind.v2.runtime.NameBuilder.attributeQNameIndexMap + com.sun.xml.internal.bind.v2.runtime.NameBuilder.attributeQNameIndexMap + sun.awt.ExtendedKeyCodes.regularKeyCodesMap + com.sun.xml.internal.bind.v2.model.impl.ElementInfoImpl.adapter + com.sun.xml.internal.bind.v2.model.impl.ElementInfoImpl.adapter + com.sun.beans.finder.PropertyEditorFinder.registry +*/ +} + + + +final class FinalEmptyClass{} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox30/Generic.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox30/Generic.java new file mode 100644 index 0000000000..41dbf23420 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox30/Generic.java @@ -0,0 +1,4 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox30; + +public class Generic { +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox30/Test.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox30/Test.java new file mode 100644 index 0000000000..c2a76fb669 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox30/Test.java @@ -0,0 +1,32 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox30; + +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; + +public class Test { + //sun.awt.ExtendedKeyCodes.regularKeyCodesMap + /* + @ShallowImmutableField("") + private static final int[] notifsInfo; + + static { + notifsInfo = new int[]{1}; + } + + public int[] getNotificationInfo() { + return notifsInfo.clone(); + } + + + @DeepImmutableField("The elements of the array can escape, but have a deep immutable reference.") + @LazyInitializedThreadSafeFieldReference("The array is thread safe lazily intialized.") + private Integer[] q; + public synchronized Integer getQ(){ + if(q==null) + q = new Integer[]{new Integer(1), new Integer(2), new Integer(3)}; + return q[2]; + }*/ + //com.sun.xml.internal.bind.v2.model.impl.ElementInfoImpl.adapter + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox30/Test2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox30/Test2.java new file mode 100644 index 0000000000..a176e4adb8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox30/Test2.java @@ -0,0 +1,28 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox30; + +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +public class Test2 { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "immutable reference with a generic types that inherits a mutable type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "effective immutable field reference", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private A a; + + Test2(A a){ + this.a = a; + } + } + +class FinalMutableClass{ + public int a = 5; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox31/TwoVirgin.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox31/TwoVirgin.java new file mode 100644 index 0000000000..bb2cf8dcf1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox31/TwoVirgin.java @@ -0,0 +1,44 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox31; + +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +class TwoVirgin { + + @ShallowImmutableField(value="field has generic parameter", analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "field is effective immutable", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private GenericBaseClass, B, C> gc1; + + public TwoVirgin(A a, B b, C c, GenericBaseClass gc1) { + this.gc1 = new GenericBaseClass, B, C>(gc1,b,c); + } +} + +final class GenericBaseClass { + + private T1 t1; + + private T2 t2; + + private T3 t3; + + public GenericBaseClass(T1 t1, T2 t2, T3 t3){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + } + + ///// com.sun.rowset.JoinRowSetImpl.vecJoinType + ///// com.sun.xml.internal.ws.message.DOMHeader.node + /////java.time.chrono.ChronoLocalDateTimeImpl.date + ///// java.time.chrono.ChronoZonedDateTimeImpl.dateTime + // com.sun.xml.internal.ws.policy.sourcemodel.PolicyModelTranslator $RawAlternative.allNestedPolicies + //com.sun.xml.internal.ws.policy.sourcemodel.PolicyModelTranslator $RawPolicy.alternatives + //sun.print.PrintServiceLookupProvider.aix_defaultPrinterEnumeration + //-com.sun.xml.internal.ws.client.sei.ValueSetter $Param + //java.util.ResourceBundle.keySet +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox32/DeepGeneric.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox32/DeepGeneric.java new file mode 100644 index 0000000000..4673f012ff --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox32/DeepGeneric.java @@ -0,0 +1,75 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox32; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DependentImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.DependentImmutableType; +import org.opalj.fpcf.properties.immutability.types.ShallowImmutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +final class DeepGeneric { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle generics", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @DeepImmutableField(value = "only deep immutable types in generics", analyses = L3FieldImmutabilityAnalysis.class) + @ImmutableFieldReference(value = "effective final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private GenericBaseClass gc1; + + public DeepGeneric(GenericBaseClass gc1){ + this.gc1 = gc1; + } + +} + +@ShallowImmutableType(value = "class is not extensible", analyses = L0TypeImmutabilityAnalysis.class) +@ShallowImmutableClass(value = "can not handle generics", analyses = L0ClassImmutabilityAnalysis.class) +@DependentImmutableType(value = "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +@DependentImmutableClass(value = "has only dependent immutable fields", analyses = L1ClassImmutabilityAnalysis.class) +final class GenericBaseClass { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value="can not work with generic type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value="effective final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T1 t1; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value="can not work with generic type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value="effective final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T2 t2; + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DependentImmutableField(value = "immutable reference with generic type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value="can not work with generic type", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value="effective final field", analyses = L0FieldReferenceImmutabilityAnalysis.class) + private T3 t3; + + public GenericBaseClass(T1 t1, T2 t2, T3 t3){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + } + +} + +final class FinalClassWithoutFields{} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox35/MethodTest.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox35/MethodTest.java new file mode 100644 index 0000000000..b693dda9fd --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox35/MethodTest.java @@ -0,0 +1,36 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox35; + +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; + +public class MethodTest { + //TODO @DeepImmutableField("") + @ShallowImmutableField("") +private Object object1 = new Object(); + @ShallowImmutableField("") +private Object object2 = new Object(); + @ShallowImmutableField("") +private Mut object3 = new Mut(); + @ShallowImmutableField("") +private Mut object4 = new Mut(); + +public MethodTest(Object o){ + this.object2 = o; +} + +public Mut get3(){ + return object3; +} + +private Mut get4(){ + return object4; +} + + + + +} + +class Mut{ + public int n = 5; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox36/Test2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox36/Test2.java new file mode 100644 index 0000000000..d1e066baf3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox36/Test2.java @@ -0,0 +1,74 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox36; + +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; + +public class Test2 { + //TODO @DeepImmutableField("") + private Object object1 = new Object(); + @ShallowImmutableField("") + private Object object2 = new Object(); + @ShallowImmutableField("") + private Mut object3 = new Mut(); + @ShallowImmutableField("") + private Mut object4 = new Mut(); + + @ShallowImmutableField("") + private Mut mut5 = new Mut(); + + @ShallowImmutableField("") + private Mut mut6 = new Mut(); + + + public Test2(Mut o){ + this.object2 = o; + } + + public Mut get3(){ + return object3; + } + + private Mut get4(){ + return object4; + } + +private Mut get5() { + return (Mut) id(mut5); +} + +private void get6() { + id(mut6); +} + + + private Object id(Object o){ + return o; + } + +} + +class Mut{ + /*java.util.concurrent.atomic.AtomicReference.unsafe + java.util.Locale.GERMANY + java.util.zip.ZipEntry.name + java.util.zip.ZipFile.name + java.util.logging.LogRecord.threadIds + java.util.concurrent.locks.AbstractQueuedSynchronizer.unsafe + java.util.WeakHashMap.NULL_KEY + java.util.TreeSet.PRESENT + java.util.EnumMap.NULL + java.util.concurrent.Exchanger.CANCEL + java.util.logging.LogRecord.threadIds + java.util.zip.InflaterInputStream.singleByteBuf + java.util.Currency.mainTable + java.util.Scanner.boolPattern + java.util.zip.ZipFile.name + java.util.regex.Pattern $Loop.body + ClassFile( + public /*SUPER*/ /*sun.util.CoreResourceBundleControl + extends java.util.ResourceBundle $Control + [version=49.0] + ) +*/ + public int n = 5; +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox40/C.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox40/C.java new file mode 100644 index 0000000000..c6f6f2a65a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox40/C.java @@ -0,0 +1,16 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox40; + +class GC{ + T t; + public GC(T t){this.t = t;} +} +/* +public class C { + private final GC gcDeep = new GC(1); + private final GC gcShallow = + new GC(new MutableClass()); + private final GC gcDependent; + public C(A a){ + gcDependent = new GC(a); + } } +*/ \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox40/GC.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox40/GC.java new file mode 100644 index 0000000000..763b299fa3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox40/GC.java @@ -0,0 +1,17 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox40; +/* +final class GC { + private final T t; + public GC(T t){this.t = t; }} + + final class DeepImmutableClass{ + private final GC gcDeep= new GC(1); + } + + final class ShallowImmutableClass{ + private final GC gcShallow = new GC(new MutableClass()); + } + + class MutableClass{public int n = 10;} +*/ + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox40/NativeFunctionCalls.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox40/NativeFunctionCalls.java new file mode 100644 index 0000000000..add5da8803 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox40/NativeFunctionCalls.java @@ -0,0 +1,13 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox40; + +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; + +public class NativeFunctionCalls { + + @MutableField("concrete class type is deep immutable") + @MutableFieldReference("field is final") + private Object finalObjectField = new Object(); + + native void hulului(); +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox41/GenericTest.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox41/GenericTest.java new file mode 100644 index 0000000000..2bf50f3455 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox41/GenericTest.java @@ -0,0 +1,59 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox41; + +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; + +public class GenericTest { + +} + +final class MutableClass{ + public int n = 5; +} +final class EmptyClass{} + +final class Generic { + + @DependentImmutableField("") + private A a; + + @DependentImmutableField("") + private B b; + + @DependentImmutableField("") + private C c; + + public Generic(A a, B b, C c){ + this.a = a; + this.b = b; + this.c = c; + } +} + +class Generic2{ + + @DependentImmutableField("") + private Generic a; + + @DependentImmutableField("") + private Generic c; + + @DependentImmutableField("") + private Generic> d; + + @DependentImmutableField("") + private Generic>> e; + + @DependentImmutableField("") + private Generic>> f; + + @ShallowImmutableField("") + private Generic g; + + @ShallowImmutableField("") + private Generic>> h; + +} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox42/Clone.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox42/Clone.java new file mode 100644 index 0000000000..0e07fa3438 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox42/Clone.java @@ -0,0 +1,21 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox42; + +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; + +public class Clone { + + @ImmutableFieldReference("") + private int i; + + public int getI() { + return i; + } + + Clone instance = new Clone(); + + public Clone clone(){ + Clone c = new Clone(); + c.i = i; + return c; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox42/CloneNotImmutable.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox42/CloneNotImmutable.java new file mode 100644 index 0000000000..927a4647a9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox42/CloneNotImmutable.java @@ -0,0 +1,20 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox42; + +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; + +public class CloneNotImmutable { + + @MutableFieldReference("") + private int mutableField; + + public static CloneNotImmutable instance; + public CloneNotImmutable clone() { + CloneNotImmutable c = new CloneNotImmutable(); + + c.mutableField = mutableField; + instance = c; + return c; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox50/Test.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox50/Test.java new file mode 100644 index 0000000000..7682b8220b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox50/Test.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.immutability.sandbox50; + +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; + +import java.util.Date; + +public class Test { + @ImmutableFieldReference("") + protected Date d; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox60/Generic.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox60/Generic.java new file mode 100644 index 0000000000..2ec9e880d6 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox60/Generic.java @@ -0,0 +1,19 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.sandbox60; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DependentImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +//@Immutable +@MutableType("") +@DependentImmutableClass("") +public class Generic { + + @DependentImmutableField("") + @ImmutableFieldReference("") + T t; + public Generic(T t){this.t = t;} +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox60/GenericFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox60/GenericFields.java new file mode 100644 index 0000000000..9ebe732273 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox60/GenericFields.java @@ -0,0 +1,30 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.sandbox60; + +import org.opalj.fpcf.fixtures.benchmark.generals.ClassWithMutableField; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; + +//@MutableType("") +//@ShallowImmutableClass("") +class GenericFields { + + @DependentImmutableField("") + private Generic generic; + @ShallowImmutableField("") + private Generic mutable = new Generic(new ClassWithMutableField()); + + @DependentImmutableField("") + private Generic> nestedDependent; + + @ShallowImmutableField("") + private Generic> nestedShallow = + new Generic<>(new Generic<>(new ClassWithMutableField())); + + public GenericFields(T t){ + this.generic = new Generic<>(t); + this.nestedDependent = new Generic<>(new Generic<>(t)); + } + +} + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox60/GenericFieldsDeep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox60/GenericFieldsDeep.java new file mode 100644 index 0000000000..316d7acdc5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox60/GenericFieldsDeep.java @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.sandbox60; + +//import edu.cmu.cs.glacier.qual.Immutable; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +//@Immutable +@MutableType("") +@DeepImmutableClass("") +class GenericFieldsDeep { + + @DeepImmutableField("") + private Generic> nestedDeep = new Generic<>(new Generic<>(new FinalEmptyClass())); + + @DeepImmutableField("") + Generic fecG = new Generic<>(new FinalEmptyClass()); + +} + +final class FinalEmptyClass {} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/WithMutableAndImmutableFieldType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/WithMutableAndImmutableFieldType.java new file mode 100644 index 0000000000..ac008d0566 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/WithMutableAndImmutableFieldType.java @@ -0,0 +1,78 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.type; + +import org.opalj.br.fpcf.analyses.L0ClassImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0TypeImmutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.classes.DeepImmutableClass; +import org.opalj.fpcf.properties.immutability.classes.MutableClass; +import org.opalj.fpcf.properties.immutability.classes.ShallowImmutableClass; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.MutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; +import org.opalj.fpcf.properties.immutability.types.DeepImmutableType; +import org.opalj.fpcf.properties.immutability.types.MutableType; +import org.opalj.fpcf.properties.immutability.types.ShallowImmutableType; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +@ShallowImmutableType("has only deep immutable fields and is final") +@ShallowImmutableClass("has only deep immutable fields") +public final class WithMutableAndImmutableFieldType { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DeepImmutableField(value = "immutable reference and deep immutable type", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "can not handle deep immutability", analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class} ) + @ImmutableFieldReference("private, effective immutable field") + private FinalEmptyClass fec = new FinalEmptyClass(); + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "has mutable type but is effectively final", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "private, effectively final field", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private SimpleMutableClass tmc = new SimpleMutableClass(); +} + +@MutableType("class is deep immutable but extensible") +@DeepImmutableClass("has no fields") +class EmptyClass { +} + +@DeepImmutableClass("Class has no fields") +@DeepImmutableType("Class has no fields and is final") +final class FinalEmptyClass { +} + +@MutableType(value = "has a public instance field", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@MutableClass(value = "has a public instance field", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) +class SimpleMutableClass{ + + @MutableField(value = "field is public", + analyses = {L0FieldReferenceImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) + @MutableFieldReference(value = "field is public", analyses = L0FieldReferenceImmutabilityAnalysis.class) + public int n=0; + //com.sun.corba.se.impl.io.ObjectStreamClass.noArgsList + //sun.misc.UCDecoder.tmp + //com.sun.xml.internal.org.jvnet.mimepull.DataHead.consumedAt + //com.sun.xml.internal.bind.DatatypeConverterImpl.hexCode + //com.sun.xml.internal.ws.client.sei.BodyBuilder $Bare.methodPos + //com.sun.xml.internal.ws.client.sei.ValueSetter $Param.idx + //com.sun.xml.internal.ws.client.sei.CallbackMethodHandler + //jdk.internal.dynalink.beans.MaximallySpecific.DYNAMIC_METHOD_TYPE_GETTER + //java.time.format.DateTimeFormatterBuilder.FIELD_MAP + //java.time.format.DateTimeFormatter.resolverFields + //com.sun.crypto.provider.DESCrypt.initPermLeft0 + //com.sun.beans.editors.FontEditor.styles + //java.time.format.DateTimeFormatter.resolverFields +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/mutability/Container.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/mutability/Container.java deleted file mode 100644 index 2bd5e586fa..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/mutability/Container.java +++ /dev/null @@ -1,66 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.fixtures.mutability; - -import org.opalj.fpcf.properties.class_mutability.ImmutableContainerObject; -import org.opalj.fpcf.properties.class_mutability.ImmutableObject; -import org.opalj.fpcf.properties.type_mutability.ImmutableContainerType; - - -public class Container { - - @ImmutableObject("Tree has no fields") - @ImmutableContainerType("Group is an ImmutableContainerObject") - private static abstract class Tree { - protected abstract void write(Object o); - - @ImmutableContainerObject("The body is of ImmutableContainerType") - @ImmutableContainerType("The body is of ImmutableContainerType") - private static final class Repeated extends Tree { - private final Tree body; - - private Repeated(Tree body) { - this.body = body; - } - - @Override - protected void write(Object o) { - body.write(o); - } - } - - @ImmutableContainerObject("The body is of ImmutableContainerType") - @ImmutableContainerType("The body is of ImmutableContainerType") - private static final class Optional extends Tree { - private final Tree body; - - private Optional(Tree body) { - this.body = body; - } - - @Override - protected void write(Object o) { - body.write(o); - } - } - - @ImmutableContainerObject("Arrays are treated as immutable") - @ImmutableContainerType("Arrays are treated as immutable") - private static final class Group extends Tree { - private final Tree[] children; - - private Group(Tree[] children) { - this.children = children; - } - - @Override - protected void write(Object o) { - for (Tree child : children) { - child.write(o); - } - } - } - } -} - - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/purity/DependentCalls.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/purity/DependentCalls.java index 328bc22427..b44d8de70b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/purity/DependentCalls.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/purity/DependentCalls.java @@ -42,23 +42,23 @@ public static DependentCalls createDependentCalls() { } @CompileTimePure(value = "object returned is immutable", - eps = @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "ImmutableObject")) + eps = @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "DeepImmutableClass")) @Pure(value = "object returned is immutable", - eps = @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "ImmutableObject"), + eps = @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "DeepImmutableClass"), analyses = { L0PurityAnalysis.class, L1PurityAnalysis.class }) - @Impure(value = "object returend not recognized as immutable", - eps = @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "ImmutableObject"), + @Impure(value = "object returned not recognized as immutable", + eps = @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "DeepImmutableClass"), negate = true, analyses = L1PurityAnalysis.class) public DependentCalls pureIdentity() { return this; } @Pure(value = "field used is effectively final", - eps = @EP(cf = DependentCalls.class, field = "myValue", pk = "FieldMutability", - p = "EffectivelyFinalField")) + eps = @EP(cf = DependentCalls.class, field = "myValue", pk = "FieldImmutability", + p = "ShallowImmutableField")) @Impure(value = "field used not recognized as effectively final", - eps = @EP(cf = DependentCalls.class, field = "myValue", pk = "FieldMutability", - p = "EffectivelyFinalField"), + eps = @EP(cf = DependentCalls.class, field = "myValue", pk = "FieldImmutability", + p = "ShallowImmutableField"), negate = true, analyses = L0PurityAnalysis.class) public static int pureUsesEffectivelyFinalField(int i, int j) { return i * j * myValue; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/purity/PrimitiveTypes.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/purity/PrimitiveTypes.java index a6faaef84b..cb41e6ecfc 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/purity/PrimitiveTypes.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/purity/PrimitiveTypes.java @@ -37,10 +37,10 @@ public int getScaledFinalField() { @CompileTimePure(value = "Uses effectively final field", eps = @EP(cf = PrimitiveTypes.class, field = "effectivelyFinalField", - pk = "FieldMutability", p = "EffectivelyFinalField")) + pk = "FieldImmutability", p = "ShallowImmutableField")) @Pure(value = "Uses effectively final field", eps = @EP(cf = PrimitiveTypes.class, field = "effectivelyFinalField", - pk = "FieldMutability", p = "EffectivelyFinalField"), + pk = "FieldImmutability", p = "ShallowImmutableField"), analyses = L1PurityAnalysis.class) @Impure(value = "Uses instance field", analyses = L0PurityAnalysis.class) public int getScaledEffectivelyFinalField() { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/purity/ReferenceTypes.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/purity/ReferenceTypes.java index 6c9f1a1e13..6a1446b2f3 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/purity/ReferenceTypes.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/purity/ReferenceTypes.java @@ -64,24 +64,24 @@ public ReferenceTypes(ReferenceTypes other) { // Setting static fields is impure @Pure(value = "Uses final static immutable object", - eps = @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "ImmutableObject")) + eps = @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "DeepImmutableClass")) @Impure(value = "DependentCalls not recognized as immutable", - eps = @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "ImmutableObject"), + eps = @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "DeepImmutableClass"), negate = true, analyses = L0PurityAnalysis.class) public static DependentCalls getStaticFinalImmutableObj() { return staticFinalImmutableObj; } @Pure(value = "Uses effectively final static immutable object", eps = { - @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "ImmutableObject"), + @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "DeepImmutableClass"), @EP(cf = ReferenceTypes.class, field = "staticEffectivelyFinalImmutableObj", - pk = "FieldMutability", p = "EffectivelyFinalField") + pk = "FieldImmutability", p = "ShallowImmutableField") }) @Impure(value = "staticEffectivelyFinalImmutableObj not recognized as effectively final static immutable object", negate = true, eps = { - @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "ImmutableObject"), + @EP(cf = DependentCalls.class, pk = "ClassImmutability", p = "DeepImmutableClass"), @EP(cf = ReferenceTypes.class, field = "staticEffectivelyFinalImmutableObj", - pk = "FieldMutability", p = "EffectivelyFinalField") + pk = "FieldImmutability", p = "ShallowImmutableField") }, analyses = L0PurityAnalysis.class) public static DependentCalls getStaticEffectivelyFinalImmutableObj() { return staticEffectivelyFinalImmutableObj; @@ -205,10 +205,10 @@ public int getFinalArrLength() { @CompileTimePure(value = "Uses array length of effectively final array", eps = @EP(cf = ReferenceTypes.class, field = "constEffectivelyFinalArr", - pk = "FieldMutability", p = "EffectivelyFinalField")) + pk = "FieldImmutability", p = "ShallowImmutableField")) @Pure(value = "Uses array length of effectively final array", eps = @EP(cf = ReferenceTypes.class, field = "constEffectivelyFinalArr", - pk = "FieldMutability", p = "EffectivelyFinalField"), + pk = "FieldImmutability", p = "ShallowImmutableField"), analyses = L1PurityAnalysis.class) @Impure(value = "Uses array length", analyses = L0PurityAnalysis.class) public int getEffectivelyFinalArrLength() { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_mutability/ImmutableContainerObject.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_mutability/ImmutableContainerObject.java index 249d458887..a13ad2eb34 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_mutability/ImmutableContainerObject.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_mutability/ImmutableContainerObject.java @@ -1,21 +1,22 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_mutability; - +/* import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.immutability.classes.ImmutableContainerObjectMatcher; import java.lang.annotation.Documented; import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.RetentionPolicy;*/ /** * Annotation to state that the annotated class is an immutable container. * * @author Florian Kuebler */ -@PropertyValidator(key = "ClassImmutability",validator = ImmutableContainerObjectMatcher.class) -@Documented -@Retention(RetentionPolicy.CLASS) -public @interface ImmutableContainerObject { +//@PropertyValidator(key = "ClassImmutability",validator = ImmutableContainerObjectMatcher.class) +//@Documented +//@Retention(RetentionPolicy.CLASS) +@interface ImmutableContainerObject { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_mutability/ImmutableObject.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_mutability/ImmutableObject.java index 3be054dcbc..c4cc3a2d3e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_mutability/ImmutableObject.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_mutability/ImmutableObject.java @@ -1,21 +1,22 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_mutability; - +/* import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.immutability.classes.ImmutableObjectMatcher; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; - +*/ /** * Annotation to state that the annotated class is immutable. * * @author Florian Kuebler */ -@PropertyValidator(key = "ClassImmutability",validator = ImmutableObjectMatcher.class) -@Documented -@Retention(RetentionPolicy.CLASS) -public @interface ImmutableObject { +//@PropertyValidator(key = "ClassImmutability",validator = ImmutableObjectMatcher.class) +//@Documented +//@Retention(RetentionPolicy.CLASS) +@interface ImmutableObject { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_mutability/MutableObject.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_mutability/MutableObject.java index 3eace87389..221e1ff535 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_mutability/MutableObject.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_mutability/MutableObject.java @@ -1,21 +1,22 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_mutability; - +/* import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.immutability.classes.MutableObjectMatcher; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; - +*/ /** * Annotation to state that the annotated class is mutable. * * @author Florian Kuebler */ -@PropertyValidator(key = "ClassImmutability",validator = MutableObjectMatcher.class) -@Documented -@Retention(RetentionPolicy.CLASS) -public @interface MutableObject { +//@PropertyValidator(key = "ClassImmutability",validator = MutableObjectMatcher.class) +//@Documented +//@Retention(RetentionPolicy.CLASS) +@interface MutableObject { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/LazyInitialized.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/LazyInitialized.java deleted file mode 100644 index 85361f9449..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/LazyInitialized.java +++ /dev/null @@ -1,56 +0,0 @@ -/* BSD 2-Clause License: - * Copyright (c) 2009 - 2017 - * Software Technology Group - * Department of Computer Science - * Technische Universität Darmstadt - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package org.opalj.fpcf.properties.field_mutability; - -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Annotation to state that the annotated field is lazily initialized. - * - * @author Michael Eichberg - * @author Dominik Helm - */ -@PropertyValidator(key="FieldMutability",validator=LazyInitializedMatcher.class) -@Documented -@Retention(RetentionPolicy.CLASS) -public @interface LazyInitialized { - - /** - * A short reasoning of this property. - */ - String value() ; // default = "N/A"; - - Class[] analyses() default { L2FieldMutabilityAnalysis.class }; -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/DeclaredFinal.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/_DeclaredFinal.java similarity index 79% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/DeclaredFinal.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/_DeclaredFinal.java index 5ced6e5306..45dcf094f6 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/DeclaredFinal.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/_DeclaredFinal.java @@ -29,10 +29,11 @@ package org.opalj.fpcf.properties.field_mutability; import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.br.fpcf.analyses.L0FieldMutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; -import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableFieldMatcher; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -44,10 +45,10 @@ * @author Michael Eichberg * @author Dominik Helm */ -@PropertyValidator(key="FieldMutability",validator=DeclaredFinalMatcher.class) +@PropertyValidator(key="FieldMutability",validator= ShallowImmutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface DeclaredFinal { +public @interface _DeclaredFinal { /** * A short reasoning of this property. @@ -55,8 +56,8 @@ String value() ; // default = "N/A"; Class[] analyses() default { - L0FieldMutabilityAnalysis.class, - L1FieldMutabilityAnalysis.class, - L2FieldMutabilityAnalysis.class + L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class }; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/EffectivelyFinal.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/_EffectivelyFinal.java similarity index 56% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/EffectivelyFinal.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/_EffectivelyFinal.java index 34c31ef187..4255cf738c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/EffectivelyFinal.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/_EffectivelyFinal.java @@ -2,10 +2,11 @@ package org.opalj.fpcf.properties.field_mutability; import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.br.fpcf.analyses.L0FieldMutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; -import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableFieldMatcher; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -17,10 +18,10 @@ * * @author Michael Eichberg */ -@PropertyValidator(key="FieldMutability",validator=EffectivelyFinalMatcher.class) +@PropertyValidator(key="FieldImmutability",validator= ShallowImmutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface EffectivelyFinal{ +public @interface _EffectivelyFinal{ /** * A short reasoning of this property. @@ -28,8 +29,8 @@ String value() ; // default = "N/A"; Class[] analyses() default { - L0FieldMutabilityAnalysis.class, - L1FieldMutabilityAnalysis.class, - L2FieldMutabilityAnalysis.class + L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class }; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/DeepImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/DeepImmutableClass.java new file mode 100644 index 0000000000..1cc3d3950f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/DeepImmutableClass.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.classes; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; + +/** + * Annotation to state that the annotated class is deep immutable + * + * @author Tobias Roth + */ +@PropertyValidator(key = "ClassImmutability",validator = DeepImmutableClassMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DeepImmutableClass { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L1ClassImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/DependentImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/DependentImmutableClass.java new file mode 100644 index 0000000000..b168d277c5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/DependentImmutableClass.java @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.classes; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; + +/** + * Annotation to state that the annotated class is dependent immutable + * + * @author Tobias Roth + */ +@PropertyValidator(key = "ClassImmutability",validator = DependentImmutableClassMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DependentImmutableClass { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L1ClassImmutabilityAnalysis.class}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/MutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/MutableClass.java new file mode 100644 index 0000000000..c66f776a1e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/MutableClass.java @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.classes; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated class is mutable + * + * @author Tobias Roth + */ +@PropertyValidator(key = "ClassImmutability",validator = MutableClassMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface MutableClass { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L1ClassImmutabilityAnalysis.class}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/ShallowImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/ShallowImmutableClass.java new file mode 100644 index 0000000000..245ce617f7 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/ShallowImmutableClass.java @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.classes; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; + +/** + * Annotation to state that the annotated class is shallow immutable + * + * @author Tobias Roth + */ +@PropertyValidator(key = "ClassImmutability",validator = ShallowImmutableClassMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ShallowImmutableClass { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L1ClassImmutabilityAnalysis.class}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/DeepImmutableField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/DeepImmutableField.java new file mode 100644 index 0000000000..6729680215 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/DeepImmutableField.java @@ -0,0 +1,26 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.fields; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; + +/** + * Annotation to state that the annotated field is deep immutable. + */ +@PropertyValidator(key="FieldImmutability",validator= DeepImmutableFieldMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DeepImmutableField { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default { L3FieldImmutabilityAnalysis.class }; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/DependentImmutableField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/DependentImmutableField.java new file mode 100644 index 0000000000..b219badec0 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/DependentImmutableField.java @@ -0,0 +1,26 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.fields; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is dependent immutable. + */ +@PropertyValidator(key="FieldImmutability",validator= DependentImmutableFieldMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DependentImmutableField { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L3FieldImmutabilityAnalysis.class}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/MutableField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/MutableField.java new file mode 100644 index 0000000000..18c33cafae --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/MutableField.java @@ -0,0 +1,38 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.fields; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is mutable. + */ +@PropertyValidator(key = "FieldImmutability",validator = MutableFieldMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface MutableField { + + /** + * A short reasoning of this property. + */ + String value(); + + /** + * True if the field is non-final because it is read prematurely. + * Tests may ignore @Mutable annotations if the FieldPrematurelyRead property for the field + * did not identify the premature read. + */ + boolean prematurelyRead() default false; + + Class[] analyses() default { L0FieldImmutabilityAnalysis.class, + L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/ShallowImmutableField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/ShallowImmutableField.java new file mode 100644 index 0000000000..0a5ce32a83 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/ShallowImmutableField.java @@ -0,0 +1,26 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.fields; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; + +/** + * Annotation to state that the annotated field is shallow immutable. + */ +@PropertyValidator(key="FieldImmutability",validator= ShallowImmutableFieldMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ShallowImmutableField { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default { L3FieldImmutabilityAnalysis.class }; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableFieldReference.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableFieldReference.java new file mode 100644 index 0000000000..204d93772d --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableFieldReference.java @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.references; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +/** + * Annotation to state that the annotated field reference is immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "FieldReferenceImmutability",validator = ImmutableFieldReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ImmutableFieldReference { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeButDeterministicReference.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeButDeterministicReference.java new file mode 100644 index 0000000000..11a807ab84 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeButDeterministicReference.java @@ -0,0 +1,27 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.references; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +/** + * Annotation to state that the annotated field reference is not thread safe but deterministic lazy initialized + */ +@PropertyValidator(key = "FieldReferenceImmutability",validator = LazyInitializedNotThreadSafeButDeterministicFieldReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface LazyInitializedNotThreadSafeButDeterministicReference { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeFieldReference.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeFieldReference.java new file mode 100644 index 0000000000..079d6ff291 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeFieldReference.java @@ -0,0 +1,27 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.references; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +/** + * Annotation to state that the annotated field reference is not thread safe lazy initialized + */ +@PropertyValidator(key = "FieldReferenceImmutability",validator = LazyInitializedNotThreadSafeFieldReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface LazyInitializedNotThreadSafeFieldReference { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeFieldReference.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeFieldReference.java new file mode 100644 index 0000000000..da5f42d9af --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeFieldReference.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.references; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + +/** + * Annotation to state that the annotated field reference is thread safe lazy initialized + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "FieldReferenceImmutability",validator = LazyInitializedThreadSafeFieldReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface LazyInitializedThreadSafeFieldReference { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/NonFinal.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableFieldReference.java similarity index 51% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/NonFinal.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableFieldReference.java index 23a8f5dfe1..3e36f1bd0d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/NonFinal.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableFieldReference.java @@ -1,30 +1,24 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.field_mutability; - -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.br.fpcf.analyses.L0FieldMutabilityAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; -import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; +package org.opalj.fpcf.properties.immutability.references; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; + /** - * Annotation to state that the annotated field is not final. + * Annotation to state that the annotated field reference is mutable * - * @author Michael Eichberg + * @author Tobias Peter Roth */ -@PropertyValidator(key = "FieldMutability",validator = NonFinalMatcher.class) +@PropertyValidator(key = "FieldReferenceImmutability",validator = MutableFieldReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface NonFinal{ +public @interface MutableFieldReference { - /** - * A short reasoning of this property. - */ - String value();// default = "N/A"; /** * True if the field is non-final because it is read prematurely. @@ -32,8 +26,11 @@ * did not identify the premature read. */ boolean prematurelyRead() default false; + /** + * A short reasoning of this property. + */ + String value() default "N/A"; - Class[] analyses() default { L0FieldMutabilityAnalysis.class, - L1FieldMutabilityAnalysis.class, L2FieldMutabilityAnalysis.class }; + Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/DeepImmutableType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/DeepImmutableType.java new file mode 100644 index 0000000000..a80f8ea26d --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/DeepImmutableType.java @@ -0,0 +1,26 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.types; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; + +/** + * Annotation to state that the annotated type deep immutable. + * */ +@PropertyValidator(key = "TypeImmutability", validator = DeepImmutableTypeMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DeepImmutableType { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L1TypeImmutabilityAnalysis.class}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/DependentImmutableType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/DependentImmutableType.java new file mode 100644 index 0000000000..dbc9efce54 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/DependentImmutableType.java @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.types; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; + +/** + * Annotation to state that the annotated type shallow immutable. + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "TypeImmutability_new", validator = DependentImmutableTypeMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DependentImmutableType { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L1TypeImmutabilityAnalysis.class}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/MutableType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/MutableType.java new file mode 100644 index 0000000000..f18910ffb1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/MutableType.java @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.types; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; + +/** + * Annotation to state that the annotated type mutable. + * + * @author Tobias Roth + */ +@PropertyValidator(key = "TypeImmutability", validator = MutableTypeMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface MutableType { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L1TypeImmutabilityAnalysis.class}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/ShallowImmutableType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/ShallowImmutableType.java new file mode 100644 index 0000000000..8cadda3c67 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/ShallowImmutableType.java @@ -0,0 +1,26 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.immutability.types; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; + +/** + * Annotation to state that the annotated type shallow immutable. + */ +@PropertyValidator(key = "TypeImmutability", validator = ShallowImmutableTypeMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ShallowImmutableType { + + /** + * A short reasoning of this property. + */ + String value(); + + Class[] analyses() default {L1TypeImmutabilityAnalysis.class}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/ImmutableContainerType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/ImmutableContainerType.java index 8ac3a5ac65..7cd60b88e6 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/ImmutableContainerType.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/ImmutableContainerType.java @@ -1,21 +1,23 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.type_mutability; +/* import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.immutability.types.ImmutableContainerTypeMatcher; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; - +*/ /** * Annotation to state that the annotated type is an immutable container. * * @author Florian Kuebler */ -@PropertyValidator(key = "TypeImmutability", validator = ImmutableContainerTypeMatcher.class) -@Documented -@Retention(RetentionPolicy.CLASS) -public @interface ImmutableContainerType { +//@PropertyValidator(key = "TypeImmutability", validator = ImmutableContainerTypeMatcher.class) +//@Documented +//@Retention(RetentionPolicy.CLASS) +@interface _ImmutableContainerType { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/ImmutableType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/ImmutableType.java index 8b1bc38dde..9e40b63430 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/ImmutableType.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/ImmutableType.java @@ -12,10 +12,10 @@ * * @author Florian Kuebler */ -@PropertyValidator(key = "TypeImmutability", validator = ImmutableTypeMatcher.class) -@Documented -@Retention(RetentionPolicy.CLASS) -public @interface ImmutableType { +//@PropertyValidator(key = "TypeImmutability", validator = ImmutableTypeMatcher.class) +//@Documented +//@Retention(RetentionPolicy.CLASS) +@interface _ImmutableType { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/MutableType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/MutableType_old.java similarity index 85% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/MutableType.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/MutableType_old.java index db0639e18f..a5d4030460 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/MutableType.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/MutableType_old.java @@ -2,6 +2,7 @@ package org.opalj.fpcf.properties.type_mutability; import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.immutability.types.MutableTypeMatcher; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -15,7 +16,7 @@ @PropertyValidator(key = "TypeImmutability", validator = MutableTypeMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface MutableType { +public @interface MutableType_old { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala new file mode 100644 index 0000000000..af6fa64966 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala @@ -0,0 +1,105 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf + +//import org.opalj.ai.fpcf.analyses.LazyL0BaseAIAnalysis +import java.net.URL +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +/*import org.opalj.tac.fpcf.analyses.immutability.EagerL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL3FieldImmutabilityAnalysis */ +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.Project +import org.opalj.tac.cg.RTACallGraphKey /* +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ +import org.opalj.ai.fpcf.analyses.LazyL0BaseAIAnalysis +import org.opalj.br.fpcf.analyses.EagerL0ClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis + +/** + * Tests if the properties specified in the test project (the classes in the (sub-)package of + * org.opalj.fpcf.fixture) and the computed ones match. The actual matching is delegated to + * PropertyMatchers to facilitate matching arbitrary complex property specifications. + * + * @author Tobias Roth + * @author Florian Kuebler + */ +class ClassAndTypeImmutabilityTests extends PropertiesTest { + + override def withRT = true + + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/benchmark/h_classAndTypeImmutability") + } + + override def init(p: Project[URL]): Unit = { + + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + } + + p.get(RTACallGraphKey) + } + + describe("the 0 class and type immutability analyses are executed") { + + val as = executeAnalyses(Set( + EagerL0ClassImmutabilityAnalysis, + EagerL0TypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2FieldImmutabilityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, + LazyL0BaseAIAnalysis + )) + + as.propertyStore.shutdown() + + validateProperties( + as, + classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), + Set("TypeImmutability", "ClassImmutability") + ) + } + + describe("the 1 class and type immutability analysis are executed") { + + val as = executeAnalyses(Set( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, + LazyL3FieldImmutabilityAnalysis, + EagerL1TypeImmutabilityAnalysis, + EagerL1ClassImmutabilityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + )) + + as.propertyStore.shutdown() + + validateProperties( + as, + classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), + Set("TypeImmutability", "ClassImmutability") + ) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeMutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeMutabilityTests.scala deleted file mode 100644 index 13e1e512a1..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeMutabilityTests.scala +++ /dev/null @@ -1,37 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf - -import org.opalj.br.fpcf.analyses.EagerClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis -import org.opalj.ai.fpcf.analyses.LazyL0BaseAIAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.TACAITransformer - -/** - * Tests if the properties specified in the test project (the classes in the (sub-)package of - * org.opalj.fpcf.fixture) and the computed ones match. The actual matching is delegated to - * PropertyMatchers to facilitate matching arbitrary complex property specifications. - * - * @author Florian Kuebler - */ -class ClassAndTypeMutabilityTests extends PropertiesTest { - - describe("the field, class and type mutability analyses are executed") { - val as = executeAnalyses(Set( - EagerClassImmutabilityAnalysis, - EagerTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyL0BaseAIAnalysis, - TACAITransformer - )) - as.propertyStore.shutdown() - validateProperties( - as, - classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), - Set("TypeImmutability", "ClassImmutability") - ) - } - -} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala new file mode 100644 index 0000000000..ef3d63afb3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -0,0 +1,122 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf + +import java.net.URL + +import org.opalj.br.analyses.Project +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis +import org.opalj.ai.domain.l2 +import org.opalj.br.fpcf.analyses.LazyVirtualCallAggregatingEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +//import org.opalj.tac.fpcf.analyses.EagerL2FieldImmutabilityAnalysis +//import org.opalj.tac.fpcf.analyses.EagerL1FieldImmutabilityAnalysis +//import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis + +/** + * Tests the field immutability analyses + * + * @author Tobias Roth + */ +class FieldImmutabilityTests extends PropertiesTest { + + override def withRT = true + + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/benchmark/arrays") + } + + override def init(p: Project[URL]): Unit = { + + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + } + + p.get(RTACallGraphKey) + } + + describe("no analysis is scheduled") { + + val as = executeAnalyses(Set.empty) + + as.propertyStore.shutdown() + + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } + /*SimpleSimple + describe("the org.opalj.fpcf.analyses.L0FieldMutabilityAnalysis is executed") { + + val as = executeAnalyses( + Set( + EagerL0FieldImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis + ) + ) + + as.propertyStore.shutdown() + + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } + + describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + + val as = executeAnalyses( + Set( + EagerL1FieldImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyInterProceduralEscapeAnalysis + ) + ) + as.propertyStore.shutdown() + + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } + + describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + + val as = executeAnalyses( + Set( + EagerL2FieldImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis + ) + ) + + as.propertyStore.shutdown() + + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } +*/ + describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + val as = executeAnalyses( + Set( + LazyL0FieldReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + EagerL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyVirtualCallAggregatingEscapeAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldLocalityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldLocalityTests.scala index 31947cfe3d..78e4397356 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldLocalityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldLocalityTests.scala @@ -12,9 +12,40 @@ import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import com.typesafe.config.ConfigValueFactory.fromAnyRef +import com.typesafe.config.Config +import com.typesafe.config.ConfigValueFactory +//import org.opalj.br.analyses.cg.InitialEntryPointsKey +//import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey class FieldLocalityTests extends PropertiesTest { + override def createConfig(): Config = { + val config = BaseConfig /*.withValue( + InitialEntryPointsKey.ConfigKeyPrefix+"analysis", + ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllEntryPointsFinder") + ).withValue( + InitialEntryPointsKey.ConfigKeyPrefix+"AllEntryPointsFinder.projectMethodsOnly", + ConfigValueFactory.fromAnyRef(true) + ) + configForEntryPoints.withValue( + InitialInstantiatedTypesKey.ConfigKeyPrefix+"analysis", + ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllInstantiatedTypesFinder") + ).withValue( + InitialInstantiatedTypesKey.ConfigKeyPrefix+ + "AllInstantiatedTypesFinder.projectClassesOnly", + ConfigValueFactory.fromAnyRef(true) + )*/ + .withValue( + "org.opalj.br.analyses.cg.ClosedPackagesKey", + fromAnyRef("org.opalj.br.analyses.cg.AllPackagesClosed") + ) + .withValue("org.opalj.br.analyses.cg.ClassExtensibilityKey", ConfigValueFactory.fromAnyRef( + "org.opalj.br.analyses.cg.ClassHierarchyIsNotExtensible" + )) + config + } + val analyses = Set[FPCFAnalysisScheduler]( EagerFieldLocalityAnalysis, LazyInterProceduralEscapeAnalysis, diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldReferenceImmutabilityTests.scala new file mode 100644 index 0000000000..7921c86a87 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldReferenceImmutabilityTests.scala @@ -0,0 +1,81 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf + +import java.net.URL + +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.Project +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.EagerL0FieldReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater + +/** + * Tests the field reference immutability analysis + * + * @author Tobias Roth + */ +class FieldReferenceImmutabilityTests extends PropertiesTest { + + override def withRT = true + + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/immutability/sandbox50") + } + + override def init(p: Project[URL]): Unit = { + + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + import org.opalj.ai.domain.l1 + Set[Class[_ <: AnyRef]](classOf[l1.DefaultDomainWithCFGAndDefUse[URL]]) + } + + p.get(RTACallGraphKey) + /*print("-----------------------------------------------------------------------------------") + print(p.config.getString( + "org.opalj.br.analyses.cg.ClosedPackagesKey.analysis" + )) + val e = 3 + if (e % 3 == 0) + throw new Exception("------------------------------------------------------------------------") */ + } + + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldReferenceImmutability")) + } + + L2PurityAnalysis.setRater(Some(SystemOutLoggingAllExceptionRater)) + + describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { + val as = executeAnalyses( + Set( + EagerL0FieldReferenceImmutabilityAnalysis, + LazyL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyStaticDataUsageAnalysis, + LazyL2PurityAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldReferenceImmutability")) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ImmutabilityBenchmarkTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ImmutabilityBenchmarkTests.scala new file mode 100644 index 0000000000..f3aa31a92e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ImmutabilityBenchmarkTests.scala @@ -0,0 +1,77 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis + +import java.net.URL +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.Project +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.EagerL0FieldReferenceImmutabilityAnalysis + +/** + * Tests if the properties specified in the test project (the classes in the (sub-)package of + * org.opalj.fpcf.fixture) and the computed ones match. The actual matching is delegated to + * PropertyMatchers to facilitate matching arbitrary complex property specifications. + * + * @author Tobias Roth + */ +class ImmutabilityBenchmarkTests extends PropertiesTest { + + override def withRT = true + + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/benchmark") + } + + override def init(p: Project[URL]): Unit = { + + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + } + + p.get(RTACallGraphKey) + } + + describe("all immutability analyses are executed") { + + val as = executeAnalyses(Set( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + EagerL0FieldReferenceImmutabilityAnalysis, + EagerL3FieldImmutabilityAnalysis, + EagerL1TypeImmutabilityAnalysis, + EagerL1ClassImmutabilityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + )) + + as.propertyStore.shutdown() + + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldReferenceImmutability")) + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + validateProperties( + as, + classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), + Set("ClassImmutability") + ) + validateProperties( + as, + classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), + Set("TypeImmutability") + ) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala index 67158a575c..c5b8c17c45 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala @@ -51,7 +51,8 @@ import org.opalj.tac.common.DefinitionSitesKey */ abstract class PropertiesTest extends FunSpec with Matchers { - final private[this] val testFilePath = s"DEVELOPING_OPAL/validate/target/scala-$ScalaMajorVersion/test-classes/" + final private[this] val testFilePath = + s"DEVELOPING_OPAL/validate/target/scala-$ScalaMajorVersion/test-classes/" final private[this] val propertyPaths = List( s"DEVELOPING_OPAL/validate/target/scala-$ScalaMajorVersion/test-classes/org/opalj/fpcf/properties", s"DEVELOPING_OPAL/validate/target/scala-$ScalaMajorVersion/test-classes/org/opalj/br/analyses/properties" @@ -116,18 +117,22 @@ abstract class PropertiesTest extends FunSpec with Matchers { def fixtureProjectPackage: List[String] = List.empty def createConfig(): Config = { - val configForEntryPoints = BaseConfig.withValue( - InitialEntryPointsKey.ConfigKeyPrefix+"analysis", - ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllEntryPointsFinder") - ).withValue( + val configForEntryPoints = BaseConfig + .withValue( + InitialEntryPointsKey.ConfigKeyPrefix+"analysis", + ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllEntryPointsFinder") + ) + .withValue( InitialEntryPointsKey.ConfigKeyPrefix+"AllEntryPointsFinder.projectMethodsOnly", ConfigValueFactory.fromAnyRef(true) ) - configForEntryPoints.withValue( - InitialInstantiatedTypesKey.ConfigKeyPrefix+"analysis", - ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllInstantiatedTypesFinder") - ).withValue( + configForEntryPoints + .withValue( + InitialInstantiatedTypesKey.ConfigKeyPrefix+"analysis", + ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllInstantiatedTypesFinder") + ) + .withValue( InitialInstantiatedTypesKey.ConfigKeyPrefix+ "AllInstantiatedTypesFinder.projectClassesOnly", ConfigValueFactory.fromAnyRef(true) @@ -203,7 +208,7 @@ abstract class PropertiesTest extends FunSpec with Matchers { val nonFinalPSs = epss.filter(_.isRefinable) assert( nonFinalPSs.isEmpty, - nonFinalPSs.mkString("some epss are not final:\n\t", "\n\t", "\n") + nonFinalPSs.mkString("some eps are not final:\n\t", "\n\t", "\n") ) val properties = epss.map(_.toFinalEP.p) matcher.validateProperty(p, ats, e, annotation, properties) match { @@ -303,8 +308,9 @@ abstract class PropertiesTest extends FunSpec with Matchers { val fp = formalParameters(dm)(i + 1) ( fp, - (a: String) ⇒ s"VirtualFormalParameter: (origin ${fp.origin} in "+ - s"${dm.declaringClassType}#${m.toJava(s"@$a")}", + (a: String) ⇒ + s"VirtualFormalParameter: (origin ${fp.origin} in "+ + s"${dm.declaringClassType}#${m.toJava(s"@$a")}", annotations ) } @@ -329,8 +335,9 @@ abstract class PropertiesTest extends FunSpec with Matchers { } yield { ( as, - (a: String) ⇒ s"AllocationSite: (pc ${as.pc} in "+ - s"${m.toJava(s"@$a").substring(24)})", + (a: String) ⇒ + s"AllocationSite: (pc ${as.pc} in "+ + s"${m.toJava(s"@$a").substring(24)})", annotations ) } @@ -364,7 +371,7 @@ abstract class PropertiesTest extends FunSpec with Matchers { new RecordAllPropertyStoreTracer, context.iterator.map(_.asTuple).toMap ) - */ + */ val ps = PKESequentialPropertyStore(context: _*) ps } @@ -392,8 +399,8 @@ abstract class PropertiesTest extends FunSpec with Matchers { val relevantPackages = fixtureProjectPackage if (fixtureProjectPackage.nonEmpty) { classFilePaths = classFilePaths ++ propertyPaths.map(new File(_)) - classFilePaths = classFilePaths ++ relevantPackages.map { - path ⇒ new File({ s"$testFilePath$path" }) + classFilePaths = classFilePaths ++ relevantPackages.map { path ⇒ + new File({ s"$testFilePath$path" }) } } else { classFilePaths = new File(testFilePath) :: classFilePaths diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PurityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PurityTests.scala index 8f58c68769..ec908732c6 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PurityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PurityTests.scala @@ -5,24 +5,29 @@ package fpcf import java.net.URL import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis -import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis -import org.opalj.br.fpcf.analyses.LazyL0FieldMutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis + import org.opalj.ai.domain.l1 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.EagerL1PurityAnalysis -import org.opalj.tac.fpcf.analyses.purity.EagerL2PurityAnalysis import org.opalj.tac.fpcf.analyses.purity.L1PurityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0ClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL1FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.EagerL2PurityAnalysis /** * Tests if the properties specified in the test project (the classes in the (sub-)package of @@ -52,9 +57,9 @@ class PurityTests extends PropertiesTest { executeAnalyses( Set( EagerL0PurityAnalysis, - LazyL0FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis + LazyL0FieldImmutabilityAnalysis, + LazyL0ClassImmutabilityAnalysis, + LazyL0TypeImmutabilityAnalysis ) ) as.propertyStore.shutdown() @@ -66,9 +71,9 @@ class PurityTests extends PropertiesTest { val as = executeAnalyses( Set( - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis, + LazyL1FieldImmutabilityAnalysis, + LazyL0ClassImmutabilityAnalysis, + LazyL0TypeImmutabilityAnalysis, EagerL1PurityAnalysis ) ) @@ -88,13 +93,38 @@ class PurityTests extends PropertiesTest { LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis + LazyL1FieldImmutabilityAnalysis, + LazyL0ClassImmutabilityAnalysis, + LazyL0TypeImmutabilityAnalysis )) as.propertyStore.shutdown() validateProperties(as, declaredMethodsWithAnnotations(as.project), Set("Purity")) } -} + describe( + "the org.opalj.fpcf.analyses.L2PurityAnalysis is executed "+ + "together with the L3FieldImmutabilityAnalysis" + ) { + + L2PurityAnalysis.setRater(Some(SystemOutLoggingAllExceptionRater)) + + val as = executeAnalyses(Set( + EagerL2PurityAnalysis, + LazyL3FieldImmutabilityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyInterProceduralEscapeAnalysis + )) + + as.propertyStore.shutdown() + + validateProperties(as, declaredMethodsWithAnnotations(as.project), Set("Purity")) + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/_AllFieldImmutabilityAnalysisTests.scala similarity index 50% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/_AllFieldImmutabilityAnalysisTests.scala index b92877b5b9..f1a5b16d28 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/_AllFieldImmutabilityAnalysisTests.scala @@ -5,15 +5,27 @@ package fpcf import java.net.URL import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.analyses.EagerL0FieldMutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyVirtualCallAggregatingEscapeAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL3FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1ClassImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL1TypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.EagerL2FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis /** * Tests if the properties specified in the test project (the classes in the (sub-)package of @@ -22,59 +34,97 @@ import org.opalj.tac.fpcf.analyses.EagerL2FieldMutabilityAnalysis * * @author Michael Eichberg */ -class FieldMutabilityTests extends PropertiesTest { +class _AllFieldImmutabilityAnalysisTests extends PropertiesTest { + + override def withRT = true + + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/immutability/xxx") + } override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) } - p.get(RTACallGraphKey) - } - override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/field_mutability") + p.get(RTACallGraphKey) } describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) } describe("the org.opalj.fpcf.analyses.L0FieldMutabilityAnalysis is executed") { + val as = executeAnalyses( Set( - EagerL0FieldMutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis ) ) + as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) } describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + val as = executeAnalyses( Set( - EagerL1FieldMutabilityAnalysis, + EagerL1FieldImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyInterProceduralEscapeAnalysis ) ) as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) } describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + val as = executeAnalyses( Set( - EagerL2FieldMutabilityAnalysis, + EagerL2FieldImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, LazyInterProceduralEscapeAnalysis ) ) + as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) } + describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + + val as = executeAnalyses( + Set( + LazyL0FieldReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + EagerL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyVirtualCallAggregatingEscapeAnalysis + ) + ) + + as.propertyStore.shutdown() + + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_mutability/AbstractClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_mutability/AbstractClassImmutabilityMatcher.scala deleted file mode 100644 index a90a50e4b5..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_mutability/AbstractClassImmutabilityMatcher.scala +++ /dev/null @@ -1,56 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package properties -package class_mutability - -import org.opalj.br.AnnotationLike -import org.opalj.br.ObjectType -import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.properties - -class AbstractClassImmutabilityMatcher( - val property: properties.ClassImmutability -) extends AbstractPropertyMatcher { - - override def validateProperty( - p: Project[_], - as: Set[ObjectType], - entity: scala.Any, - a: AnnotationLike, - properties: Traversable[Property] - ): Option[String] = { - if (!properties.exists { - case `property` ⇒ true - case _ ⇒ false - }) { - Some(a.elementValuePairs.head.value.asStringValue.value) - } else { - None - } - } -} - -class ImmutableObjectMatcher - extends AbstractClassImmutabilityMatcher(properties.ImmutableObject) - -class ImmutableContainerObjectMatcher - extends AbstractClassImmutabilityMatcher(properties.ImmutableContainer) - -class MutableObjectMatcher extends AbstractPropertyMatcher { - override def validateProperty( - p: Project[_], - as: Set[ObjectType], - entity: scala.Any, - a: AnnotationLike, - properties: Traversable[Property] - ): Option[String] = { - if (properties.exists { - case _: MutableObject ⇒ true - case _ ⇒ false - }) - Some(a.elementValuePairs.head.value.asStringValue.value) - else - None - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/AbstractClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/AbstractClassImmutabilityMatcher.scala new file mode 100644 index 0000000000..3dc2eb6405 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/AbstractClassImmutabilityMatcher.scala @@ -0,0 +1,75 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf +package properties +package immutability +package classes + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.Project +import org.opalj.br.fpcf.properties + +class AbstractClassImmutabilityMatcher(val property: properties.ClassImmutability) extends AbstractPropertyMatcher { + + import org.opalj.br.analyses.SomeProject + + override def isRelevant( + p: SomeProject, + as: Set[ObjectType], + entity: Object, + a: AnnotationLike + ): Boolean = { + val annotationType = a.annotationType.asObjectType + + val analysesElementValues = + getValue(p, annotationType, a.elementValuePairs, "analyses").asArrayValue.values + val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) + + analyses.exists(as.contains) + } + + override def validateProperty( + p: Project[_], + as: Set[ObjectType], + entity: scala.Any, + a: AnnotationLike, + properties: Traversable[Property] + ): Option[String] = { + if (!properties.exists { + case `property` ⇒ true + case _ ⇒ false + }) { + Some(a.elementValuePairs.head.value.asStringValue.value) + } else { + None + } + } +} + +class DeepImmutableClassMatcher + extends AbstractClassImmutabilityMatcher(properties.DeepImmutableClass) + +class DependentImmutableClassMatcher + extends AbstractClassImmutabilityMatcher(properties.DependentImmutableClass) + +class ShallowImmutableClassMatcher + extends AbstractClassImmutabilityMatcher(properties.ShallowImmutableClass) + +class MutableClassMatcher extends AbstractPropertyMatcher { + override def validateProperty( + p: Project[_], + as: Set[ObjectType], + entity: scala.Any, + a: AnnotationLike, + properties: Traversable[Property] + ): Option[String] = { + if (properties.exists { + case _: MutableClass ⇒ true + case _ ⇒ false + }) + Some(a.elementValuePairs.head.value.asStringValue.value) + else + None + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/_ClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/_ClassImmutabilityMatcher.scala new file mode 100644 index 0000000000..0343f254b0 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/_ClassImmutabilityMatcher.scala @@ -0,0 +1,59 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf +package properties +package immutability +package classes + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.ClassImmutability + +/** + * This is the basis for the matchers that match a class immutability + * @author Tobias Peter Roth + */ +class _ClassImmutabilityMatcher(val property: ClassImmutability) extends AbstractPropertyMatcher { + + final private val PropertyReasonID = 0 + + override def isRelevant( + p: SomeProject, + as: Set[ObjectType], + entity: Object, + a: AnnotationLike + ): Boolean = { + val annotationType = a.annotationType.asObjectType + + val analysesElementValues = + getValue(p, annotationType, a.elementValuePairs, "analyses").asArrayValue.values + + val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) + + analyses.exists(as.contains) + } + + def validateProperty( + p: SomeProject, + as: Set[ObjectType], + entity: Entity, + a: AnnotationLike, + properties: Traversable[Property] + ): Option[String] = { + if (!properties.exists(p ⇒ p == property)) { + // ... when we reach this point the expected property was not found. + Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) + } else { + None + } + } +} + +class _MutableClassMatcher extends _ClassImmutabilityMatcher(br.fpcf.properties.MutableClass) + +class _DependentImmutableClassMatcher extends _ClassImmutabilityMatcher(br.fpcf.properties.DependentImmutableClass) + +class _ShallowImmutableClassMatcher extends _ClassImmutabilityMatcher(br.fpcf.properties.ShallowImmutableClass) + +class _DeepImmutableClassMatcher extends _ClassImmutabilityMatcher(br.fpcf.properties.DeepImmutableClass) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_mutability/FieldMutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/fields/FieldImmutabilityMatcher.scala similarity index 67% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_mutability/FieldMutabilityMatcher.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/fields/FieldImmutabilityMatcher.scala index 2f04704357..028d6adc4e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_mutability/FieldMutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/fields/FieldImmutabilityMatcher.scala @@ -2,24 +2,23 @@ package org.opalj package fpcf package properties -package field_mutability +package immutability +package fields import org.opalj.br.AnnotationLike import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties.DeclaredFinalField -import org.opalj.br.fpcf.properties.EffectivelyFinalField -import org.opalj.br.fpcf.properties.FieldMutability -import org.opalj.br.fpcf.properties.LazyInitializedField +import org.opalj.br.fpcf.properties.FieldImmutability /** - * Matches a field's `FieldMutability` property. The match is successful if the field has the + * Matches a field's `FieldImmutability` property. The match is successful if the field has the * given property and a sufficiently capable analysis was scheduled. * + * @author Tobias Roth * @author Michael Eichberg * @author Dominik Helm */ -class FieldMutabilityMatcher(val property: FieldMutability) extends AbstractPropertyMatcher { +class FieldImmutabilityMatcher(val property: FieldImmutability) extends AbstractPropertyMatcher { private final val PropertyReasonID = 0 @@ -52,11 +51,11 @@ class FieldMutabilityMatcher(val property: FieldMutability) extends AbstractProp None } } - } -class DeclaredFinalMatcher extends FieldMutabilityMatcher(DeclaredFinalField) +class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.ShallowImmutableField) + +class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.DependentImmutableField) -class EffectivelyFinalMatcher extends FieldMutabilityMatcher(EffectivelyFinalField) +class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.DeepImmutableField) -class LazyInitializedMatcher extends FieldMutabilityMatcher(LazyInitializedField) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_mutability/NonFinalMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/fields/MutableFieldMatcher.scala similarity index 77% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_mutability/NonFinalMatcher.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/fields/MutableFieldMatcher.scala index 08526a42b0..507a71abc2 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_mutability/NonFinalMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/fields/MutableFieldMatcher.scala @@ -1,28 +1,28 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package properties -package field_mutability +package org.opalj.fpcf.properties.immutability.fields +import org.opalj.br import org.opalj.br.AnnotationLike -import org.opalj.br.ObjectType import org.opalj.br.BooleanValue +import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.FieldPrematurelyRead -import org.opalj.br.fpcf.properties.NonFinalField import org.opalj.br.fpcf.properties.PrematurelyReadField +import org.opalj.fpcf.Entity +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher /** - * Matches a field's `FieldMutability` property. The match is successful if the field either + * Matches a field's `FieldImmutability` property. The match is successful if the field either * does not have a corresponding property (in which case the fallback property will be * `NonFinalField`) or if the property is an instance of `NonFinalField`. * * @author Michael Eichberg * @author Dominik Helm */ -class NonFinalMatcher extends AbstractPropertyMatcher { +class MutableFieldMatcher extends AbstractPropertyMatcher { override def isRelevant( p: SomeProject, @@ -59,7 +59,8 @@ class NonFinalMatcher extends AbstractPropertyMatcher { a: AnnotationLike, properties: Traversable[Property] ): Option[String] = { - if (properties.forall(p ⇒ p.isInstanceOf[NonFinalField] || p.key != FieldMutability.key)) + import org.opalj.br.fpcf.properties.FieldImmutability + if (properties.forall(p ⇒ p == br.fpcf.properties.MutableField || p.key != FieldImmutability.key)) None else { Some(a.elementValuePairs.head.value.toString) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/fields/_FieldImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/fields/_FieldImmutabilityMatcher.scala new file mode 100644 index 0000000000..d6c91a8e42 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/fields/_FieldImmutabilityMatcher.scala @@ -0,0 +1,59 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf +package properties +package immutability +package fields + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.FieldImmutability + +/** + * This is the basis for the matchers that match the immutability of a field + * @author Tobias Peter Roth + */ +class _FieldImmutabilityMatcher(val property: FieldImmutability) extends AbstractPropertyMatcher { + + final private val PropertyReasonID = 0 + + override def isRelevant( + p: SomeProject, + as: Set[ObjectType], + entity: Object, + a: AnnotationLike + ): Boolean = { + val annotationType = a.annotationType.asObjectType + + val analysesElementValues = + getValue(p, annotationType, a.elementValuePairs, "analyses").asArrayValue.values + val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) + + analyses.exists(as.contains) + + } + + def validateProperty( + p: SomeProject, + as: Set[ObjectType], + entity: Entity, + a: AnnotationLike, + properties: Traversable[Property] + ): Option[String] = { + if (!properties.exists(p ⇒ p == property)) { + // ... when we reach this point the expected property was not found. + Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) + } else { + None + } + } +} + +class _MutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.MutableField) + +class _ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.ShallowImmutableField) + +class _DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.DependentImmutableField) + +class _DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.DeepImmutableField) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/references/FieldReferenceImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/references/FieldReferenceImmutabilityMatcher.scala new file mode 100644 index 0000000000..d33fdb3f82 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/references/FieldReferenceImmutabilityMatcher.scala @@ -0,0 +1,59 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf +package properties +package immutability +package references + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.FieldReferenceImmutability + +/** + * This is the basis for the matchers that match the immutability of a field reference + * @author Tobias Roth + */ +class FieldReferenceImmutabilityMatcher(val property: FieldReferenceImmutability) + extends AbstractPropertyMatcher { + + final private val PropertyReasonID = 0 + + override def isRelevant( + p: SomeProject, + as: Set[ObjectType], + entity: Object, + a: AnnotationLike + ): Boolean = { + val annotationType = a.annotationType.asObjectType + + val analysesElementValues = + getValue(p, annotationType, a.elementValuePairs, "analyses").asArrayValue.values + val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) + + analyses.exists(as.contains) + } + + def validateProperty( + p: SomeProject, + as: Set[ObjectType], + entity: Entity, + a: AnnotationLike, + properties: Traversable[Property] + ): Option[String] = { + if (!properties.exists(p ⇒ p == property)) { + // ... when we reach this point the expected property was not found. + Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) + } else { + None + } + } +} + +class LazyInitializedThreadSafeFieldReferenceMatcher extends FieldReferenceImmutabilityMatcher(br.fpcf.properties.LazyInitializedThreadSafeFieldReference) + +class LazyInitializedNotThreadSafeButDeterministicFieldReferenceMatcher extends FieldReferenceImmutabilityMatcher(br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicFieldReference) + +class LazyInitializedNotThreadSafeFieldReferenceMatcher extends FieldReferenceImmutabilityMatcher(br.fpcf.properties.LazyInitializedNotThreadSafeFieldReference) + +class ImmutableFieldReferenceMatcher extends FieldReferenceImmutabilityMatcher(br.fpcf.properties.ImmutableFieldReference) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/references/MutableFieldReferenceMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/references/MutableFieldReferenceMatcher.scala new file mode 100644 index 0000000000..e27852235b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/references/MutableFieldReferenceMatcher.scala @@ -0,0 +1,71 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf +package properties +package immutability +package references + +import org.opalj.br.AnnotationLike +import org.opalj.br.BooleanValue +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.fpcf.properties.FieldPrematurelyRead +import org.opalj.br.fpcf.properties.PrematurelyReadField + +/** + * Matches mutable field references + * @author Tobias Peter Roth + */ +class MutableFieldReferenceMatcher extends AbstractPropertyMatcher { + + val property = br.fpcf.properties.MutableFieldReference + + final private val PropertyReasonID = 0 + + override def isRelevant( + p: SomeProject, + as: Set[ObjectType], + entity: Object, + a: AnnotationLike + ): Boolean = { + + val annotationType = a.annotationType.asObjectType + + val analysesElementValues = + getValue(p, annotationType, a.elementValuePairs, "analyses").asArrayValue.values + + val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) + + if (!analyses.exists(as.contains)) return false; + + val prematurelyRead = getValue(p, annotationType, a.elementValuePairs, "prematurelyRead") + .asInstanceOf[BooleanValue] + .value + + if (prematurelyRead) { + val propertyStore = p.get(PropertyStoreKey) + propertyStore(entity, FieldPrematurelyRead.key) match { + case FinalP(PrematurelyReadField) ⇒ true + case _ ⇒ false + } + } else { + true + } + } + + def validateProperty( + p: SomeProject, + as: Set[ObjectType], + entity: Entity, + a: AnnotationLike, + properties: Traversable[Property] + ): Option[String] = { + if (!properties.exists(p ⇒ p == property)) { + // ... when we reach this point the expected property was not found. + Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) + } else { + None + } + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_mutability/AbstractTypeImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/AbstractTypeImmutabilityMatcher.scala similarity index 52% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_mutability/AbstractTypeImmutabilityMatcher.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/AbstractTypeImmutabilityMatcher.scala index 6414bf7e6f..a112aa8a5e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_mutability/AbstractTypeImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/AbstractTypeImmutabilityMatcher.scala @@ -1,18 +1,34 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package properties -package type_mutability +package org.opalj.fpcf.properties.immutability.types import org.opalj.br.AnnotationLike import org.opalj.br.ObjectType import org.opalj.br.analyses.Project import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher class AbstractTypeImmutabilityMatcher( val property: TypeImmutability ) extends AbstractPropertyMatcher { + import org.opalj.br.analyses.SomeProject + + override def isRelevant( + p: SomeProject, + as: Set[ObjectType], + entity: Object, + a: AnnotationLike + ): Boolean = { + val annotationType = a.annotationType.asObjectType + + val analysesElementValues = + getValue(p, annotationType, a.elementValuePairs, "analyses").asArrayValue.values + val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) + + analyses.exists(as.contains) + } + override def validateProperty( p: Project[_], as: Set[ObjectType], @@ -31,9 +47,12 @@ class AbstractTypeImmutabilityMatcher( } } -class ImmutableTypeMatcher - extends AbstractTypeImmutabilityMatcher(org.opalj.br.fpcf.properties.ImmutableType) -class ImmutableContainerTypeMatcher - extends AbstractTypeImmutabilityMatcher(org.opalj.br.fpcf.properties.ImmutableContainerType) +class DeepImmutableTypeMatcher + extends AbstractTypeImmutabilityMatcher(org.opalj.br.fpcf.properties.DeepImmutableType) +class DependentImmutableTypeMatcher + extends AbstractTypeImmutabilityMatcher(org.opalj.br.fpcf.properties.DependentImmutableType) +class ShallowImmutableTypeMatcher + extends AbstractTypeImmutabilityMatcher(org.opalj.br.fpcf.properties.ShallowImmutableType) class MutableTypeMatcher extends AbstractTypeImmutabilityMatcher(org.opalj.br.fpcf.properties.MutableType) + diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/_NewTypeImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/_NewTypeImmutabilityMatcher.scala new file mode 100644 index 0000000000..701c8234a9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/_NewTypeImmutabilityMatcher.scala @@ -0,0 +1,58 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf +package properties +package immutability +package types + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.TypeImmutability + +/** + * This is the basis for the matchers that match the immutability of a type + * @author Tobias Peter Roth + */ +class _NewTypeImmutabilityMatcher(val property: TypeImmutability) + extends AbstractPropertyMatcher { + + final private val PropertyReasonID = 0 + + override def isRelevant( + p: SomeProject, + as: Set[ObjectType], + entity: Object, + a: AnnotationLike + ): Boolean = { + val annotationType = a.annotationType.asObjectType + + val analysesElementValues = + getValue(p, annotationType, a.elementValuePairs, "analyses").asArrayValue.values + val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) + + analyses.exists(as.contains) + } + + def validateProperty( + p: SomeProject, + as: Set[ObjectType], + entity: Entity, + a: AnnotationLike, + properties: Traversable[Property] + ): Option[String] = { + if (!properties.exists(p ⇒ p == property)) { + // ... when we reach this point the expected property was not found. + Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) + } else { + None + } + } +} +class _NewMutableTypeMatcher extends _NewTypeImmutabilityMatcher(br.fpcf.properties.MutableType) + +class _ShallowImmutableTypeMatcher extends _NewTypeImmutabilityMatcher(br.fpcf.properties.ShallowImmutableType) + +class _DependentImmutableTypeMatcher extends _NewTypeImmutabilityMatcher(br.fpcf.properties.DependentImmutableType) + +class _DeepImmutableTypeMatcher extends _NewTypeImmutabilityMatcher(br.fpcf.properties.DeepImmutableType) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/purity/PurityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/purity/PurityMatcher.scala index d1ae473e56..edd3b99438 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/purity/PurityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/purity/PurityMatcher.scala @@ -12,13 +12,13 @@ import org.opalj.br.MethodDescriptor import org.opalj.br.ObjectType import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties.ClassImmutability import org.opalj.br.fpcf.properties.FieldLocality -import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.Purity import org.opalj.br.fpcf.properties.ReturnValueFreshness import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.properties.ClassifiedImpure +import org.opalj.br.fpcf.properties.ClassImmutability +import org.opalj.br.fpcf.properties.FieldImmutability /** * Base trait for matchers that match a method's `Purity` property. @@ -70,8 +70,8 @@ sealed abstract class PurityMatcher(val property: Purity) extends AbstractProper val pk = getValue(project, annotationType, ep.elementValuePairs, "pk").asStringValue.value match { case "Purity" ⇒ Purity.key - case "FieldMutability" ⇒ FieldMutability.key case "ClassImmutability" ⇒ ClassImmutability.key + case "FieldImmutability" ⇒ FieldImmutability.key case "ReturnValueFreshness" ⇒ ReturnValueFreshness.key case "FieldLocality" ⇒ FieldLocality.key } diff --git a/OPAL/ai/src/main/scala/org/opalj/ai/common/DomainRegistry.scala b/OPAL/ai/src/main/scala/org/opalj/ai/common/DomainRegistry.scala index 88eb587c41..78fd61356f 100644 --- a/OPAL/ai/src/main/scala/org/opalj/ai/common/DomainRegistry.scala +++ b/OPAL/ai/src/main/scala/org/opalj/ai/common/DomainRegistry.scala @@ -260,7 +260,8 @@ object DomainRegistry { register( "computations related to reference types track nullness, must alias and origin information; records the ai-time def-use information", classOf[domain.l1.DefaultReferenceValuesDomainWithCFGAndDefUse[_]], - lessPreciseDomains = Set(classOf[domain.l0.BaseDomain[_]]), + lessPreciseDomains = Set(classOf[domain.l0.PrimitiveTACAIDomain]), //TODO + //lessPreciseDomains = Set(classOf[domain.l0.BaseDomain[_]]), (project: SomeProject, method: Method) ⇒ { new domain.l1.DefaultReferenceValuesDomainWithCFGAndDefUse(project, method) } diff --git a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBFieldValuesAnalysis.scala b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBFieldValuesAnalysis.scala index 8abd7827c8..a9ed6071ca 100644 --- a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBFieldValuesAnalysis.scala +++ b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBFieldValuesAnalysis.scala @@ -368,7 +368,7 @@ class LBFieldValuesAnalysis private[analyses] ( classHierarchy.isKnownToBeFinal(dv.leastUpperType.get)) { Result(FinalEP(f, vi)) } else { - InterimResult.forLB(f, vi, newDependees, c) + InterimResult.forLB(f, vi, newDependees.toSet, c) } } @@ -378,7 +378,7 @@ class LBFieldValuesAnalysis private[analyses] ( Result(FinalEP(f, vi)) } else { // println(f.toJava+"======>>>>>>\n\t\t"+vi+"\n\t\t"+relevantDependees) - InterimResult.forLB(f, vi, relevantDependees, c) + InterimResult.forLB(f, vi, relevantDependees.toSet, c) } } diff --git a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBMethodReturnValuesAnalysis.scala b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBMethodReturnValuesAnalysis.scala index b4eb9fec3c..f069f0ecc1 100644 --- a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBMethodReturnValuesAnalysis.scala +++ b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBMethodReturnValuesAnalysis.scala @@ -158,7 +158,7 @@ class LBMethodReturnValuesAnalysis private[analyses] ( } else { // We have potentially relevant dependencies (please, recall that we are currently // not flow-sensitive). - InterimResult.forLB(method, MethodReturnValue(vi), dependees, c) + InterimResult.forLB(method, MethodReturnValue(vi), dependees.toSet, c) } } else { //... in this run (!) no refinement was possible and therefore we had an early @@ -169,7 +169,7 @@ class LBMethodReturnValuesAnalysis private[analyses] ( if (dependees.isEmpty) { Result(FinalEP(method, mrv)) } else { - InterimResult.forLB(method, mrv, dependees, c) + InterimResult.forLB(method, mrv, dependees.toSet, c) } } } diff --git a/OPAL/br/src/main/resources/reference.conf b/OPAL/br/src/main/resources/reference.conf index 24d814f92c..983debed82 100644 --- a/OPAL/br/src/main/resources/reference.conf +++ b/OPAL/br/src/main/resources/reference.conf @@ -25,12 +25,12 @@ org.opalj { analyses { cg { ClosedPackagesKey { - analysis = "org.opalj.br.analyses.cg.AllPackagesClosed" # considers all packages closed (e.g. suitable when analyzing an application) + #analysis = "org.opalj.br.analyses.cg.AllPackagesClosed" # considers all packages closed (e.g. suitable when analyzing an application) - #analysis = "org.opalj.br.analyses.cg.OpenCodeBase" # considers all packages open (e.g. suitable for security analyses) + analysis = "org.opalj.br.analyses.cg.OpenCodeBase" # considers all packages open (e.g. suitable for security analyses) #analysis = "org.opalj.br.analyses.cg.ClosedPackagesConfiguration" - #closedPackages = "java(/.*)*" + closedPackages = "java(/.*)*" # Use a regular expresion (e.g. "java(/.*)*") to specify all packages # that shall be considered closed. In some cases, it might be easier to # specify all open packages. In this case it's possible to invert the @@ -45,9 +45,9 @@ org.opalj { } InitialEntryPointsKey { - #analysis = "org.opalj.br.analyses.cg.ApplicationEntryPointsFinder" - analysis = "org.opalj.br.analyses.cg.ApplicationWithoutJREEntryPointsFinder" - #analysis = "org.opalj.br.analyses.cg.LibraryEntryPointsFinder" + analysis = "org.opalj.br.analyses.cg.ApplicationEntryPointsFinder" + #analysis = "org.opalj.br.analyses.cg.ApplicationWithoutJREEntryPointsFinder" + ### analysis = "org.opalj.br.analyses.cg.LibraryEntryPointsFinder" #analysis = "org.opalj.br.analyses.cg.MetaEntryPointsFinder" entryPoints = [ {declaringClass = "java/lang/System", name = "initializeSystemClass", descriptor = "()V"}, @@ -81,7 +81,7 @@ org.opalj { InitialInstantiatedTypesKey { analysis = "org.opalj.br.analyses.cg.ApplicationInstantiatedTypesFinder" - #analysis = "org.opalj.br.analyses.cg.LibraryInstantiatedTypesFinder" + ###analysis = "org.opalj.br.analyses.cg.LibraryInstantiatedTypesFinder" instantiatedTypes = [ @@ -102,79 +102,79 @@ org.opalj { analyses { "L0FieldMutabilityAnalysis" { description = "Determines if fields are (effectively) final.", - eagerFactory = "org.opalj.fpcf.analyses.EagerL0FieldMutabilityAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyL0FieldMutabilityAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerL0FieldMutabilityAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyL0FieldMutabilityAnalysis" }, "L0CompileTimeConstancyAnalysis" { description = "Determines if static fields are compile time constants.", - eagerFactory = "org.opalj.fpcf.analyses.EagerL0CompileTimeConstancyAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerL0CompileTimeConstancyAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis" }, "L0SelfReferenceLeakageAnalysis" { description = "Determines if an object may leak its self reference (`this`).", - eagerFactory = "org.opalj.fpcf.analyses.L0SelfReferenceLeakageAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.L0SelfReferenceLeakageAnalysis" #TODO This one does not yet have a lazy factory }, "ClassImmutabilityAnalysis" { description = "Determines if instances of a class are immutable.", - eagerFactory = "org.opalj.fpcf.analyses.EagerClassImmutabilityAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyClassImmutabilityAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerClassImmutabilityAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis" }, "L1ThrownExceptionsAnalysis" { description = "Determines the exceptions that are thrown by a method.", - eagerFactory = "org.opalj.fpcf.analyses.EagerL1ThrownExceptionsAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyL1ThrownExceptionsAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerL1ThrownExceptionsAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyL1ThrownExceptionsAnalysis" }, "L0AllocationFreenessAanalysis" { description = "Determines if a method may (transitively) cause allocations.", - eagerFactory = "org.opalj.fpcf.analyses.EagerL0AllocationFreenessAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyL0AllocationFreenessAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerL0AllocationFreenessAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyL0AllocationFreenessAnalysis" }, "StaticDataUsageAnalysis" { description = "Determines if a method uses only compile time constant static state.", - eagerFactory = "org.opalj.fpcf.analyses.EagerStaticDataUsageAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyStaticDataUsageAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerStaticDataUsageAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis" }, "L0PurityAnalysis" { description = "Determines a method's purity.", - eagerFactory = "org.opalj.fpcf.analyses.EagerL0PurityAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyL0PurityAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyL0PurityAnalysis" }, // The virtual/aggregating ones... "TypeImmutabilityAnalysis" { description = "Determines if instances of a type (including subclasses) are immutable.", - eagerFactory = "org.opalj.fpcf.analyses.EagerTypeImmutabilityAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyTypeImmutabilityAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis" }, "VirtualMethodThrownExceptionsAnalysis" { description = "Determines the aggregated thrown exceptions for a virtual method.", - eagerFactory = "org.opalj.fpcf.analyses.EagerVirtualMethodThrownExceptionsAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyVirtualMethodThrownExceptionsAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerVirtualMethodThrownExceptionsAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyVirtualMethodThrownExceptionsAnalysis" }, "VirtualMethodAllocationFreenessAnalysis" { description = "Determines the aggregated allocation freeness for a virtual method.", - eagerFactory = "org.opalj.fpcf.analyses.EagerVirtualMethodPurityAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyVirtualMethodPurityAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerVirtualMethodPurityAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyVirtualMethodPurityAnalysis" }, "VirtualMethodStaticDataUsageAnalysis" { description = "Determines the aggregated static data use freeness for a virtual method.", - eagerFactory = "org.opalj.fpcf.analyses.EagerVirtualMethodStaticDataUsageAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyVirtualMethodStaticDataUsageAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerVirtualMethodStaticDataUsageAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyVirtualMethodStaticDataUsageAnalysis" }, "VirtualMethodPurityAnalysis" { description = "Determines the aggregated purity for a virtual method.", - eagerFactory = "org.opalj.fpcf.analyses.EagerVirtualMethodPurityAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyVirtualMethodPurityAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerVirtualMethodPurityAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyVirtualMethodPurityAnalysis" }, "VirtualCallAggregatingEscapeAnalysis" { description = "Determines the aggregated escape level for a virtual formal parameter.", - eagerFactory = "org.opalj.fpcf.analyses.EagerVirtualCallAggregatingEscapeAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyVirtualCallAggregatingEscapeAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerVirtualCallAggregatingEscapeAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyVirtualCallAggregatingEscapeAnalysis" }, "VirtualReturnValueFreshnessAnalysis" { description = "Determines the aggregated return value freshness for a virtual method.", - eagerFactory = "org.opalj.fpcf.analyses.EagerVirtualReturnValueFreshnessAnalysis", - lazyFactory = "org.opalj.fpcf.analyses.LazyVirtualReturnValueFreshnessAnalysis" + eagerFactory = "org.opalj.br.fpcf.analyses.EagerVirtualReturnValueFreshnessAnalysis", + lazyFactory = "org.opalj.br.fpcf.analyses.LazyVirtualReturnValueFreshnessAnalysis" } } } diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/AnalysisApplication.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/AnalysisApplication.scala index d382842826..c9a77efe28 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/AnalysisApplication.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/AnalysisApplication.scala @@ -117,8 +117,8 @@ trait AnalysisApplication { // // 1. Process args // - def splitCPath(path: String) = path.substring(4).split(File.pathSeparator) - def splitLibCPath(path: String) = path.substring(7).split(File.pathSeparator) + def splitCPath(path: String) = path.substring(path.indexOf('=') + 1).split(File.pathSeparator) + def splitLibCPath(path: String) = path.substring(path.indexOf('=') + 1).split(File.pathSeparator) args.foreach { arg ⇒ if (arg == "-help") { printUsage @@ -130,7 +130,7 @@ trait AnalysisApplication { } else if (arg == "-completelyLoadLibraries") { completelyLoadLibraries = true } else if (arg.startsWith("-projectConfig=")) { - projectConfig = Some(arg.substring(13)) + projectConfig = Some(arg.substring(arg.indexOf('=') + 1)) } else if (arg == "-renderConfig") { renderConfig = true } else { diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ConfiguredPurity.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ConfiguredPurity.scala index 8994758fba..325adc1d7a 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ConfiguredPurity.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ConfiguredPurity.scala @@ -79,8 +79,6 @@ class ConfiguredPurity( dm } - propertyStore.waitOnPhaseCompletion() // wait until setting configured purities is completed - def wasSet(dm: DeclaredMethod): Boolean = methods.contains(dm) } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0AllocationFreenessAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0AllocationFreenessAnalysis.scala index 3c2c111e00..ebc0ea738b 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0AllocationFreenessAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0AllocationFreenessAnalysis.scala @@ -50,9 +50,9 @@ class L0AllocationFreenessAnalysis private[analyses] ( def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { case FinalP(af) ⇒ Result(dm, af) case ep @ InterimLUBP(lb, ub) ⇒ - InterimResult(dm, lb, ub, Seq(ep), c) + InterimResult(dm, lb, ub, Set(ep), c) case epk ⇒ - InterimResult(dm, MethodWithAllocations, AllocationFreeMethod, Seq(epk), c) + InterimResult(dm, MethodWithAllocations, AllocationFreeMethod, Set(epk), c) } c(propertyStore(declaredMethods(dm.definedMethod), AllocationFreeness.key)) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ClassImmutabilityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0ClassImmutabilityAnalysis.scala similarity index 80% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ClassImmutabilityAnalysis.scala rename to OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0ClassImmutabilityAnalysis.scala index c116c173ac..fd349cee06 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ClassImmutabilityAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0ClassImmutabilityAnalysis.scala @@ -29,19 +29,19 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.MutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableClass import org.opalj.br.fpcf.properties.ClassImmutability -import org.opalj.br.fpcf.properties.FieldMutability -import org.opalj.br.fpcf.properties.FinalField -import org.opalj.br.fpcf.properties.ImmutableContainer -import org.opalj.br.fpcf.properties.ImmutableContainerType -import org.opalj.br.fpcf.properties.ImmutableObject -import org.opalj.br.fpcf.properties.ImmutableType -import org.opalj.br.fpcf.properties.MutableObject -import org.opalj.br.fpcf.properties.MutableObjectByAnalysis -import org.opalj.br.fpcf.properties.MutableObjectDueToUnknownSupertypes -import org.opalj.br.fpcf.properties.MutableType -import org.opalj.br.fpcf.properties.NonFinalField +import org.opalj.br.fpcf.properties.FieldImmutability import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.DeepImmutableField +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.DependentImmutableField +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.MutableType +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.br.fpcf.properties.ShallowImmutableType /** * Determines the mutability of instances of a specific class. In case the class @@ -50,7 +50,7 @@ import org.opalj.br.fpcf.properties.TypeImmutability * defined fields are taken into consideration. An interfaces is always considered to be immutable. * If you need to know if all possible instances of an interface or some type; i.e., all instances * of the classes that implement the respective interface/inherit from some class are immutable, - * you can query the [[org.opalj.br.fpcf.properties.TypeImmutability]] property. + * you can query the [[org.opalj.br.fpcf.properties.TypeImmutability_old]] property. * * In case of incomplete class hierarchies or if the class hierarchy is complete, but some * class files are not found the sound approximation is done that the respective classes are @@ -64,8 +64,10 @@ import org.opalj.br.fpcf.properties.TypeImmutability * @author Michael Eichberg * @author Florian Kübler * @author Dominik Helm + * @author Tobias Roth */ -class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { +class L0ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { + /* * The analysis is implemented as an incremental analysis which starts with the analysis * of those types which directly inherit from java.lang.Object and then propagates the @@ -81,7 +83,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { */ @inline private[this] def createResultForAllSubtypes( t: ObjectType, - immutability: MutableObject + immutability: ClassImmutability ): MultiResult = { val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) val r = allSubtypes.map { st ⇒ new FinalEP(st, immutability) }.toSeq @@ -101,14 +103,14 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { project.classFile(t) match { case Some(scf) ⇒ nextComputations ::= ( - (determineClassImmutability(t, cfMutability, cfMutabilityIsFinal, false) _, scf) + (determineL0ClassImmutability(t, cfMutability, cfMutabilityIsFinal, false) _, scf) ) case None ⇒ OPALLogger.warn( "project configuration - object immutability analysis", s"missing class file of ${t.toJava}; setting all subtypes to mutable" ) - results ::= createResultForAllSubtypes(t, MutableObjectDueToUnknownSupertypes) + results ::= createResultForAllSubtypes(t, MutableClass) //MutableObjectDueToUnknownSupertypes) } } IncrementalResult(Results(results), nextComputations.iterator) @@ -119,25 +121,26 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { case t: ObjectType ⇒ //this is safe classHierarchy.superclassType(t) match { - case None ⇒ Result(t, MutableObjectDueToUnknownSupertypes) + case None ⇒ Result(t, MutableClass) case Some(superClassType) ⇒ + val cf = project.classFile(t) match { case None ⇒ - return Result(t, MutableObjectByAnalysis) //TODO consider other lattice element + return Result(t, MutableClass) //TODO consider other lattice element case Some(cf) ⇒ cf } propertyStore(superClassType, ClassImmutability.key) match { - case UBP(p: MutableObject) ⇒ Result(t, p) + case UBP(MutableClass) ⇒ Result(t, MutableClass) case eps: EPS[ObjectType, ClassImmutability] ⇒ - determineClassImmutability( + determineL0ClassImmutability( superClassType, eps, eps.isFinal, lazyComputation = true )(cf) case epk ⇒ - determineClassImmutability( + determineL0ClassImmutability( superClassType, epk, superClassMutabilityIsFinal = false, @@ -163,7 +166,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { * must not be "MutableObject"; this case has to be handled explicitly. Hence, * the mutability is either unknown, immutable or (at least) conditionally immutable. */ - def determineClassImmutability( + def determineL0ClassImmutability( superClassType: ObjectType, superClassInformation: EOptionP[Entity, Property], superClassMutabilityIsFinal: Boolean, @@ -171,6 +174,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { )( cf: ClassFile ): ProperPropertyComputationResult = { + // assert(superClassMutability.isMutable.isNoOrUnknown) val t = cf.thisType @@ -184,13 +188,14 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { var hasFieldsWithUnknownMutability = false val instanceFields = cf.fields.filter { f ⇒ !f.isStatic } - dependees ++= (propertyStore(instanceFields, FieldMutability) collect { - case FinalP(_: NonFinalField) ⇒ + dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { + + case FinalP(MutableField) ⇒ // <=> The class is definitively mutable and therefore also all subclasses. if (lazyComputation) - return Result(t, MutableObjectByAnalysis); + return Result(t, MutableClass); else - return createResultForAllSubtypes(t, MutableObjectByAnalysis); + return createResultForAllSubtypes(t, MutableClass); case ep @ InterimE(e) ⇒ hasFieldsWithUnknownMutability = true (e, ep) @@ -204,25 +209,26 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { var minLocalImmutability: ClassImmutability = if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) - MutableObjectByAnalysis + MutableClass else - ImmutableContainer + ShallowImmutableClass // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability = superClassInformation match { - case UBP(ImmutableContainer) ⇒ ImmutableContainer - case _ ⇒ ImmutableObject + case UBP(ShallowImmutableClass) ⇒ ShallowImmutableClass + case _ ⇒ + DeepImmutableClass } if (cf.fields.exists(f ⇒ !f.isStatic && f.fieldType.isArrayType)) { // IMPROVE We could analyze if the array is effectively final. // I.e., it is only initialized once (at construction time) and no reference to it // is passed to another object. - maxLocalImmutability = ImmutableContainer + maxLocalImmutability = ShallowImmutableClass } var fieldTypes: Set[ObjectType] = Set.empty - if (maxLocalImmutability == ImmutableObject) { + if (maxLocalImmutability == DeepImmutableClass) { fieldTypes = // IMPROVE Use the precise type of the field (if available)! cf.fields.collect { @@ -262,16 +268,18 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { val hasMutableOrConditionallyImmutableField = // IMPROVE Use the precise type of the field (if available)! fieldTypesImmutability.exists { eOptP ⇒ - eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) + import org.opalj.br.fpcf.properties.ShallowImmutableType + eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub == ShallowImmutableType) } if (hasMutableOrConditionallyImmutableField) { - maxLocalImmutability = ImmutableContainer + maxLocalImmutability = ShallowImmutableClass } else { val fieldTypesWithUndecidedMutability: Traversable[EOptionP[Entity, Property]] = // Recall: we don't have fields which are mutable or conditionally immutable fieldTypesImmutability.filterNot { eOptP ⇒ - eOptP.hasUBP && eOptP.ub == ImmutableType && eOptP.isFinal + import org.opalj.br.fpcf.properties.DeepImmutableType + eOptP.hasUBP && eOptP.ub == DeepImmutableType && eOptP.isFinal } fieldTypesWithUndecidedMutability.foreach { eOptP ⇒ dependees += (eOptP.e → eOptP) @@ -300,45 +308,49 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { someEPS match { // Superclass related dependencies: // - case UBP(_: MutableObject) ⇒ return Result(t, MutableObjectByAnalysis); + case UBP(MutableClass) ⇒ return Result(t, MutableClass); - case LBP(ImmutableObject) ⇒ // the super class + case LBP(DeepImmutableClass) ⇒ // the super class dependees -= SuperClassKey - case UBP(ImmutableContainer) ⇒ // super class is at most immutable container + case UBP(ShallowImmutableClass) ⇒ // super class is at most immutable container if (someEPS.isFinal) dependees -= SuperClassKey - maxLocalImmutability = ImmutableContainer + maxLocalImmutability = ShallowImmutableClass dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - case LBP(ImmutableContainer) ⇒ // super class is a least immutable container - if (minLocalImmutability != ImmutableContainer && - !dependees.valuesIterator.exists(_.pk == FieldMutability.key)) - minLocalImmutability = ImmutableContainer // Lift lower bound when possible + case LBP(ShallowImmutableClass) ⇒ // super class is a shallow immutable + if (minLocalImmutability != ShallowImmutableClass && + !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) + minLocalImmutability = ShallowImmutableClass // Lift lower bound when possible - case LUBP(_: MutableObject, ImmutableObject) ⇒ // No information about superclass + case LUBP(MutableClass, DeepImmutableClass) ⇒ // No information about superclass // Properties related to the type of the class' fields. // - case UBP(ImmutableContainerType | MutableType) ⇒ - maxLocalImmutability = ImmutableContainer + case UBP(ShallowImmutableType | MutableType) ⇒ + maxLocalImmutability = ShallowImmutableClass dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - case ELBP(e, ImmutableType) ⇒ // Immutable field type, no influence on mutability + case ELBP(e, DeepImmutableType) ⇒ // Immutable field type, no influence on mutability dependees -= e - case UBP(ImmutableType) ⇒ // No information about field type + case UBP(DeepImmutableType) ⇒ // No information about field type // Field Mutability related dependencies: // - case UBP(_: NonFinalField) ⇒ return Result(t, MutableObjectByAnalysis); + case UBP(MutableField) ⇒ return Result(t, MutableClass); - case ELBP(e, _: FinalField) ⇒ + case ELBP(e, ShallowImmutableField | + DependentImmutableField | + DeepImmutableField) ⇒ dependees -= e - if (minLocalImmutability != ImmutableContainer && + if (minLocalImmutability != ShallowImmutableClass && !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) - minLocalImmutability = ImmutableContainer // Lift lower bound when possible + minLocalImmutability = ShallowImmutableClass // Lift lower bound when possible - case UBP(_: FinalField) ⇒ // no information about field mutability + case UBP(DeepImmutableField | + DependentImmutableField | + ShallowImmutableField) ⇒ // no information about field mutability } @@ -355,9 +367,9 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { */ // Lift lower bound once no dependencies other than field type mutabilities are left - if (minLocalImmutability != ImmutableContainer && + if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) - minLocalImmutability = ImmutableContainer + minLocalImmutability = ShallowImmutableClass //ImmutableContainer if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { /*[DEBUG] @@ -382,13 +394,13 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { Result(t, maxLocalImmutability) } else { - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.valuesIterator.toSet, c) } } //[DEBUG] assert(initialImmutability.isRefinable) val result = - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.valuesIterator.toSet, c) if (lazyComputation) result else { @@ -400,12 +412,12 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { } } -trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +trait L0ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability) final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability, TypeImmutability, FieldMutability) + PropertyBounds.lubs(ClassImmutability, TypeImmutability, FieldImmutability) override type InitializationData = TraversableOnce[ClassFile] @@ -421,12 +433,12 @@ trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { // 1.1 // java.lang.Object is by definition immutable. - set(ObjectType.Object, ImmutableObject) + set(ObjectType.Object, DeepImmutableClass) // 1.2 // All (instances of) interfaces are (by their very definition) also immutable. val allInterfaces = project.allClassFiles.filter(cf ⇒ cf.isInterfaceDeclaration) - allInterfaces.map(cf ⇒ set(cf.thisType, ImmutableObject)) + allInterfaces.map(cf ⇒ set(cf.thisType, DeepImmutableClass)) // 2. // All classes that do not have complete superclass information are mutable @@ -439,7 +451,7 @@ trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { unexpectedRootClassTypes foreach { rt ⇒ allSubtypes(rt, reflexive = true) foreach { ot ⇒ project.classFile(ot) foreach { cf ⇒ - set(cf.thisType, MutableObjectDueToUnknownSupertypes) + set(cf.thisType, MutableClass) } } } @@ -462,7 +474,7 @@ trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { s"${t.toJava}'s class file is not available" ) allSubtypes(t, reflexive = true).foreach(project.classFile(_).foreach { cf ⇒ - set(cf.thisType, MutableObjectDueToUnknownSupertypes) + set(cf.thisType, MutableClass) }) } cfs @@ -488,8 +500,8 @@ trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { * * @author Michael Eichberg */ -object EagerClassImmutabilityAnalysis - extends ClassImmutabilityAnalysisScheduler +object EagerL0ClassImmutabilityAnalysis + extends L0ClassImmutabilityAnalysisScheduler with FPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -497,11 +509,11 @@ object EagerClassImmutabilityAnalysis override def derivesCollaboratively: Set[PropertyBounds] = Set.empty override def start(p: SomeProject, ps: PropertyStore, cfs: InitializationData): FPCFAnalysis = { - val analysis = new ClassImmutabilityAnalysis(p) + val analysis = new L0ClassImmutabilityAnalysis(p) ps.scheduleEagerComputationsForEntities(cfs)( - analysis.determineClassImmutability( + analysis.determineL0ClassImmutability( superClassType = null, - FinalEP(ObjectType.Object, ImmutableObject), + FinalEP(ObjectType.Object, DeepImmutableClass), superClassMutabilityIsFinal = true, lazyComputation = false ) @@ -515,8 +527,8 @@ object EagerClassImmutabilityAnalysis * * @author Michael Eichberg */ -object LazyClassImmutabilityAnalysis - extends ClassImmutabilityAnalysisScheduler +object LazyL0ClassImmutabilityAnalysis + extends L0ClassImmutabilityAnalysisScheduler with FPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) @@ -526,7 +538,8 @@ object LazyClassImmutabilityAnalysis ps: PropertyStore, unused: InitializationData ): FPCFAnalysis = { - val analysis = new ClassImmutabilityAnalysis(p) + + val analysis = new L0ClassImmutabilityAnalysis(p) ps.registerLazyPropertyComputation( ClassImmutability.key, analysis.doDetermineClassImmutability ) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0CompileTimeConstancyAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0CompileTimeConstancyAnalysis.scala index 36c309bf07..00ef740bbe 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0CompileTimeConstancyAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0CompileTimeConstancyAnalysis.scala @@ -65,7 +65,7 @@ class L0CompileTimeConstancyAnalysis private[analyses] ( final val project: Some field, CompileTimeVaryingField, CompileTimeConstantField, - Seq(dependee), + Set(dependee), c ) @@ -75,7 +75,7 @@ class L0CompileTimeConstancyAnalysis private[analyses] ( final val project: Some } } - InterimResult(field, CompileTimeVaryingField, CompileTimeConstantField, Seq(dependee), c) + InterimResult(field, CompileTimeVaryingField, CompileTimeConstantField, Set(dependee), c) } /** Called when the analysis is scheduled lazily. */ diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0FieldMutabilityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0FieldImmutabilityAnalysis.scala similarity index 72% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0FieldMutabilityAnalysis.scala rename to OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 3653d72c42..5fab9f8063 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0FieldMutabilityAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -11,12 +11,10 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.br.analyses.FieldAccessInformationKey import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties.DeclaredFinalField -import org.opalj.br.fpcf.properties.EffectivelyFinalField -import org.opalj.br.fpcf.properties.FieldMutability -import org.opalj.br.fpcf.properties.NonFinalFieldByAnalysis -import org.opalj.br.fpcf.properties.NonFinalFieldByLackOfInformation import org.opalj.br.instructions.PUTSTATIC +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.ShallowImmutableField /** * Determines if a private, static, non-final field is always initialized at most once or @@ -25,7 +23,7 @@ import org.opalj.br.instructions.PUTSTATIC * available data-store) are not considered. This is in-line with the semantics of final, * which also does not prevent reads of partially initialized objects. */ -class L0FieldMutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { +class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { final val fieldAccessInformation = project.get(FieldAccessInformationKey) @@ -34,11 +32,11 @@ class L0FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext * Final fields are considered [[org.opalj.br.fpcf.properties.DeclaredFinalField]], non-final and * non-private fields or fields of library classes whose method bodies are not available are * considered [[org.opalj.br.fpcf.properties.NonFinalFieldByAnalysis]]. - * For all other cases the call is delegated to [[determineFieldMutability]]. + * For all other cases the call is delegated to [[determineFieldImmutability]]. */ - def determineFieldMutabilityLazy(e: Entity): ProperPropertyComputationResult = { + def determineFieldImmutabilityLazy(e: Entity): ProperPropertyComputationResult = { e match { - case field: Field ⇒ determineFieldMutability(field) + case field: Field ⇒ determineFieldImmutability(field) case _ ⇒ throw new IllegalArgumentException(s"$e is not a Field") } } @@ -54,18 +52,19 @@ class L0FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext * @param field A field without native methods and where the method body of all * non-abstract methods is available. */ - def determineFieldMutability(field: Field): ProperPropertyComputationResult = { + def determineFieldImmutability(field: Field): ProperPropertyComputationResult = { + if (field.isFinal) - return Result(field, DeclaredFinalField); + return Result(field, ShallowImmutableField); if (!field.isPrivate) - return Result(field, NonFinalFieldByLackOfInformation); + return Result(field, MutableField); if (!field.isStatic) - return Result(field, NonFinalFieldByLackOfInformation); + return Result(field, MutableField); if (field.classFile.methods.exists(_.isNative)) - return Result(field, NonFinalFieldByLackOfInformation); + return Result(field, MutableField); val classFile = field.classFile val thisType = classFile.thisType @@ -83,33 +82,33 @@ class L0FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext // resolution of the field reference. val field = classFile.findField(fieldName, fieldType) if (field.isDefined) { - return Result(field.get, NonFinalFieldByAnalysis); + return Result(field.get, MutableField); } case _ ⇒ } } - Result(field, EffectivelyFinalField) + Result(field, ShallowImmutableField) } } -trait L0FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { final override def uses: Set[PropertyBounds] = Set.empty final def derivedProperty: PropertyBounds = { // currently, the analysis will derive the final result in a single step - PropertyBounds.finalP(FieldMutability) + PropertyBounds.finalP(FieldImmutability) } } /** - * Factory object to create instances of the FieldMutabilityAnalysis. + * Factory object to create instances of the FieldImmutabilityAnalysis. */ -object EagerL0FieldMutabilityAnalysis - extends L0FieldMutabilityAnalysisScheduler +object EagerL0FieldImmutabilityAnalysis + extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -117,7 +116,7 @@ object EagerL0FieldMutabilityAnalysis override def derivesCollaboratively: Set[PropertyBounds] = Set.empty override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0FieldMutabilityAnalysis(p) + val analysis = new L0FieldImmutabilityAnalysis(p) val classFileCandidates = if (p.libraryClassFilesAreInterfacesOnly) p.allProjectClassFiles @@ -126,22 +125,23 @@ object EagerL0FieldMutabilityAnalysis val fields = { classFileCandidates.filter(cf ⇒ cf.methods.forall(m ⇒ !m.isNative)).flatMap(_.fields) } - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldMutability) + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) analysis } } -object LazyL0FieldMutabilityAnalysis - extends L0FieldMutabilityAnalysisScheduler +object LazyL0FieldImmutabilityAnalysis + extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0FieldMutabilityAnalysis(p) + + val analysis = new L0FieldImmutabilityAnalysis(p) ps.registerLazyPropertyComputation( - FieldMutability.key, - (field: Field) ⇒ analysis.determineFieldMutabilityLazy(field) + FieldImmutability.key, + (field: Field) ⇒ analysis.determineFieldImmutabilityLazy(field) ) analysis } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0PurityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0PurityAnalysis.scala index fb58860158..03c828ea52 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0PurityAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0PurityAnalysis.scala @@ -26,17 +26,20 @@ import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.CompileTimePure -import org.opalj.br.fpcf.properties.FieldMutability -import org.opalj.br.fpcf.properties.FinalField -import org.opalj.br.fpcf.properties.ImmutableContainerType -import org.opalj.br.fpcf.properties.ImmutableType import org.opalj.br.fpcf.properties.ImpureByAnalysis import org.opalj.br.fpcf.properties.ImpureByLackOfInformation -import org.opalj.br.fpcf.properties.NonFinalField import org.opalj.br.fpcf.properties.Pure import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.properties.TypeImmutability import org.opalj.br.instructions._ +import org.opalj.br.fpcf.properties.DependentImmutableType +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.br.fpcf.properties.DeepImmutableField +import org.opalj.br.fpcf.properties.DependentImmutableField +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.TypeImmutability /** * Very simple, fast, sound but also imprecise analysis of the purity of methods. See the @@ -107,7 +110,7 @@ class L0PurityAnalysis private[analyses] ( final val project: SomeProject) exten } if (!fieldType.isBaseType) { propertyStore(fieldType, TypeImmutability.key) match { - case FinalP(ImmutableType) ⇒ + case FinalP(DeepImmutableType) ⇒ case _: FinalEP[_, TypeImmutability] ⇒ return Result(definedMethod, ImpureByAnalysis); case ep ⇒ @@ -115,9 +118,10 @@ class L0PurityAnalysis private[analyses] ( final val project: SomeProject) exten } } if (field.isNotFinal) { - propertyStore(field, FieldMutability.key) match { - case FinalP(_: FinalField) ⇒ - case _: FinalEP[Field, FieldMutability] ⇒ + + propertyStore(field, FieldImmutability.key) match { + case FinalP(ShallowImmutableField | DependentImmutableField | DeepImmutableField) ⇒ + case _: FinalEP[Field, FieldImmutability] ⇒ return Result(definedMethod, ImpureByAnalysis); case ep ⇒ dependees += ep @@ -225,7 +229,7 @@ class L0PurityAnalysis private[analyses] ( final val project: SomeProject) exten dependees += eps InterimResult(definedMethod, ImpureByAnalysis, Pure, dependees, c) - case FinalP(_: FinalField | ImmutableType) ⇒ + case FinalP(DeepImmutableField | DependentImmutableField | ShallowImmutableField | DeepImmutableType) ⇒ if (dependees.isEmpty) { Result(definedMethod, Pure) } else { @@ -234,12 +238,12 @@ class L0PurityAnalysis private[analyses] ( final val project: SomeProject) exten InterimResult(definedMethod, ImpureByAnalysis, Pure, dependees, c) } - case FinalP(ImmutableContainerType) ⇒ + case FinalP(ShallowImmutableType | DependentImmutableType) ⇒ //ImmutableContainerType) ⇒ Result(definedMethod, ImpureByAnalysis) // The type is at most conditionally immutable. case FinalP(_: TypeImmutability) ⇒ Result(definedMethod, ImpureByAnalysis) - case FinalP(_: NonFinalField) ⇒ Result(definedMethod, ImpureByAnalysis) + case FinalP(MutableField) ⇒ Result(definedMethod, ImpureByAnalysis) case FinalP(CompileTimePure | Pure) ⇒ if (dependees.isEmpty) @@ -279,10 +283,10 @@ class L0PurityAnalysis private[analyses] ( final val project: SomeProject) exten var dependees: Set[EOptionP[Entity, Property]] = Set.empty referenceTypedParameters foreach { e ⇒ propertyStore(e, TypeImmutability.key) match { - case FinalP(ImmutableType) ⇒ /*everything is Ok*/ + case FinalP(DeepImmutableType) ⇒ /*everything is Ok*/ case _: FinalEP[_, _] ⇒ return Result(definedMethod, ImpureByAnalysis); - case InterimUBP(ub) if ub ne ImmutableType ⇒ + case InterimUBP(ub) if ub ne DeepImmutableType ⇒ return Result(definedMethod, ImpureByAnalysis); case epk ⇒ dependees += epk } @@ -299,10 +303,10 @@ class L0PurityAnalysis private[analyses] ( final val project: SomeProject) exten def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { case FinalP(p) ⇒ Result(dm, p) - case ep @ InterimLUBP(lb, ub) ⇒ InterimResult(dm, lb, ub, Seq(ep), c) + case ep @ InterimLUBP(lb, ub) ⇒ InterimResult(dm, lb, ub, Set(ep), c) case epk ⇒ - InterimResult(dm, ImpureByAnalysis, CompileTimePure, Seq(epk), c) + InterimResult(dm, ImpureByAnalysis, CompileTimePure, Set(epk), c) } c(propertyStore(declaredMethods(dm.definedMethod), Purity.key)) @@ -334,7 +338,7 @@ class L0PurityAnalysis private[analyses] ( final val project: SomeProject) exten trait L0PurityAnalysisScheduler extends FPCFAnalysisScheduler { final override def uses: Set[PropertyBounds] = { - Set(PropertyBounds.ub(TypeImmutability), PropertyBounds.ub(FieldMutability)) + Set(PropertyBounds.ub(TypeImmutability), PropertyBounds.ub(FieldImmutability)) } final def derivedProperty: PropertyBounds = PropertyBounds.lub(Purity) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0SelfReferenceLeakageAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0SelfReferenceLeakageAnalysis.scala index f3016b1bd4..77fc4c3874 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0SelfReferenceLeakageAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0SelfReferenceLeakageAnalysis.scala @@ -194,7 +194,7 @@ class L0SelfReferenceLeakageAnalysis( if (dependees.isEmpty) { determineSelfReferenceLeakageContinuation(classFile) } else { - InterimResult(classType, lb, ub, dependees.values, c) + InterimResult(classType, lb, ub, dependees.valuesIterator.toSet, c) } } @@ -204,7 +204,7 @@ class L0SelfReferenceLeakageAnalysis( InterimResult( classFile, lb = LeaksSelfReference, ub = DoesNotLeakSelfReference, - dependees.values, + dependees.valuesIterator.toSet, c ) } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/TypeImmutabilityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala similarity index 81% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/TypeImmutabilityAnalysis.scala rename to OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala index 499e0586aa..fa476f9bfd 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/TypeImmutabilityAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala @@ -25,13 +25,13 @@ import org.opalj.fpcf.UBP import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.TypeExtensibilityKey import org.opalj.br.fpcf.properties.ClassImmutability -import org.opalj.br.fpcf.properties.ImmutableContainer -import org.opalj.br.fpcf.properties.ImmutableContainerType -import org.opalj.br.fpcf.properties.ImmutableObject -import org.opalj.br.fpcf.properties.ImmutableType -import org.opalj.br.fpcf.properties.MutableObject import org.opalj.br.fpcf.properties.MutableType +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.MutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableClass import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.br.fpcf.properties.DeepImmutableType /** * Determines the mutability of a specific type by checking if all subtypes of a specific @@ -39,9 +39,9 @@ import org.opalj.br.fpcf.properties.TypeImmutability * * @author Michael Eichberg */ -class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnalysis { +class L0TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnalysis { - def doDetermineTypeMutability( + def doDetermineL0TypeImmutability( typeExtensibility: ObjectType ⇒ Answer )( e: Entity @@ -79,7 +79,7 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal if (eps.isFinal) Result(t, thisUB) else - InterimResult(t, thisLB, thisUB, Seq(eps), c) + InterimResult(t, thisLB, thisUB, Set(eps), c) } } } @@ -90,24 +90,24 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal case eps @ InterimLUBP(lb, ub) ⇒ val thisUB = ub.correspondingTypeImmutability val thisLB = lb.correspondingTypeImmutability - InterimResult(t, thisLB, thisUB, Seq(eps), c) + InterimResult(t, thisLB, thisUB, Set(eps), c) case epk ⇒ - InterimResult(t, MutableType, ImmutableType, Seq(epk), c) + InterimResult(t, MutableType, DeepImmutableType, Set(epk), c) } } else { var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] - var joinedImmutability: TypeImmutability = ImmutableType // this may become "Mutable..." - var maxImmutability: TypeImmutability = ImmutableType + var joinedImmutability: TypeImmutability = DeepImmutableType // this may become "Mutable..." + var maxImmutability: TypeImmutability = DeepImmutableType ps(t, ClassImmutability.key) match { - case FinalP(ImmutableObject) ⇒ + case FinalP(DeepImmutableClass) ⇒ - case FinalP(_: MutableObject) ⇒ + case FinalP(MutableClass) ⇒ return Result(t, MutableType); - case FinalP(ImmutableContainer) ⇒ - joinedImmutability = ImmutableContainerType - maxImmutability = ImmutableContainerType + case FinalP(ShallowImmutableClass) ⇒ + joinedImmutability = ShallowImmutableType + maxImmutability = ShallowImmutableType case eps @ InterimLUBP(lb, ub) ⇒ joinedImmutability = lb.correspondingTypeImmutability @@ -121,14 +121,14 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal directSubtypes foreach { subtype ⇒ ps(subtype, TypeImmutability.key) match { - case FinalP(ImmutableType) ⇒ + case FinalP(DeepImmutableType) ⇒ case UBP(MutableType) ⇒ return Result(t, MutableType); - case FinalP(ImmutableContainerType) ⇒ - joinedImmutability = joinedImmutability.meet(ImmutableContainerType) - maxImmutability = ImmutableContainerType + case FinalP(ShallowImmutableType) ⇒ + joinedImmutability = joinedImmutability.meet(ShallowImmutableType) + maxImmutability = ShallowImmutableType case eps @ InterimLUBP(subtypeLB, subtypeUB) ⇒ joinedImmutability = joinedImmutability.meet(subtypeLB) @@ -180,27 +180,28 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal } } if (joinedImmutability == maxImmutability) { - assert(maxImmutability == ImmutableContainerType) + assert(maxImmutability == ShallowImmutableType) Result(t, maxImmutability) } else { InterimResult( t, joinedImmutability, maxImmutability, - dependencies.values, c + dependencies.values.toSet, c ) } } } (eps: @unchecked) match { - case FinalEP(e, ImmutableType | ImmutableObject) ⇒ + case FinalEP(e, DeepImmutableType | DeepImmutableClass) ⇒ dependencies = dependencies - e nextResult() - case UBP(MutableType | _: MutableObject) ⇒ + case UBP(MutableType | MutableClass) ⇒ Result(t, MutableType) - case FinalEP(e, ImmutableContainerType | ImmutableContainer) ⇒ - maxImmutability = ImmutableContainerType + case FinalEP(e, ShallowImmutableType | ShallowImmutableClass) ⇒ + + maxImmutability = ShallowImmutableType dependencies = dependencies - e nextResult() @@ -217,13 +218,13 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal } } - InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) + InterimResult(t, joinedImmutability, maxImmutability, dependencies.values.toSet, c) } } } } -trait TypeImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +trait L0TypeImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability) @@ -237,8 +238,8 @@ trait TypeImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { * * @author Michael Eichberg */ -object EagerTypeImmutabilityAnalysis - extends TypeImmutabilityAnalysisScheduler +object EagerL0TypeImmutabilityAnalysis + extends L0TypeImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -247,7 +248,7 @@ object EagerTypeImmutabilityAnalysis override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val typeExtensibility = project.get(TypeExtensibilityKey) - val analysis = new TypeImmutabilityAnalysis(project) + val analysis = new L0TypeImmutabilityAnalysis(project) val allClassFilesIterator = project.allClassFiles.iterator val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) @@ -260,8 +261,8 @@ object EagerTypeImmutabilityAnalysis } -object LazyTypeImmutabilityAnalysis - extends TypeImmutabilityAnalysisScheduler +object LazyL0TypeImmutabilityAnalysis + extends L0TypeImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) @@ -271,9 +272,9 @@ object LazyTypeImmutabilityAnalysis */ override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val typeExtensibility = p.get(TypeExtensibilityKey) - val analysis = new TypeImmutabilityAnalysis(p) + val analysis = new L0TypeImmutabilityAnalysis(p) val analysisRunner: ProperPropertyComputation[Entity] = - analysis.doDetermineTypeMutability(typeExtensibility) + analysis.doDetermineL0TypeImmutability(typeExtensibility) ps.registerLazyPropertyComputation(TypeImmutability.key, analysisRunner) analysis diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/StaticDataUsageAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/StaticDataUsageAnalysis.scala index 5f7dc3d1ef..b24328a494 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/StaticDataUsageAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/StaticDataUsageAnalysis.scala @@ -57,9 +57,9 @@ class StaticDataUsageAnalysis private[analyses] ( final val project: SomeProject def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { case FinalP(sdu) ⇒ Result(dm, sdu) case ep @ InterimLUBP(lb, ub) ⇒ - InterimResult(dm, lb, ub, Seq(ep), c) + InterimResult(dm, lb, ub, Set(ep), c) case epk ⇒ - InterimResult(dm, UsesVaryingData, UsesNoStaticData, Seq(epk), c) + InterimResult(dm, UsesVaryingData, UsesNoStaticData, Set(epk), c) } c(propertyStore(declaredMethods(dm.definedMethod), StaticDataUsage.key)) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualCallAggregatingEscapeAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualCallAggregatingEscapeAnalysis.scala index bd77f97868..2ca6b432c9 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualCallAggregatingEscapeAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualCallAggregatingEscapeAnalysis.scala @@ -12,6 +12,7 @@ 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.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.SomeProject @@ -47,7 +48,7 @@ class VirtualCallAggregatingEscapeAnalysis private[analyses] ( final val project // ANALYSIS STATE var escapeState: EscapeProperty = NoEscape - var dependees: Set[EOptionP[VirtualFormalParameter, EscapeProperty]] = Set.empty + var dependees: Set[SomeEOptionP] = Set.empty val maybeFile = project.classFile(dm.declaringClassType) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodAllocationFreenessAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodAllocationFreenessAnalysis.scala index 6bc3ad7fc1..059bd061cd 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodAllocationFreenessAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodAllocationFreenessAnalysis.scala @@ -12,6 +12,7 @@ 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.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.SomeProject @@ -37,7 +38,7 @@ class VirtualMethodAllocationFreenessAnalysis private[analyses] ( if (!dm.hasSingleDefinedMethod && !dm.hasMultipleDefinedMethods) return Result(dm, VMethodWithAllocations); - var dependees: Set[EOptionP[DeclaredMethod, AllocationFreeness]] = Set.empty + var dependees: Set[SomeEOptionP] = Set.empty val cfo = project.classFile(dm.declaringClassType) val methods = diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodPurityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodPurityAnalysis.scala index d9b4729444..ac7d071bc9 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodPurityAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodPurityAnalysis.scala @@ -11,6 +11,7 @@ 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 @@ -36,7 +37,7 @@ class VirtualMethodPurityAnalysis private[analyses] ( final val project: SomePro return Result(dm, VImpureByLackOfInformation); var maxPurity: Purity = CompileTimePure - var dependees: Set[EOptionP[DeclaredMethod, Purity]] = Set.empty + var dependees: Set[SomeEOptionP] = Set.empty val cfo = project.classFile(dm.declaringClassType) val methods = diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodStaticDataUsageAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodStaticDataUsageAnalysis.scala index cd8062ca71..7a7cd4fa64 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodStaticDataUsageAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodStaticDataUsageAnalysis.scala @@ -13,6 +13,7 @@ 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.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.SomeProject @@ -38,7 +39,7 @@ class VirtualMethodStaticDataUsageAnalysis private[analyses] ( if (!dm.hasSingleDefinedMethod && !dm.hasMultipleDefinedMethods) return Result(dm, VUsesVaryingData); - var dependees: Set[EOptionP[DeclaredMethod, StaticDataUsage]] = Set.empty + var dependees: Set[SomeEOptionP] = Set.empty var maxLevel: StaticDataUsage = UsesNoStaticData diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability.scala index 2c9af888d4..cfa98c1393 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability.scala @@ -10,92 +10,33 @@ import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - final type Self = ClassImmutability - } /** - * Specifies the (im)mutability of instances of a specific class. - * The highest rating is "Immutable", then "Conditionally Immutable", then "Mutable". - * - * An instance of a class is rated as immutable if the state of the object does not change after - * initialization in a client visible manner! This includes all objects referenced by the instances - * (transitive hull). However, fields that are lazily initialized (in a thread-safe manner) and - * which don't change after that do not impede immutability. - * Conditionally immutable means that the state of the instance of the respective class - * cannot be mutated, but objects referenced by it can be mutated (so called - * immutable collections are typically rated as "conditionally immutable"). - * Mutable means that a client can mutate (directly or indirectly) - * the state of respective objects. In general the state of a class is determined w.r.t. - * the declared fields. I.e., an impure method which has, e.g., a call time dependent behavior - * because it uses the current time, but which does not mutate the state of the class does not affect - * the mutability rating. The same is true for methods with side-effects related to the state of - * other types of object. - * - * The mutability assessment is by default done on a per class basis and only directly depends on the - * super class of the analyzed class. A rating that is based on all actual usages is only meaningful - * if we analyze an application. E.g., imagine a simple mutable data container class where no field - * – in the concrete context of a specific application – is ever updated. + * Describes the class immutability of org.opalj.br.ClassFile. + * The immutability of the classes are represented via the lower bound of the immutability of + * their instance fields and the immutability of its supertype. * - * ==Thread-safe Lazily Initialized Fields== - * A field that is initialized lazily in a thread-safe manner; i.e., - * which is set at most once after construction and which is always set to the - * same value independent of the time of (lazy) initialization, may not affect the - * mutability rating. However, an analysis may rate such a class as mutable. An - * example of such a field is the field that stores the lazily calculated hashCode of - * a `String` object. + * [[MutableClass]] A class with a mutable state. * - * ==Inheritance== - * - Instances of `java.lang.Object` are immutable. However, if a class defines a - * constructor which has a parameter of type object and which assigns the respective - * parameter value to a field will at-most be conditionally immutable (instances of the - * class object are immutable, but instances of the type (which includes all subtypes) are - * not immutable; in general - * we must assume that the referenced object may be (at runtime) some mutable object. - * - In general, only classes that inherit from (conditionally) immutable class can be - * (conditionally) immutable; if a class is mutable, all subclasses are also - * considered to be mutable. I.e., a subclass can never have a higher mutability rating - * than a superclass. - * - All classes for which the superclasstype information is not complete are rated - * as unknown. (Interfaces are generally ignored as they are always immutable.) + * [[ShallowImmutableClass]] A class which transitive state is not immutable but the values or objects representing + * this transitive state (are not / can not be) exchanged. * - * ==Native Methods== - * Unknown native methods are considered as mutating the state unless all state is - * explicitly final; however, this is already handled by the - * [[org.opalj.br.fpcf.analyses.L0FieldMutabilityAnalysis]]. + * [[DependentImmutableClass]] A class that is at least shallow immutable. + * Whether it is shallow or deep immutable depends on generic parameters. * - * ==Identifying Immutable Objects in Practice== - * Identifying real world immutable classes as such by means of an analysis is in general a - * challenging task. For example, to - * identify the well known immutable class "java.lang.String" as such requires: - * - Identifying that the field hash is effectively immutable though the field is only lazily - * initialized (in a thread-safe manner). - * - Determing that all calls to the package-private constructor java.lang.String(byte[] buf, - * Boolean shared) are actually passed an array that is not shared afterwards. I.e., the - * ownership is in all cases effectively transfered to the class java.lang.String. + * [[DeepImmutableClass]] A class with a transitive immutable state. * - * ==Interfaces== - * Are not considered during the analysis as they are always immutable. (All fields are (implicitly) - * `static` and `final`.) - * - * @author Andre Pacak - * @author Michael Eichberg + * @author Tobias Roth */ sealed trait ClassImmutability extends OrderedProperty with ClassImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ClassImmutability] = ClassImmutability.key - def correspondingTypeImmutability: TypeImmutability - - /** `true` if instances of the class are mutable. */ - def isMutable: Boolean } -/** - * Common constants use by all [[ClassImmutability]] properties associated with methods. - */ + object ClassImmutability extends ClassImmutabilityPropertyMetaInformation { /** @@ -103,73 +44,61 @@ object ClassImmutability extends ClassImmutabilityPropertyMetaInformation { */ final val key: PropertyKey[ClassImmutability] = PropertyKey.create( "opalj.ClassImmutability", - MutableObjectDueToUnresolvableDependency + MutableClass ) } -/** - * An instance of the respective class is effectively immutable - * and also all (transitively) referenced objects. I.e., after creation it is not - * possible for a client to set a field or to call a method that updates the internal state - * of the instance or an object referred to by the instance in such a way that the client - * can observe the state change. - * - */ -case object ImmutableObject extends ClassImmutability { +case object DeepImmutableClass extends ClassImmutability { - final val correspondingTypeImmutability = ImmutableType + override def correspondingTypeImmutability: TypeImmutability = DeepImmutableType override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - final def isMutable: Boolean = false + def meet(that: ClassImmutability): ClassImmutability = that } -/** - * An instance of the respective class is (at least) effectively immutable. I.e., after creation - * it is not possible for a client to set a field or to call a method that updates the direct - * internal state; changing the transitive state may be possible. - */ -case object ImmutableContainer extends ClassImmutability { +case object DependentImmutableClass extends ClassImmutability { + override def correspondingTypeImmutability: TypeImmutability = DependentImmutableType - final val correspondingTypeImmutability = ImmutableContainerType + def meet(that: ClassImmutability): ClassImmutability = + if (that == MutableClass || that == ShallowImmutableClass) + that + else + this override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == ImmutableObject) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + if (other == DeepImmutableClass) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } } - - final def isMutable: Boolean = false } -sealed trait MutableObject extends ClassImmutability { +case object ShallowImmutableClass extends ClassImmutability { + override def correspondingTypeImmutability: TypeImmutability = ShallowImmutableType - def reason: String - final val correspondingTypeImmutability = MutableType + def meet(that: ClassImmutability): ClassImmutability = { + if (that == MutableClass) + that + else + this + } override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == ImmutableObject || other == ImmutableContainer) { + if (other == DeepImmutableClass || other == DependentImmutableClass) { throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } } - - final def isMutable: Boolean = true - - final override def toString: String = s"MutableObject(reason=$reason)" } -case object MutableObjectDueToIncompleteAnalysis extends MutableObject { - final def reason = "analysis has not yet completed" -} +case object MutableClass extends ClassImmutability { -case object MutableObjectByAnalysis extends MutableObject { - final def reason = "determined by analysis" -} + def correspondingTypeImmutability: TypeImmutability = MutableType -case object MutableObjectDueToUnknownSupertypes extends MutableObject { - final def reason = "the type hierarchy is upwards incomplete" -} + def meet(other: ClassImmutability): ClassImmutability = this -case object MutableObjectDueToUnresolvableDependency extends MutableObject { - final def reason = "a dependency cannot be resolved" + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other != MutableClass) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + } + } } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_old.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_old.scala new file mode 100644 index 0000000000..26a98baa5b --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_old.scala @@ -0,0 +1,175 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package br +package fpcf +package properties + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.OrderedProperty +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +sealed trait ClassImmutabilityPropertyMetaInformation_old extends PropertyMetaInformation { + + final type Self = ClassImmutability_old + +} + +/** + * Specifies the (im)mutability of instances of a specific class. + * The highest rating is "Immutable", then "Conditionally Immutable", then "Mutable". + * + * An instance of a class is rated as immutable if the state of the object does not change after + * initialization in a client visible manner! This includes all objects referenced by the instances + * (transitive hull). However, fields that are lazily initialized (in a thread-safe manner) and + * which don't change after that do not impede immutability. + * Conditionally immutable means that the state of the instance of the respective class + * cannot be mutated, but objects referenced by it can be mutated (so called + * immutable collections are typically rated as "conditionally immutable"). + * Mutable means that a client can mutate (directly or indirectly) + * the state of respective objects. In general the state of a class is determined w.r.t. + * the declared fields. I.e., an impure method which has, e.g., a call time dependent behavior + * because it uses the current time, but which does not mutate the state of the class does not affect + * the mutability rating. The same is true for methods with side-effects related to the state of + * other types of object. + * + * The mutability assessment is by default done on a per class basis and only directly depends on the + * super class of the analyzed class. A rating that is based on all actual usages is only meaningful + * if we analyze an application. E.g., imagine a simple mutable data container class where no field + * – in the concrete context of a specific application – is ever updated. + * + * ==Thread-safe Lazily Initialized Fields== + * A field that is initialized lazily in a thread-safe manner; i.e., + * which is set at most once after construction and which is always set to the + * same value independent of the time of (lazy) initialization, may not affect the + * mutability rating. However, an analysis may rate such a class as mutable. An + * example of such a field is the field that stores the lazily calculated hashCode of + * a `String` object. + * + * ==Inheritance== + * - Instances of `java.lang.Object` are immutable. However, if a class defines a + * constructor which has a parameter of type object and which assigns the respective + * parameter value to a field will at-most be conditionally immutable (instances of the + * class object are immutable, but instances of the type (which includes all subtypes) are + * not immutable; in general + * we must assume that the referenced object may be (at runtime) some mutable object. + * - In general, only classes that inherit from (conditionally) immutable class can be + * (conditionally) immutable; if a class is mutable, all subclasses are also + * considered to be mutable. I.e., a subclass can never have a higher mutability rating + * than a superclass. + * - All classes for which the superclasstype information is not complete are rated + * as unknown. (Interfaces are generally ignored as they are always immutable.) + * + * ==Native Methods== + * Unknown native methods are considered as mutating the state unless all state is + * explicitly final; however, this is already handled by the + * [[org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis]]. + * + * ==Identifying Immutable Objects in Practice== + * Identifying real world immutable classes as such by means of an analysis is in general a + * challenging task. For example, to + * identify the well known immutable class "java.lang.String" as such requires: + * - Identifying that the field hash is effectively immutable though the field is only lazily + * initialized (in a thread-safe manner). + * - Determing that all calls to the package-private constructor java.lang.String(byte[] buf, + * Boolean shared) are actually passed an array that is not shared afterwards. I.e., the + * ownership is in all cases effectively transfered to the class java.lang.String. + * + * ==Interfaces== + * Are not considered during the analysis as they are always immutable. (All fields are (implicitly) + * `static` and `final`.) + * + * @author Andre Pacak + * @author Michael Eichberg + */ +sealed trait ClassImmutability_old + extends OrderedProperty + with ClassImmutabilityPropertyMetaInformation_old { + + final def key: PropertyKey[ClassImmutability_old] = ClassImmutability_old.key + + def correspondingTypeImmutability: TypeImmutability_old + + /** `true` if instances of the class are mutable. */ + def isMutable: Boolean +} +/** + * Common constants use by all [[ClassImmutability_old]] properties associated with methods. + */ +object ClassImmutability_old extends ClassImmutabilityPropertyMetaInformation_old { + + /** + * The key associated with every [[ClassImmutability_old]] property. + */ + final val key: PropertyKey[ClassImmutability_old] = PropertyKey.create( + "opalj.ClassImmutability_old", + MutableObjectDueToUnresolvableDependency + ) +} + +/** + * An instance of the respective class is effectively immutable + * and also all (transitively) referenced objects. I.e., after creation it is not + * possible for a client to set a field or to call a method that updates the internal state + * of the instance or an object referred to by the instance in such a way that the client + * can observe the state change. + * + */ +case object ImmutableObject extends ClassImmutability_old { + + final val correspondingTypeImmutability = ImmutableType + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + + final def isMutable: Boolean = false +} + +/** + * An instance of the respective class is (at least) effectively immutable. I.e., after creation + * it is not possible for a client to set a field or to call a method that updates the direct + * internal state; changing the transitive state may be possible. + */ +case object ImmutableContainer extends ClassImmutability_old { + + final val correspondingTypeImmutability = ImmutableContainerType + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other == ImmutableObject) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } + } + + final def isMutable: Boolean = false +} + +sealed trait MutableObject extends ClassImmutability_old { + + def reason: String + final val correspondingTypeImmutability = MutableType_old + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other == ImmutableObject || other == ImmutableContainer) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + } + } + + final def isMutable: Boolean = true + + final override def toString: String = s"MutableObject(reason=$reason)" +} + +case object MutableObjectDueToIncompleteAnalysis extends MutableObject { + final def reason = "analysis has not yet completed" +} + +case object MutableObjectByAnalysis extends MutableObject { + final def reason = "determined by analysis" +} + +case object MutableObjectDueToUnknownSupertypes extends MutableObject { + final def reason = "the type hierarchy is upwards incomplete" +} + +case object MutableObjectDueToUnresolvableDependency extends MutableObject { + final def reason = "a dependency cannot be resolved" +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldImmutability.scala new file mode 100644 index 0000000000..794ad59a2f --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldImmutability.scala @@ -0,0 +1,94 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package br +package fpcf +package properties + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.OrderedProperty +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +sealed trait FieldImmutabilityPropertyMetaInformation extends PropertyMetaInformation { + type Self = FieldImmutability +} + +/** + * Describes the field immutability of org.opalj.br.Field + * + * [[MutableField]] A field with a mutable field reference + * + * [[ShallowImmutableField]] A field with an immutable field reference and a shallow immutable or mutable data type + * + * [[DependentImmutableField]] A field with an immutable field reference and a generic type and parts of it are no + * substantiated in an shallow or mutable way. + * + * [[DeepImmutableField]] A field with an immutable field reference and a deep immutable field type or with an + * immutable field reference and a referenced object that can not escape or its state be mutated. + * + * @author Tobias Roth + */ +sealed trait FieldImmutability extends OrderedProperty with FieldImmutabilityPropertyMetaInformation { + final def key: PropertyKey[FieldImmutability] = FieldImmutability.key +} + +object FieldImmutability extends FieldImmutabilityPropertyMetaInformation { + final val PropertyKeyName = "opalj.FieldImmutability" + + final val key: PropertyKey[FieldImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableField + ) + } +} + +case object DeepImmutableField extends FieldImmutability { + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + + def meet(that: FieldImmutability): FieldImmutability = that +} + +case object DependentImmutableField extends FieldImmutability { + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other == DeepImmutableField) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } + } + + def meet(that: FieldImmutability): FieldImmutability = { + if (that == MutableField || that == ShallowImmutableField) + that + else + this + } +} + +case object ShallowImmutableField extends FieldImmutability { + + def meet(that: FieldImmutability): FieldImmutability = { + if (that == MutableField) + that + else + this + } + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other == DeepImmutableField || other == DependentImmutableField) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } + } +} + +case object MutableField extends FieldImmutability { + + def meet(other: FieldImmutability): this.type = this + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other != MutableField) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + } + } +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldReferenceImmutability.scala new file mode 100644 index 0000000000..7822994046 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldReferenceImmutability.scala @@ -0,0 +1,126 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package br +package fpcf +package properties + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.OrderedProperty +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +sealed trait FieldReferenceImmutabilityPropertyMetaInformation extends PropertyMetaInformation { + type Self = FieldReferenceImmutability +} + +/** + * Describes the reference immutability of org.opalj.br.Field. + * + * [[MutableFieldReference]] The referenced object can be exchanged. + * + * [[LazyInitializedNotThreadSafeFieldReference]] The field reference is lazy initialized in a not thread safe way. + * + * [[LazyInitializedNotThreadSafeButDeterministicFieldReference]] The field reference is lazy initialized in a for + * objects not thread safe way but it has a primitive type. + * + * [[LazyInitializedThreadSafeFieldReference]] The field reference is lazy initialized in a thread safe way. The write + * is atomic + * + * [[ImmutableFieldReference]] The value or object the field reference refer can not be exchanged. + * + * @author Tobias Peter Roth + */ +sealed trait FieldReferenceImmutability extends OrderedProperty with FieldReferenceImmutabilityPropertyMetaInformation { + + final def key: PropertyKey[FieldReferenceImmutability] = FieldReferenceImmutability.key + + def isImmutable = false +} + +object FieldReferenceImmutability extends FieldReferenceImmutabilityPropertyMetaInformation { + + var notEscapes: Boolean = false + + final val PropertyKeyName = "opalj.FieldReferenceImmutability" + + final val key: PropertyKey[FieldReferenceImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableFieldReference + ) + } +} + +case object ImmutableFieldReference extends FieldReferenceImmutability { + + override def isImmutable = true + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + + def meet(that: FieldReferenceImmutability): FieldReferenceImmutability = that +} + +case object LazyInitializedThreadSafeFieldReference extends FieldReferenceImmutability { + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = other match { + case ImmutableFieldReference ⇒ + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + case _ ⇒ + } + + def meet(other: FieldReferenceImmutability): FieldReferenceImmutability = + if (other == MutableFieldReference || + other == LazyInitializedNotThreadSafeFieldReference || + other == LazyInitializedNotThreadSafeButDeterministicFieldReference) { + other + } else { + this + } +} + +case object LazyInitializedNotThreadSafeButDeterministicFieldReference extends FieldReferenceImmutability { + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = other match { + case ImmutableFieldReference | LazyInitializedThreadSafeFieldReference ⇒ + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + case _ ⇒ + } + + def meet(other: FieldReferenceImmutability): FieldReferenceImmutability = { + if (other == MutableFieldReference || other == LazyInitializedNotThreadSafeFieldReference) { + other + } else { + this + } + } +} + +case object LazyInitializedNotThreadSafeFieldReference extends FieldReferenceImmutability { + + def meet(other: FieldReferenceImmutability): properties.FieldReferenceImmutability = { + if (other == MutableFieldReference) { + other + } else { + this + } + } + + override def checkIsEqualOrBetterThan(e: Entity, other: FieldReferenceImmutability): Unit = { + other match { + case ImmutableFieldReference | LazyInitializedNotThreadSafeButDeterministicFieldReference | + LazyInitializedThreadSafeFieldReference ⇒ + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + case _ ⇒ + } + } +} + +case object MutableFieldReference extends FieldReferenceImmutability { + def meet(other: FieldReferenceImmutability): FieldReferenceImmutability = this + + override def checkIsEqualOrBetterThan(e: Entity, other: FieldReferenceImmutability): Unit = { + if (other != MutableFieldReference) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other => $this") + } + } +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/Purity.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/Purity.scala index 67fcd69df1..4299ba57e4 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/Purity.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/Purity.scala @@ -225,11 +225,11 @@ object Purity extends PurityPropertyMetaInformation { case _ if name.startsWith("ContextuallyPure{") ⇒ Some(ContextuallyPure(parseParams(name.substring(17, name.length - 1)))) case _ if name.startsWith("ContextuallySideEffectFree{") ⇒ - Some(ContextuallyPure(parseParams(name.substring(27, name.length - 1)))) + Some(ContextuallySideEffectFree(parseParams(name.substring(27, name.length - 1)))) case _ if name.startsWith("DContextuallyPure{") ⇒ - Some(ContextuallyPure(parseParams(name.substring(18, name.length - 1)))) + Some(DContextuallyPure(parseParams(name.substring(18, name.length - 1)))) case _ if name.startsWith("DContextuallySideEffectFree{") ⇒ - Some(ContextuallyPure(parseParams(name.substring(28, name.length - 1)))) + Some(DContextuallySideEffectFree(parseParams(name.substring(28, name.length - 1)))) case _ ⇒ None } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability.scala index 7f1c71fe6d..5235c04b9f 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability.scala @@ -10,12 +10,12 @@ import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait TypeImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - final type Self = TypeImmutability } /** - * Specifies if all instances of a respective type (this includes the instances of the + * Specifies whether all instances of a respective type (this includes the instances of the + * type's subtypes) are (conditionally) immutable. Conditionally immutable means that only the * type's subtypes) are (conditionally) immutable. Conditionally immutable means that only the * instance of the type itself is guaranteed to be immutable, but not all reachable objects. * In general, all -- so called -- immutable collections are only conditionally immutable. I.e., @@ -27,24 +27,27 @@ sealed trait TypeImmutabilityPropertyMetaInformation extends PropertyMetaInforma * [[ClassImmutability]]. * * @author Michael Eichberg + * @author Tobias Roth */ -sealed trait TypeImmutability extends OrderedProperty with TypeImmutabilityPropertyMetaInformation { +sealed trait TypeImmutability + extends OrderedProperty + with TypeImmutabilityPropertyMetaInformation { /** * Returns the key used by all `TypeImmutability` properties. */ final def key = TypeImmutability.key - def isImmutable: Boolean - def isImmutableContainer: Boolean - /** `true` if the mutability is unknown or if the type is mutable.*/ + def isDeepImmutable: Boolean + def isShallowImmutable: Boolean + def isDependentImmutable: Boolean + + /** `true` if the immutability is unknown or if the type is mutable.*/ def isMutable: Boolean def meet(other: TypeImmutability): TypeImmutability } -/** - * Common constants use by all [[TypeImmutability]] properties associated with methods. - */ + object TypeImmutability extends TypeImmutabilityPropertyMetaInformation { /** @@ -60,45 +63,69 @@ object TypeImmutability extends TypeImmutabilityPropertyMetaInformation { * An instance of the respective class is effectively immutable. I.e., after creation it is not * possible for a client to set a field or to call a method that updates the internal state */ -case object ImmutableType extends TypeImmutability { +case object DeepImmutableType extends TypeImmutability { - override def isImmutable: Boolean = true - override def isImmutableContainer: Boolean = false + override def isDeepImmutable: Boolean = true + override def isShallowImmutable: Boolean = false override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = false override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: TypeImmutability): TypeImmutability = if (this == that) this else that + def meet(that: TypeImmutability): TypeImmutability = that +} + +case object DependentImmutableType extends TypeImmutability { + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = true + + def meet(that: TypeImmutability): TypeImmutability = + if (that == MutableType || that == ShallowImmutableType) + that + else + this + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other == DeepImmutableType) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + } + } } -case object ImmutableContainerType extends TypeImmutability { +case object ShallowImmutableType extends TypeImmutability { - override def isImmutable: Boolean = false - override def isImmutableContainer: Boolean = true + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = true override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = false - def meet(that: TypeImmutability): TypeImmutability = if (that == MutableType) that else this + def meet(that: TypeImmutability): TypeImmutability = + if (that == MutableType) + that + else + this override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == ImmutableType) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + if (other == DeepImmutableType || other == DependentImmutableType) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } } } case object MutableType extends TypeImmutability { - override def isImmutable: Boolean = false - override def isImmutableContainer: Boolean = false + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = false override def isMutable: Boolean = true + override def isDependentImmutable: Boolean = false - def meet(other: TypeImmutability): this.type = this + def meet(other: TypeImmutability): TypeImmutability = this override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { if (other != MutableType) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } } } - diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_old.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_old.scala new file mode 100644 index 0000000000..4fbb04d086 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_old.scala @@ -0,0 +1,104 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package br +package fpcf +package properties + +import org.opalj.fpcf.OrderedProperty +import org.opalj.fpcf.Entity +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +sealed trait TypeImmutabilityPropertyMetaInformation_old extends PropertyMetaInformation { + + final type Self = TypeImmutability_old +} + +/** + * Specifies if all instances of a respective type (this includes the instances of the + * type's subtypes) are (conditionally) immutable. Conditionally immutable means that only the + * instance of the type itself is guaranteed to be immutable, but not all reachable objects. + * In general, all -- so called -- immutable collections are only conditionally immutable. I.e., + * the collection as a whole is only immutable if only immutable objects are stored in the + * collection. If this is not the case, the collection is only conditionally immutable. + * + * This property is of particular interest if the precise type cannot be computed statically. This + * property basically depends on the [[org.opalj.br.analyses.cg.TypeExtensibilityKey]] and + * [[ClassImmutability_old]]. + * + * @author Michael Eichberg + */ +sealed trait TypeImmutability_old extends OrderedProperty with TypeImmutabilityPropertyMetaInformation_old { + + /** + * Returns the key used by all `TypeImmutability` properties. + */ + final def key = TypeImmutability_old.key + + def isImmutable: Boolean + def isImmutableContainer: Boolean + /** `true` if the mutability is unknown or if the type is mutable.*/ + def isMutable: Boolean + + def meet(other: TypeImmutability_old): TypeImmutability_old +} +/** + * Common constants use by all [[TypeImmutability_old]] properties associated with methods. + */ +object TypeImmutability_old extends TypeImmutabilityPropertyMetaInformation_old { + + /** + * The key associated with every [[TypeImmutability_old]] property. + */ + final val key: PropertyKey[TypeImmutability_old] = PropertyKey.create( + "org.opalj.TypeImmutability", + MutableType_old + ) +} + +/** + * An instance of the respective class is effectively immutable. I.e., after creation it is not + * possible for a client to set a field or to call a method that updates the internal state + */ +case object ImmutableType extends TypeImmutability_old { + + override def isImmutable: Boolean = true + override def isImmutableContainer: Boolean = false + override def isMutable: Boolean = false + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + + def meet(that: TypeImmutability_old): TypeImmutability_old = if (this == that) this else that + +} + +case object ImmutableContainerType extends TypeImmutability_old { + + override def isImmutable: Boolean = false + override def isImmutableContainer: Boolean = true + override def isMutable: Boolean = false + + def meet(that: TypeImmutability_old): TypeImmutability_old = if (that == MutableType_old) that else this + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other == ImmutableType) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } + } +} + +case object MutableType_old extends TypeImmutability_old { + + override def isImmutable: Boolean = false + override def isImmutableContainer: Boolean = false + override def isMutable: Boolean = true + + def meet(other: TypeImmutability_old): this.type = this + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other != MutableType_old) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } + } +} + diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/cg/Callees.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/cg/Callees.scala index 45f6ed0fd9..db7cf50aa8 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/cg/Callees.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/cg/Callees.scala @@ -137,7 +137,7 @@ sealed trait Callees extends Property with CalleesPropertyMetaInformation { /** * Returns for a given call site pc and indirect target method the receiver information. - * If the receiver can not be determined, the [[scala.Option]] will be empty, otherwise it will + * If the receiver can not be determined, the `scala.Option` will be empty, otherwise it will * contain all [[PCs]] and the the negative indices of parameters that may define the value of * the receiver. * The parameter at index 0 always corresponds to the *this* local and is `null` for static @@ -147,7 +147,7 @@ sealed trait Callees extends Property with CalleesPropertyMetaInformation { /** * Returns for a given call site pc and indirect target method the sequence of parameter - * sources. If a parameter source can not be determined, the [[scala.Option]] will be empty, + * sources. If a parameter source can not be determined, the `scala.Option` will be empty, * otherwise it will contain all PCs and the negative indices of parameters that may define the * value of the corresponding actual parameter. * The parameter at index 0 always corresponds to the *this* local and is `null` for static diff --git a/OPAL/br/src/main/scala/org/opalj/br/instructions/ClassFileFactory.scala b/OPAL/br/src/main/scala/org/opalj/br/instructions/ClassFileFactory.scala index cd89142eb1..25972a248e 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/instructions/ClassFileFactory.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/instructions/ClassFileFactory.scala @@ -867,11 +867,11 @@ object ClassFileFactory { } /** - * Creates a static proxy method used by the `$deserializeLambda$` method. + * Creates a static proxy method used by the `\$deserializeLambda$` method. * * @param caller The class where the lambda is implemented. * @param callerIsInterface `true `if the class is an interface, false if not. - * @return The static proxy method relaying the `$deserializedLambda$` invocation to the actual + * @return The static proxy method relaying the `\$deserializedLambda$` invocation to the actual * class that implements the lambda. */ def createDeserializeLambdaProxy( diff --git a/OPAL/common/src/main/scala/org/opalj/util/PerformanceEvaluation.scala b/OPAL/common/src/main/scala/org/opalj/util/PerformanceEvaluation.scala index e49dfbb53e..0279c33e5a 100644 --- a/OPAL/common/src/main/scala/org/opalj/util/PerformanceEvaluation.scala +++ b/OPAL/common/src/main/scala/org/opalj/util/PerformanceEvaluation.scala @@ -203,8 +203,8 @@ object PerformanceEvaluation { * import org.opalj.util.PerformanceEvaluation._ * import org.opalj.util._ * time[String](2,4,3,{Thread.sleep(300).toString}){ (t, ts) => - * val sTs = ts.map(t => f"${t.toSeconds.timeSpan}%1.4f").mkString(", ") - * println(f"Avg: ${avg(ts).timeSpan}%1.4f; T: ${t.toSeconds.timeSpan}%1.4f; Ts: $sTs") + * val sTs = ts.map(t => f"\${t.toSeconds.timeSpan}%1.4f").mkString(", ") + * println(f"Avg: \${avg(ts).timeSpan}%1.4f; T: \${t.toSeconds.timeSpan}%1.4f; Ts: \$sTs") * } * }}} * {{{ @@ -219,8 +219,8 @@ object PerformanceEvaluation { * {store = Array.fill(1000000){val l : Object = List(i); l}}, * runGC=true * ){ (t, ts) => - * val sTs = ts.map(t => f"${t.toSeconds.timeSpan}%1.4f").mkString(", ") - * println(f"Avg: ${avg(ts).timeSpan}%1.4f; T:${t.toSeconds.timeSpan}%1.4f; Ts:$sTs") + * val sTs = ts.map(t => f"\${t.toSeconds.timeSpan}%1.4f").mkString(", ") + * println(f"Avg: \${avg(ts).timeSpan}%1.4f; T:\${t.toSeconds.timeSpan}%1.4f; Ts:\$sTs") * } * }(l => println("non-empty:"+l)) * } diff --git a/OPAL/framework/src/main/resources/ApplicationProject.conf b/OPAL/framework/src/main/resources/ApplicationProject.conf new file mode 100644 index 0000000000..93c837d068 --- /dev/null +++ b/OPAL/framework/src/main/resources/ApplicationProject.conf @@ -0,0 +1,21 @@ +org.opalj.br.analyses { + cg { + ClosedPackagesKey { + analysis = "org.opalj.br.analyses.cg.AllPackagesClosed" + }, + + ClassExtensibilityKey { + analysis = "org.opalj.br.analyses.cg.ClassHierarchyIsNotExtensible" + }, + + InitialEntryPointsKey { + analysis = "org.opalj.br.analyses.cg.ApplicationWithoutJREEntryPointsFinder", + entryPoints = [] + }, + + InitialInstantiatedTypesKey { + analysis = "org.opalj.br.analyses.cg.ApplicationInstantiatedTypesFinder", + instantiatedTypes = [] + } + } +} \ No newline at end of file diff --git a/OPAL/si/src/main/resources/reference.conf b/OPAL/si/src/main/resources/reference.conf index 9d3f08204c..6e0f02b7de 100644 --- a/OPAL/si/src/main/resources/reference.conf +++ b/OPAL/si/src/main/resources/reference.conf @@ -5,6 +5,7 @@ org.opalj { // I.e., debug performs a wide range of additionaly checks to identify errors as // early as possible. fpcf.PropertyStore.Debug = false + //true fpcf.PropertyStore.TraceFallbacks = false fpcf.PropertyStore.TraceSuppressedNotifications = false diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/EOptionP.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/EOptionP.scala index 5f965cec32..73dd816e59 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/EOptionP.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/EOptionP.scala @@ -392,7 +392,7 @@ object LBP { */ final class FinalEP[+E <: Entity, +P <: Property](val e: E, val p: P) extends EPS[E, P] { - override def pk: PropertyKey[P] = p.key.asInstanceOf[PropertyKey[P]] + override lazy val pk: PropertyKey[P] = p.key.asInstanceOf[PropertyKey[P]] override def isFinal: Boolean = true override def asFinal: FinalEP[E, P] = this @@ -429,7 +429,7 @@ final class FinalEP[+E <: Entity, +P <: Property](val e: E, val p: P) extends EP } } - override def hashCode: Int = e.hashCode() * 727 + p.hashCode() + override lazy val hashCode: Int = e.hashCode() * 727 + p.hashCode() override def toString: String = { s"FinalEP($e@${System.identityHashCode(e).toHexString},p=$p)" @@ -546,7 +546,7 @@ final class InterimELUBP[+E <: Entity, +P <: Property]( ubAsOP.checkIsEqualOrBetterThan(e, lb.asInstanceOf[ubAsOP.Self]) } - override def pk: PropertyKey[P] = ub /* or lb */ .key.asInstanceOf[PropertyKey[P]] + override lazy val pk: PropertyKey[P] = ub /* or lb */ .key.asInstanceOf[PropertyKey[P]] override private[fpcf] def toFinalEP: FinalEP[E, P] = FinalEP(e, ub) @@ -562,13 +562,13 @@ final class InterimELUBP[+E <: Entity, +P <: Property]( override def equals(other: Any): Boolean = { other match { case that: InterimELUBP[_, _] ⇒ - (this.e eq that.e) || (e == that.e && lb == that.lb && ub == that.ub) + (this eq that) || (e == that.e && lb == that.lb && ub == that.ub) case _ ⇒ false } } - override def hashCode: Int = ((e.hashCode() * 31 + lb.hashCode()) * 31) + ub.hashCode() + override lazy val hashCode: Int = ((e.hashCode() * 31 + lb.hashCode()) * 31) + ub.hashCode() override def toString: String = { s"InterimELUBP($e@${System.identityHashCode(e).toHexString},lb=$lb,ub=$ub)" @@ -601,7 +601,7 @@ final class InterimEUBP[+E <: Entity, +P <: Property]( assert(ub != null) - override def pk: PropertyKey[P] = ub.key.asInstanceOf[PropertyKey[P]] + override lazy val pk: PropertyKey[P] = ub.key.asInstanceOf[PropertyKey[P]] override private[fpcf] def toFinalEP: FinalEP[E, P] = FinalEP(e, ub) @@ -625,7 +625,7 @@ final class InterimEUBP[+E <: Entity, +P <: Property]( } } - override def hashCode: Int = e.hashCode() * 31 + ub.hashCode() + override lazy val hashCode: Int = e.hashCode() * 31 + ub.hashCode() override def toString: String = { s"InterimEUBP($e@${System.identityHashCode(e).toHexString},ub=$ub)" @@ -717,7 +717,7 @@ final class InterimELBP[+E <: Entity, +P <: Property]( assert(lb != null) - override def pk: PropertyKey[P] = lb.key.asInstanceOf[PropertyKey[P]] + override lazy val pk: PropertyKey[P] = lb.key.asInstanceOf[PropertyKey[P]] override private[fpcf] def toFinalEP: FinalEP[E, P] = FinalEP(e, lb) @@ -741,7 +741,7 @@ final class InterimELBP[+E <: Entity, +P <: Property]( } } - override def hashCode: Int = e.hashCode() * 31 + lb.hashCode() + override lazy val hashCode: Int = e.hashCode() * 31 + lb.hashCode() override def toString: String = { s"InterimELBP($e@${System.identityHashCode(e).toHexString},lb=$lb)" @@ -827,7 +827,7 @@ final class EPK[+E <: Entity, +P <: Property]( } } - override def hashCode: Int = e.hashCode() * 31 + pk.id + override lazy val hashCode: Int = e.hashCode() * 31 + pk.id override def toString: String = { val pkId = pk.id diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyComputationResult.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyComputationResult.scala index 7694446da3..adab86b8aa 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyComputationResult.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyComputationResult.scala @@ -141,7 +141,7 @@ object MultiResult { private[fpcf] final val id = 2 } */ final class InterimResult[P >: Null <: Property] private ( val eps: InterimEP[Entity, P], - val dependees: Traversable[SomeEOptionP], + val dependees: Set[SomeEOptionP], //IMPROVE: require EOptionPSets? val c: ProperOnUpdateContinuation ) extends ProperPropertyComputationResult { result ⇒ @@ -179,7 +179,7 @@ final class InterimResult[P >: Null <: Property] private ( case that: InterimResult[_] if this.eps == that.eps ⇒ val dependees = this.dependees dependees.size == that.dependees.size && - dependees.forall(thisDependee ⇒ that.dependees.exists(_ == thisDependee)) + dependees.forall(thisDependee ⇒ that.dependees.contains(thisDependee)) case _ ⇒ false @@ -190,13 +190,14 @@ final class InterimResult[P >: Null <: Property] private ( s"InterimResult($eps,dependees=${dependees.mkString("[", ", ", "]")},c=$c)" } } + object InterimResult { private[fpcf] final val id = 3 def apply[P >: Null <: Property]( eps: InterimEP[Entity, P], - dependees: Traversable[SomeEOptionP], + dependees: Set[SomeEOptionP], c: ProperOnUpdateContinuation ): InterimResult[P] = { new InterimResult[P](eps, dependees, c) @@ -206,7 +207,7 @@ object InterimResult { e: Entity, lb: P, ub: P, - dependees: Traversable[SomeEOptionP], + dependees: Set[SomeEOptionP], c: ProperOnUpdateContinuation ): InterimResult[P] = { require(lb != null && ub != null) @@ -217,7 +218,7 @@ object InterimResult { e: Entity, lb: P, ub: P, - dependees: Traversable[EOptionP[DependeeE, DependeeP]], + dependees: Set[SomeEOptionP], c: QualifiedOnUpdateContinuation[DependeeE, DependeeP] ): InterimResult[P] = { require(lb != null && ub != null) @@ -237,7 +238,7 @@ object InterimResult { def forLB[P >: Null <: Property]( e: Entity, lb: P, - dependees: Traversable[SomeEOptionP], + dependees: Set[SomeEOptionP], c: ProperOnUpdateContinuation ): InterimResult[P] = { new InterimResult[P](InterimELBP(e, lb), dependees, c) @@ -246,7 +247,7 @@ object InterimResult { def forUB[P >: Null <: Property]( e: Entity, ub: P, - dependees: Traversable[SomeEOptionP], + dependees: Set[SomeEOptionP], c: ProperOnUpdateContinuation ): InterimResult[P] = { new InterimResult[P](InterimEUBP(e, ub), dependees, c) @@ -388,7 +389,7 @@ object PartialResult { private[fpcf] final val id = 6 } */ case class InterimPartialResult[SE >: Null <: Property]( us: Traversable[SomePartialResult], // can be empty! - dependees: Traversable[SomeEOptionP], + dependees: Set[SomeEOptionP], //IMPROVE: require EOptionPSets? c: OnUpdateContinuation ) extends ProperPropertyComputationResult { @@ -409,7 +410,7 @@ object InterimPartialResult { * a depending computation. */ def apply[SE >: Null <: Property]( - dependees: Traversable[SomeEOptionP], + dependees: Set[SomeEOptionP], c: OnUpdateContinuation ): InterimPartialResult[SE] = { new InterimPartialResult[SE](Nil, dependees, c) @@ -428,7 +429,7 @@ object InterimPartialResult { uE: UE, uPK: PropertyKey[UP], u: UpdateComputation[UE, UP], - dependees: Traversable[SomeEOptionP], + dependees: Set[SomeEOptionP], c: OnUpdateContinuation ): InterimPartialResult[SE] = { val pruc = PartialResult(uE, uPK, u) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyStore.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyStore.scala index b71ffb0795..c8c13aa186 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyStore.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyStore.scala @@ -271,11 +271,11 @@ abstract class PropertyStore { val s = if (debug) mutable.LinkedHashMap( - "scheduled tasks" -> + "scheduled tasks" → scheduledTasksCount, - "scheduled on update computations" -> + "scheduled on update computations" → scheduledOnUpdateComputationsCount, - "computations of fallback properties for computed properties" -> + "computations of fallback properties for computed properties" → fallbacksUsedForComputedPropertiesCount ) else @@ -620,8 +620,7 @@ abstract class PropertyStore { // Save the information about the finalization order (of properties which are // collaboratively computed). val cleanUpSubPhase = - (propertyKindsComputedInThisPhase -- finalizationOrder.flatten.toSet) + - AnalysisKey + (propertyKindsComputedInThisPhase -- finalizationOrder.flatten.toSet) + AnalysisKey this.subPhaseFinalizationOrder = if (cleanUpSubPhase.isEmpty) { finalizationOrder.toArray diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/EPKState.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/EPKState.scala deleted file mode 100644 index bdcb48c2af..0000000000 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/EPKState.scala +++ /dev/null @@ -1,522 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package par - -/** - * Encapsulates the state of a single entity and a property of a specific kind. - * - * @note The property may change (monotonically), but the property kind has to be stable. - * - * @note All methods are effectively thread safe; i.e., clients will always see a - * consistent state. - */ -private[par] sealed abstract class EPKState { - - // - // ------------------- PROPERTIES OF THIS EPK STATE ------------------------ - // - - /** - * Returns `true` if this entity/property pair is not yet final. - * - * @note Even the InterimEPKState object may eventually reference a final property! - */ - def isRefinable: Boolean - - def isFinal: Boolean - - // - // ---------------- PROPERTIES OF THE UNDERLYING EP PAIR ------------------- - // - - /** - * Returns the current property extension. - */ - def eOptionP: SomeEOptionP - - /** - * Returns `true` if no property has been computed yet; `false` otherwise. - * - * @note Just a convenience method for `eOptionP.isEPK` - */ - final def isEPK: Boolean = eOptionP.isEPK - - /** - * Returns the underlying entity. - * - * @note Just a convenience method for `eOptionP.e`. - */ - final def e: Entity = eOptionP.e - - /** - * Returns the underlying property key – which must never change. - * - * @note Just a convenience method for `eOptionP.pk`. - */ - final def pk: SomePropertyKey = eOptionP.pk - - /** - * Returns the underlying property key if – which must never change. - * - * @note Just a convenience method for `eOptionP.pk.id`. - */ - final def pkId: Int = eOptionP.pk.id - - /** - * Atomically updates the underlying `EOptionP` value; if the update is relevant, the current - * dependers that should be informed are removed and returned along with the old - * `eOptionP` value. - * - * @note This function is only defined if the current `EOptionP` value is not already a - * final value. Hence, the client is required to handle (potentially) idempotent updates - * and to take care of appropriate synchronization. - * - * @note Update is never called concurrently, however, the changes still have to applied - * atomically because some of the other methods rely on a consistent snapshot regarding - * the relation of the values. - * - * @param suppressInterimUpdates (See the corresponding property store datastructure.) - */ - def update( - newEOptionP: SomeInterimEP, - c: OnUpdateContinuation, - dependees: Traversable[SomeEOptionP], - // BASICALLY CONSTANTS: - hasSuppressedDependers: Array[Boolean], - suppressInterimUpdates: Array[Array[Boolean]], - debug: Boolean - ): Option[(SomeEOptionP, Traversable[SomeEPK])] - // newEOptionP.isUpdatedComparedTo(eOptionP) - - /** - * Atomically updates the underlying `EOptionP` value by applying the given update function; - * if the update is relevant, the current dependers that should be informed are removed and - * returned along with the old `eOptionP` value. - */ - def update( - u: SomeUpdateComputation, - // BASICALLY CONSTANTS: - hasSuppressedDependers: Array[Boolean], - suppressInterimUpdates: Array[Array[Boolean]] - ): Option[(SomeEOptionP, SomeInterimEP, Traversable[SomeEPK])] - - def update( - expectedEOptionP: SomeEOptionP, - updatedEOptionP: SomeInterimEP, - u: SomeUpdateComputation, - hasSuppressedDependers: Array[Boolean], - suppressInterimUpdates: Array[Array[Boolean]] - ): Option[(SomeEOptionP, SomeInterimEP, Traversable[SomeEPK])] - - /** - * Atomically updates the underlying `eOptionP` and returns the set of dependers. - */ - def finalUpdate(newEOptionP: SomeFinalEP): Traversable[SomeEPK] - - /** - * Adds the given EPK as a depender if the current `(this.)eOptionP` - * equals the given `eOptionP` – based on a reference comparison. If this `eOptionP` - * has changed `false` will be returned (adding the depender was not successful); `true` - * otherwise. - * - * @note This operation is idempotent; that is, adding the same EPK multiple times has no - * special effect. - * - * @param alwaysExceptIfFinal The depender is always added unless the current eOptionP is final. - */ - def addDepender( - expectedEOptionP: SomeEOptionP, - someEPK: SomeEPK, - alwaysExceptIfFinal: Boolean - ): Boolean - - /** - * If a continuation function still exists and the given dependee is among the set - * of current dependees then the continuation function is cleared and returned. - * - * @note The set of dependees will not be updated! - */ - def prepareInvokeC(updatedDependeeEOptionP: SomeEOptionP): Option[OnUpdateContinuation] - - /** - * If the current continuation function is reference equal to the given function the the - * continuation function is cleared and `true` is returned; otherwise `false` is returned. - * - * @note The set of dependees will not be updated! - */ - def prepareInvokeC(expectedC: OnUpdateContinuation): Boolean - - def clearDependees(): Unit - - /** - * Removes the given EPK from the list of dependers of this EPKState. - * - * @note This method is always defined and never throws an exception for convenience purposes. - */ - def removeDepender(someEPK: SomeEPK): Unit - - /** - * Returns `true` if the current continuation function `c` is reference equal to the given one. - */ - def isCurrentC(c: OnUpdateContinuation): Boolean - - /** - * Returns `true` if and only if this `EPKState` has dependees. - * - * @note The set of dependees is only updated when a property computation result is processed. - * There exists, w.r.t. an Entity/Property kind pair, always at most one - * `PropertyComputationResult`. - * (Partially computed properties never have dependees on their own.) - */ - def hasDependees: Boolean - - /** - * Returns the current set of depeendes or `null`. - * - * @note The set of dependees is only updated when a property computation result is processed. - * There exists, w.r.t. an Entity/Property kind pair, always at most one - * `PropertyComputationResult`. - * (Partially computed properties never have dependees on their own.) - */ - def dependees: Traversable[SomeEOptionP] - - /** - * Removes the depender/dependee relations related to the given pksTOFinalize - * - * __This method is not thread safe. Concurrent execution is NOT supported.__ - */ - def cleanUp(pksToFinalize: List[PropertyKind]): Unit -} - -/** - * Represents the intermediate property of a specific kind related to a specific entity. - * - * @note Though an `InterimEPKState` object primarily stores `InterimEP` objects, it may - * eventually reference a `FinalEP` object to ensure that clients, which didn't get - * the update `FinalEPKState` object are still able to determine that the analysis - * of the respective property has finished. - * - * @note Basically every `InterimEPKState` object is eventually lifted to a `FinalEPKState` - * object which requires no more synchronization and also less memory. - * - * @param eOptionP The current property extension; never null. - * @param c The on update continuation function; null if triggered. - * @param dependees The dependees. - */ -private[par] final class InterimEPKState( - @volatile var eOptionP: SomeEOptionP, - @volatile var c: OnUpdateContinuation, - @volatile var dependees: Traversable[SomeEOptionP], - @volatile private[this] var dependers: Set[SomeEPK] -) extends EPKState /*with Locking*/ { - - assert(eOptionP.isRefinable) // an update which makes it final is possible... - - private[this] final val thisPKId = eOptionP.pk.id - - // NOT THREAD SAFE! - override def cleanUp(pksToFinalize: List[PropertyKind]): Unit = { - pksToFinalize.foreach { pkToFinalize ⇒ - if (eOptionP.pk == pkToFinalize) { - clearDependees() - } - dependers = dependers.filter(dependerEPK ⇒ !pksToFinalize.contains(dependerEPK.pk)) - } - } - - override def isRefinable: Boolean = eOptionP.isRefinable - override def isFinal: Boolean = eOptionP.isFinal - - override def update( - eOptionP: SomeInterimEP, - c: OnUpdateContinuation, - dependees: Traversable[SomeEOptionP], - hasSuppressedDependers: Array[Boolean], - suppressInterimUpdates: Array[Array[Boolean]], - debug: Boolean - ): Option[(SomeEOptionP, Traversable[SomeEPK])] = { - assert(this.c == null) - // The following assert is not possible, because we only strive for - // eventual consistency w.r.t. the depender/dependee relation: - // assert(this.dependees.isEmpty) - - val dependeePKId = this.eOptionP.pk.id - this.synchronized { - val oldEOptionP = this.eOptionP - if (debug) oldEOptionP.checkIsValidPropertiesUpdate(eOptionP, dependees) - - this.c = c - this.dependees = dependees - - val isRelevantUpdate = eOptionP.isUpdatedComparedTo(oldEOptionP) - if (isRelevantUpdate) { - this.eOptionP = eOptionP - val oldDependers = this.dependers - if (oldDependers.nonEmpty) { - // IMPROVE Given that suppression is rarely required/used(?) it may be more efficient to filter those dependers that should not be informed and then substract that set from the original set. - val (suppressedDependers, dependersToBeNotified) = - oldDependers.partition { dependerEPK ⇒ - suppressInterimUpdates(dependerEPK.pk.id)(dependeePKId) - } - this.dependers = suppressedDependers - Some((oldEOptionP, dependersToBeNotified)) - } else { - Some((oldEOptionP, oldDependers /* <= basically "NIL" */ )) - } - } else { - None - } - } - } - - override def update( - u: SomeUpdateComputation, - hasSuppressedDependers: Array[Boolean], - suppressInterimUpdates: Array[Array[Boolean]] - ): Option[(SomeEOptionP, SomeInterimEP, Traversable[SomeEPK])] = { - this.synchronized { - val oldEOptionP = this.eOptionP - val newInterimEPOption = u.asInstanceOf[SomeEOptionP ⇒ Option[SomeInterimEP]](oldEOptionP) - if (newInterimEPOption.isEmpty) { - return None; - } - - val newInterimEP = newInterimEPOption.get - // The test whether we have a relevant update or not should have been done - // by the update function - it must return "None" if the update is not - // relevant; i.e., there is no change. - if (PropertyStore.Debug && !newInterimEP.isUpdatedComparedTo(oldEOptionP)) { - throw new IllegalArgumentException( - s"the update ($u) computed an irrelevant update: $oldEOptionP => $newInterimEP" - ) - } - - performUpdate(oldEOptionP, newInterimEP, hasSuppressedDependers, suppressInterimUpdates) - } - } - - override def update( - expectedEOptionP: SomeEOptionP, - updatedEOptionP: SomeInterimEP, - u: SomeUpdateComputation, - hasSuppressedDependers: Array[Boolean], - suppressInterimUpdates: Array[Array[Boolean]] - ): Option[(SomeEOptionP, SomeInterimEP, Traversable[SomeEPK])] = { - this.synchronized { - if (this.eOptionP eq expectedEOptionP) { - performUpdate( - expectedEOptionP, updatedEOptionP, - hasSuppressedDependers, suppressInterimUpdates - ) - } else { - update(u, hasSuppressedDependers, suppressInterimUpdates) - } - } - } - - private[this] def performUpdate( - oldEOptionP: SomeEOptionP, - newInterimEP: SomeInterimEP, - hasSuppressedDependers: Array[Boolean], - suppressInterimUpdates: Array[Array[Boolean]] - ): Option[(SomeEOptionP, SomeInterimEP, Traversable[SomeEPK])] = { - this.eOptionP = newInterimEP - - val oldDependers = this.dependers - if (oldDependers.nonEmpty) { - if (hasSuppressedDependers(thisPKId)) { - // IMPROVE Given that suppression is rarely required/used(?) it may be more efficient to filter those dependers that should not be informed and then substract that set from the original set. - val (suppressedDependers, dependersToBeNotified) = - oldDependers.partition { dependerEPK ⇒ - suppressInterimUpdates(dependerEPK.pk.id)(thisPKId) - } - this.dependers = suppressedDependers - Some((oldEOptionP, newInterimEP, dependersToBeNotified)) - } else { - this.dependers = Set.empty - Some((oldEOptionP, newInterimEP, oldDependers)) - } - } else { - Some((oldEOptionP, newInterimEP, oldDependers /*<= there are none!*/ )) - } - } - - override def finalUpdate(eOptionP: SomeFinalEP): Traversable[SomeEPK] = { - this.synchronized { - assert(this.eOptionP.isRefinable) - - this.eOptionP = eOptionP - val oldDependers = this.dependers - this.dependers = null - oldDependers - } - } - - override def addDepender( - expectedEOptionP: SomeEOptionP, - someEPK: SomeEPK, - alwaysExceptIfFinal: Boolean - ): Boolean = { - assert(expectedEOptionP.isRefinable) - - this.synchronized { - val thisEOptionP = this.eOptionP - if ((thisEOptionP eq expectedEOptionP) || - (alwaysExceptIfFinal && thisEOptionP.isRefinable)) { - this.dependers += someEPK - true - } else { - false - } - } - } - - override def prepareInvokeC( - updatedDependeeEOptionP: SomeEOptionP - ): Option[OnUpdateContinuation] = { - if (this.c eq null) - return None; - - this.synchronized { - val c = this.c - if (c != null) { - // IMPROVE ? Use a set based contains check. - val isDependee = (this.dependees ne null /*TODO Ugly hack to fix NPE because of ProperyStore bug*/ ) && - this.dependees.exists(dependee ⇒ - dependee.e == updatedDependeeEOptionP.e && - dependee.pk == updatedDependeeEOptionP.pk) - if (isDependee) { - this.c = null - Some(c) - } else { - None - } - } else { - None - } - } - } - - override def prepareInvokeC(expectedC: OnUpdateContinuation): Boolean = { - if (this.c ne expectedC) - return false; - - this.synchronized { - if (this.c eq expectedC) { - this.c = null - true - } else { - false - } - } - } - - override def clearDependees(): Unit = this.dependees = null - - override def removeDepender(someEPK: SomeEPK): Unit = { - this.synchronized { - val dependers = this.dependers - if (dependers == null) - return ; - - // The write lock is required to avoid lost updates; e.g., if - // two dependers are removed "concurrently". - this.dependers -= someEPK - } - } - - override def isCurrentC(c: OnUpdateContinuation): Boolean = c eq this.c - - override def hasDependees: Boolean = dependees != null && dependees.nonEmpty - - override def toString: String = { - "InterimEPKState("+ - s"eOptionP=${eOptionP},"+ - s","+ - s"dependees=${Option(dependees)},"+ - s"dependers=$dependers)" - } -} - -private[par] final class FinalEPKState(override val eOptionP: SomeEOptionP) extends EPKState { - - override def isRefinable: Boolean = false - override def isFinal: Boolean = true - - override def update( - newEOptionP: SomeInterimEP, - c: OnUpdateContinuation, - dependees: Traversable[SomeEOptionP], - hasSuppressedDependers: Array[Boolean], - suppressInterimUpdates: Array[Array[Boolean]], - debug: Boolean - ): Option[(SomeEOptionP, Set[SomeEPK])] = { - throw new UnknownError(s"the final property $eOptionP can't be updated to $newEOptionP") - } - - override def update( - u: SomeUpdateComputation, - hasSuppressedDependers: Array[Boolean], - suppressInterimUpdates: Array[Array[Boolean]] - ): Option[(SomeEOptionP, SomeInterimEP, Set[SomeEPK])] = { - throw new UnknownError(s"the final property $eOptionP can't be updated using $u") - } - - override def update( - expectedEOptionP: SomeEOptionP, - updatedEOptionP: SomeInterimEP, - u: SomeUpdateComputation, - hasSuppressedDependers: Array[Boolean], - suppressInterimUpdates: Array[Array[Boolean]] - ): Option[(SomeEOptionP, SomeInterimEP, Traversable[SomeEPK])] = { - throw new UnknownError(s"the final property $eOptionP can't be updated using $updatedEOptionP") - } - - override def finalUpdate(newEOptionP: SomeFinalEP): Set[SomeEPK] = { - throw new UnknownError(s"the final property $eOptionP can't be updated to $newEOptionP") - } - - override def addDepender( - expectedEOptionP: SomeEOptionP, - someEPK: SomeEPK, - alwaysExceptIfFinal: Boolean - ): Boolean = false - - override def prepareInvokeC( - updatedDependeeEOptionP: SomeEOptionP - ): Option[OnUpdateContinuation] = { - None - } - - override def prepareInvokeC(expectedC: OnUpdateContinuation): Boolean = false - override def clearDependees(): Unit = { /* Nothing to do! */ } - override def removeDepender(someEPK: SomeEPK): Unit = { /* Nothing to do! */ } - override def isCurrentC(c: OnUpdateContinuation): Boolean = false - override def hasDependees: Boolean = false - override def dependees: Traversable[SomeEOptionP] = null - - override def cleanUp(pksToFinalize: List[PropertyKind]): Unit = { /* Nothing to do! */ } - - override def toString: String = s"FinalEPKState(finalEP=$eOptionP)" -} - -object EPKState { - - def apply(finalEP: SomeFinalEP): EPKState = new FinalEPKState(finalEP) - - def apply(eOptionP: SomeEOptionP): InterimEPKState = { - new InterimEPKState(eOptionP, null, Nil, Set.empty) - } - - def apply( - eOptionP: SomeEOptionP, - c: OnUpdateContinuation, - dependees: Traversable[SomeEOptionP] - ): InterimEPKState = { - new InterimEPKState(eOptionP, c, dependees, Set.empty) - } - - def unapply(epkState: EPKState): Some[SomeEOptionP] = Some(epkState.eOptionP) - -} diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/PKECPropertyStore.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/PKECPropertyStore.scala index 78b83289a1..38b457e5d1 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/PKECPropertyStore.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/PKECPropertyStore.scala @@ -4,106 +4,77 @@ package fpcf package par import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.ConcurrentLinkedQueue -import java.util.{Arrays ⇒ JArrays} +import java.util.concurrent.LinkedBlockingQueue +import java.util.ArrayDeque -import com.typesafe.config.Config -import scala.collection.JavaConverters._ import scala.collection.mutable.ArrayBuffer +import scala.util.control.ControlThrowable + +import com.typesafe.config.Config -import org.opalj.log.OPALLogger.{debug ⇒ trace} import org.opalj.log.LogContext -import org.opalj.log.OPALLogger import org.opalj.fpcf.PropertyKey.fallbackPropertyBasedOnPKId -import org.opalj.fpcf.PropertyKind.SupportedPropertyKinds /** - * A concurrent implementation of the property store which executes the scheduled computations - * in parallel. - * - * The number of threads that is used for parallelizing the computation is determined by: - * `NumberOfThreadsForProcessingPropertyComputations` + * Yet another parallel property store. * - * @author Michael Eichberg + * @author Dominik Helm */ -final class PKECPropertyStore( +class PKECPropertyStore( final val ctx: Map[Class[_], AnyRef], - implicit final val tasksManager: TasksManager, - final val tracer: Option[PropertyStoreTracer] + val THREAD_COUNT: Int, + override val MaxEvaluationDepth: Int )( implicit val logContext: LogContext -) extends ParallelPropertyStore { store ⇒ +) extends ParallelPropertyStore { - private[par] implicit def self: PKECPropertyStore = this + implicit val propertyStore: PKECPropertyStore = this - override def MaxEvaluationDepth: Int = tasksManager.MaxEvaluationDepth - override def shutdown(): Unit = tasksManager.shutdown() - override def isIdle: Boolean = tasksManager.isIdle + val taskManager: PKECTaskManager = PKECNoPriorityTaskManager - // - // - // PARALLELIZATION RELATED FUNCTIONALITY - // - // + var evaluationDepth: Int = 0 - import tasksManager.phaseSetupCompleted - import tasksManager.awaitPoolQuiescence - import tasksManager.parallelize - import tasksManager.forkResultHandler - import tasksManager.forkOnUpdateContinuation - import tasksManager.forkLazyPropertyComputation - import tasksManager.schedulePropertyComputation + val ps: Array[ConcurrentHashMap[Entity, EPKState]] = + Array.fill(PropertyKind.SupportedPropertyKinds) { new ConcurrentHashMap() } - // -------------------------------------------------------------------------------------------- - // - // STATISTICS - // - // -------------------------------------------------------------------------------------------- + private[this] val triggeredComputations: Array[Array[SomePropertyComputation]] = + new Array(PropertyKind.SupportedPropertyKinds) - private[this] var quiescenceCounter = 0 - override def quiescenceCount: Int = quiescenceCounter + private[this] val queues: Array[LinkedBlockingQueue[QualifiedTask]] = + Array.fill(THREAD_COUNT) { new LinkedBlockingQueue[QualifiedTask]() } - override def scheduledTasksCount: Int = tasksManager.scheduledTasksCount - override def scheduledOnUpdateComputationsCount: Int = { - tasksManager.scheduledOnUpdateComputationsCount - } + private[this] val initialQueues: Array[ArrayDeque[QualifiedTask]] = + Array.fill(THREAD_COUNT) { new ArrayDeque[QualifiedTask](50000 / THREAD_COUNT) } - private[this] val fallbacksUsedForComputedPropertiesCounter = new AtomicInteger(0) - override def fallbacksUsedForComputedPropertiesCount: Int = { - fallbacksUsedForComputedPropertiesCounter.get() - } - override private[fpcf] def incrementFallbacksUsedForComputedPropertiesCounter(): Unit = { - if (debug) fallbacksUsedForComputedPropertiesCounter.incrementAndGet() - } + private[this] var setAndPreinitializedValues: List[SomeEPK] = List.empty + + override def shutdown(): Unit = {} + + var idle = true + override def isIdle: Boolean = idle // -------------------------------------------------------------------------------------------- // - // CORE DATA STRUCTURES + // STATISTICS // // -------------------------------------------------------------------------------------------- - // Per PropertyKind we use one concurrent hash map to store the entities' properties. - // The value (EPKState) encompasses the current property along with some helper information. - private[par] val properties: Array[ConcurrentHashMap[Entity, EPKState]] = { - Array.fill(SupportedPropertyKinds) { new ConcurrentHashMap() } - } + private[this] var quiescenceCounter = 0 + override def quiescenceCount: Int = quiescenceCounter - /** - * Computations that will be triggered when a new property becomes available. - * - * Please note, that the triggered computations have to be registered strictly before the first - * computation is started. - */ - private[par] val triggeredComputations: Array[Array[SomePropertyComputation]] = { - new Array(SupportedPropertyKinds) - } + private[this] val scheduledTasks = new AtomicInteger(0) + override def scheduledTasksCount: Int = scheduledTasks.get() - private[par] val forcedEPKs: ConcurrentLinkedQueue[SomeEPK] = new ConcurrentLinkedQueue() + private[this] val scheduledOnUpdateComputations = new AtomicInteger(0) + override def scheduledOnUpdateComputationsCount: Int = scheduledOnUpdateComputations.get - private[par] var setAndPreinitializedValues: List[SomeEPK] = List.empty + private[this] val fallbacksForComputedProperties = new AtomicInteger(0) + override def fallbacksUsedForComputedPropertiesCount: Int = fallbacksForComputedProperties.get + override private[fpcf] def incrementFallbacksUsedForComputedPropertiesCounter(): Unit = { + fallbacksForComputedProperties.getAndIncrement() + } // -------------------------------------------------------------------------------------------- // @@ -111,77 +82,73 @@ final class PKECPropertyStore( // // -------------------------------------------------------------------------------------------- - override def toString(printProperties: Boolean): String = { + override def toString(printProperties: Boolean): String = if (printProperties) { - val ps = for { - (entitiesMap, pkId) ← properties.iterator.zipWithIndex.take(PropertyKey.maxId + 1) - } yield { - entitiesMap.values().iterator().asScala - .map(_.eOptionP.toString.replace("\n", "\n\t")) - .toList.sorted - .mkString(s"Entities for property key $pkId:\n\t", "\n\t", "\n") + val properties = for (pkId ← 0 to PropertyKey.maxId) yield { + var entities: List[String] = List.empty + ps(pkId).forEachValue(Long.MaxValue, { state: EPKState ⇒ + entities ::= state.eOptP.toString.replace("\n", "\n\t") + }) + entities.sorted.mkString(s"Entities for property key $pkId:\n\t", "\n\t", "\n") } - ps.mkString("PropertyStore(\n\t", "\n\t", "\n)") + properties.mkString("PropertyStore(\n\t", "\n\t", "\n)") } else { - s"PropertyStore(properties=${properties.iterator.map(_.size).sum})" + s"PropertyStore(properties=${ps.iterator.map(_.size).sum})" } - } - override def entities(p: SomeEPS ⇒ Boolean): Iterator[Entity] = { - this.properties.iterator.flatMap { propertiesPerKind ⇒ - propertiesPerKind - .elements().asScala - .collect { case EPKState(eps: SomeEPS) if p(eps) ⇒ eps.e } + override def entities(propertyFilter: SomeEPS ⇒ Boolean): Iterator[Entity] = { + ps.iterator.flatMap { propertiesPerKind ⇒ + var result: List[Entity] = List.empty + propertiesPerKind.forEachValue(Long.MaxValue, { state: EPKState ⇒ if (propertyFilter(state.eOptP.asEPS)) result ::= state.eOptP.e }) + result } } override def entities[P <: Property](pk: PropertyKey[P]): Iterator[EPS[Entity, P]] = { - properties(pk.id) - .values().iterator().asScala - .collect { case EPKState(eps: EPS[Entity, P] @unchecked) ⇒ eps } + var result: List[EPS[Entity, P]] = List.empty + ps(pk.id).forEachValue(Long.MaxValue, { state: EPKState ⇒ result ::= state.eOptP.asInstanceOf[EPS[Entity, P]] }) + result.iterator } override def entities[P <: Property](lb: P, ub: P): Iterator[Entity] = { - for { EPKState(ELUBP(e, `lb`, `ub`)) ← properties(lb.id).elements().asScala } yield { e } + entities { eps ⇒ eps.lb == lb && eps.ub == ub } } override def entitiesWithLB[P <: Property](lb: P): Iterator[Entity] = { - for { EPKState(ELBP(e, `lb`)) ← properties(lb.id).elements().asScala } yield { e } + entities { eps ⇒ eps.lb == lb } } override def entitiesWithUB[P <: Property](ub: P): Iterator[Entity] = { - for { EPKState(EUBP(e, `ub`)) ← properties(ub.id).elements().asScala } yield { e } + entities { eps ⇒ eps.ub == ub } } override def properties[E <: Entity](e: E): Iterator[EPS[E, Property]] = { - this.properties.iterator.flatMap { map ⇒ - val ePKState = map.get(e) - if (ePKState != null && ePKState.eOptionP.isEPS) - Iterator.single(ePKState.eOptionP.asInstanceOf[EPS[E, Property]]) + ps.iterator.flatMap { propertiesPerKind ⇒ + val ePKState = propertiesPerKind.get(e) + if ((ePKState ne null) && ePKState.eOptP.isEPS) + Iterator.single(ePKState.eOptP.asInstanceOf[EPS[E, Property]]) else Iterator.empty } - } override def hasProperty(e: Entity, pk: PropertyKind): Boolean = { - val state = properties(pk.id).get(e) - state != null && { - val eOptionP = state.eOptionP - eOptionP.hasUBP || eOptionP.hasLBP - } + val ePKState = ps(pk.id).get(e) + (ePKState ne null) && (ePKState.eOptP.hasUBP || ePKState.eOptP.hasLBP) } override def isKnown(e: Entity): Boolean = { - properties.exists(propertiesOfKind ⇒ propertiesOfKind.containsKey(e)) + ps.exists { propertiesPerKind ⇒ + propertiesPerKind.containsKey(e) + } } override def get[E <: Entity, P <: Property](e: E, pk: PropertyKey[P]): Option[EOptionP[E, P]] = { - val state = properties(pk.id).get(e) - if (state == null) + val ePKState = ps(pk.id).get(e) + if (ePKState eq null) None else - Some(state.eOptionP.asInstanceOf[EOptionP[E, P]]) + Some(ePKState.eOptP.asInstanceOf[EOptionP[E, P]]) } override def get[E <: Entity, P <: Property](epk: EPK[E, P]): Option[EOptionP[E, P]] = { @@ -194,15 +161,13 @@ final class PKECPropertyStore( // // -------------------------------------------------------------------------------------------- - override def doScheduleEagerComputationForEntity[E <: Entity]( + override protected[this] def doScheduleEagerComputationForEntity[E <: Entity]( e: E - )( - pc: PropertyComputation[E] - ): Unit = { + )(pc: PropertyComputation[E]): Unit = { schedulePropertyComputation(e, pc) } - override def doRegisterTriggeredComputation[E <: Entity, P <: Property]( + override protected[this] def doRegisterTriggeredComputation[E <: Entity, P <: Property]( pk: PropertyKey[P], pc: PropertyComputation[E] ): Unit = { @@ -218,30 +183,28 @@ final class PKECPropertyStore( if (oldComputations == null) { newComputations = Array[SomePropertyComputation](pc) } else { - newComputations = JArrays.copyOf(oldComputations, oldComputations.length + 1) + newComputations = java.util.Arrays.copyOf(oldComputations, oldComputations.length + 1) newComputations(oldComputations.length) = pc } triggeredComputations(pkId) = newComputations } - override def doSet(e: Entity, p: Property): Unit = { - val epkState = EPKState(FinalEP(e, p)) - if (tracer.isDefined) tracer.get.set(epkState) - val oldP = properties(p.id).put(e, epkState) - if (oldP != null) { - throw new IllegalStateException(s"$e had already the property $oldP") + override protected[this] def doSet(e: Entity, p: Property): Unit = { + val epkState = EPKState(FinalEP(e, p), null, null) + + val oldP = ps(p.id).put(e, epkState) + if (oldP ne null) { + throw new IllegalStateException(s"$e already had the property $oldP") } setAndPreinitializedValues ::= EPK(e, p.key) } - override def doPreInitialize[E <: Entity, P <: Property]( + override protected[this] def doPreInitialize[E <: Entity, P <: Property]( e: E, pk: PropertyKey[P] - )( - pc: EOptionP[E, P] ⇒ InterimEP[E, P] - ): Unit = { + )(pc: EOptionP[E, P] ⇒ InterimEP[E, P]): Unit = { val pkId = pk.id - val propertiesOfKind = properties(pkId) + val propertiesOfKind = ps(pkId) val oldEPKState = propertiesOfKind.get(e) val newInterimEP: SomeInterimEP = oldEPKState match { @@ -250,11 +213,10 @@ final class PKECPropertyStore( setAndPreinitializedValues ::= epk pc(epk) case epkState ⇒ - pc(epkState.eOptionP.asInstanceOf[EOptionP[E, P]]) + pc(epkState.eOptP.asInstanceOf[EOptionP[E, P]]) } assert(newInterimEP.isRefinable) - val newEPKState = EPKState(newInterimEP) - if (tracer.isDefined) tracer.get.preInitialize(oldEPKState, newEPKState) + val newEPKState = EPKState(newInterimEP, null, null) propertiesOfKind.put(e, newEPKState) } @@ -264,39 +226,162 @@ final class PKECPropertyStore( // // -------------------------------------------------------------------------------------------- - private[this] def triggerComputations(e: Entity, pkId: Int): Unit = { - val triggeredComputations = this.triggeredComputations(pkId) - if (triggeredComputations == null) - return ; - - triggeredComputations foreach { pc ⇒ - if (tracer.isDefined) tracer.get.triggeredComputation(e, pkId, pc) - schedulePropertyComputation(e, pc.asInstanceOf[PropertyComputation[Entity]]) + private[par] def scheduleTask(task: QualifiedTask): Unit = { + val numTasks = scheduledTasks.incrementAndGet() + if (idle) { + initialQueues(numTasks % THREAD_COUNT).offer(task) + } else { + activeTasks.incrementAndGet() + //queues.minBy(_.size()).offer(task) + queues(numTasks % THREAD_COUNT).offer(task) } } + private[this] def schedulePropertyComputation[E <: Entity]( + e: E, + pc: PropertyComputation[E] + ): Unit = { + scheduleTask(new PropertyComputationTask(e, pc)) + } + override def force[E <: Entity, P <: Property](e: E, pk: PropertyKey[P]): Unit = { - val epk = EPK(e, pk) - if (tracer.isDefined) tracer.get.enqueueingEPKToForce(epk) - forcedEPKs.add(epk) + doApply(EPK(e, pk), e, pk.id) + } + + override def execute(f: ⇒ Unit): Unit = { + scheduleTask(new ExecuteTask(f)) + } + + override def handleResult(r: PropertyComputationResult): Unit = { + r.id match { + + case NoResult.id ⇒ + // A computation reported no result; i.e., it is not possible to + // compute a/some property/properties for a given entity. + + // + // Result containers + // + + case Results.id ⇒ + r.asResults.foreach { handleResult } + + case IncrementalResult.id ⇒ + val IncrementalResult(ir, npcs) = r + handleResult(ir) + npcs /*: Iterator[(PropertyComputation[e],e)]*/ foreach { npc ⇒ + val (pc, e) = npc + schedulePropertyComputation(e, pc) + } + + // + // Methods which actually store results... + // + + case Result.id ⇒ + handleFinalResult(r.asResult.finalEP) + + case MultiResult.id ⇒ + val MultiResult(results) = r + results foreach { finalEP ⇒ handleFinalResult(finalEP) } + + case InterimResult.id ⇒ + val interimR = r.asInterimResult + handleInterimResult( + interimR.eps, + interimR.c, + interimR.dependees + ) + + case PartialResult.id ⇒ + val PartialResult(e, pk, u) = r + handlePartialResult(u, e, pk) + + case InterimPartialResult.id ⇒ + val InterimPartialResult(prs, dependees, c) = r + + prs foreach { pr ⇒ + handlePartialResult( + pr.u.asInstanceOf[SomeEOptionP ⇒ Option[SomeInterimEP]], + pr.e, + pr.pk + ) + } + + val e = new FakeEntity() + val epk = EPK(e, AnalysisKey) + + val epkState = EPKState(epk, null, dependees) + epkState.c = { dependee: SomeEPS ⇒ + val result = c(dependee) + + val state = ps(AnalysisKeyId).remove(e) + state.dependees = null + + result + } + + ps(AnalysisKeyId).put(e, epkState) + + updateDependees(epkState, dependees) + } } - override def execute(f: ⇒ Unit): Unit = handleExceptions { parallelize(f) } + private[this] def handleFinalResult( + finalEP: FinalEP[Entity, Property], + unnotifiedPKs: Set[PropertyKind] = Set.empty + ): Unit = { + val SomeEPS(e, pk) = finalEP + var isFresh = false + val ePKState = ps(pk.id).computeIfAbsent(e, { _ ⇒ isFresh = true; EPKState(finalEP, null, null) }) + if (isFresh) triggerComputations(e, pk.id) + else ePKState.setFinal(finalEP, unnotifiedPKs) - private[par] def triggerAndClearForcedEPKs(): Boolean = { - val hadForcedEPKs = !forcedEPKs.isEmpty - var forcedEPK = forcedEPKs.poll() - while (forcedEPK != null) { - if (tracer.isDefined) tracer.get.force(forcedEPK) - this(forcedEPK) - forcedEPK = forcedEPKs.poll() + //TODO remove depender status + } + + private[par] def triggerComputations(e: Entity, pkId: Int): Unit = { + val computations = triggeredComputations(pkId) + if (computations ne null) { + computations foreach { pc ⇒ + schedulePropertyComputation(e, pc.asInstanceOf[PropertyComputation[Entity]]) + } } - hadForcedEPKs } - override def handleResult(r: PropertyComputationResult): Unit = handleExceptions { - if (tracer.isDefined) tracer.get.scheduledResultProcessing(r) - forkResultHandler(r) + private[this] def handleInterimResult( + interimEP: InterimEP[Entity, _ >: Null <: Property], + c: ProperOnUpdateContinuation, + dependees: Set[SomeEOptionP] + ): Unit = { + val SomeEPS(e, pk) = interimEP + var isFresh = false + val ePKState = + ps(pk.id).computeIfAbsent(e, { _ ⇒ isFresh = true; EPKState(interimEP, c, dependees) }) + if (isFresh) { + triggerComputations(e, pk.id) + updateDependees(ePKState, dependees) + } else ePKState.interimUpdate(interimEP, c, dependees) + + //TODO update depender status + } + + private[this] def handlePartialResult( + update: UpdateComputation[Entity, Property], + e: Entity, + pk: PropertyKey[Property] + ): Unit = { + val ePKState = ps(pk.id).computeIfAbsent(e, _ ⇒ EPKState(EPK(e, pk), null, null)) + ePKState.partialUpdate(update) + } + + def updateDependees(depender: EPKState, newDependees: Set[SomeEOptionP]): Unit = { + val suppressedPKs = suppressInterimUpdates(depender.eOptP.pk.id) + newDependees.forall { dependee ⇒ + val dependeePK = dependee.pk.id + val dependeeState = ps(dependeePK).get(dependee.e) + dependeeState.addDependerOrScheduleContinuation(depender, dependee, dependeePK, suppressedPKs) + } } override protected[this] def doApply[E <: Entity, P <: Property]( @@ -304,714 +389,642 @@ final class PKECPropertyStore( e: E, pkId: Int ): EOptionP[E, P] = { - val psOfKind = properties(pkId) - - // In the following, we just ensure that we only create a new EPKState object if - // the chances are high that we need it. Conceptually, we just do a "putIfAbsent" - var oldEPKState = psOfKind.get(e) - if (oldEPKState != null) { - // just return the current value - return oldEPKState.eOptionP.asInstanceOf[EOptionP[E, P]]; - } - val epkState = EPKState(epk) /* eagerly construct EPKState */ - oldEPKState = psOfKind.putIfAbsent(e, epkState) - if (oldEPKState != null) { - return oldEPKState.eOptionP.asInstanceOf[EOptionP[E, P]]; - } - - // ----------------------------------------------------------------------------------------- - // ---- This part is executed exactly once per EP Pair and (hence), never concurrently. - // ---- But, given that the EPK State object is already registered, it may be possible - // ---- that dependers are registered! - // - // WE CREATED A NEW EPK STATE OBJECT - LET'S CHECK WHAT NEEDS TO BE DONE: - // - trigger lazy computation ? - // - compute fall back value ? (If no analysis is scheduled.) - // - "just wait" ? (If the property is eagerly computed but the respective computation - // did not yet complete.) - - val lc = lazyComputations(pkId) - if (lc != null) { - if (tracer.isDefined) tracer.get.scheduledLazyComputation(epk, lc) - forkLazyPropertyComputation(epk, lc.asInstanceOf[PropertyComputation[E]]) - } else if (propertyKindsComputedInThisPhase(pkId)) { - val transformerSpecification = transformersByTargetPK(pkId) - if (transformerSpecification != null) { - // ... we have a transformer that can produce a property of the required kind; - // let's check if we can invoke it now or have to invoke it later. - val (sourcePK, transform) = transformerSpecification - val sourceEPK = EPK(e, sourcePK) - var sourceEOptionP: SomeEOptionP = null - var sourceEPKState: EPKState = null - var cSet: Boolean = false - do { - // "apply" is necessary to ensure that all necessary lazy analyses get triggered. - sourceEOptionP = apply(sourceEPK) - if (sourceEOptionP.isFinal) { - val sourceP = sourceEOptionP.asFinal.lb - val finalEP = transform(e, sourceP).asInstanceOf[FinalEP[E, P]] - if (tracer.isDefined) tracer.get.evaluatedTransformer(sourceEOptionP, finalEP) - handleFinalResult(finalEP) - return finalEP; + val current = ps(pkId).get(e) + if (current eq null) { + val lazyComputation = lazyComputations(pkId).asInstanceOf[E ⇒ PropertyComputationResult] + if (lazyComputation ne null) { + val previous = ps(pkId).putIfAbsent(e, EPKState(epk, null, null)) + if (previous eq null) { + if (evaluationDepth < MaxEvaluationDepth) { + evaluationDepth += 1 + handleResult(lazyComputation(e)) + evaluationDepth -= 1 + ps(pkId).get(e).eOptP.asInstanceOf[EOptionP[E, P]] } else { - // Add this transformer as a depender to the transformer's source; - // this strictly requires that intermediate values are suppressed. - sourceEPKState = properties(sourcePK.id).get(e) - // We are not yet registered with the dependee; hence we can't have concurrent - // notifications even though we set the dependees here! - if (!cSet) { - epkState.c = (eps) ⇒ { Result(transform(e, eps.lb /*or ub*/ )) } - cSet = true + scheduleTask( + new LazyComputationTask( + e, + lazyComputation, + pkId + ) + ) + epk + } + } else { + previous.eOptP.asInstanceOf[EOptionP[E, P]] + } + } else if (propertyKindsComputedInThisPhase(pkId)) { + val transformer = transformersByTargetPK(pkId) + if (transformer ne null) { + val dependee = this(e, transformer._1) + if (dependee.isFinal) { + val result = transformer._2(e, dependee.asFinal.p) + val previous = ps(pkId).putIfAbsent(e, EPKState(result, null, null)) + if (previous eq null) { + triggerComputations(e, pkId) + result.asInstanceOf[FinalEP[E, P]] + } else { + previous.eOptP.asInstanceOf[EOptionP[E, P]] } - epkState.dependees = Set(sourceEOptionP) + } else { + val newState = EPKState(epk, d ⇒ new Result(transformer._2(e, d.asFinal.p)), Set(dependee)) + val previous = ps(pkId).putIfAbsent(e, newState) + if (previous eq null) { + updateDependees(newState, Set(dependee)) + epk + } else { + previous.eOptP.asInstanceOf[EOptionP[E, P]] + } + } + } else { + val previous = ps(pkId).putIfAbsent(e, EPKState(epk, null, null)) + if (previous eq null) { + epk + } else { + previous.eOptP.asInstanceOf[EOptionP[E, P]] } - } while (!sourceEPKState.addDepender(sourceEOptionP, epk, alwaysExceptIfFinal = true)) - if (tracer.isDefined) tracer.get.registeredTransformer(sourceEPKState, epkState) + } + } else { + val finalEP = computeFallback[E, P](e, pkId) + val previous = ps(pkId).putIfAbsent(e, EPKState(finalEP, null, null)) + if (previous eq null) { + triggerComputations(e, pkId) + finalEP + } else { + previous.eOptP.asInstanceOf[EOptionP[E, P]] + } } - epk } else { - // ... we have no lazy computation - // ... the property is also not computed - val finalEP = computeFallback[E, P](e, pkId) - if (tracer.isDefined) tracer.get.computedFallback(finalEP, "doApply call") - handleFinalResult(finalEP, potentiallyIdemPotentUpdate = true) - finalEP + current.eOptP.asInstanceOf[EOptionP[E, P]] } } - // NOTES REGARDING CONCURRENCY - // Before a continuation function is called a depender has to be removed from - // its dependees! - private[par] def removeDependerFromDependeesAndClearDependees( - dependerEPK: SomeEPK, - dependerEPKState: EPKState - ): Unit = { - val dependees = dependerEPKState.dependees - dependees foreach { dependee ⇒ - val dependeeEPKState = properties(dependee.pk.id).get(dependee.e) - dependeeEPKState.removeDepender(dependerEPK) - } - dependerEPKState.clearDependees() - } + private[this] val activeTasks = new AtomicInteger(0) + private[this] val threads: Array[PKECThread] = Array.fill(THREAD_COUNT) { null } - private[this] def notifyDepender( - dependerEPK: SomeEPK, - oldEOptionP: SomeEOptionP, - newEPS: SomeEPS - ): Unit = { - val dependerPKId = dependerEPK.pk.id - val dependerEPKState = properties(dependerPKId).get(dependerEPK.e) - if (dependerPKId == AnalysisKeyId && dependerEPKState == null) - // Recall that we delete dependerEPKState objects when they are not longer - // required; i.e., after they were triggered - return ; - - assert(dependerEPKState != null, s"EPKState object went missing: $dependerEPK") - - val cOption = dependerEPKState.prepareInvokeC(oldEOptionP) - if (cOption.isDefined) { - // We first have to remove the depender from the dependees before - // we can fork the computation of the continuation function. - removeDependerFromDependeesAndClearDependees(dependerEPK, dependerEPKState) - if (tracer.isDefined) - tracer.get.scheduledOnUpdateComputation(dependerEPK, oldEOptionP, newEPS, cOption.get) - if (newEPS.isFinal) - forkOnUpdateContinuation(cOption.get, newEPS.asFinal) - else - forkOnUpdateContinuation(cOption.get, newEPS.e, newEPS.pk) + private[this] def startThreads(thread: Int ⇒ PKECThread): Unit = { + var tId = 0 + while (tId < THREAD_COUNT) { + val t = thread(tId) + threads(tId) = t + tId += 1 } - if (dependerEPK.pk == AnalysisKey) { - properties(AnalysisKey.id).remove(dependerEPK.e) + threads.foreach { _.start() } + threads.foreach { _.join } + if (doTerminate) { + if (exception ne null) throw exception + else throw new InterruptedException } } - private[this] def handleFinalResult( - finalEP: SomeFinalEP, - potentiallyIdemPotentUpdate: Boolean = false - ): Unit = { - val e = finalEP.e - val pkId = finalEP.pk.id - val oldEPKState = properties(pkId).put(e, EPKState(finalEP)) - var invokeTriggeredComputations = true - if (oldEPKState != null) { - if (oldEPKState.isFinal) { - if (oldEPKState.eOptionP == finalEP && potentiallyIdemPotentUpdate) { - if (tracer.isDefined) - tracer.get.idempotentUpdate(properties(pkId).get(e)) - return ; // IDEMPOTENT UPDATE - } else - throw new IllegalStateException( - s"already final: $oldEPKState; illegal property update: $finalEP)" - ) - } + override def waitOnPhaseCompletion(): Unit = { + idle = false - if (debug) oldEPKState.eOptionP.checkIsValidPropertiesUpdate(finalEP, Nil) - - // Recall that we do not clear the dependees eagerly when we have to register a transformer - // DOESN'T WORK: assert(oldState.dependees.isEmpty) - - // We have to update the value of the oldEPKState to ensure that clients that - // want to register a depender see the most current value. - val oldEOptionP = oldEPKState.eOptionP - invokeTriggeredComputations = oldEOptionP.isEPK - val dependers = oldEPKState.finalUpdate(finalEP) - // We now have to inform the dependers. - // We can simply inform all dependers because is it guaranteed that they have not seen - // the final value since dependencies on final values are not allowed! - // However, it is possible that the depender is actually no longer interested in the - // update, which is checked for by notifyDepender. - dependers.foreach(epk ⇒ notifyDepender(epk, oldEOptionP, finalEP)) - } - if (invokeTriggeredComputations) { - triggerComputations(e, pkId) + // If some values were explicitly set, we have to trigger corresponding triggered + // computations. + setAndPreinitializedValues.foreach { epk ⇒ triggerComputations(epk.e, epk.pk.id) } + setAndPreinitializedValues = List.empty + + activeTasks.addAndGet(initialQueues.map(_.size()).sum) + + while (subPhaseId < subPhaseFinalizationOrder.length) { + var continueCycles = false + do { + var continueFallbacks = false + do { + startThreads(new WorkerThread(_)) + + quiescenceCounter += 1 + + startThreads(new FallbackThread(_)) + + continueFallbacks = activeTasks.get() != 0 //!queues.forall(_.isEmpty) + } while (continueFallbacks) + + startThreads(new CycleResolutionThread(_)) + + resolveCycles() + + continueCycles = activeTasks.get() != 0 //!queues.forall(_.isEmpty) + } while (continueCycles) + + startThreads(new PartialPropertiesFinalizerThread(_)) + + subPhaseId += 1 + ps(AnalysisKeyId).clear() } + + idle = true } - // NOTES REGARDING CONCURRENCY - // W.r.t. one EPK there may be multiple executions of this method concurrently! - private[this] def handleInterimResult( - interimEP: SomeInterimEP, - c: OnUpdateContinuation, - dependees: Traversable[SomeEOptionP] - ): Unit = { - val interimEPKId = interimEP.pk.id - val psPerKind = properties(interimEPKId) - - // 0. Get EPKState object. - var epkStateUpdateRequired = true - val epkState = { - var epkState = psPerKind.get(interimEP.e) - if (epkState == null) { - val newEPKState = EPKState(interimEP, c, dependees) - epkState = psPerKind.putIfAbsent(interimEP.e, newEPKState) - if (epkState == null) { - epkStateUpdateRequired = false - newEPKState - } else { - epkState - } - } else { - epkState - } + private[this] val interimStates: Array[ArrayBuffer[EPKState]] = + Array.fill(THREAD_COUNT)(null) + private[this] val successors: Array[EPKState ⇒ Traversable[EPKState]] = + Array.fill(THREAD_COUNT)(null) + + // executed on the main thread only + private[this] def resolveCycles(): Unit = { + val theInterimStates = new ArrayBuffer[EPKState](interimStates.iterator.map(_.size).sum) + var tId = 0 + while (tId < THREAD_COUNT) { + theInterimStates ++= interimStates(tId) + tId += 1 } - // 1. Update the property if necessary. - // Though we can have concurrent executions of this method, it is still always - // the case that we will only see monotonic updates; i.e., this part of this - // method is never executed concurrently; only the first part may be executed - // concurrently with the second part. - val eOptionPWithDependersOption: Option[(SomeEOptionP, Traversable[SomeEPK])] = - if (epkStateUpdateRequired) { - epkState.update( - interimEP, c, dependees, - hasSuppressedDependers, suppressInterimUpdates, debug - ) - } else { - None + val theSuccessors = (interimEPKState: EPKState) ⇒ { + successors(getResponsibleTId(interimEPKState.eOptP.e))(interimEPKState) + } + + val cSCCs = graphs.closedSCCs(theInterimStates, theSuccessors) + + for (cSCC ← cSCCs) { + for (interimEPKState ← cSCC) { + interimEPKState.dependees = null + scheduleTask(new SetTask(interimEPKState.eOptP.toFinalEP)) } + } + } - // ATTENTION: - - - - - - - - - - - H E R E A R E D R A G O N S - - - - - - - - - - - - // As soon as we register with the first dependee, we can have concurrent - // updates, which (in an extreme case) can already be completely finished - // between every two statements of this method! That is, the dependees - // and the continuation function given to this method may already be outdated! - // Hence, before we call the given OnUpdateContinuation due to an updated - // dependee, we have to check if it is still the current one! - - // 2. Register with dependees (as depender) and while doing so check if the value - // was updated. - // We stop the registration with the dependees when the continuation function - // is triggered. - val dependerEPK = interimEP.toEPK - val dependerPKId = dependerEPK.pk.id - val suppressInterimUpdatesOf = this.suppressInterimUpdates(dependerPKId) - dependees forall { processedDependee ⇒ - epkState.isCurrentC(c) /* <= this is just an optimization! */ && { - val processedDependeePKId = processedDependee.pk.id - val psPerDependeeKind = properties(processedDependeePKId) - val dependeeEPKState = psPerDependeeKind.get(processedDependee.e) - val dependerAdded = - dependeeEPKState.addDepender( - processedDependee, - dependerEPK, - alwaysExceptIfFinal = suppressInterimUpdatesOf(processedDependeePKId) - ) - if (!dependerAdded) { - if (debug && dependeeEPKState.isEPK) { - throw new IllegalArgumentException( - dependees.mkString( - "the dependees contains at least one externally constructed epk: ", - ", ", - "" - ) - ) - } - // addDepender failed... i.e., the dependee was updated... - if (epkState.prepareInvokeC(c)) { - // we now remove _our_ dependee registrations... - dependees forall /* <= forall is just used to limit the iteration to the relevant dependees */ { registeredDependee ⇒ - if (registeredDependee ne processedDependee) { - val registeredDependeeEPKState = - properties(registeredDependee.pk.id).get(registeredDependee.e) - registeredDependeeEPKState.removeDepender(dependerEPK) - if (tracer.isDefined) - tracer.get.removedDepender(dependerEPK, registeredDependeeEPKState) - true + class PKECThread(name: String) extends Thread(name) + + class WorkerThread(ownTId: Int) extends PKECThread(s"PropertyStoreThread-#$ownTId") { + + override def run(): Unit = { + try { + val initialTasks = initialQueues(ownTId) + val initialTaskSize = initialTasks.size() + var curInitialTask: QualifiedTask = null + while ({ curInitialTask = initialTasks.poll(); curInitialTask != null }) { + curInitialTask.apply() + } + activeTasks.addAndGet(-initialTaskSize) + val tasksQueue = queues(ownTId) + val tasks = new ArrayDeque[QualifiedTask](50000 / THREAD_COUNT) + while (!doTerminate) { + tasksQueue.drainTo(tasks) + if (tasks.isEmpty) { + val active = activeTasks.get() + if (active == 0) { + return ; + } else { + // try workstealing: + val largestQueue = queues.maxBy(_.size()) + val largestQueueSize = largestQueue.size() + if (largestQueueSize > 100) { + largestQueue.drainTo(tasks, largestQueueSize / (THREAD_COUNT + 1)) } else { - false + val nextTask = tasksQueue.take() + if (!doTerminate) { + nextTask.apply() + activeTasks.decrementAndGet() + } } } - if (tracer.isDefined) - tracer.get.immediatelyRescheduledOnUpdateComputation( - dependerEPK, - processedDependee, - dependeeEPKState.eOptionP, - c - ) - forkOnUpdateContinuation(c, processedDependee.e, processedDependee.pk) } else { - // Clear possibly dangling dependees... which can happen if - // we have registered with a dependee which is updated while - // we still process the current dependees; based on the result - // of running "c" the set of dependees is changed - // when compared to this dependees. Now, we have to remove - // those dependees that are no longer relevant. - // Basically, we check for each dependee if the dependee is still - // relevant. If not, we remove it. Note that a client is NOT - // allowed to ever reregister a dependency. I.e., a client can change - // the set of dependees, but the new set must never contain a dependee - // – w.r.t. the underlying EPK – which was already a dependee but which - // is not part of the directly preceding set. - val currentDependees = epkState.dependees - dependees forall { registeredDependee ⇒ - if (registeredDependee ne processedDependee) { - if (currentDependees == null || - currentDependees.forall(_ != registeredDependee)) { - val registeredDependeeEPKState = - properties(registeredDependee.pk.id).get(registeredDependee.e) - registeredDependeeEPKState.removeDepender(dependerEPK) - if (tracer.isDefined) - tracer.get.removedDepender(dependerEPK, registeredDependeeEPKState) - } - true - } else { - false + var curTask: QualifiedTask = null + while ({ curTask = tasks.poll(); curTask != null } && !doTerminate) { + curTask.apply() + activeTasks.decrementAndGet() + } + } + } + } catch { + case ct: ControlThrowable ⇒ throw ct + case _: InterruptedException ⇒ + case ex: Throwable ⇒ + exception = ex + doTerminate = true + } finally { + threads.foreach { t ⇒ + if (t ne this) + t.interrupt() + } + } + } + } + + class FallbackThread(ownTId: Int) extends PKECThread(s"PropertyStoreFallbackThread-#$ownTId") { + + override def run(): Unit = { + var pkId = 0 + while (pkId <= PropertyKey.maxId) { + if (propertyKindsComputedInThisPhase(pkId) && (lazyComputations(pkId) eq null)) { + ps(pkId).forEachValue(Long.MaxValue, { epkState: EPKState ⇒ + if (epkState.eOptP.isEPK && ((epkState.dependees eq null) || epkState.dependees.isEmpty)) { + val e = epkState.eOptP.e + if (getResponsibleTId(e) == ownTId) { + val reason = PropertyIsNotDerivedByPreviouslyExecutedAnalysis + val p = fallbackPropertyBasedOnPKId(propertyStore, reason, e, pkId) + val finalEP = FinalEP(e, p) + incrementFallbacksUsedForComputedPropertiesCounter() + handleFinalResult(finalEP) } } + }) + } + pkId += 1 + } + } + } + + class CycleResolutionThread(ownTId: Int) extends PKECThread(s"PropertyStoreCycleResolutionThread-#$ownTId") { + + override def run(): Unit = { + val localInterimStates = ArrayBuffer.empty[EPKState] + var pkId = 0 + while (pkId <= PropertyKey.maxId) { + if (propertyKindsComputedInThisPhase(pkId)) { + ps(pkId).forEachValue(Long.MaxValue, { epkState: EPKState ⇒ + val eOptP = epkState.eOptP + if (eOptP.isRefinable && getResponsibleTId(eOptP.e) == ownTId) { + localInterimStates.append(epkState) + } + }) + } + pkId += 1 + } + interimStates(ownTId) = localInterimStates + + successors(ownTId) = (interimEPKState: EPKState) ⇒ { + val dependees = interimEPKState.dependees + if (dependees != null) { + dependees.map { eOptionP ⇒ + ps(eOptionP.pk.id).get(eOptionP.e) } - false } else { - true + Traversable.empty } } } + } - // 3. Notify dependers if required - if (eOptionPWithDependersOption.isDefined) { - val (oldEOptionP, dependers) = eOptionPWithDependersOption.get - if (oldEOptionP.isEPK) triggerComputations(interimEP.e, interimEPKId) - dependers.foreach(epk ⇒ notifyDepender(epk, oldEOptionP, interimEP)) - } else { - if (!epkStateUpdateRequired /* <=> we created a new EPKState with an intermediate property */ ) - triggerComputations(interimEP.e, interimEPKId) + class PartialPropertiesFinalizerThread(ownTId: Int) extends PKECThread(s"PropertyStorePartialPropertiesFinalizerThread-#$ownTId") { + + override def run(): Unit = { + val pksToFinalize = subPhaseFinalizationOrder(subPhaseId).toSet + + pksToFinalize foreach { pk ⇒ + val pkId = pk.id + ps(pkId).forEachValue(Long.MaxValue, { epkState: EPKState ⇒ + val eOptP = epkState.eOptP + if (getResponsibleTId(eOptP.e) == ownTId && eOptP.isRefinable && !eOptP.isEPK) //TODO Won't be required once subPhaseFinalizationOrder is reliably only the partial properties + handleFinalResult(eOptP.toFinalEP, pksToFinalize) + }) + } } } - // NOTES REGARDING CONCURRENCY - // W.r.t. one EPK there may be multiple executions of this method concurrently! - private[this] def handlePartialResult( - e: Entity, - pk: SomePropertyKey, - u: UpdateComputation[_ <: Entity, _ <: Property] - ): Unit = { - val pkId = pk.id - val psPerKind = properties(pkId) - - // 0. Get EPKState object. - val epk = EPK(e, pk) - val newEPKState = EPKState(epk) - var epkState = psPerKind.putIfAbsent(e, newEPKState) - if (epkState == null) epkState = newEPKState - - // 1. Update the property if necessary. - val interimEPWithDependersOption = - epkState.update(u, hasSuppressedDependers, suppressInterimUpdates) - handlePartialResultUpdate(epkState, interimEPWithDependersOption) + trait QualifiedTask extends (() ⇒ Unit) with Comparable[QualifiedTask] { + val priority: Int + + override def compareTo(other: QualifiedTask): Int = priority - other.priority } - private[this] def handlePartialResultUpdate( - epkState: EPKState, - interimEPWithDependersOption: Option[(SomeEOptionP, SomeInterimEP, Traversable[SomeEPK])] - ): Unit = { - if (tracer.isDefined) - tracer.get.appliedUpdateComputation(epkState, interimEPWithDependersOption) - - // 2. Notify relevant dependers - if (interimEPWithDependersOption.isDefined) { - val (oldEOptionP, newInterimEP, dependers) = interimEPWithDependersOption.get - if (oldEOptionP.isEPK) triggerComputations(oldEOptionP.e, oldEOptionP.pk.id) - dependers.foreach(epk ⇒ notifyDepender(epk, oldEOptionP, newInterimEP)) + class ExecuteTask(f: ⇒ Unit) extends QualifiedTask { + val priority = 0 + + override def apply(): Unit = { + f } } - private[par] def processResult(r: PropertyComputationResult): Unit = handleExceptions { + class SetTask[E <: Entity, P <: Property]( + finalEP: FinalEP[E, P] + ) extends QualifiedTask { + val priority = 0 - if (tracer.isDefined) - tracer.get.processingResult(r) + override def apply(): Unit = { + handleFinalResult(finalEP) + } + } - r.id match { + class PropertyComputationTask[E <: Entity]( + e: E, + pc: PropertyComputation[E] + ) extends QualifiedTask { + val priority = 0 - case NoResult.id ⇒ { - // A computation reported no result; i.e., it is not possible to - // compute a/some property/properties for a given entity. + override def apply(): Unit = { + handleResult(pc(e)) + } + } + + class LazyComputationTask[E <: Entity]( + e: E, + pc: PropertyComputation[E], + pkId: Int + ) extends QualifiedTask { + val priority = 0 + + override def apply(): Unit = { + val state = ps(pkId).get(e) + state.synchronized { + if (state.eOptP.isEPK) + handleResult(pc(e)) } + } + } - // - // Result containers - // + class ContinuationTask( + depender: EPKState, oldDependee: SomeEOptionP, dependee: EPKState + ) extends QualifiedTask { + scheduledOnUpdateComputations.incrementAndGet() - case Results.id ⇒ r.asResults.foreach { processResult } + val priority: Int = taskManager.weight(depender, dependee) - case IncrementalResult.id ⇒ - val IncrementalResult(ir, npcs) = r - processResult(ir) - npcs /*: Iterator[(PropertyComputation[e],e)]*/ foreach { npc ⇒ - val (pc, e) = npc - schedulePropertyComputation(e, pc) - } + override def apply(): Unit = { + depender.applyContinuation(oldDependee) + } + } - // - // Methods which actually store results... - // + private[this] def getResponsibleTId(e: Entity): Int = { + Math.abs(e.hashCode() >> 5) % THREAD_COUNT + } +} - case Result.id ⇒ - val Result(finalEP) = r - handleFinalResult(finalEP) +case class EPKState( + var eOptP: SomeEOptionP, + var c: OnUpdateContinuation, + var dependees: Set[SomeEOptionP], + dependers: java.util.HashSet[EPKState] = new java.util.HashSet(), + suppressedDependers: java.util.HashSet[EPKState] = new java.util.HashSet() +) { - case MultiResult.id ⇒ - val MultiResult(results) = r - results foreach { finalEP ⇒ handleFinalResult(finalEP) } + override lazy val hashCode: Int = eOptP.hashCode() - case InterimResult.id ⇒ - val interimR = r.asInterimResult - handleInterimResult( - interimR.eps, - interimR.c, - interimR.dependees - ) - - case PartialResult.id ⇒ - val PartialResult(e, pk, u) = r - handlePartialResult(e, pk, u) + override def equals(obj: Any): Boolean = obj match { + case other: EPKState ⇒ eOptP == other.eOptP + case _ ⇒ false + } - case InterimPartialResult.id ⇒ - val InterimPartialResult(prs, dependees, c) = r + def setFinal(finalEP: FinalEP[Entity, Property], unnotifiedPKs: Set[PropertyKind])(implicit ps: PKECPropertyStore): Unit = { + var theEOptP: SomeEOptionP = null + this.synchronized { + theEOptP = eOptP + if (theEOptP.isFinal) { + throw new IllegalStateException(s"${theEOptP.e} already had the property $theEOptP") + } else { + if (ps.debug) eOptP.checkIsValidPropertiesUpdate(finalEP, Nil) + dependers.synchronized { + eOptP = finalEP - if (debug && dependees.isEmpty) { - throw new IllegalArgumentException(s"interim partial result $r without dependees") + notifyAndClearDependers(theEOptP, dependers, unnotifiedPKs) + notifyAndClearDependers(finalEP, suppressedDependers, unnotifiedPKs) } + } + dependees = null + } - // 1. Handle partial results. - prs foreach { pr ⇒ handlePartialResult(pr.e, pr.pk, pr.u) } - - // 2. Register with dependees (as depender) and while doing so check if the value - // was updated. - // We stop the registration with the dependees when the continuation function - // is triggered. - val dependerE = new FakeEntity() - val dependerEPK = EPK(dependerE, AnalysisKey) - val dependerPKId = AnalysisKey.id - val dependerEPKState = EPKState(dependerEPK, c, dependees) - properties(dependerPKId).put(dependerE, dependerEPKState) - dependees forall { processedDependee ⇒ - assert(processedDependee.isRefinable) - dependerEPKState.isCurrentC(c) /* <= this is just an optimization! */ && { - val processedDependeePKId = processedDependee.pk.id - val psPerDependeeKind = properties(processedDependeePKId) - val dependeeEPKState = psPerDependeeKind.get(processedDependee.e) - val dependerAdded = - dependeeEPKState.addDepender( - processedDependee, - dependerEPK, - alwaysExceptIfFinal = false - ) - if (!dependerAdded) { - // addDepender failed... i.e., the dependee was updated... - if (dependerEPKState.prepareInvokeC(c)) { - if (tracer.isDefined) - tracer.get.immediatelyRescheduledOnUpdateComputation( - dependerEPK, - processedDependee, - dependeeEPKState.eOptionP, - c - ) - forkOnUpdateContinuation(c, processedDependee.e, processedDependee.pk) - } - // We now remove our dependee registrations; given that we use - // each fake entity only once, there is no danger that we remove - // wrong depender registrations. - dependees forall { registeredDependee ⇒ - if (registeredDependee ne processedDependee) { - val registeredDependeeEPKState = - properties(registeredDependee.pk.id).get(registeredDependee.e) - registeredDependeeEPKState.removeDepender(dependerEPK) - if (tracer.isDefined) - tracer.get.removedDepender(dependerEPK, registeredDependeeEPKState) - true - } else { - false - } - } - false - } else { - true - } + if (theEOptP.isEPK) ps.triggerComputations(theEOptP.e, theEOptP.pk.id) + } + + def interimUpdate( + interimEP: InterimEP[Entity, Property], + newC: OnUpdateContinuation, + newDependees: Set[SomeEOptionP] + )(implicit ps: PKECPropertyStore): Unit = { + + var theEOptP: SomeEOptionP = null + this.synchronized { + theEOptP = eOptP + if (theEOptP.isFinal) { + throw new IllegalStateException(s"${theEOptP.e} already had the property $theEOptP") + } else { + if (ps.debug) theEOptP.checkIsValidPropertiesUpdate(interimEP, newDependees) + if (interimEP.isUpdatedComparedTo(theEOptP)) { + dependers.synchronized { + eOptP = interimEP + + notifyAndClearDependers(theEOptP, dependers) } } - + c = newC + dependees = newDependees + } } - } - override def waitOnPhaseCompletion(): Unit = { - phaseSetupCompleted() + ps.updateDependees(this, newDependees) - handleExceptions { - val maxPKIndex = PropertyKey.maxId + if (theEOptP.isEPK) ps.triggerComputations(theEOptP.e, theEOptP.pk.id) + } - // If some values were explicitly set, we have to trigger corresponding triggered - // computations. - setAndPreinitializedValues.foreach { epk ⇒ triggerComputations(epk.e, epk.pk.id) } - setAndPreinitializedValues = List.empty + def partialUpdate(updateComputation: UpdateComputation[Entity, Property])(implicit ps: PKECPropertyStore): Unit = { + var theEOptP: SomeEOptionP = null - val continueComputation = new AtomicBoolean(false) - do { - if (tracer.isDefined) tracer.get.startedMainLoop() - - continueComputation.set(false) - - triggerAndClearForcedEPKs() // Ignored the return value because we call awaitPoolQuiescence next - - awaitPoolQuiescence() - if (tracer.isDefined) tracer.get.reachedQuiescence() - - quiescenceCounter += 1 - if (debug) trace("analysis progress", s"reached quiescence $quiescenceCounter") - - // We have reached quiescence.... - - // 1. Let's search for all EPKs (not EPS) and use the fall back for them. - // (Please note that FakeEntities – related to InterimPartialResults – - // which are associated with the "FakeAnalysisKey", are not handled here.) - // (Recall that we return fallback properties eagerly if no analysis is - // scheduled or will be scheduled, However, it is still possible that we will - // not have computed a property for a specific entity if the underlying - // analysis doesn't compute one; in that case we need to put in fallback - // values.) - var pkIdIterator = 0 - while (pkIdIterator <= maxPKIndex) { - if (propertyKindsComputedInThisPhase(pkIdIterator)) { - val pkId = pkIdIterator - parallelize { - val epkStateIterator = - properties(pkId) - .values.iterator().asScala - .filter { epkState ⇒ - epkState.isEPK && - // There is no suppression; i.e., we have no dependees - epkState.dependees.isEmpty - } - if (epkStateIterator.hasNext) continueComputation.set(true) - epkStateIterator.foreach { epkState ⇒ - // TODO TRACE: println("State without dependees: "+epkState) - val e = epkState.e - val reason = PropertyIsNotDerivedByPreviouslyExecutedAnalysis - val p = fallbackPropertyBasedOnPKId(this, reason, e, pkId) - if (traceFallbacks) { - trace("analysis progress", s"used fallback $p for $e") - } - val finalEP = FinalEP(e, p) - if (tracer.isDefined) - tracer.get.computedFallback( - finalEP, - "the analysis didn't compute the property" - ) - incrementFallbacksUsedForComputedPropertiesCounter() - handleFinalResult(finalEP) - } - } - } - pkIdIterator += 1 - } - awaitPoolQuiescence() - - // 2. (Handle suppression) - // Let's search for entities with interim properties where some dependers - // were not yet notified about intermediate updates. In this case, the - // current results of the dependers cannot be finalized; instead, we need - // to finalize (the cyclic dependent) dependees first and notify the - // dependers. - // Recall, that collaboratively computed properties are not allowed to be - // part of a cyclic computation if we also have suppressed notifications. - if (!continueComputation.get() && hasSuppressedNotifications) { - // Collect all InterimEPs to find cycles. - val interimEPKStates = ArrayBuffer.empty[EPKState] - var pkId = 0 - while (pkId <= maxPKIndex) { - if (propertyKindsComputedInThisPhase(pkId)) { - properties(pkId).values.forEach { epkState ⇒ - if (epkState.isRefinable) interimEPKStates += epkState - } - } - pkId += 1 - } - val successors = (interimEPKState: EPKState) ⇒ { - val dependees = interimEPKState.dependees - if (dependees != null) { - interimEPKState.dependees.map(eOptionP ⇒ properties(eOptionP.pk.id).get(eOptionP.e)) - } else { - Traversable.empty - } - } - val cSCCs = graphs.closedSCCs(interimEPKStates, successors) - if (tracer.isDefined) { - tracer.get.handlingInterimEPKsDueToSuppression( - interimEPKStates.map(_.toString).mkString("[\n\t", ",\n\t", "]"), - cSCCs - .map(_.mkString(" cSCC=[\n\t\t", ",\n\t\t", "]")) - .mkString("[\n\t", "\n\t", "]") - ) - } - continueComputation.set(cSCCs.nonEmpty) - for (cSCC ← cSCCs) { - // Clear all dependees of all members of a cycle to avoid inner cycle - // notifications! - for (interimEPKState ← cSCC) { - removeDependerFromDependeesAndClearDependees( - interimEPKState.eOptionP.toEPK, interimEPKState - ) - } - // 2. set all values - for (interimEPKState ← cSCC) { - if (tracer.isDefined) { - tracer.get.makingIntermediateEPKStateFinal(interimEPKState) - } - handleFinalResult(interimEPKState.eOptionP.toFinalEP) - } + this.synchronized { + theEOptP = eOptP + updateComputation(theEOptP) match { + case Some(interimEP) ⇒ + if (ps.debug) assert(eOptP != interimEP) + dependers.synchronized { + eOptP = interimEP + + notifyAndClearDependers(theEOptP, dependers) } - } + interimEP + case _ ⇒ + null + } + } + + if (theEOptP.isEPK) ps.triggerComputations(theEOptP.e, theEOptP.pk.id) + } - if (triggerAndClearForcedEPKs() /* forces the evaluation as a side-effect */ ) { - awaitPoolQuiescence() - continueComputation.set(true) + def addDependerOrScheduleContinuation( + depender: EPKState, + dependee: SomeEOptionP, + dependeePK: Int, + suppressedPKs: Array[Boolean] + )(implicit ps: PKECPropertyStore): Boolean = { + dependers.synchronized { + val theEOptP = eOptP + // If the epk state is already updated (compared to the given dependee) + // AND that update must not be suppressed (either final or not a suppressed PK). + val isSuppressed = suppressedPKs(dependeePK) + if ((theEOptP ne dependee) && (!isSuppressed || theEOptP.isFinal)) { + if (isSuppressed) + ps.scheduleTask(new ps.ContinuationTask(depender, theEOptP, this)) + else + ps.scheduleTask(new ps.ContinuationTask(depender, dependee, this)) + false + } else { + if (isSuppressed) { + suppressedDependers.add(depender) + } else { + dependers.add(depender) } + true + } + } + } - // 3. Let's finalize remaining interim EPS; e.g., those related to - // collaboratively computed properties or "just all" if we don't have suppressed - // notifications. Recall that we may have cycles if we have no suppressed - // notifications, because in the latter case, we may have dependencies. - // We used no fallbacks, but we may still have collaboratively computed properties - // (e.g. CallGraph) which are not yet final; let's finalize them in the specified - // order (i.e., let's finalize the subphase)! - while (!continueComputation.get() && subPhaseId < subPhaseFinalizationOrder.length) { - val pksToFinalize = subPhaseFinalizationOrder(subPhaseId) - if (debug) { - trace( - "analysis progress", - pksToFinalize.map(PropertyKey.name).mkString("finalization of: ", ", ", "") - ) - } - if (tracer.isDefined) { - tracer.get.subphaseFinalization( - pksToFinalize.map(PropertyKey.name).mkString("finalization of: ", ", ", "") - ) - } - // The following will also kill dependers related to anonymous computations using - // the generic property key: "AnalysisKey"; i.e., those without explicit properties! - properties.foreach { psPerKind ⇒ - val eps = psPerKind.values() - if (!eps.isEmpty) { - parallelize { - eps.forEach { _.cleanUp(pksToFinalize) } - } - } - } - /* - pksToFinalize foreach { pk ⇒ - val propertyKey = PropertyKey.key(pk.id) - parallelize { - val dependeesIt = properties(pk.id).elements().asScala.filter(_.hasDependees) - if (dependeesIt.hasNext) continueComputation.set(true) - dependeesIt foreach { epkState ⇒ - val dependerEPK = EPK(epkState.e, propertyKey) - removeDependerFromDependeesAndClearDependees( - dependerEPK, - properties(dependerEPK.pk.id).get(dependerEPK.e) - ) - } - } - } - */ - awaitPoolQuiescence() - - pksToFinalize foreach { pk ⇒ - parallelize { - if (pk == AnalysisKey) { - assert( - properties(pk.id).values.asScala.forall(!_.hasDependees), - properties(pk.id).values.asScala. - filter(_.hasDependees). - mkString("fake entities with unexpected dependencies: [", ",", "]") - ) - properties(pk.id) = new ConcurrentHashMap() - } else { - val interimEPSStates = properties(pk.id).values.asScala.filter(_.isRefinable) - interimEPSStates foreach { interimEPKState ⇒ - val oldEOptionP = interimEPKState.eOptionP - val finalEP = oldEOptionP.toFinalEP - if (tracer.isDefined) - tracer.get.finalizedProperty(oldEOptionP, finalEP) - handleFinalResult(finalEP) - } - } - } - } - awaitPoolQuiescence() + def removeDepender(dependerState: EPKState): Unit = { + dependers.synchronized { + dependers.remove(dependerState) + suppressedDependers.remove(dependerState) + } + } - if (triggerAndClearForcedEPKs()) { - awaitPoolQuiescence() - continueComputation.set(true) - } + def notifyAndClearDependers( + oldEOptP: SomeEOptionP, + theDependers: java.util.HashSet[EPKState], + unnotifiedPKs: Set[PropertyKind] = Set.empty + )(implicit ps: PKECPropertyStore): Unit = { + theDependers.forEach { dependerState ⇒ + if (!unnotifiedPKs.contains(dependerState.eOptP.pk) && dependerState.dependees != null) { + ps.scheduleTask(new ps.ContinuationTask(dependerState, oldEOptP, this)) + } + } - subPhaseId += 1 - } - } while (continueComputation.get()) + // Clear all dependers that will be notified, they will re-register if required + theDependers.clear() + } - // TODO assert that we don't have any more InterimEPKStates + def applyContinuation(oldDependee: SomeEOptionP)(implicit ps: PKECPropertyStore): Unit = { + this.synchronized { + val theDependees = dependees + // Are we still interested in that dependee? + if (theDependees != null && + (oldDependee.isFinal || theDependees.contains(oldDependee))) { + // We always retrieve the most up-to-date state of the dependee. + val currentDependee = ps.ps(oldDependee.pk.id).get(oldDependee.e).eOptP.asEPS + // IMPROVE: If we would know about ordering, we could only perform the operation + // if the given value of the dependee is actually the "newest". + ps.handleResult(c(currentDependee)) + } } - if (exception != null) throw exception; } +} + +trait PKECTaskManager { + def weight( + depender: EPKState, // The state to be updated + dependee: EPKState // The dependee that triggered this update + ): Int +} + +object PKECTaskManager { + def dependeesCount(depender: EPKState): Int = { + val dependerDependees = if (depender == null) null else depender.dependees + if (dependerDependees == null) 0 else dependerDependees.size + } + + def dependersCount(dependee: EPKState): Int = { + dependee.dependers.size() + dependee.suppressedDependers.size() + } +} + +case object PKECNoPriorityTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(0) + + override def weight( + depender: EPKState, + dependee: EPKState + ): Int = { + 0 + } +} + +case object PKECFIFOTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(0) + + override def weight( + depender: EPKState, + dependee: EPKState + ): Int = { + counter.getAndIncrement() + } +} + +case object PKECLIFOTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) + + override def weight( + depender: EPKState, + dependee: EPKState + ): Int = { + counter.getAndDecrement() + } +} + +case object PKECManyDependeesFirstTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) + + override def weight( + depender: EPKState, + dependee: EPKState + ): Int = { + -PKECTaskManager.dependeesCount(depender) + } +} + +case object PKECManyDependeesLastTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) + + override def weight( + depender: EPKState, + dependee: EPKState + ): Int = { + PKECTaskManager.dependeesCount(depender) + } +} + +case object PKECManyDependersFirstTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) + + override def weight( + depender: EPKState, + dependee: EPKState + ): Int = { + -PKECTaskManager.dependersCount(dependee) + } +} + +case object PKECManyDependersLastTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) + + override def weight( + depender: EPKState, + dependee: EPKState + ): Int = { + PKECTaskManager.dependersCount(dependee) + } +} + +case object PKECManyDependenciesFirstTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) - override protected[this] def onFirstException(t: Throwable): Unit = { - super.onFirstException(t) - if (tracer.isDefined) tracer.get.firstException(t) + override def weight( + depender: EPKState, + dependee: EPKState + ): Int = { + + -(Math.max(1, PKECTaskManager.dependersCount(dependee)) * + Math.max(PKECTaskManager.dependeesCount(depender), 1)) } +} + +case object PKECManyDependenciesLastTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) - private[par] def propagateExceptions(): Unit = { - val exception = this.exception - if (exception != null) throw exception; + override def weight( + depender: EPKState, + dependee: EPKState + ): Int = { + Math.max(1, PKECTaskManager.dependersCount(dependee)) * + Math.max(PKECTaskManager.dependeesCount(depender), 1) } +} +private class FakeEntity { + override def toString: String = "FakeEntity" } object PKECPropertyStore extends PropertyStoreFactory[PKECPropertyStore] { - final val TasksManagerKey = "org.opalj.fpcf.par.PKECPropertyStore.TasksManager" final val MaxEvaluationDepthKey = "org.opalj.fpcf.par.PKECPropertyStore.MaxEvaluationDepth" - final val Strategies = List( - "Seq", - "Par" - ) + @volatile var MaxThreads: Int = org.opalj.concurrent.NumberOfThreadsForCPUBoundTasks def apply( context: PropertyStoreContext[_ <: AnyRef]* @@ -1020,48 +1033,16 @@ object PKECPropertyStore extends PropertyStoreFactory[PKECPropertyStore] { logContext: LogContext ): PKECPropertyStore = { val contextMap: Map[Class[_], AnyRef] = context.map(_.asTuple).toMap + val config = contextMap.get(classOf[Config]) match { case Some(config: Config) ⇒ config case _ ⇒ org.opalj.BaseConfig } - val taskManagerId = config.getString(TasksManagerKey) - val maxEvaluationDepth = config.getInt(MaxEvaluationDepthKey) - apply(taskManagerId, maxEvaluationDepth)(contextMap) - } - def apply( - taskManagerId: String, - maxEvaluationDepth: Int - )( - context: Map[Class[_], AnyRef] = Map.empty - )( - implicit - logContext: LogContext - ): PKECPropertyStore = { - val tasksManager: TasksManager = taskManagerId match { - case "Seq" ⇒ new SeqTasksManager(maxEvaluationDepth) - case "Par" ⇒ new ParTasksManager(maxEvaluationDepth) - case _ ⇒ throw new IllegalArgumentException(s"unknown task manager $taskManagerId") - } + val maxEvaluationDepth = config.getInt(MaxEvaluationDepthKey) - OPALLogger.info( - "property store", - s"using $taskManagerId task manager; evaluation depth $maxEvaluationDepth" - ) - - val ps = - new PKECPropertyStore( - context, - tasksManager, - context.get(classOf[PropertyStoreTracer]).asInstanceOf[Option[PropertyStoreTracer]] - ) + val ps = new PKECPropertyStore(contextMap, MaxThreads, maxEvaluationDepth) ps } - -} - -private[par] class FakeEntity() { - - override def toString: String = "FakeEntity" } diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/ParTasksManager.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/ParTasksManager.scala deleted file mode 100644 index 0d691d489b..0000000000 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/ParTasksManager.scala +++ /dev/null @@ -1,254 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package par - -import java.util.concurrent.Executors -import java.util.concurrent.ExecutorService -import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.CountDownLatch - -import scala.collection.mutable.Buffer - -import org.opalj.log.OPALLogger - -object ParTasksManagerConfig { - @volatile var MaxThreads = org.opalj.concurrent.NumberOfThreadsForCPUBoundTasks -} - -/** - * A task manager which performs all tasks in parallel using a standard fixed thread pool. - * This store is intended to be used for debugging and evaluation purposes only, because it - * doesn't support any kind of "smart" scheduling strategies. - * - * @author Michael Eichberg - */ -class ParTasksManager( final val MaxEvaluationDepth: Int) extends TasksManager { - - val MaxThreads = ParTasksManagerConfig.MaxThreads - - // The idea is to start the execution of analyses when the store's "waitOnPhaseCompletion" - // method is called. - - private[this] val queuedTasks: Buffer[Runnable] = Buffer.empty - - private[this] var nextThreadId: AtomicInteger = _ - private[this] var es: ExecutorService = _ - private[this] var tasks: AtomicInteger = _ - private[this] var currentEvaluationDepth: Int = _ - - @volatile private[this] var latch: CountDownLatch = new CountDownLatch(1) - - def phaseSetupCompleted()(implicit ps: PKECPropertyStore): Unit = { - if (es == null) { - // Initialize the thread pool and all helper data structures. - nextThreadId = new AtomicInteger(1) - es = Executors.newFixedThreadPool( - MaxThreads, - (r: Runnable) ⇒ { - val threadId = nextThreadId.getAndIncrement() - new Thread(r, s"PKECPropertyStore-Thread #$threadId") - }: Thread - ) - tasks = new AtomicInteger(0) - currentEvaluationDepth = 0 - } - - // Submit the scheduled tasks. - queuedTasks.foreach { t ⇒ es.submit(t); tasks.incrementAndGet() } - queuedTasks.clear() - } - - def shutdown()(implicit ps: PKECPropertyStore): Unit = { - assert( - ps.doTerminate || tasks == null || tasks.get == 0, - "some tasks are still running/are still scheduled" - ) - try { - val es = this.es - if (es != null) es.shutdownNow() - } catch { - case e: Throwable ⇒ - OPALLogger.error("property store", "shutdown failed", e)(ps.logContext) - } - es = null - nextThreadId = null - tasks = null - - // We have to ensure that "the every/the last latch" is count down! - latch.countDown() - } - - def isIdle: Boolean = { val tasks = this.tasks; tasks == null || tasks.get == 0 } - - def awaitPoolQuiescence()(implicit ps: PKECPropertyStore): Unit = { - // Recall the overall program flow: - // 1. register computations - // 2. wait on phase completion is called - // 2.1. prepare thread pool is called - // (this may already lead to the execution of analyses) - // 2.2. await pool quiescence is called - // (potentially multiple times, but never concurrently!) - // 2.3. clean up thread pool is called - - // We have to get the latch _before_ checking that there are tasks - // otherwise it may happen that between querying the number of - // tasks and calling await a new latch was created for the next - // phase. In this case, this new latch may not be count down. - val latch = this.latch - // We have to check if "tasks" is still valid or if – due to an exception – - // the store was already shut down. - val tasks = this.tasks - if (tasks != null && tasks.get > 0) { - if (ps.doTerminate) { - throw new InterruptedException(); - } - latch.await() - } - } - - private def decrementTasks()(implicit ps: PKECPropertyStore): Unit = { - if (tasks.decrementAndGet() == 0) { - val oldLatch = latch - latch = new CountDownLatch(1) - oldLatch.countDown() - } - - if (ps.doTerminate) { - latch.countDown() - throw new InterruptedException() - } - } - - def doParallelize(f: ⇒ Unit)(implicit store: PKECPropertyStore): Unit = { - val r: Runnable = () ⇒ try { - // FIXME handleExceptions - f - } finally { - decrementTasks() - } - val es = this.es - val tasks = this.tasks - if (es != null && tasks != null) { - es.submit(r) - tasks.incrementAndGet() - } else { - queuedTasks += r - } - } - - def doForkResultHandler( - result: PropertyComputationResult - )( - implicit - ps: PKECPropertyStore - ): Unit = { - val r: Runnable = () ⇒ try { ps.processResult(result) } finally { decrementTasks() } - es.submit(r) - tasks.incrementAndGet() - } - - def doSchedulePropertyComputation[E <: Entity]( - e: E, - pc: PropertyComputation[E] - )( - implicit - ps: PKECPropertyStore - ): Unit = { - val r: Runnable = () ⇒ try { - val r = try { pc(e) } catch { case t: Throwable ⇒ ps.collectAndThrowException(t) } - ps.processResult(r) - } finally { - decrementTasks() - } - if (es != null) { - es.submit(r) - tasks.incrementAndGet() - } else { - queuedTasks += r - } - } - - def doForkLazyPropertyComputation[E <: Entity, P <: Property]( - epk: EPK[E, P], - pc: PropertyComputation[E] - )( - implicit - ps: PKECPropertyStore - ): EOptionP[E, P] = { - if (currentEvaluationDepth < MaxEvaluationDepth) { - currentEvaluationDepth += 1 - try { - if (ps.doTerminate) - throw new InterruptedException() - - if (ps.tracer.isDefined) - ps.tracer.get - .immediateEvaluationOfLazyComputation(epk, currentEvaluationDepth, pc) - - val r = try { pc(epk.e) } catch { case t: Throwable ⇒ ps.collectAndThrowException(t) } - ps.processResult(r) - val newEOptionP = ps(epk) - newEOptionP - } finally { - currentEvaluationDepth -= 1 - } - } else { - val r: Runnable = () ⇒ try { - val r = try { pc(epk.e) } catch { case t: Throwable ⇒ ps.collectAndThrowException(t) } - ps.processResult(r) - } finally { - decrementTasks() - } - es.submit(r) - tasks.incrementAndGet() - epk - } - } - - def doForkOnUpdateContinuation( - c: OnUpdateContinuation, - e: Entity, - pk: SomePropertyKey - )( - implicit - ps: PKECPropertyStore - ): Unit = { - val r: Runnable = () ⇒ - try { - val r = try { - val latestEPS = ps(e, pk).asEPS - c(latestEPS) - } catch { case t: Throwable ⇒ ps.collectAndThrowException(t) } - ps.processResult(r) - } finally { - decrementTasks() - } - es.submit(r) - tasks.incrementAndGet() - } - - def doForkOnUpdateContinuation( - c: OnUpdateContinuation, - finalEP: SomeFinalEP - )( - implicit - ps: PKECPropertyStore - ): Unit = { - es.submit((() ⇒ - try { - val r = try { - c(finalEP) - } catch { - case t: Throwable ⇒ - val ex = new Error(s"unknown error after applying $c to $finalEP", t) - ps.collectAndThrowException(ex) - } - ps.processResult(r) - } finally { - decrementTasks() - }): Runnable) - tasks.incrementAndGet() - } - -} diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/QualifiedTask.scala.temp b/OPAL/si/src/main/scala/org/opalj/fpcf/par/QualifiedTask.scala.temp deleted file mode 100644 index 215b7ee371..0000000000 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/QualifiedTask.scala.temp +++ /dev/null @@ -1,138 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package par - -import org.opalj.fpcf.PropertyStore.{Debug ⇒ debug} - -/** - * We generally distinguish between tasks that compute properties which are explicitly required - * and those tasks which are not yet/no longer required, because no strictly depending analyses - * requires them (anymore.) - * - * @author Michael Eichberg - */ -private[par] sealed trait QualifiedTask[E <: Entity] extends (() ⇒ Unit) { - - def isInitialTask: Boolean - def asInitialTask: InitialPropertyComputationTask[E] = throw new ClassCastException(); -} - -private[par] sealed trait FirstPropertyComputationTask[E <: Entity] extends QualifiedTask[E] { - def e: Entity -} - -private[par] final case class InitialPropertyComputationTask[E <: Entity]( - ps: PKEParallelTasksPropertyStore, - e: E, - pc: PropertyComputation[E] -) extends FirstPropertyComputationTask[E] { - - override def apply(): Unit = { - val r = pc(e) - ps.handleResult(r) - } - - override def isInitialTask: Boolean = true - override def asInitialTask: InitialPropertyComputationTask[E] = this -} - -private[par] sealed abstract class TriggeredTask[E <: Entity] extends QualifiedTask[E] { - final override def isInitialTask: Boolean = false -} - -private[par] final case class PropertyComputationTask[E <: Entity]( - ps: PKEParallelTasksPropertyStore, - e: E, - pkId: Int, - pc: PropertyComputation[E] -) extends TriggeredTask[E] with FirstPropertyComputationTask[E] { - - override def apply(): Unit = ps.handleResult(pc(e)) -} - -private[par] sealed abstract class ContinuationTask[E <: Entity] extends TriggeredTask[E] { - def dependeeE: Entity - def dependeePKId: Int -} - -private[par] final case class OnFinalUpdateComputationTask[E <: Entity, P <: Property]( - ps: PKEParallelTasksPropertyStore, - dependeeFinalP: FinalEP[E, P], - c: OnUpdateContinuation -) extends ContinuationTask[E] { - - override def dependeeE: E = dependeeFinalP.e - - override def dependeePKId: Int = dependeeFinalP.p.id - - override def apply(): Unit = ps.handleResult(c(dependeeFinalP)) -} - -private[par] final case class OnUpdateComputationTask[E <: Entity, P <: Property]( - ps: PKEParallelTasksPropertyStore, - dependeeEPK: EPK[E, P], - c: OnUpdateContinuation -) extends ContinuationTask[E] { - - override def dependeeE: E = dependeeEPK.e - - override def dependeePKId: Int = dependeeEPK.pk.id - - override def apply(): Unit = { - // get the most current property when the depender is eventually evaluated; - // the effectiveness of this check depends on the scheduling strategy(!) - val eps = ps(dependeeEPK).asEPS - val newResult = c(eps) - ps.handleResult(newResult) - } -} - -private[par] final case class ImmediateOnUpdateComputationTask[E <: Entity, P <: Property]( - ps: PKEParallelTasksPropertyStore, - dependeeEPK: EPK[E, P], - previousResult: PropertyComputationResult, - c: OnUpdateContinuation -) extends ContinuationTask[E] { - - override def dependeeE: E = dependeeEPK.e - - override def dependeePKId: Int = dependeeEPK.pk.id - - override def apply(): Unit = { - // Get the most current property when the depender is eventually evaluated; - // the effectiveness of this check depends on the scheduling strategy(!). - val newResult = c(ps(dependeeEPK).asEPS) - if (debug && newResult == previousResult) { - throw new IllegalStateException( - s"an on-update continuation resulted in the same result as before: $newResult" - ) - } - ps.handleResult(newResult) - } -} - -private[par] final case class ImmediateOnFinalUpdateComputationTask[E <: Entity, P <: Property]( - ps: PKEParallelTasksPropertyStore, - dependeeFinalP: FinalEP[E, P], - previousResult: PropertyComputationResult, - c: OnUpdateContinuation -) extends ContinuationTask[E] { - - override def dependeeE: E = dependeeFinalP.e - - override def dependeePKId: Int = dependeeFinalP.pk.id - - override def apply(): Unit = { - // get the most current property when the depender is eventually evaluated; - // the effectiveness of this check depends on the scheduling strategy(!) - val newResult = c(dependeeFinalP) - if (debug && newResult == previousResult) { - throw new IllegalStateException( - s"an on-update continuation resulted in the same result as before: $newResult" - ) - } - ps.handleResult(newResult) - } -} - diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/SeqTasksManager.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/SeqTasksManager.scala deleted file mode 100644 index 7a35c90d49..0000000000 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/SeqTasksManager.scala +++ /dev/null @@ -1,111 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package par - -import org.opalj.collection.mutable.RefArrayStack - -/** - * A task manager which performs all tasks sequentially; hence, turning the parallel - * properties store into a sequential store. This store is intended to be used for debugging - * and evaluation (i.e., the memory overhead of the parallel store when compared to the - * sequential store) purposes only. - */ -class SeqTasksManager( final val MaxEvaluationDepth: Int) extends TasksManager { - - // NOTHING TO DO IN THIS SPECIAL CASE - def phaseSetupCompleted()(implicit store: PKECPropertyStore): Unit = {} - - private[this] var currentEvaluationDepth = 0 - - private[this] val runnables = RefArrayStack.empty[Runnable] - - def isIdle: Boolean = { - runnables.isEmpty - } - - def shutdown()(implicit store: PKECPropertyStore): Unit = { - runnables.clear() - } - - def awaitPoolQuiescence()(implicit store: PKECPropertyStore): Unit = { - while (runnables.nonEmpty) { - if (store.doTerminate) throw new InterruptedException() - val runnable = runnables.pop() - runnable.run() - } - } - - def doParallelize(f: ⇒ Unit)(implicit store: PKECPropertyStore): Unit = { - f - } - - def doForkResultHandler( - r: PropertyComputationResult - )( - implicit - store: PKECPropertyStore - ): Unit = { - runnables.push(() ⇒ store.processResult(r)) - } - - def doSchedulePropertyComputation[E <: Entity]( - e: E, - pc: PropertyComputation[E] - )( - implicit - store: PKECPropertyStore - ): Unit = { - runnables.push(() ⇒ store.processResult(pc(e))) - } - - def doForkLazyPropertyComputation[E <: Entity, P <: Property]( - epk: EPK[E, P], - pc: PropertyComputation[E] - )( - implicit - store: PKECPropertyStore - ): EOptionP[E, P] = { - if (currentEvaluationDepth < MaxEvaluationDepth) { - currentEvaluationDepth += 1 - try { - if (store.tracer.isDefined) - store.tracer.get.immediateEvaluationOfLazyComputation(epk, currentEvaluationDepth, pc) - store.processResult(pc(epk.e)) - val newEOptionP = store(epk) - newEOptionP - } finally { - currentEvaluationDepth -= 1 - } - } else { - runnables.push(() ⇒ store.processResult(pc(epk.e))) - epk - } - } - - def doForkOnUpdateContinuation( - c: OnUpdateContinuation, - e: Entity, - pk: SomePropertyKey - )( - implicit - store: PKECPropertyStore - ): Unit = { - val latestEPS = store(e, pk).asEPS // XXXX FIXME TODO move inside the closure below - runnables.push(() ⇒ { - - store.processResult(c(latestEPS)) - }) - } - - def doForkOnUpdateContinuation( - c: OnUpdateContinuation, - finalEP: SomeFinalEP - )( - implicit - store: PKECPropertyStore - ): Unit = { - runnables.push(() ⇒ store.processResult(c(finalEP))) - } - -} diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/TasksManager.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/TasksManager.scala deleted file mode 100644 index d405e78ad7..0000000000 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/TasksManager.scala +++ /dev/null @@ -1,163 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package par - -import java.util.concurrent.atomic.AtomicInteger - -import org.opalj.concurrent.NumberOfThreadsForCPUBoundTasks - -trait TaskManagerFactory { - - @volatile var NumberOfThreadsForProcessingPropertyComputations: Int = { - // We need at least one thread for processing property computations. - Math.max(NumberOfThreadsForCPUBoundTasks, 1) - } - -} - -abstract class TasksManager { - - final val Debug = PropertyStore.Debug - - def MaxEvaluationDepth: Int - - def isIdle: Boolean - - /** - * Called to enable the task manager to clean up all threads. - */ - def shutdown()(implicit ps: PKECPropertyStore): Unit - - /** - * Called after the setup of a phase has completed. - * This, e.g., enables the task manager to initialize its threads. - * The task manager is allowed to – but doesn't have to – immediately start the execution - * of scheduled tasks. - * - * In general, the task manager has to assume that all data structures that are - * initialized during the setup phase – and which will not be mutated while the analyses - * are run – are not explicitly synchronized. - */ - def phaseSetupCompleted()(implicit store: PKECPropertyStore): Unit - - def awaitPoolQuiescence()(implicit store: PKECPropertyStore): Unit - - def parallelize(f: ⇒ Unit)(implicit store: PKECPropertyStore): Unit = { - incrementScheduledTasksCounter() - doParallelize(f) - } - - def doParallelize(f: ⇒ Unit)(implicit store: PKECPropertyStore): Unit - - final def forkResultHandler(r: PropertyComputationResult)(implicit store: PKECPropertyStore): Unit = { - incrementScheduledTasksCounter() - doForkResultHandler(r) - } - - def doForkResultHandler(r: PropertyComputationResult)(implicit store: PKECPropertyStore): Unit - - final def schedulePropertyComputation[E <: Entity]( - e: E, - pc: PropertyComputation[E] - )( - implicit - store: PKECPropertyStore - ): Unit = { - incrementScheduledTasksCounter() - doSchedulePropertyComputation(e, pc) - } - - def doSchedulePropertyComputation[E <: Entity]( - e: E, - pc: PropertyComputation[E] - )( - implicit - store: PKECPropertyStore - ): Unit - - /** - * Schedule or execute the given lazy property computation for the given entity. - * - * It is the responsibility of the task manager to ensure that we don't run in - * a `StackOverflowError` if if executes the property computation eagerly. - * - * *Potential Optimizations* - * Run the property computation by a thread that has just analyzed the entity... - * if no thread is analyzing the entity analyze it using the current thread to minimize - * the overall number of notifications. - */ - final def forkLazyPropertyComputation[E <: Entity, P <: Property]( - e: EPK[E, P], - pc: PropertyComputation[E] - )( - implicit - store: PKECPropertyStore - ): EOptionP[E, P] = { - incrementScheduledTasksCounter() - doForkLazyPropertyComputation(e, pc) - } - - def doForkLazyPropertyComputation[E <: Entity, P <: Property]( - e: EPK[E, P], - pc: PropertyComputation[E] - )( - implicit - store: PKECPropertyStore - ): EOptionP[E, P] - - final def forkOnUpdateContinuation( - c: OnUpdateContinuation, - e: Entity, - pk: SomePropertyKey - )( - implicit - store: PKECPropertyStore - ): Unit = { - incrementOnUpdateContinuationsCounter() - doForkOnUpdateContinuation(c, e, pk) - } - - def doForkOnUpdateContinuation( - c: OnUpdateContinuation, - e: Entity, - pk: SomePropertyKey - )( - implicit - store: PKECPropertyStore - ): Unit - - final def forkOnUpdateContinuation( - c: OnUpdateContinuation, - finalEP: SomeFinalEP - )( - implicit - store: PKECPropertyStore - ): Unit = { - incrementOnUpdateContinuationsCounter() - doForkOnUpdateContinuation(c, finalEP) - } - - def doForkOnUpdateContinuation( - c: OnUpdateContinuation, - finalEP: SomeFinalEP - )( - implicit - store: PKECPropertyStore - ): Unit - - private[this] val scheduledOnUpdateComputationsCounter = new AtomicInteger(0) - protected[this] def incrementOnUpdateContinuationsCounter(): Unit = { - if (Debug) scheduledOnUpdateComputationsCounter.incrementAndGet() - } - final def scheduledOnUpdateComputationsCount: Int = { - scheduledOnUpdateComputationsCounter.get() - } - - private[this] val scheduledTasksCounter = new AtomicInteger(0) - protected[this] def incrementScheduledTasksCounter(): Unit = { - if (Debug) scheduledTasksCounter.incrementAndGet() - } - final def scheduledTasksCount: Int = scheduledTasksCounter.get() - -} diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/UpdateAndNotifyState.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/UpdateAndNotifyState.scala deleted file mode 100644 index 327248ff25..0000000000 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/UpdateAndNotifyState.scala +++ /dev/null @@ -1,21 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package par - -sealed trait UpdateAndNotifyState { - def isNotificationRequired: Boolean - def areDependersNotified: Boolean -} -case object NoRelevantUpdate extends UpdateAndNotifyState { - override def isNotificationRequired: Boolean = false - override def areDependersNotified: Boolean = false -} -case object RelevantUpdateButNoNotification extends UpdateAndNotifyState { - override def isNotificationRequired: Boolean = true - override def areDependersNotified: Boolean = false -} -case object RelevantUpdateAndNotification extends UpdateAndNotifyState { - override def isNotificationRequired: Boolean = false - override def areDependersNotified: Boolean = true -} diff --git a/OPAL/si/src/test/scala/org/opalj/fpcf/PropertyComputationResultsTest.scala b/OPAL/si/src/test/scala/org/opalj/fpcf/PropertyComputationResultsTest.scala index 63de606a2c..f50a6f798f 100644 --- a/OPAL/si/src/test/scala/org/opalj/fpcf/PropertyComputationResultsTest.scala +++ b/OPAL/si/src/test/scala/org/opalj/fpcf/PropertyComputationResultsTest.scala @@ -90,7 +90,7 @@ class InterimResultTest extends FunSuite { val r: PropertyComputationResult = InterimResult( InterimEUBP(new Object, NilProperty), - List(EPK(new Object, NilProperty.key)), + Set(EPK(new Object, NilProperty.key)), _ ⇒ ??? ) assert(r.isInstanceOf[ProperPropertyComputationResult]) @@ -101,7 +101,7 @@ class InterimResultTest extends FunSuite { val r: PropertyComputationResult = InterimResult( InterimELBP(new Object, NilProperty), - List(EPK(new Object, NilProperty.key)), + Set(EPK(new Object, NilProperty.key)), _ ⇒ ??? ) assert(r.isInstanceOf[ProperPropertyComputationResult]) @@ -112,7 +112,7 @@ class InterimResultTest extends FunSuite { val r: PropertyComputationResult = InterimResult( InterimELUBP("c", NoPalindrome, Palindrome), - List(EPK(new Object, NilProperty.key)), + Set(EPK(new Object, NilProperty.key)), _ ⇒ ??? ) assert(r.isInstanceOf[ProperPropertyComputationResult]) @@ -123,7 +123,7 @@ class InterimResultTest extends FunSuite { val r: PropertyComputationResult = InterimResult( InterimELUBP("c", NoPalindrome, Palindrome), - List(EPK(new Object, NilProperty.key)), + Set(EPK(new Object, NilProperty.key)), _ ⇒ ??? ) assert(r.isInterimResult) @@ -133,14 +133,14 @@ class InterimResultTest extends FunSuite { val r: PropertyComputationResult = InterimResult( InterimELUBP("c", NoPalindrome, Palindrome), - List(EPK(new Object, NilProperty.key)), + Set(EPK(new Object, NilProperty.key)), _ ⇒ ??? ) assert(r.asInterimResult eq r) } test("the specialized factory method for InterimResults with a lower and and upper bound creates the same EPS as if the EPS was created explicitly") { - val dependees = List(EPK(new Object, NilProperty.key)) + val dependees: Set[SomeEOptionP] = Set(EPK(new Object, NilProperty.key)) val r: PropertyComputationResult = InterimResult(InterimELUBP("c", NoPalindrome, Palindrome), dependees, _ ⇒ ???) val rFactory: PropertyComputationResult = @@ -149,7 +149,7 @@ class InterimResultTest extends FunSuite { } test("two InterimResults for upper bounds are equal when property and dependency lists are equal") { - val dependees = List(EPK(new Object, NilProperty.key)) + val dependees: Set[SomeEOptionP] = Set(EPK(new Object, NilProperty.key)) val r: PropertyComputationResult = InterimResult(InterimEUBP("c", Palindrome), dependees, _ ⇒ ???) val rFactory: PropertyComputationResult = @@ -158,7 +158,7 @@ class InterimResultTest extends FunSuite { } test("two InterimResults for lower bounds are equal when property and dependency lists are equal") { - val dependees = List(EPK(new Object, NilProperty.key)) + val dependees: Set[SomeEOptionP] = Set(EPK(new Object, NilProperty.key)) val r: PropertyComputationResult = InterimResult(InterimELBP("c", Palindrome), dependees, _ ⇒ ???) val rFactory: PropertyComputationResult = @@ -194,26 +194,26 @@ class PartialResultTest extends FunSuite { class InterimPartialResultTest extends FunSuite { test("an InterimPartialResult is a proper result") { - val dependees = List(EPK(new Object, NilProperty.key)) + val dependees: Set[SomeEOptionP] = Set(EPK(new Object, NilProperty.key)) val r: PropertyComputationResult = InterimPartialResult(dependees, _ ⇒ ???) assert(r.isInstanceOf[ProperPropertyComputationResult]) assert(!r.isInstanceOf[FinalPropertyComputationResult]) } test("an InterimPartialResult is not an InterimResult") { - val dependees = List(EPK(new Object, NilProperty.key)) + val dependees: Set[SomeEOptionP] = Set(EPK(new Object, NilProperty.key)) val r: PropertyComputationResult = InterimPartialResult(dependees, _ ⇒ ???) assert(!r.isInterimResult) } test("an InterimPartialResult cannot be cast to an InterimResult") { - val dependees = List(EPK(new Object, NilProperty.key)) + val dependees: Set[SomeEOptionP] = Set(EPK(new Object, NilProperty.key)) val r: PropertyComputationResult = InterimPartialResult(dependees, _ ⇒ ???) assertThrows[ClassCastException](r.asInterimResult) } test("an InterimPartialResult cannot be cast to a Result") { - val dependees = List(EPK(new Object, NilProperty.key)) + val dependees: Set[SomeEOptionP] = Set(EPK(new Object, NilProperty.key)) val r: PropertyComputationResult = InterimPartialResult(dependees, _ ⇒ ???) assertThrows[ClassCastException](r.asResult) } diff --git a/OPAL/si/src/test/scala/org/opalj/fpcf/PropertyStoreTest.scala b/OPAL/si/src/test/scala/org/opalj/fpcf/PropertyStoreTest.scala index 63e56af7b3..a6c422a79f 100644 --- a/OPAL/si/src/test/scala/org/opalj/fpcf/PropertyStoreTest.scala +++ b/OPAL/si/src/test/scala/org/opalj/fpcf/PropertyStoreTest.scala @@ -129,7 +129,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] "a", NoPalindrome, Palindrome, - Seq(dependee), + Set(dependee), eps ⇒ { Result("a", Palindrome) } ), // This computation should not be executed! @@ -197,7 +197,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] ps.scheduleEagerComputationForEntity("e1") { e ⇒ val e3EPK = EPK("e3", PalindromeKey) - val dependees = Seq(EPK("e2", PalindromeKey), e3EPK) + val dependees: Set[SomeEOptionP] = Set(EPK("e2", PalindromeKey), e3EPK) ps(e3EPK) // we have to query it (e2 is already set => no need to query it)! InterimResult( "e1", @@ -284,7 +284,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] InterimResult( "a", NoPalindrome, Palindrome, - Seq(dependee), + Set(dependee), _ ⇒ Result("a", Palindrome) ) } @@ -374,7 +374,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] case FinalP(NoAnalysisForPalindromeProperty) /*<= the test...*/ ⇒ Result(e, Marker.NotMarked) case epk: SomeEPK ⇒ - InterimResult(e, Marker.IsMarked, Marker.NotMarked, List(epk), c) + InterimResult(e, Marker.IsMarked, Marker.NotMarked, Set(epk), c) } } c(ps(e, Palindromes.PalindromeKey)) @@ -408,7 +408,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] Result(e, Marker.NotMarked) case epk: SomeEPK ⇒ InterimResult( - e, Marker.IsMarked, Marker.NotMarked, List(epk), c + e, Marker.IsMarked, Marker.NotMarked, Set(epk), c ) } } @@ -558,7 +558,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] ps.set("dummyymmud", SuperPalindrome) ps.set("dummyBADymmud", NoSuperPalindrome) - ps.setupPhase(Set(PalindromeKey, SuperPalindromeKey), Set.empty) + ps.setupPhase(Set(PalindromeKey, SuperPalindromeKey, Marker.Key), Set.empty) val invocationCount = new AtomicInteger(0) ps.registerLazyPropertyComputation( @@ -578,7 +578,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] InterimResult( e, NoSuperPalindrome, SuperPalindrome, - Seq(initialSomeEOptionP), + Set(initialSomeEOptionP), eps ⇒ { if (eps.lb == Palindrome /*&& ...*/ ) Result(e, SuperPalindrome) @@ -593,7 +593,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] initialSomeEOptionP should be(EPK("e", SuperPalindromeKey)) InterimResult( "e", Marker.NotMarked, Marker.IsMarked, - Seq(initialSomeEOptionP), + Set(initialSomeEOptionP), eps ⇒ { // Depending on the scheduling, we can have a final result here as well. if (eps.isFinal) { @@ -604,7 +604,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] } else InterimResult( "e", Marker.NotMarked, Marker.IsMarked, - Seq(eps), + Set(eps), eps ⇒ { if (!eps.isFinal) fail("unexpected non final value") @@ -855,7 +855,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] } val r = { if (dependeePs.nonEmpty) - InterimResult(n, AllNodes, newUB, dependeePs, c) + InterimResult(n, AllNodes, newUB, dependeePs.asInstanceOf[Set[SomeEOptionP]], c) else Result(n, newUB) } @@ -875,7 +875,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] if (dependeePs.isEmpty) Result(n, currentReachableNodes) else - InterimResult(n, AllNodes, currentReachableNodes, dependeePs, c) + InterimResult(n, AllNodes, currentReachableNodes, dependeePs.asInstanceOf[Set[SomeEOptionP]], c) } // Expected to be scheduled as an eager analysis for all nodes... @@ -888,9 +888,9 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] if (nTargets.isEmpty) return Result(n, NoReachableNodes); - var dependeePs: List[EOptionP[Entity, _ <: ReachableNodes]] = + var dependeePs: Set[EOptionP[Entity, _ <: ReachableNodes]] = ps(nTargets - n /* ignore self-dependency */ , ReachableNodes.Key) - .filter { dependeeP ⇒ dependeeP.isRefinable }.toList + .filter { dependeeP ⇒ dependeeP.isRefinable }.toSet def createPartialResult(currentNodes: SomeSet[Node]): SomePartialResult = { PartialResult( @@ -907,7 +907,8 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] } else { None } - case _ ⇒ ??? + case _ ⇒ + ??? } ) } @@ -919,8 +920,8 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] val pr = createPartialResult(depeendeeReachableNodes) dependeePs = dependeePs.filter(_.e ne dependeeP.e) if (dependeeP.isRefinable) - dependeePs ::= dependeeP.asInstanceOf[EOptionP[Entity, _ <: ReachableNodes]] - InterimPartialResult(List(pr), dependeePs, c) + dependeePs += dependeeP.asInstanceOf[EOptionP[Entity, _ <: ReachableNodes]] + InterimPartialResult(List(pr), dependeePs.asInstanceOf[Set[SomeEOptionP]], c) } val allNodes = @@ -936,7 +937,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] Result(n, ReachableNodes(allNodes)) else { val pr = createPartialResult(allNodes) - InterimPartialResult(List(pr), dependeePs, c) + InterimPartialResult(List(pr), dependeePs.asInstanceOf[Set[SomeEOptionP]], c) } } @@ -945,7 +946,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] ) extends (Node ⇒ ProperPropertyComputationResult) { def apply(n: Node): ProperPropertyComputationResult = { - var dependees: List[SomeEOptionP] = Nil + var dependees: Set[SomeEOptionP] = Set.empty var ub: Int = n.targets.size def c(eps: SomeEOptionP): ProperPropertyComputationResult = { @@ -954,7 +955,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] if (ub + otherUB > 4) Result(n, TooManyNodesReachable) else { - dependees = eps :: dependees.filter(_.e ne eps.e) + dependees = dependees.filter(_.e ne eps.e) + eps InterimResult( n, TooManyNodesReachable, ReachableNodesCount(ub), dependees, @@ -986,7 +987,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] } else { ps(successor, ReachableNodesCount.Key) match { case epk: EPK[_, _] ⇒ - dependees ::= epk + dependees += epk true case iep @ InterimUBP(ReachableNodesCount(otherUB)) ⇒ if (ub + otherUB > 4) { @@ -995,7 +996,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] } else { // we have to wait for the final value before we can // add the count - dependees ::= iep + dependees += iep true } case FinalP(reachableNodesCount) ⇒ @@ -1029,7 +1030,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] case eps @ InterimUBP(ReachableNodes(nodes)) ⇒ val lb = TooManyNodesReachable val ub = ReachableNodesCount(nodes.size) - InterimResult(n, lb, ub, List(eps), c) + InterimResult(n, lb, ub, Set(eps), c) case FinalP(ReachableNodes(nodes)) ⇒ Result(n, ReachableNodesCount(nodes.size)) @@ -1040,7 +1041,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] case epk: EPK[_, _] ⇒ val lb = TooManyNodesReachable val ub = ReachableNodesCount(0) - InterimResult(n, lb, ub, List(epk), c) + InterimResult(n, lb, ub, Set(epk), c) case eps: SomeEOptionP ⇒ c(eps) @@ -1546,7 +1547,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] Result(eOptionP.e, p) case eOptionP ⇒ val interimELBP = InterimELBP(eOptionP.e, Marker.NotMarked) - InterimResult(interimELBP, List(eOptionP), handleEOptionP) + InterimResult(interimELBP, Set(eOptionP), handleEOptionP) } } ps.registerLazyPropertyComputation( @@ -1728,7 +1729,7 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] case epk: EPK[_, _] ⇒ InterimResult( s, NoPalindrome, Palindrome, - List(epk), + Set(epk), (eps: SomeEPS) ⇒ { if (eps.lb == null || eps.ub == null) fail("clients should never see null properties") @@ -1773,10 +1774,10 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] // node from the one that was updated. (successorNode: @unchecked) match { case epk: EPK[_, _] ⇒ - InterimResult(node, Impure, Pure, Iterable(epk), c) + InterimResult(node, Impure, Pure, Set(epk), c) case eps @ InterimLUBP(lb: Property, ub: Property) ⇒ - InterimResult(node, lb, ub, Iterable(eps), c) + InterimResult(node, lb, ub, Set(eps), c) // required when we resolve the cycle case FinalP(Pure) ⇒ Result(node, Pure) @@ -1841,10 +1842,10 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] // node from the one that was updated. (successorNode: @unchecked) match { case epk: EPK[_, _] ⇒ - InterimResult.forUB(node, Pure, Iterable(epk), c) + InterimResult.forUB(node, Pure, Set(epk), c) case eps @ InterimUBP(ub: Property) ⇒ - InterimResult.forUB(node, ub, Iterable(eps), c) + InterimResult.forUB(node, ub, Set(eps), c) // HERE, the following is not required - the cycle will be // automatically lifted when we reach "quiescence" @@ -1912,7 +1913,7 @@ abstract class PropertyStoreTestWithDebugging[PS <: PropertyStore] extends Prope val bEOptionP = ps(nodeB, ReachableNodesCount.Key) val lb = ReachableNodesCount(10) val invalidUB = ReachableNodesCount(20) - InterimResult(nodeA, lb, invalidUB, List(bEOptionP), eps ⇒ ???) + InterimResult(nodeA, lb, invalidUB, Set(bEOptionP), eps ⇒ ???) } try { @@ -1954,7 +1955,7 @@ abstract class PropertyStoreTestWithDebugging[PS <: PropertyStore] extends Prope n, ReachableNodesCount(100), // <= invalid refinement of lower bound! ReachableNodesCount(count), - List(ps(nodeD, ReachableNodesCount.Key)), + Set(ps(nodeD, ReachableNodesCount.Key)), c(nodeB) ) } @@ -1965,7 +1966,7 @@ abstract class PropertyStoreTestWithDebugging[PS <: PropertyStore] extends Prope val bEOptionP = ps(nodeB, ReachableNodesCount.Key) InterimResult( nodeA, ReachableNodesCount(20), ReachableNodesCount(0), - List(bEOptionP), + Set(bEOptionP), c(nodeA) ) @@ -1974,7 +1975,7 @@ abstract class PropertyStoreTestWithDebugging[PS <: PropertyStore] extends Prope count += 1 InterimResult( n, ReachableNodesCount(100 - count), ReachableNodesCount(count), - List(cEOptionP), + Set(cEOptionP), c(nodeB) ) @@ -2031,7 +2032,7 @@ abstract class PropertyStoreTestWithDebugging[PS <: PropertyStore] extends Prope n, ReachableNodesCount(100 - count), ReachableNodesCount(0), // <= invalid refinement of upper bound! - List(ps(nodeD, ReachableNodesCount.Key)), + Set(ps(nodeD, ReachableNodesCount.Key)), c(nodeB) ) } @@ -2042,7 +2043,7 @@ abstract class PropertyStoreTestWithDebugging[PS <: PropertyStore] extends Prope val bEOptionP = ps(nodeB, ReachableNodesCount.Key) InterimResult( nodeA, ReachableNodesCount(20), ReachableNodesCount(0), - List(bEOptionP), + Set(bEOptionP), c(nodeA) ) @@ -2051,7 +2052,7 @@ abstract class PropertyStoreTestWithDebugging[PS <: PropertyStore] extends Prope count += 1 InterimResult( n, ReachableNodesCount(100 - count), ReachableNodesCount(10), - List(cEOptionP), + Set(cEOptionP), c(nodeB) ) @@ -2102,7 +2103,7 @@ abstract class PropertyStoreTestWithDebugging[PS <: PropertyStore] extends Prope n, ReachableNodesCount(40), ReachableNodesCount(50), - List(ps(nodeD, ReachableNodesCount.Key)), + Set(ps(nodeD, ReachableNodesCount.Key)), c(nodeB) ) } @@ -2113,7 +2114,7 @@ abstract class PropertyStoreTestWithDebugging[PS <: PropertyStore] extends Prope val bEOptionP = ps(nodeB, ReachableNodesCount.Key) InterimResult( nodeA, ReachableNodesCount(20), ReachableNodesCount(0), - List(bEOptionP), + Set(bEOptionP), c(nodeA) ) @@ -2122,7 +2123,7 @@ abstract class PropertyStoreTestWithDebugging[PS <: PropertyStore] extends Prope count += 1 InterimResult( n, ReachableNodesCount(100 - count), ReachableNodesCount(10), - List(cEOptionP), + Set(cEOptionP), c(nodeB) ) diff --git a/OPAL/si/src/test/scala/org/opalj/fpcf/par/PKECPropertyStoreTest.scala b/OPAL/si/src/test/scala/org/opalj/fpcf/par/PKECPropertyStoreTest.scala index 52bc28ebbd..7088982933 100644 --- a/OPAL/si/src/test/scala/org/opalj/fpcf/par/PKECPropertyStoreTest.scala +++ b/OPAL/si/src/test/scala/org/opalj/fpcf/par/PKECPropertyStoreTest.scala @@ -1,142 +1,151 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf +package org.opalj +package fpcf package par -import com.typesafe.config.Config -import com.typesafe.config.ConfigValueFactory.fromAnyRef - -abstract class PKECPropertyStoreTestWithDebugging +abstract class AbstractPKECPropertyStoreTestWithDebugging extends PropertyStoreTestWithDebugging[PKECPropertyStore] { override def afterAll(ps: PKECPropertyStore): Unit = { - assert(ps.tracer.get.toTxt.nonEmpty) // basically just a smoke test + // TODO Basic smoke test? } } -class PKECPropertyStoreTestWithDebuggingMaxEvalDepthDefault - extends PKECPropertyStoreTestWithDebugging { +class PKECPropertyStoreTestWithDebugging + extends AbstractPKECPropertyStoreTestWithDebugging { def createPropertyStore(): PKECPropertyStore = { - val ps = PKECPropertyStore(classOf[PropertyStoreTracer], new RecordAllPropertyStoreEvents()) + val ps = new PKECPropertyStore(Map.empty, 8, 32) ps.suppressError = true ps } } -class PKECPropertyStoreTestWithDebuggingMaxEvalDepth32AndSeqTaskManager - extends PKECPropertyStoreTestWithDebugging { +class PKECPropertyStoreTestWithDebuggingSingleThreaded + extends AbstractPKECPropertyStoreTestWithDebugging { def createPropertyStore(): PKECPropertyStore = { - import PKECPropertyStore.MaxEvaluationDepthKey - import PKECPropertyStore.TasksManagerKey - val config = org.opalj.BaseConfig - .withValue(MaxEvaluationDepthKey, fromAnyRef(32)) - .withValue(TasksManagerKey, fromAnyRef("Seq")) - val ps = PKECPropertyStore( - PropertyStoreContext(classOf[PropertyStoreTracer], new RecordAllPropertyStoreEvents()), - PropertyStoreContext(classOf[Config], config) - ) + val ps = new PKECPropertyStore(Map.empty, 1, 32) ps.suppressError = true ps } } -class PKECPropertyStoreTestWithDebuggingMaxEvalDepth0AndSeqTaskManager - extends PKECPropertyStoreTestWithDebugging { +class PKECPropertyStoreTestWithDebuggingNoLocalEvaluation + extends AbstractPKECPropertyStoreTestWithDebugging { def createPropertyStore(): PKECPropertyStore = { - import PKECPropertyStore.MaxEvaluationDepthKey - import PKECPropertyStore.TasksManagerKey - val config = org.opalj.BaseConfig - .withValue(MaxEvaluationDepthKey, fromAnyRef(0)) - .withValue(TasksManagerKey, fromAnyRef("Seq")) - val ps = PKECPropertyStore( - PropertyStoreContext(classOf[PropertyStoreTracer], new RecordAllPropertyStoreEvents()), - PropertyStoreContext(classOf[Config], config) - ) + val ps = new PKECPropertyStore(Map.empty, 8, 0) ps.suppressError = true ps } } -class PKECPropertyStoreTestWithDebuggingMaxEvalDepth32AndParTaskManager - extends PKECPropertyStoreTestWithDebugging { +class PKECPropertyStoreTestWithDebuggingSingleThreadedNoLocalEvaluation + extends AbstractPKECPropertyStoreTestWithDebugging { def createPropertyStore(): PKECPropertyStore = { - import PKECPropertyStore.MaxEvaluationDepthKey - import PKECPropertyStore.TasksManagerKey - val config = org.opalj.BaseConfig - .withValue(MaxEvaluationDepthKey, fromAnyRef(32)) - .withValue(TasksManagerKey, fromAnyRef("Par")) - val ps = PKECPropertyStore( - PropertyStoreContext(classOf[PropertyStoreTracer], new RecordAllPropertyStoreEvents()), - PropertyStoreContext(classOf[Config], config) - ) + val ps = new PKECPropertyStore(Map.empty, 1, 0) ps.suppressError = true ps } } -// FIXME: PKECPropertyStore seems to be broken -/*class PKECPropertyStoreTestWithDebuggingMaxEvalDepth1AndParTaskManager - extends PKECPropertyStoreTestWithDebugging { +class PKECPropertyStoreTestWithDebugging128Threads + extends AbstractPKECPropertyStoreTestWithDebugging { def createPropertyStore(): PKECPropertyStore = { - import PKECPropertyStore.MaxEvaluationDepthKey - import PKECPropertyStore.TasksManagerKey - val config = org.opalj.BaseConfig - .withValue(MaxEvaluationDepthKey, fromAnyRef(1)) - .withValue(TasksManagerKey, fromAnyRef("Par")) - val ps = PKECPropertyStore( - PropertyStoreContext(classOf[PropertyStoreTracer], new RecordAllPropertyStoreEvents()), - PropertyStoreContext(classOf[Config], config) - ) + val ps = new PKECPropertyStore(Map.empty, 128, 32) ps.suppressError = true ps } -}*/ +} + +class PKECPropertyStoreTestWithDebugging128ThreadsNoLocalEvaluation + extends AbstractPKECPropertyStoreTestWithDebugging { + + def createPropertyStore(): PKECPropertyStore = { + val ps = new PKECPropertyStore(Map.empty, 128, 0) + ps.suppressError = true + ps + } + +} // ************************************************************************************************* // ************************************* NO DEBUGGING ********************************************** // ************************************************************************************************* -abstract class PKECPropertyStoreTestWithoutDebugging +abstract class AbstractPKECPropertyStoreTestWithoutDebugging extends PropertyStoreTestWithoutDebugging[PKECPropertyStore] -class PKECPropertyStoreTestWithoutDebuggingMaxEvalDepth128AndSeqTaskManager - extends PKECPropertyStoreTestWithoutDebugging { +class PKECPropertyStoreTestWithoutDebugging + extends AbstractPKECPropertyStoreTestWithoutDebugging { + + def createPropertyStore(): PKECPropertyStore = { + val ps = new PKECPropertyStore(Map.empty, 8, 32) + ps.suppressError = true + ps + } + +} + +class PKECPropertyStoreTestWithoutDebuggingSingleThreaded + extends AbstractPKECPropertyStoreTestWithoutDebugging { + + def createPropertyStore(): PKECPropertyStore = { + val ps = new PKECPropertyStore(Map.empty, 1, 32) + ps.suppressError = true + ps + } + +} + +class PKECPropertyStoreTestWithoutDebuggingNoLocalEvaluation + extends AbstractPKECPropertyStoreTestWithoutDebugging { + + def createPropertyStore(): PKECPropertyStore = { + val ps = new PKECPropertyStore(Map.empty, 8, 0) + ps.suppressError = true + ps + } + +} + +class PKECPropertyStoreTestWithoutDebuggingSingleThreadedNoLocalEvaluation + extends AbstractPKECPropertyStoreTestWithoutDebugging { def createPropertyStore(): PKECPropertyStore = { - val ps = PKECPropertyStore("Seq", 128)() + val ps = new PKECPropertyStore(Map.empty, 1, 0) ps.suppressError = true ps } } -class PKECPropertyStoreTestWithoutDebuggingMaxEvalDepth0AndSeqTaskManager - extends PKECPropertyStoreTestWithoutDebugging { +class PKECPropertyStoreTestWithoutDebugging128Threads + extends AbstractPKECPropertyStoreTestWithoutDebugging { def createPropertyStore(): PKECPropertyStore = { - val ps = PKECPropertyStore("Seq", 0)() + val ps = new PKECPropertyStore(Map.empty, 128, 32) ps.suppressError = true ps } } -/*class PKECPropertyStoreTestWithoutDebuggingMaxEvalDepth1AndParTaskManager - extends PKECPropertyStoreTestWithoutDebugging { +class PKECPropertyStoreTestWithoutDebugging128ThreadsNoLocalEvaluation + extends AbstractPKECPropertyStoreTestWithoutDebugging { def createPropertyStore(): PKECPropertyStore = { - val ps = PKECPropertyStore("Par", 1)() + val ps = new PKECPropertyStore(Map.empty, 128, 0) ps.suppressError = true ps } -}*/ +} \ No newline at end of file diff --git a/OPAL/tac/src/it/resources/org/opalj/tac/fpcf/analyses/FPCFAnalysesIntegrationTest.config b/OPAL/tac/src/it/resources/org/opalj/tac/fpcf/analyses/FPCFAnalysesIntegrationTest.config index a4419addb6..3ddb9e9d56 100644 --- a/OPAL/tac/src/it/resources/org/opalj/tac/fpcf/analyses/FPCFAnalysesIntegrationTest.config +++ b/OPAL/tac/src/it/resources/org/opalj/tac/fpcf/analyses/FPCFAnalysesIntegrationTest.config @@ -1,7 +1,7 @@ SimplePurityTest L2PurityAnalysis => - org.opalj.fpcf.properties.Purity + org.opalj.br.fpcf.properties.Purity ExtensiveAnalysesTest L0TACAIAnalysis @@ -15,10 +15,10 @@ ExtensiveAnalysesTest TypeImmutabilityAnalysis L2PurityAnalysis => - org.opalj.fpcf.properties.EscapeProperty - org.opalj.fpcf.properties.ReturnValueFreshness - org.opalj.fpcf.properties.FieldLocality - org.opalj.fpcf.properties.FieldMutability - org.opalj.fpcf.properties.ClassImmutability - org.opalj.fpcf.properties.TypeImmutability - org.opalj.fpcf.properties.Purity \ No newline at end of file + org.opalj.br.fpcf.properties.EscapeProperty + org.opalj.br.fpcf.properties.ReturnValueFreshness + org.opalj.br.fpcf.properties.FieldLocality + org.opalj.br.fpcf.properties.FieldMutability + org.opalj.br.fpcf.properties.ClassImmutability + org.opalj.br.fpcf.properties.TypeImmutability + org.opalj.br.fpcf.properties.Purity \ No newline at end of file diff --git a/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/L1PuritySmokeTest.scala b/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/L1PuritySmokeTest.scala index 46d3117a82..3d64171ad0 100644 --- a/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/L1PuritySmokeTest.scala +++ b/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/L1PuritySmokeTest.scala @@ -17,8 +17,8 @@ import org.opalj.br.TestSupport.createJREProject import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.Purity import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.analyses.EagerClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0ClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0TypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerVirtualMethodPurityAnalysis import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.PropertyStoreKey @@ -44,9 +44,9 @@ class L1PuritySmokeTest extends FunSpec with Matchers { ) val supportAnalyses: Set[ComputationSpecification[FPCFAnalysis]] = Set( - EagerL1FieldMutabilityAnalysis, - EagerClassImmutabilityAnalysis, - EagerTypeImmutabilityAnalysis + EagerL1FieldImmutabilityAnalysis, + EagerL0ClassImmutabilityAnalysis, + EagerL0TypeImmutabilityAnalysis ) def checkProject(p: SomeProject, withSupportAnalyses: Boolean): Unit = { diff --git a/OPAL/tac/src/main/resources/reference.conf b/OPAL/tac/src/main/resources/reference.conf index b1ff40bcfa..bd37679e33 100644 --- a/OPAL/tac/src/main/resources/reference.conf +++ b/OPAL/tac/src/main/resources/reference.conf @@ -14,10 +14,46 @@ org.opalj { eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL0TACAIAnalysis", lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL0TACAIAnalysis" }, - "L1FieldMutabilityAnalysis" { + "L1FieldImmutabilityAnalysis" { description = "Determines if (instance and static) fields are (effectively) final.", - eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis", - lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis" + eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL1FieldImmutabilityAnalysis", + lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL1FieldImmutabilityAnalysis" + }, + "L2FieldImmutabilityAnalysis" { + description = "Determines if (instance and static) fields are (effectively) final or lazy initialized", + eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL2FieldImmutabilityAnalysis", + lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL2FieldImmutabilityAnalysis" + }, + "L3FieldImmutabilityAnalysis" { + description = "Determines if (instance and static) fields are deep immutable", + eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL3FieldImmutabilityAnalysis", + lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL3FieldImmutabilityAnalysis" + }, + "L0FieldReferenceImmutabilityAnalysis" { + description = "Determines if (instance and static) fields are deep immutable", + eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL0FieldReferenceImmutabilityAnalysis", + lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL0FieldReferenceImmutabilityAnalysis", + considerLazyInitialization = true + }, + "L0ClassImmutabilityAnalysis" { + description = "", + eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL0ClassImmutabilityAnalysis", + lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL0ClassImmutabilityAnalysis" + }, + "L1ClassImmutabilityAnalysis" { + description = "", + eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL0ClassImmutabilityAnalysis", + lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL0ClassImmutabilityAnalysis" + }, + "L0TypeImmutabilityAnalysis" { + description = "", + eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL0TypeImmutabilityAnalysis", + lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL0TypeImmutabilityAnalysis" + }, + "L1TypeImmutabilityAnalysis" { + description = "", + eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL0TypeImmutabilityAnalysis", + lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL0TypeImmutabilityAnalysis" }, "SimpleEscapeAnalysis" { description = "Determines whether objects escape a method.", @@ -79,12 +115,22 @@ org.opalj { } ] }, + L0FieldReferenceImmutabilityAnalysis { + considerLazyInitialization = "true" + }, + L3FieldImmutabilityAnalysis { + considerGenericity = true, + considerEscape = true + }, L1PurityAnalysis { domainSpecificRater = "org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater" }, L2PurityAnalysis { domainSpecificRater = "org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater" }, + L2PurityAnalysis_new { + domainSpecificRater = "org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater" + }, ConfiguredPurity { purities = [ # Native methods @@ -137,15 +183,15 @@ org.opalj { {cf = "java/lang/reflect/Array", m = "getShort", desc = "(Ljava/lang/Object;I)S", p = "SideEffectFree"}, {cf = "java/lang/reflect/Array", m = "multiNewArray", desc = "(Ljava/lang/Class;[I)Ljava/lang/Object;", p = "SideEffectFree"}, {cf = "java/lang/reflect/Array", m = "newArray", desc = "(Ljava/lang/Class;I)Ljava/lang/Object;", p = "SideEffectFree"}, - {cf = "java/lang/reflect/Array", m = "set", desc = "(Ljava/lang/Object;ILjava/lang/Object;)V", p = "ContextuallySideEffectFree{0}"}, - {cf = "java/lang/reflect/Array", m = "setBoolean", desc = "(Ljava/lang/Object;IZ)V", p = "ContextuallySideEffectFree{0}"}, - {cf = "java/lang/reflect/Array", m = "setByte", desc = "(Ljava/lang/Object;IB)V", p = "ContextuallySideEffectFree{0}"}, - {cf = "java/lang/reflect/Array", m = "setChar", desc = "(Ljava/lang/Object;IC)V", p = "ContextuallySideEffectFree{0}"}, - {cf = "java/lang/reflect/Array", m = "setDouble", desc = "(Ljava/lang/Object;ID)V", p = "ContextuallySideEffectFree{0}"}, - {cf = "java/lang/reflect/Array", m = "setFloat", desc = "(Ljava/lang/Object;IF)V", p = "ContextuallySideEffectFree{0}"}, - {cf = "java/lang/reflect/Array", m = "setInt", desc = "(Ljava/lang/Object;II)V", p = "ContextuallySideEffectFree{0}"}, - {cf = "java/lang/reflect/Array", m = "setLong", desc = "(Ljava/lang/Object;IJ)V", p = "ContextuallySideEffectFree{0}"}, - {cf = "java/lang/reflect/Array", m = "setShort", desc = "(Ljava/lang/Object;IS)V", p = "ContextuallySideEffectFree{0}"}, + {cf = "java/lang/reflect/Array", m = "set", desc = "(Ljava/lang/Object;ILjava/lang/Object;)V", p = "ContextuallySideEffectFree{1}"}, + {cf = "java/lang/reflect/Array", m = "setBoolean", desc = "(Ljava/lang/Object;IZ)V", p = "ContextuallySideEffectFree{1}"}, + {cf = "java/lang/reflect/Array", m = "setByte", desc = "(Ljava/lang/Object;IB)V", p = "ContextuallySideEffectFree{1}"}, + {cf = "java/lang/reflect/Array", m = "setChar", desc = "(Ljava/lang/Object;IC)V", p = "ContextuallySideEffectFree{1}"}, + {cf = "java/lang/reflect/Array", m = "setDouble", desc = "(Ljava/lang/Object;ID)V", p = "ContextuallySideEffectFree{1}"}, + {cf = "java/lang/reflect/Array", m = "setFloat", desc = "(Ljava/lang/Object;IF)V", p = "ContextuallySideEffectFree{1}"}, + {cf = "java/lang/reflect/Array", m = "setInt", desc = "(Ljava/lang/Object;II)V", p = "ContextuallySideEffectFree{1}"}, + {cf = "java/lang/reflect/Array", m = "setLong", desc = "(Ljava/lang/Object;IJ)V", p = "ContextuallySideEffectFree{1}"}, + {cf = "java/lang/reflect/Array", m = "setShort", desc = "(Ljava/lang/Object;IS)V", p = "ContextuallySideEffectFree{1}"}, {cf = "java/lang/reflect/Executable", m = "getParameters0", desc = "()[Ljava/lang/reflect/Parameter;", p = "SideEffectFree"}, {cf = "java/lang/reflect/Executable", m = "getTypeAnnotationBytes0", desc = "()[B", p = "SideEffectFree"}, {cf = "java/lang/reflect/Field", m = "getTypeAnnotationBytes0", desc = "()[B", p = "SideEffectFree"}, @@ -179,7 +225,7 @@ org.opalj { {cf = "java/lang/StrictMath", m = "tan", desc = "(D)D", p = "CompileTimePure"}, {cf = "java/lang/StrictMath", m = "tanh", desc = "(D)D", p = "CompileTimePure"}, {cf = "java/lang/String", m = "intern", desc = "()Ljava/lang/String;", p = "SideEffectFree"}, - {cf = "java/lang/System", m = "arraycopy", desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V", p = "ContextuallySideEffectFree{2}"}, + {cf = "java/lang/System", m = "arraycopy", desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V", p = "ContextuallySideEffectFree{3}"}, {cf = "java/lang/System", m = "currentTimeMillis", desc = "()J", p = "SideEffectFree"}, {cf = "java/lang/System", m = "identityHashCode", desc = "(Ljava/lang/Object;)I", p = "Pure"}, {cf = "java/lang/System", m = "mapLibraryName", desc = "(Ljava/lang/String;)Ljava/lang/String;", p = "SideEffectFree"}, diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/Expr.scala b/OPAL/tac/src/main/scala/org/opalj/tac/Expr.scala index 143ca257f0..5d5b9de954 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Expr.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Expr.scala @@ -94,6 +94,7 @@ trait Expr[+V <: Var[V]] extends ASTNode[V] { def asMethodTypeConst: MethodTypeConst = throw new ClassCastException(); def isMethodHandleConst: Boolean = false def asMethodHandleConst: MethodHandleConst = throw new ClassCastException(); + def isCompare: Boolean = false def isConst: Boolean = false def isIntConst: Boolean = false def asIntConst: IntConst = throw new ClassCastException(); @@ -116,6 +117,7 @@ trait Expr[+V <: Var[V]] extends ASTNode[V] { def asNew: New = throw new ClassCastException(); def isNewArray: Boolean = false def asNewArray: NewArray[V] = throw new ClassCastException(); + def isArrayLoad: Boolean = false def asArrayLoad: ArrayLoad[V] = throw new ClassCastException(); def asArrayLength: ArrayLength[V] = throw new ClassCastException(); def isFieldRead: Boolean = false @@ -177,7 +179,7 @@ case class Compare[+V <: Var[V]]( condition: RelationalOperator, right: Expr[V] ) extends Expr[V] { - + final override def isCompare: Boolean = true final override def asCompare: this.type = this final override def astID: Int = Compare.ASTID final override def cTpe: ComputationalType = ComputationalTypeInt @@ -544,6 +546,7 @@ object NewArray { final val ASTID = -18 } case class ArrayLoad[+V <: Var[V]](pc: PC, index: Expr[V], arrayRef: Expr[V]) extends ArrayExpr[V] { final override def asArrayLoad: this.type = this + final override def isArrayLoad: Boolean = true final override def astID: Int = ArrayLoad.ASTID final override def cTpe: ComputationalType = ComputationalTypeReference final override def isSideEffectFree: Boolean = false diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala index 059dfb8e9d..a0914aa47e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala @@ -79,11 +79,19 @@ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { def asCheckcast: Checkcast[V] = throw new ClassCastException(); def isAssignment: Boolean = false + def isArrayStore: Boolean = false def isExprStmt: Boolean = false def isNonVirtualMethodCall: Boolean = false def isVirtualMethodCall: Boolean = false def isStaticMethodCall: Boolean = false + def isIf: Boolean = false + def isMonitorEnter: Boolean = false + def isMonitorExit: Boolean = false + def isPutStatic: Boolean = false + def isPutField: Boolean = false + def isNop: Boolean = false + def isReturnValue: Boolean = false } /** @@ -109,6 +117,7 @@ case class If[+V <: Var[V]]( ) extends Stmt[V] { final override def asIf: this.type = this + final override def isIf: Boolean = true final override def astID: Int = If.ASTID final def leftExpr: Expr[V] = left final def rightExpr: Expr[V] = right @@ -121,7 +130,7 @@ case class If[+V <: Var[V]]( */ def targetStmt: Int = target - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -169,9 +178,10 @@ case class Goto(pc: PC, private var target: Int) extends VariableFreeStmt { final override def asGoto: this.type = this final override def astID: Int = Goto.ASTID - final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = true + final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = + true - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -207,9 +217,10 @@ case class Ret(pc: PC, private var returnAddresses: PCs) extends VariableFreeStm final override def asRet: this.type = this final override def astID: Int = Ret.ASTID - final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = true + final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = + true - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -241,9 +252,10 @@ case class JSR(pc: PC, private[tac] var target: Int) extends VariableFreeStmt { final override def asJSR: this.type = this final override def astID: Int = JSR.ASTID - final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = true + final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = + true - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -289,7 +301,7 @@ case class Switch[+V <: Var[V]]( p(index) } - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -361,7 +373,7 @@ case class Assignment[+V <: Var[V]]( p(expr) } - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -390,12 +402,13 @@ object Assignment { case class ReturnValue[+V <: Var[V]](pc: Int, expr: Expr[V]) extends Stmt[V] { final override def asReturnValue: this.type = this + final override def isReturnValue: Boolean = true final override def astID: Int = ReturnValue.ASTID final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { p(expr) } - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -425,7 +438,7 @@ sealed abstract class SimpleStmt extends VariableFreeStmt { /** * Nothing to do. */ - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = {} @@ -436,7 +449,8 @@ case class Return(pc: Int) extends SimpleStmt { final override def asReturn: this.type = this final override def astID: Int = Return.ASTID - final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = true + final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = + true final override def isSideEffectFree: Boolean = { // IMPROVE Check if the method does call synchronization statements; if so we may get an exception when we return from the method; otherwise the method is side-effect free @@ -474,8 +488,10 @@ object Return { case class Nop(pc: Int) extends SimpleStmt { final override def asNop: this.type = this + final override def isNop: Boolean = true final override def astID: Int = Nop.ASTID - final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = true + final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = + true final override def isSideEffectFree: Boolean = true @@ -491,7 +507,7 @@ sealed abstract class SynchronizationStmt[+V <: Var[V]] extends Stmt[V] { def objRef: Expr[V] - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -503,6 +519,9 @@ sealed abstract class SynchronizationStmt[+V <: Var[V]] extends Stmt[V] { case class MonitorEnter[+V <: Var[V]](pc: PC, objRef: Expr[V]) extends SynchronizationStmt[V] { final override def asMonitorEnter: this.type = this + + final override def isMonitorEnter: Boolean = true + final override def astID: Int = MonitorEnter.ASTID final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { p(objRef) @@ -529,6 +548,8 @@ object MonitorEnter { case class MonitorExit[+V <: Var[V]](pc: PC, objRef: Expr[V]) extends SynchronizationStmt[V] { final override def asMonitorExit: this.type = this + + final override def isMonitorExit: Boolean = true final override def astID: Int = MonitorExit.ASTID final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { p(objRef) @@ -561,6 +582,7 @@ case class ArrayStore[+V <: Var[V]]( ) extends Stmt[V] { final override def asArrayStore: this.type = this + final override def isArrayStore: Boolean = true final override def astID: Int = ArrayStore.ASTID final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { p(arrayRef) && p(index) && p(value) @@ -571,7 +593,7 @@ case class ArrayStore[+V <: Var[V]]( false } - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -601,7 +623,7 @@ case class Throw[+V <: Var[V]](pc: PC, exception: Expr[V]) extends Stmt[V] { p(exception) } - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -650,12 +672,13 @@ case class PutStatic[+V <: Var[V]]( ) extends FieldWriteAccessStmt[V] { final override def asPutStatic: this.type = this + final override def isPutStatic: Boolean = true final override def astID: Int = PutStatic.ASTID final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { p(value) } - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -694,14 +717,14 @@ case class PutField[+V <: Var[V]]( objRef: Expr[V], value: Expr[V] ) extends FieldWriteAccessStmt[V] { - + final override def isPutField: Boolean = true final override def asPutField: this.type = this final override def astID: Int = PutField.ASTID final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { p(objRef) && p(value) } - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -754,12 +777,14 @@ sealed abstract class InstanceMethodCall[+V <: Var[V]] extends MethodCall[V] { p(receiver) && params.forall(param ⇒ p(param)) } - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { receiver.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - params foreach { p ⇒ p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) } + params foreach { p ⇒ + p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } } } @@ -918,11 +943,13 @@ case class StaticMethodCall[+V <: Var[V]]( resolveCallTarget(p).toSet } - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { - params.foreach { p ⇒ p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) } + params.foreach { p ⇒ + p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } } final override def toCanonicalForm( @@ -972,11 +999,13 @@ case class InvokedynamicMethodCall[+V <: Var[V]]( params.forall(param ⇒ p(param)) } - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { - params.foreach { p ⇒ p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) } + params.foreach { p ⇒ + p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } } override def hashCode(): Int = { @@ -1018,7 +1047,7 @@ case class ExprStmt[+V <: Var[V]](pc: Int, expr: Expr[V]) extends AssignmentLike p(expr) } - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { @@ -1112,11 +1141,13 @@ case class CaughtException[+V <: Var[V]]( final override def isSideEffectFree: Boolean = false - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { - throwingStmts = throwingStmts map { pc ⇒ ai.remapPC(pcToIndex)(pc) } + throwingStmts = throwingStmts map { pc ⇒ + ai.remapPC(pcToIndex)(pc) + } } final override def toCanonicalForm( @@ -1185,7 +1216,7 @@ case class Checkcast[+V <: Var[V]](pc: PC, value: Expr[V], cmpTpe: ReferenceType p(value) } - private[tac] override def remapIndexes( + override private[tac] def remapIndexes( pcToIndex: Array[Int], isIndexOfCaughtExceptionStmt: Int ⇒ Boolean ): Unit = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/APIBasedAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/APIBasedAnalysis.scala index 0bdab16e68..96d2be4d6b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/APIBasedAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/APIBasedAnalysis.scala @@ -71,12 +71,10 @@ trait APIBasedAnalysis extends FPCFAnalysis { } if (callersEOptP.isRefinable) - results ::= InterimPartialResult( - Some(callersEOptP), c(newSeenCallers) - ) + results ::= InterimPartialResult(Set(callersEOptP), c(newSeenCallers)) Results(results) - case _: EPK[_, _] ⇒ InterimPartialResult(Some(callersEOptP), c(seenCallers)) + case _: EPK[_, _] ⇒ InterimPartialResult(Set(callersEOptP), c(seenCallers)) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/AbstractIFDSAnalysis.scala index ebfda6b5e2..5bfe04dbed 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/AbstractIFDSAnalysis.scala @@ -219,7 +219,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn return InterimResult.forUB( entity, createPropertyValue(Map.empty), - Seq(epk), + Set(epk), _ ⇒ performAnalysis(entity) ); @@ -340,11 +340,11 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn collectResult(state.cfg.abnormalReturnNode) )) - var dependees: Traversable[SomeEOptionP] = state.pendingIfdsDependees.values + var dependees: Set[SomeEOptionP] = state.pendingIfdsDependees.valuesIterator.toSet // In the follwing, we really want to avoid useless copying of dependees: if (state.cgDependency.isDefined) { if (dependees.isEmpty) { - dependees = Seq(state.cgDependency.get) + dependees = Set(state.cgDependency.get) } else { // We only implement what is required by the propery store/interface new Iterable[SomeEOptionP] { @@ -863,10 +863,10 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn case FinalP(p) ⇒ Result(source, p) case ep @ InterimUBP(ub: Property) ⇒ - InterimResult.forUB(source, ub, Seq(ep), c) + InterimResult.forUB(source, ub, Set(ep), c) case epk ⇒ - InterimResult.forUB(source, createPropertyValue(Map.empty), Seq(epk), c) + InterimResult.forUB(source, createPropertyValue(Map.empty), Set(epk), c) } c(propertyStore((declaredMethods(source._1.definedMethod), source._2), propertyKey.key)) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/FieldLocalityState.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/FieldLocalityState.scala index e73eced836..d2b1d7f911 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/FieldLocalityState.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/FieldLocalityState.scala @@ -7,9 +7,9 @@ package analyses import scala.collection.mutable import org.opalj.collection.immutable.IntTrieSet -import org.opalj.fpcf.Entity import org.opalj.fpcf.EOptionP import org.opalj.fpcf.Property +import org.opalj.fpcf.SomeEOptionP import org.opalj.br.DeclaredMethod import org.opalj.br.Field import org.opalj.br.Method @@ -39,12 +39,12 @@ class FieldLocalityState(val field: Field, val thisIsCloneable: Boolean) { private[this] var temporary: FieldLocality = LocalField - def dependees: Traversable[EOptionP[Entity, Property]] = { + def dependees: Set[SomeEOptionP] = { // TODO This really looks very imperformant... (declaredMethodsDependees.iterator ++ definitionSitesDependees.valuesIterator.map(_._1) ++ tacDependees.valuesIterator ++ - calleeDependees.valuesIterator.map(_._1).filter(_.isRefinable)).toTraversable + calleeDependees.valuesIterator.map(_._1).filter(_.isRefinable)).toSet } def hasNoDependees: Boolean = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0TACAIAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0TACAIAnalysis.scala index 8fb760f8e5..c8b617f66b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0TACAIAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0TACAIAnalysis.scala @@ -75,7 +75,7 @@ class L0TACAIAnalysis private[analyses] (val project: SomeProject) extends FPCFA m, newLB, newUB, - List(currentAIResult), + Set(currentAIResult), c = c ) @@ -86,7 +86,7 @@ class L0TACAIAnalysis private[analyses] (val project: SomeProject) extends FPCFA m, computeTheTACAI(m, aiResult, false), NoTACAI, - List(epk), + Set(epk), c = c ) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L1FieldMutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L1FieldImmutabilityAnalysis.scala similarity index 88% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L1FieldMutabilityAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L1FieldImmutabilityAnalysis.scala index c338548a1e..85c924a220 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L1FieldMutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L1FieldImmutabilityAnalysis.scala @@ -20,14 +20,9 @@ import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.SomeInterimEP import org.opalj.value.ValueInformation import org.opalj.br.fpcf.properties.AtMost -import org.opalj.br.fpcf.properties.DeclaredFinalField -import org.opalj.br.fpcf.properties.EffectivelyFinalField import org.opalj.br.fpcf.properties.EscapeInCallee import org.opalj.br.fpcf.properties.EscapeViaReturn -import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.NoEscape -import org.opalj.br.fpcf.properties.NonFinalFieldByAnalysis -import org.opalj.br.fpcf.properties.NonFinalFieldByLackOfInformation import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler import org.opalj.br.fpcf.FPCFAnalysisScheduler @@ -45,6 +40,9 @@ import org.opalj.br.fpcf.properties.EscapeProperty import org.opalj.tac.common.DefinitionSite import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.br.fpcf.properties.FieldImmutability /** * Simple analysis that checks if a private (static or instance) field is always initialized at @@ -52,11 +50,12 @@ import org.opalj.tac.fpcf.properties.TACAI * * @note Requires that the 3-address code's expressions are not deeply nested. * + * @author Tobias Roth * @author Dominik Helm * @author Florian Kübler * @author Michael Eichberg */ -class L1FieldMutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { +class L1FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { class State( val field: Field, @@ -71,9 +70,9 @@ class L1FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext final val fieldAccessInformation = project.get(FieldAccessInformationKey) final val definitionSites = project.get(DefinitionSitesKey) - def doDetermineFieldMutability(entity: Entity): PropertyComputationResult = { + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { entity match { - case field: Field ⇒ determineFieldMutability(field) + case field: Field ⇒ determineFieldImmutability(field) case _ ⇒ val m = entity.getClass.getName+"is not an org.opalj.br.Field" throw new IllegalArgumentException(m) @@ -87,23 +86,23 @@ class L1FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext * If the analysis is schedulued using its companion object all class files with * native methods are filtered. */ - private[analyses] def determineFieldMutability( + private[analyses] def determineFieldImmutability( field: Field ): ProperPropertyComputationResult = { if (field.isFinal) { - return Result(field, DeclaredFinalField) + return Result(field, ShallowImmutableField) } val thisType = field.classFile.thisType if (field.isPublic) { - return Result(field, NonFinalFieldByLackOfInformation); + return Result(field, MutableField); } val initialClasses = if (field.isProtected || field.isPackagePrivate) { if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, NonFinalFieldByLackOfInformation); + return Result(field, MutableField); } project.classesPerPackage(thisType.packageName) } else { @@ -113,7 +112,7 @@ class L1FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext val classesHavingAccess: Iterator[ClassFile] = if (field.isProtected) { if (typeExtensibility(thisType).isYesOrUnknown) { - return Result(field, NonFinalFieldByLackOfInformation); + return Result(field, MutableField); } val subclassesIterator: Iterator[ClassFile] = classHierarchy.allSubclassTypes(thisType, reflexive = false). @@ -126,7 +125,7 @@ class L1FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } if (classesHavingAccess.exists(_.methods.exists(_.isNative))) { - return Result(field, NonFinalFieldByLackOfInformation); + return Result(field, MutableField); } // We now (compared to the simple one) have to analyze the static initializer as @@ -156,7 +155,7 @@ class L1FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext taCode ← getTACAIOption(method, pcs) } { if (methodUpdatesField(method, taCode, pcs)) - return Result(field, NonFinalFieldByAnalysis); + return Result(field, MutableField); } returnResult() @@ -235,12 +234,12 @@ class L1FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext def returnResult()(implicit state: State): ProperPropertyComputationResult = { if (state.tacDependees.isEmpty && state.escapeDependees.isEmpty) - Result(state.field, EffectivelyFinalField) + Result(state.field, ShallowImmutableField) else InterimResult( state.field, - NonFinalFieldByAnalysis, - EffectivelyFinalField, + MutableField, + ShallowImmutableField, state.escapeDependees ++ state.tacDependees.valuesIterator.map(_._1), c ) @@ -264,7 +263,7 @@ class L1FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } if (isNonFinal) - Result(state.field, NonFinalFieldByAnalysis); + Result(state.field, MutableField); else returnResult() } @@ -327,22 +326,22 @@ class L1FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } } -sealed trait L1FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +sealed trait L1FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeExtensibilityKey, ClosedPackagesKey, FieldAccessInformationKey, DefinitionSitesKey) final override def uses: Set[PropertyBounds] = PropertyBounds.lubs(TACAI, EscapeProperty) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) } /** * Executor for the field mutability analysis. */ -object EagerL1FieldMutabilityAnalysis - extends L1FieldMutabilityAnalysisScheduler +object EagerL1FieldImmutabilityAnalysis + extends L1FieldImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -350,9 +349,9 @@ object EagerL1FieldMutabilityAnalysis override def derivesCollaboratively: Set[PropertyBounds] = Set.empty override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L1FieldMutabilityAnalysis(p) + val analysis = new L1FieldImmutabilityAnalysis(p) val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldMutability) + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) analysis } } @@ -360,16 +359,16 @@ object EagerL1FieldMutabilityAnalysis /** * Executor for the lazy field mutability analysis. */ -object LazyL1FieldMutabilityAnalysis - extends L1FieldMutabilityAnalysisScheduler +object LazyL1FieldImmutabilityAnalysis + extends L1FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L1FieldMutabilityAnalysis(p) + val analysis = new L1FieldImmutabilityAnalysis(p) ps.registerLazyPropertyComputation( - FieldMutability.key, analysis.determineFieldMutability + FieldImmutability.key, analysis.determineFieldImmutability ) analysis } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldMutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala similarity index 88% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldMutabilityAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala index 5970c63b5b..3a3d06dfe7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldMutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala @@ -8,35 +8,19 @@ import scala.annotation.switch import org.opalj.RelationalOperators.EQ import org.opalj.RelationalOperators.NE - import org.opalj.collection.immutable.IntTrieSet import org.opalj.br.fpcf.properties.AtMost -import org.opalj.br.fpcf.properties.DeclaredFinalField -import org.opalj.br.fpcf.properties.EffectivelyFinalField import org.opalj.br.fpcf.properties.EscapeInCallee -import org.opalj.br.fpcf.properties.EscapeProperty import org.opalj.br.fpcf.properties.EscapeViaReturn -import org.opalj.br.fpcf.properties.FieldMutability -import org.opalj.br.fpcf.properties.FieldPrematurelyRead -import org.opalj.br.fpcf.properties.FinalField -import org.opalj.br.fpcf.properties.LazyInitializedField import org.opalj.br.fpcf.properties.NoEscape -import org.opalj.br.fpcf.properties.NonFinalField -import org.opalj.br.fpcf.properties.NonFinalFieldByAnalysis -import org.opalj.br.fpcf.properties.NonFinalFieldByLackOfInformation import org.opalj.br.fpcf.properties.NotPrematurelyReadField import org.opalj.br.fpcf.properties.PrematurelyReadField -import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler import org.opalj.fpcf.EOptionP import org.opalj.fpcf.FinalEP import org.opalj.fpcf.FinalP import org.opalj.fpcf.Entity import org.opalj.fpcf.Result -import org.opalj.fpcf.Property -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.fpcf.InterimEP import org.opalj.fpcf.InterimResult import org.opalj.fpcf.InterimUBP @@ -67,9 +51,6 @@ import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.cfg.BasicBlock import org.opalj.br.cfg.CFG import org.opalj.br.cfg.CFGNode -import org.opalj.br.fpcf.properties.FieldMutability -import org.opalj.br.fpcf.properties.FinalField -import org.opalj.br.fpcf.properties.NonFinalField import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.EscapeProperty import org.opalj.br.fpcf.properties.FieldPrematurelyRead @@ -83,6 +64,11 @@ import org.opalj.ai.pcOfMethodExternalException import org.opalj.tac.common.DefinitionSite import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.br.fpcf.properties.DeepImmutableField +import org.opalj.br.fpcf.properties.DependentImmutableField +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.br.fpcf.properties.MutableField /** * Simple analysis that checks if a private (static or instance) field is always initialized at @@ -90,32 +76,36 @@ import org.opalj.tac.fpcf.properties.TACAI * * @note Requires that the 3-address code's expressions are not deeply nested. * + * @author Tobias Roth * @author Dominik Helm * @author Florian Kübler * @author Michael Eichberg */ -class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { +class L2FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { case class State( - field: Field, - var fieldMutability: FieldMutability = DeclaredFinalField, - var prematurelyReadDependee: Option[EOptionP[Field, FieldPrematurelyRead]] = None, - var purityDependees: Set[EOptionP[DeclaredMethod, Purity]] = Set.empty, - var lazyInitInvocation: Option[(DeclaredMethod, PC)] = None, - var calleesDependee: Option[EOptionP[DeclaredMethod, Callees]] = None, - var fieldMutabilityDependees: Set[EOptionP[Field, FieldMutability]] = Set.empty, - var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, - var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty + field: Field, + var fieldImmutability: FieldImmutability = ShallowImmutableField, //DeclaredFinalField, + var prematurelyReadDependee: Option[EOptionP[Field, FieldPrematurelyRead]] = None, + var purityDependees: Set[EOptionP[DeclaredMethod, Purity]] = Set.empty, + var lazyInitInvocation: Option[(DeclaredMethod, PC)] = None, + var calleesDependee: Option[EOptionP[DeclaredMethod, Callees]] = None, + var fieldImmutabilityDependees: Set[EOptionP[Field, FieldImmutability]] = Set.empty, + var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty ) { + + import org.opalj.fpcf.SomeEOptionP + def hasDependees: Boolean = { prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || fieldMutabilityDependees.nonEmpty || + calleesDependee.isDefined || fieldImmutabilityDependees.nonEmpty || escapeDependees.nonEmpty || tacDependees.nonEmpty } - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - fieldMutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + def dependees: Set[SomeEOptionP] = { + (tacDependees.valuesIterator.map(_._1) ++ prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + fieldImmutabilityDependees ++ escapeDependees).toSet } } @@ -127,8 +117,8 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext final val definitionSites = project.get(DefinitionSitesKey) implicit final val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) - def doDetermineFieldMutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field ⇒ determineFieldMutability(field) + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field ⇒ determineFieldImmutability(field) case _ ⇒ val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" throw new IllegalArgumentException(m) @@ -141,24 +131,27 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext * If the analysis is schedulued using its companion object all class files with * native methods are filtered. */ - private[analyses] def determineFieldMutability( + private[analyses] def determineFieldImmutability( field: Field ): ProperPropertyComputationResult = { + import org.opalj.br.fpcf.properties.MutableField implicit val state: State = State(field) // Fields are not final if they are read prematurely! - if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) - return Result(field, NonFinalFieldByAnalysis); + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { + return Result(field, MutableField) + }; if (field.isFinal) return createResult(); - state.fieldMutability = EffectivelyFinalField + state.fieldImmutability = ShallowImmutableField val thisType = field.classFile.thisType - if (field.isPublic) - return Result(field, NonFinalFieldByLackOfInformation) + if (field.isPublic) { + return Result(field, MutableField) + } // Collect all classes that have access to the field, i.e. the declaring class and possibly // classes in the same package as well as subclasses @@ -166,7 +159,7 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext val initialClasses = if (field.isProtected || field.isPackagePrivate) { if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, NonFinalFieldByLackOfInformation); + return Result(field, MutableField); } project.classesPerPackage(thisType.packageName) } else { @@ -176,7 +169,7 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext val classesHavingAccess: Iterator[ClassFile] = if (field.isProtected) { if (typeExtensibility(thisType).isYesOrUnknown) { - return Result(field, NonFinalFieldByLackOfInformation); + return Result(field, MutableField); } val subclassesIterator: Iterator[ClassFile] = classHierarchy.allSubclassTypes(thisType, reflexive = false). @@ -189,8 +182,9 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } // If there are native methods, we give up - if (classesHavingAccess.exists(_.methods.exists(_.isNative))) - return Result(field, NonFinalFieldByLackOfInformation); + if (classesHavingAccess.exists(_.methods.exists(_.isNative))) { + return Result(field, MutableField) + }; // We now (compared to the simple one) have to analyze the static initializer as // the static initializer can be used to initialize a private field of an instance @@ -216,8 +210,9 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method, pcs) } { - if (methodUpdatesField(method, taCode, pcs)) - return Result(field, NonFinalFieldByAnalysis); + if (methodUpdatesField(method, taCode, pcs)) { + return Result(field, MutableField) + }; } if (state.lazyInitInvocation.isDefined) { @@ -250,12 +245,12 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext def handleCallees(callees: Callees)(implicit state: State): Boolean = { val pc = state.lazyInitInvocation.get._2 if (callees.isIncompleteCallSite(pc)) { - state.fieldMutability = NonFinalFieldByAnalysis + state.fieldImmutability = MutableField true } else { val targets = callees.callees(pc).toTraversable if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { - state.fieldMutability = NonFinalFieldByAnalysis + state.fieldImmutability = MutableField true } else false } @@ -336,16 +331,17 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext * dependees or as Result otherwise. */ def createResult()(implicit state: State): ProperPropertyComputationResult = { - if (state.hasDependees && (state.fieldMutability ne NonFinalFieldByAnalysis)) + if (state.hasDependees && (state.fieldImmutability ne MutableField)) InterimResult( state.field, - NonFinalFieldByAnalysis, - state.fieldMutability, + MutableField, + state.fieldImmutability, state.dependees, c ) - else - Result(state.field, state.fieldMutability) + else { + Result(state.field, state.fieldImmutability) + } } /** @@ -374,16 +370,16 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) isNonDeterministic(newEP) - case FieldMutability.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[Field, FieldMutability]] - state.fieldMutabilityDependees = - state.fieldMutabilityDependees.filter(_.e ne newEP.e) + case FieldImmutability.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Field, FieldImmutability]] + state.fieldImmutabilityDependees = + state.fieldImmutabilityDependees.filter(_.e ne newEP.e) !isFinalField(newEP) } - if (isNotFinal) - Result(state.field, NonFinalFieldByAnalysis) - else + if (isNotFinal) { + Result(state.field, MutableField) + } else createResult() } @@ -542,30 +538,30 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext case PutStatic.ASTID | PutField.ASTID ⇒ if (method.isInitializer) { if (field.isStatic) { - if (method.isConstructor) - return true; + if (method.isConstructor) { + return true + }; } else { val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - if (receiverDefs != SelfReferenceParameter) - return true; + if (receiverDefs != SelfReferenceParameter) { + return true + }; } } else { if (field.isStatic || stmt.asPutField.objRef.asVar.definedBy == SelfReferenceParameter) { - // We consider lazy initialization if there is only single write - // outside an initializer, so we can ignore synchronization - if (state.fieldMutability == LazyInitializedField) - return true; // A lazily initialized instance field must be initialized only // by its owning instance if (!field.isStatic && - stmt.asPutField.objRef.asVar.definedBy != SelfReferenceParameter) - return true; + stmt.asPutField.objRef.asVar.definedBy != SelfReferenceParameter) { + return true + }; val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; + if (defaultValue.isEmpty) { + return true + }; // A field written outside an initializer must be lazily // initialized or it is non-final @@ -576,10 +572,11 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext taCode.stmts, taCode.cfg, taCode.pcToIndex - )) - return true; + )) { + return true + }; - state.fieldMutability = LazyInitializedField + state.fieldImmutability = ShallowImmutableField } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { // note that here we assume real three address code (flat hierarchy) @@ -761,7 +758,7 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext val expr = code(index).asAssignment.expr expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { case Some(field) ⇒ - isFinalField(propertyStore(field, FieldMutability.key)) + isFinalField(propertyStore(field, FieldImmutability.key)) case _ ⇒ // Unknown field false }) @@ -778,7 +775,7 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext case GetStatic.ASTID | GetField.ASTID ⇒ value.asFieldRead.resolveField(p) match { case Some(field) ⇒ - isFinalField(propertyStore(field, FieldMutability.key)) + isFinalField(propertyStore(field, FieldImmutability.key)) case _ ⇒ // Unknown field false } @@ -1035,7 +1032,7 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } /** - * Checkes if the method that defines the value assigned to a (potentially) lazily initialized + * Checks if the method that defines the value assigned to a (potentially) lazily initialized * field is deterministic, ensuring that the same value is written even for concurrent * executions. */ @@ -1055,18 +1052,18 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext * ensuring that the same value is written even for concurrent executions. */ def isFinalField( - eop: EOptionP[Field, FieldMutability] + eop: EOptionP[Field, FieldImmutability] )(implicit state: State): Boolean = eop match { - case LBP(_: FinalField) ⇒ + case LBP(DeepImmutableField | DependentImmutableField | ShallowImmutableField) ⇒ true - case UBP(_: NonFinalField) ⇒ false + case UBP(MutableField) ⇒ false case _ ⇒ - state.fieldMutabilityDependees += eop + state.fieldImmutabilityDependees += eop true } } -trait L2FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +trait L2FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { override def requiredProjectInformation: ProjectInformationKeys = Seq( TypeExtensibilityKey, @@ -1080,25 +1077,25 @@ trait L2FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { PropertyBounds.lub(Purity), PropertyBounds.lub(FieldPrematurelyRead), PropertyBounds.ub(TACAI), - PropertyBounds.lub(FieldMutability), + PropertyBounds.lub(FieldImmutability), PropertyBounds.ub(EscapeProperty) ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) } /** * Executor for the field mutability analysis. */ -object EagerL2FieldMutabilityAnalysis - extends L2FieldMutabilityAnalysisScheduler +object EagerL2FieldImmutabilityAnalysis + extends L2FieldImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L2FieldMutabilityAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldMutability) + val analysis = new L2FieldImmutabilityAnalysis(p) + val fields = p.allProjectClassFiles.flatMap(_.fields) // p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) analysis } @@ -1110,8 +1107,8 @@ object EagerL2FieldMutabilityAnalysis /** * Executor for the lazy field mutability analysis. */ -object LazyL2FieldMutabilityAnalysis - extends L2FieldMutabilityAnalysisScheduler +object LazyL2FieldImmutabilityAnalysis + extends L2FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { final override def register( @@ -1119,9 +1116,9 @@ object LazyL2FieldMutabilityAnalysis ps: PropertyStore, unused: Null ): FPCFAnalysis = { - val analysis = new L2FieldMutabilityAnalysis(p) + val analysis = new L2FieldImmutabilityAnalysis(p) ps.registerLazyPropertyComputation( - FieldMutability.key, analysis.determineFieldMutability + FieldImmutability.key, analysis.determineFieldImmutability ) analysis } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/SystemPropertiesAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/SystemPropertiesAnalysisScheduler.scala index 99150792fe..5f8000c21b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/SystemPropertiesAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/SystemPropertiesAnalysisScheduler.scala @@ -91,7 +91,7 @@ class SystemPropertiesAnalysisScheduler private[analyses] ( project, SystemProperties.key, update, - Some(tacaiEP), + Set(tacaiEP), continuationForTAC(declaredMethod) ) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIBasedAPIBasedAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIBasedAPIBasedAnalysis.scala index f63ebdbed8..fb1d034922 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIBasedAPIBasedAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIBasedAPIBasedAnalysis.scala @@ -68,11 +68,11 @@ trait TACAIBasedAPIBasedAnalysis extends APIBasedAnalysis { result else { val continuationResult = - InterimPartialResult(Some(tacEOptP), continueDirectCallWithTAC(caller, pc)) + InterimPartialResult(Set(tacEOptP), continueDirectCallWithTAC(caller, pc)) Results(result, continuationResult) } - case _ ⇒ InterimPartialResult(Some(tacEOptP), continueDirectCallWithTAC(caller, pc)) + case _ ⇒ InterimPartialResult(Set(tacEOptP), continueDirectCallWithTAC(caller, pc)) } private[this] def processNewCaller( @@ -99,7 +99,7 @@ trait TACAIBasedAPIBasedAnalysis extends APIBasedAnalysis { else { val continuationResult = InterimPartialResult( - Some(tacEPS), + Set(tacEPS), continueIndirectCallWithTACOrCallees( caller, pc, tacEPS, calleesEPS ) @@ -126,7 +126,7 @@ trait TACAIBasedAPIBasedAnalysis extends APIBasedAnalysis { case _ ⇒ InterimPartialResult( - List(tacEOptP, calleesEOptP), + Set(tacEOptP, calleesEOptP), continueIndirectCallWithTACOrCallees(caller, pc, tacEOptP, calleesEOptP) ) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIBasedAnalysisState.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIBasedAnalysisState.scala index a2d473020d..9e8e9fe853 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIBasedAnalysisState.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIBasedAnalysisState.scala @@ -34,10 +34,10 @@ trait TACAIBasedAnalysisState { * Inherited classes that introduce new dependencies must override this method and call add a * call to super! */ - def dependees: List[SomeEOptionP] = if (_tacDependee.isRefinable) - List(_tacDependee) + def dependees: Set[SomeEOptionP] = if (_tacDependee.isRefinable) + Set(_tacDependee) else - Nil + Set.empty final def updateTACDependee(tacDependee: EOptionP[Method, TACAI]): Unit = { _tacDependee = tacDependee 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 8ad9d4a7b3..854c403056 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 @@ -52,7 +52,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { * Subclasses that might have other dependencies must override this method and should call * `super.c(...)` for updates of other property kinds then the new one. * - * @see [[org.opalj.tac.fpcf.analyses.cg.rta.RTACallGraphAnalysis.c*]] for an example. + * @see [[org.opalj.tac.fpcf.analyses.cg.rta.RTACallGraphAnalysis.c]] for an example. */ def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = eps match { case UBP(tacai: TACAI) if tacai.tac.isDefined ⇒ 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 0713d55ae7..a02a16b827 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 @@ -40,7 +40,7 @@ import org.opalj.tac.fpcf.analyses.pointsto.AllocationSiteBasedAnalysis import org.opalj.tac.fpcf.properties.TheTACAI /** - * Models the behavior for [[java.security.AccessController.doPrivileged*]]. + * Models the behavior for `java.security.AccessController.doPrivileged*`. * * On each call of the concrete [[doPrivilegedMethod]] method it will call the * [[declaredRunMethod]] upon its first parameter and returns the result of this call. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/LoadedClassesAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/LoadedClassesAnalysis.scala index 1b608ed5bd..0f0d31820e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/LoadedClassesAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/LoadedClassesAnalysis.scala @@ -103,7 +103,7 @@ class LoadedClassesAnalysis( } else { InterimPartialResult( Nil, - Some(tacaiEP), + Set(tacaiEP), continuationForTAC(declaredMethod) ) } @@ -120,7 +120,7 @@ class LoadedClassesAnalysis( case _ ⇒ InterimPartialResult( Nil, - Some(eps), + Set(eps), continuationForTAC(method) ) } @@ -141,7 +141,7 @@ class LoadedClassesAnalysis( Some(PartialResult(propertyStore, LoadedClasses.key, update(newLoadedClasses))) else None, - Some(tacaiEP), + Set(tacaiEP), continuationForTAC(declaredMethod) ) } else if (newLoadedClasses.nonEmpty) { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ReachableMethodAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ReachableMethodAnalysis.scala index 33fd750744..b32ca76259 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ReachableMethodAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ReachableMethodAnalysis.scala @@ -68,7 +68,7 @@ trait ReachableMethodAnalysis extends FPCFAnalysis { if (tacEP.hasUBP && tacEP.ub.tac.isDefined) { processMethod(declaredMethod.asDefinedMethod, tacEP.asEPS) } else { - InterimPartialResult(Seq(tacEP), continuationForTAC(declaredMethod.asDefinedMethod)) + InterimPartialResult(Set(tacEP), continuationForTAC(declaredMethod.asDefinedMethod)) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/StaticInitializerAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/StaticInitializerAnalysis.scala index 9add2d1fba..72f873a276 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/StaticInitializerAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/StaticInitializerAnalysis.scala @@ -90,7 +90,7 @@ class StaticInitializerAnalysis(val project: SomeProject) extends FPCFAnalysis { val emptyResult = if (state.lcDependee.isDefined) Some(InterimPartialResult( None, - state.lcDependee, + state.lcDependee.toSet, continuation )) else 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 761f8a7291..71027b5038 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 @@ -144,13 +144,13 @@ class PointsToBasedCGState[PointsToSet <: PointsToSetLike[_, _, PointsToSet]]( hasPointsToDependees || super.hasOpenDependencies } - override def dependees: List[SomeEOptionP] = { + override def dependees: Set[SomeEOptionP] = { // IMPROVE: make it more efficient (maybe use immutable map and join traversables) var allDependees = super.dependees _pointsToDependees.valuesIterator.foreach { d ⇒ assert(_dependeeToDependers.contains(d.e)) - allDependees ::= d + allDependees += d } allDependees 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 2e4d71b6f1..78cdabb39c 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 @@ -10,6 +10,7 @@ import org.opalj.fpcf.Entity import org.opalj.fpcf.EOptionP import org.opalj.fpcf.EPS import org.opalj.fpcf.EUBPS +import org.opalj.fpcf.FinalEP import org.opalj.fpcf.InterimPartialResult import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.PropertyBounds @@ -38,6 +39,7 @@ import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.analyses.pointsto.AbstractPointsToBasedAnalysis import org.opalj.tac.fpcf.analyses.pointsto.AllocationSiteBasedAnalysis import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.tac.fpcf.properties.TheTACAI /** * On calls to Thread.start(), it adds calls to the corresponding run method. @@ -47,7 +49,7 @@ import org.opalj.tac.fpcf.properties.TACAI * @author Dominik Helm */ trait PointsToBasedThreadStartAnalysis - extends APIBasedAnalysis + extends TACAIBasedAPIBasedAnalysis with AbstractPointsToBasedAnalysis { def threadStartMethod: DeclaredMethod @@ -56,16 +58,21 @@ trait PointsToBasedThreadStartAnalysis override type State = PointsToBasedCGState[PointsToSet] override type DependerType = CallSiteT - override def handleNewCaller( - caller: DefinedMethod, pc: Int, isDirect: Boolean + override def processNewCaller( + caller: DefinedMethod, + pc: Int, + tac: TACode[TACMethodParameter, V], + receiverOption: Option[Expr[V]], + params: Seq[Option[Expr[V]]], + targetVarOption: Option[V], + isDirect: Boolean ): ProperPropertyComputationResult = { val indirectCalls = new IndirectCalls() - val tacEPS = propertyStore(caller.definedMethod, TACAI.key) - implicit val state: State = new PointsToBasedCGState[PointsToSet](caller, tacEPS) + implicit val state: State = new PointsToBasedCGState[PointsToSet](caller, FinalEP(caller.definedMethod, TheTACAI(tac))) if (isDirect) { - val receiver = state.tac.stmts(state.tac.pcToIndex(pc)).asInstanceMethodCall.receiver + val receiver = tac.stmts(state.tac.pcToIndex(pc)).asInstanceMethodCall.receiver handleStart(caller, receiver, pc, indirectCalls) } else indirectCalls.addIncompleteCallSite(pc) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/TamiFlexCallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/TamiFlexCallGraphAnalysis.scala index 7513fed5a1..796d295c7d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/TamiFlexCallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/TamiFlexCallGraphAnalysis.scala @@ -107,6 +107,7 @@ class TamiFlexMethodInvokeAnalysis private[analyses] ( Results(indirectCalls.partialResults(caller)) } + private[this] def handleMethodInvoke( caller: DefinedMethod, pc: Int, diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/InstantiatedTypesAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/InstantiatedTypesAnalysis.scala index 14fb866b4a..7c9a817c77 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/InstantiatedTypesAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/InstantiatedTypesAnalysis.scala @@ -177,7 +177,7 @@ class InstantiatedTypesAnalysis private[analyses] ( NoResult } else { InterimPartialResult( - Some(callersEOptP), + Set(callersEOptP), continuation(declaredMethod, declaredType, newSeenSuperCallers) ) } 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 eb365bf419..0febf0728c 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 @@ -9,9 +9,8 @@ package rta import scala.collection.mutable import org.opalj.collection.immutable.UIDSet -import org.opalj.fpcf.Entity import org.opalj.fpcf.EOptionP -import org.opalj.fpcf.Property +import org.opalj.fpcf.SomeEOptionP import org.opalj.br.DefinedMethod import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject @@ -101,9 +100,9 @@ class RTAState( _instantiatedTypesDependee.isRefinable || super.hasOpenDependencies } - override def dependees: List[EOptionP[Entity, Property]] = { + override def dependees: Set[SomeEOptionP] = { if (instantiatedTypesDependee().isDefined) - instantiatedTypesDependee().get :: super.dependees + super.dependees + instantiatedTypesDependee().get else super.dependees } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/InstantiatedTypesAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/InstantiatedTypesAnalysis.scala index e268d9165e..cc3436aa17 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/InstantiatedTypesAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/InstantiatedTypesAnalysis.scala @@ -124,7 +124,7 @@ class InstantiatedTypesAnalysis private[analyses] ( } else { val reRegistration = InterimPartialResult( - Some(callersEOptP), + Set(callersEOptP), continuation(declaredMethod, declaredType, newSeenCallers) ) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/LibraryInstantiatedTypesBasedEntryPointsAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/LibraryInstantiatedTypesBasedEntryPointsAnalysis.scala index 0e4e0b6dd6..6a029d75a2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/LibraryInstantiatedTypesBasedEntryPointsAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/LibraryInstantiatedTypesBasedEntryPointsAnalysis.scala @@ -83,7 +83,7 @@ class LibraryInstantiatedTypesBasedEntryPointsAnalysis private[analyses] ( val c = if (!isFinal) Some(InterimPartialResult( Nil, - Some(instantiatedTypes), + Set(instantiatedTypes), continuation(size) )) else 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 169440e0f9..fc75f0f26e 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 @@ -6,19 +6,18 @@ package analyses package cg package xta +import scala.collection.mutable + +import org.opalj.collection.immutable.UIDSet +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.SomeEOptionP import org.opalj.br.DefinedMethod import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.ReferenceType import org.opalj.br.fpcf.properties.cg.InstantiatedTypes -import org.opalj.collection.immutable.UIDSet -import org.opalj.fpcf.EOptionP -import org.opalj.fpcf.Entity -import org.opalj.fpcf.Property import org.opalj.tac.fpcf.properties.TACAI -import scala.collection.mutable - /** * Manages the state of each method analyzed by [[PropagationBasedCallGraphAnalysis]]. * @@ -106,7 +105,7 @@ class PropagationBasedCGState( _instantiatedTypesDependeeMap.exists(_._2.isRefinable) || super.hasOpenDependencies } - override def dependees: List[EOptionP[Entity, Property]] = { - _instantiatedTypesDependeeMap.values ++: super.dependees + override def dependees: Set[SomeEOptionP] = { + super.dependees ++ _instantiatedTypesDependeeMap.valuesIterator } } \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/TypePropagationState.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/TypePropagationState.scala index 8e7067e316..e4cf3be12c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/TypePropagationState.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/TypePropagationState.scala @@ -208,17 +208,17 @@ final class TypePropagationState( _backwardPropagationDependees.nonEmpty } - override def dependees: List[SomeEOptionP] = { + override def dependees: Set[SomeEOptionP] = { var dependees = super.dependees - dependees ::= _ownInstantiatedTypesDependee + dependees += _ownInstantiatedTypesDependee if (calleeDependee.isDefined) - dependees ::= calleeDependee.get + dependees += calleeDependee.get // Note: The values are copied here. The "++" operator on List // forces immediate evaluation of the map values iterator. - dependees ++= _backwardPropagationDependees.values + dependees ++= _backwardPropagationDependees.valuesIterator dependees } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/AbstractEscapeAnalysisState.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/AbstractEscapeAnalysisState.scala index 80ebc0b30d..1c3b2265e1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/AbstractEscapeAnalysisState.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/AbstractEscapeAnalysisState.scala @@ -9,6 +9,7 @@ import org.opalj.collection.immutable.IntTrieSet import org.opalj.fpcf.Entity import org.opalj.fpcf.EOptionP import org.opalj.fpcf.Property +import org.opalj.fpcf.SomeEOptionP import org.opalj.br.analyses.VirtualFormalParameter import org.opalj.br.fpcf.properties.EscapeProperty import org.opalj.br.fpcf.properties.NoEscape @@ -30,6 +31,7 @@ trait AbstractEscapeAnalysisState { // callees of declared methods, i.e. different entities for each property kind. // therefore using a map is safe! private[this] var _dependees = Map.empty[Entity, EOptionP[Entity, Property]] + private[this] var _dependeesSet = Set.empty[SomeEOptionP] private[this] var _mostRestrictiveProperty: EscapeProperty = NoEscape @@ -56,6 +58,7 @@ trait AbstractEscapeAnalysisState { @inline private[escape] final def addDependency(eOptionP: EOptionP[Entity, Property]): Unit = { assert(!_dependees.contains(eOptionP.e)) _dependees += eOptionP.e → eOptionP + _dependeesSet += eOptionP } /** @@ -66,7 +69,9 @@ trait AbstractEscapeAnalysisState { ep: EOptionP[Entity, Property] ): Unit = { assert(_dependees.contains(ep.e)) + val oldEOptionP = _dependees(ep.e) _dependees -= ep.e + _dependeesSet -= oldEOptionP } /** @@ -85,8 +90,8 @@ trait AbstractEscapeAnalysisState { /** * The set of open dependees. */ - private[escape] final def dependees: Traversable[EOptionP[Entity, Property]] = { - _dependees.values + private[escape] final def dependees: Set[SomeEOptionP] = { + _dependeesSet } /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/ReturnValueFreshnessAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/ReturnValueFreshnessAnalysis.scala index 4eee9382f6..f9a4579cff 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/ReturnValueFreshnessAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/ReturnValueFreshnessAnalysis.scala @@ -72,12 +72,12 @@ class ReturnValueFreshnessState(val dm: DefinedMethod) { private[this] var upperBound: ReturnValueFreshness = FreshReturnValue // TODO: Use EOptionPSet - def dependees: Traversable[EOptionP[Entity, Property]] = { + def dependees: Set[SomeEOptionP] = { (returnValueDependees.valuesIterator ++ fieldDependees.valuesIterator ++ defSiteDependees.valuesIterator ++ tacaiDependee.iterator ++ - _calleesDependee.iterator.filter(_.isRefinable)).toTraversable + _calleesDependee.iterator.filter(_.isRefinable)).toSet } def hasDependees: Boolean = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L1ClassImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L1ClassImmutabilityAnalysis.scala new file mode 100644 index 0000000000..f1fdb4df72 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L1ClassImmutabilityAnalysis.scala @@ -0,0 +1,537 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package tac +package fpcf +package analyses +package immutability + +import org.opalj.br.ClassFile +import org.opalj.br.ClassSignature +import org.opalj.br.ClassTypeSignature +import org.opalj.br.FormalTypeParameter +import org.opalj.br.ObjectType +import org.opalj.br.SimpleClassTypeSignature +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.fpcf.FPCFEagerAnalysisScheduler +import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler +import org.opalj.br.fpcf.properties.ClassImmutability +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.DeepImmutableField +import org.opalj.br.fpcf.properties.DependentImmutableClass +import org.opalj.br.fpcf.properties.DependentImmutableField +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.MutableClass +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.ShallowImmutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.fpcf.ELBP +import org.opalj.fpcf.EOptionP + +import org.opalj.fpcf.EPS +import org.opalj.fpcf.Entity +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.IncrementalResult + +import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.LBP +import org.opalj.fpcf.LUBP +import org.opalj.fpcf.MultiResult +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyComputation +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.Result +import org.opalj.fpcf.Results +import org.opalj.fpcf.SomeEPS +import org.opalj.fpcf.UBP +import org.opalj.log.LogContext +import org.opalj.log.OPALLogger +import org.opalj.fpcf.EPK +import org.opalj.fpcf.InterimE + +/** + * + * Determines the mutability of instances of a specific class. In case the class + * is abstract the (implicit) assumption is made that all abstract methods (if any) are/can + * be implemented without necessarily/always requiring additional state; i.e., only the currently + * defined fields are taken into consideration. An interfaces is always considered to be immutable. + * If you need to know if all possible instances of an interface or some type; i.e., all instances + * of the classes that implement the respective interface/inherit from some class are immutable, + * you can query the [[org.opalj.br.fpcf.properties.TypeImmutability]] property. + * + * In case of incomplete class hierarchies or if the class hierarchy is complete, but some + * class files are not found the sound approximation is done that the respective classes are + * mutable. + * + * This analysis uses the [[org.opalj.br.fpcf.properties.FieldImmutability]] property to determine + * the field immutability. + * + * TODO Discuss the case if a constructor calls an instance method which is overrideable (See Verifiable Functional Purity Paper for some arguements.) + * + * @author Michael Eichberg + * @author Florian Kübler + * @author Dominik Helm + * @author Tobias Roth + * + */ +class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { + /* + * The analysis is implemented as an incremental analysis which starts with the analysis + * of those types which directly inherit from java.lang.Object and then propagates the + * mutability information down the class hierarchy. + * + * This propagation needs to be done eagerly to ensure that all types are associated with + * some property when the initial computation finishes and fallback properties are associated. + */ + + /** + * Creates a result object that sets this type and all subclasses of if to the given + * immutability rating. + */ + @inline private[this] def createResultForAllSubtypes( + t: ObjectType, + immutability: ClassImmutability + ): MultiResult = { + val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) + val r = allSubtypes.map { st ⇒ + new FinalEP(st, immutability) + }.toSeq + MultiResult(r) + } + @inline private[this] def createIncrementalResult( + t: ObjectType, + cfMutability: EOptionP[Entity, Property], + cfMutabilityIsFinal: Boolean, + result: ProperPropertyComputationResult + ): IncrementalResult[ClassFile] = { + var results: List[ProperPropertyComputationResult] = List(result) + var nextComputations: List[(PropertyComputation[ClassFile], ClassFile)] = Nil + val directSubtypes = classHierarchy.directSubtypesOf(t) + directSubtypes.foreach { t ⇒ + project.classFile(t) match { + case Some(scf) ⇒ + nextComputations ::= ( + ( + determineL1ClassImmutability(t, cfMutability, cfMutabilityIsFinal, + lazyComputation = false), scf + ) + ) + case None ⇒ + OPALLogger.warn( + "project configuration - object immutability analysis", + s"missing class file of ${t.toJava}; setting all subtypes to mutable" + ) + results ::= createResultForAllSubtypes(t, MutableClass) + } + } + IncrementalResult(Results(results), nextComputations.iterator) + } + + def determineGenericTypeBounds(classFile: ClassFile): Set[(String, String)] = { + var genericTypeBounds: Set[(String, String)] = Set.empty + classFile.attributes.toList.collectFirst({ + case ClassSignature(typeParameters, _, _) ⇒ typeParameters.collect({ + case ftp @ FormalTypeParameter(_, _, _) ⇒ ftp + }) + .foreach { + case FormalTypeParameter(identifier, classBound, _) ⇒ classBound match { + + case Some(ClassTypeSignature(_, SimpleClassTypeSignature(simpleName, _), _)) ⇒ + genericTypeBounds += ((identifier, simpleName)) + + case _ ⇒ + } + + } + }) + genericTypeBounds + } + + def doDetermineL1ClassImmutability(e: Entity): ProperPropertyComputationResult = { + e match { + case t: ObjectType ⇒ + //this is safe + val a = classHierarchy.superclassType(t) + a match { + case None ⇒ Result(t, MutableClass); + case Some(superClassType) ⇒ + val cf = project.classFile(t) match { + case None ⇒ + return Result(t, MutableClass); //TODO consider other lattice element + case Some(cf) ⇒ cf + } + + propertyStore(superClassType, ClassImmutability.key) match { + case UBP(MutableClass) ⇒ + Result(t, MutableClass) + case eps: EPS[ObjectType, ClassImmutability] ⇒ + determineL1ClassImmutability( + superClassType, + eps, + eps.isFinal, + lazyComputation = true + )(cf) + case epk ⇒ + determineL1ClassImmutability( + superClassType, + epk, + superClassMutabilityIsFinal = false, + lazyComputation = true + )(cf) + } + } + case _ ⇒ + val m = e.getClass.getSimpleName+" is not an org.opalj.br.ObjectType" + throw new IllegalArgumentException(m) + } + } + + private[this] object SuperClassKey + + /** + * Determines the immutability of instances of the given class type `t`. + * + * @param superClassType The direct super class of the given object type `t`. + * Can be `null` if `superClassMutability` is `ImmutableObject`. + * @param superClassInformation The mutability of the given super class. The mutability + * must not be "MutableObject"; this case has to be handled explicitly. Hence, + * the mutability is either unknown, immutable or (at least) conditionally immutable. + */ + def determineL1ClassImmutability( + superClassType: ObjectType, + superClassInformation: EOptionP[Entity, Property], + superClassMutabilityIsFinal: Boolean, + lazyComputation: Boolean + )( + cf: ClassFile + ): ProperPropertyComputationResult = { + val t = cf.thisType + + var dependees = Map.empty[Entity, EOptionP[Entity, Property]] + + if (!superClassMutabilityIsFinal) { + dependees += (SuperClassKey -> superClassInformation) + } + + // Collect all fields for which we need to determine the effective mutability! + var hasFieldsWithUnknownMutability = false + + val instanceFields = cf.fields.iterator.filter { f ⇒ + !f.isStatic + }.toList + var hasShallowImmutableFields = false + var hasDependentImmutableFields = false + + val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) + + fieldsPropertyStoreInformation.foreach { + + case FinalP(MutableField) ⇒ + if (lazyComputation) + return Result(t, MutableClass); + else + return createResultForAllSubtypes(t, MutableClass); + + case FinalP(ShallowImmutableField) ⇒ hasShallowImmutableFields = true + + case FinalP(DependentImmutableField) ⇒ hasDependentImmutableFields = true + + case FinalP(DeepImmutableField) ⇒ + + case ep @ InterimE(e) ⇒ + hasFieldsWithUnknownMutability = true + dependees += (e -> ep) + + case epk @ EPK(e: Entity, _) ⇒ + // <=> The mutability information is not yet available. + hasFieldsWithUnknownMutability = true + dependees += (e -> epk) + + case _ ⇒ + if (lazyComputation) //TODO check + return Result(t, MutableClass); + else + return createResultForAllSubtypes(t, MutableClass); + } + + var minLocalImmutability: ClassImmutability = MutableClass + + // NOTE: maxLocalImmutability does not take the super classes' mutability into account! + var maxLocalImmutability: ClassImmutability = superClassInformation match { + case UBP(MutableClass) ⇒ MutableClass + case UBP(ShallowImmutableClass) ⇒ ShallowImmutableClass + case UBP(DependentImmutableClass) ⇒ DependentImmutableClass + case _ ⇒ DeepImmutableClass + } + if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass + } + + if (hasDependentImmutableFields && + maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { + maxLocalImmutability = DependentImmutableClass + } + + if (cf.fields.exists(f ⇒ !f.isStatic && f.fieldType.isArrayType)) { + // IMPROVE We could analyze if the array is effectively final. + // I.e., it is only initialized once (at construction time) and no reference to it + // is passed to another object. + maxLocalImmutability = ShallowImmutableClass + } + + if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { + // <=> the super classes' immutability is final + // (i.e., ImmutableObject or ImmutableContainer) + // <=> all fields are (effectively) final + // <=> the type mutability of all fields is final + // (i.e., ImmutableType or ImmutableContainerType) + if (lazyComputation) + return Result(t, maxLocalImmutability); + + return createIncrementalResult( + t, + FinalEP(t, maxLocalImmutability), + cfMutabilityIsFinal = true, + Result(t, maxLocalImmutability) + ); + } + + def c(someEPS: SomeEPS): ProperPropertyComputationResult = { + //[DEBUG] + //val oldDependees = dependees + dependees = dependees.iterator.filter(_._1 ne someEPS.e).toMap + someEPS match { + // Superclass related dependencies: + // + case UBP(MutableClass) ⇒ + return Result(t, MutableClass); + + case LBP(DeepImmutableClass) ⇒ // the super class + dependees -= SuperClassKey + + case UBP(ShallowImmutableClass) ⇒ // super class is at most immutable container + if (someEPS.isFinal) dependees -= SuperClassKey + maxLocalImmutability = ShallowImmutableClass + + case UBP(DependentImmutableClass) ⇒ + if (someEPS.isFinal) dependees -= SuperClassKey + if (maxLocalImmutability != ShallowImmutableClass) + maxLocalImmutability = DependentImmutableClass + + case LBP(ShallowImmutableClass) ⇒ // super class is a least shallow immutable + if (minLocalImmutability != ShallowImmutableClass && + !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) + minLocalImmutability = ShallowImmutableClass // Lift lower bound when possible + + case LUBP(MutableClass, DeepImmutableClass) ⇒ // No information about superclass + + case FinalP(DependentImmutableField) ⇒ + if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass + } else if (maxLocalImmutability != MutableClass && maxLocalImmutability != ShallowImmutableClass) { + maxLocalImmutability = DependentImmutableClass + } + + // Field Immutability related dependencies: + case FinalP(DeepImmutableField) ⇒ + case FinalP(ShallowImmutableField) ⇒ maxLocalImmutability = ShallowImmutableClass + case FinalP(MutableField) ⇒ return Result(t, MutableClass); + case UBP(MutableField) ⇒ return Result(t, MutableClass); + case ELBP(e, ShallowImmutableField | DeepImmutableField) ⇒ dependees -= e + case UBP(DeepImmutableField) ⇒ // no information about field mutability + case UBP(ShallowImmutableField) ⇒ maxLocalImmutability = ShallowImmutableClass + case UBP(DependentImmutableField) if maxLocalImmutability != ShallowImmutableClass ⇒ + maxLocalImmutability = DependentImmutableClass + case _ ⇒ Result(t, MutableClass) //TODO check + } + + if (someEPS.isRefinable) { + val entity = if (someEPS.pk == ClassImmutability.key) SuperClassKey else someEPS.e + dependees += (entity -> someEPS) + } + + /*[DEBUG] + assert( + oldDependees != dependees, + s"dependees are not correctly updated $e($p)\n:old=$oldDependees\nnew=$dependees" + ) + */ + + if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { + /*[DEBUG] + assert( + maxLocalImmutability == ConditionallyImmutableObject || + maxLocalImmutability == ImmutableObject + ) + assert( + ( + currentSuperClassMutability == AtLeastConditionallyImmutableObject && + maxLocalImmutability == ConditionallyImmutableObject + ) || + currentSuperClassMutability == ConditionallyImmutableObject || + currentSuperClassMutability == ImmutableObject, + s"$e: $p resulted in no dependees with unexpected "+ + s"currentSuperClassMutability=$currentSuperClassMutability/"+ + s"maxLocalImmutability=$maxLocalImmutability - "+ + s"(old dependees: ${oldDependees.mkString(",")}" + ) + */ + Result(t, maxLocalImmutability) + } else { + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values.toSet, c) + } + } + + val result = + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values.toSet, c) + if (lazyComputation) + result + else { + val isFinal = dependees.isEmpty + createIncrementalResult( + t, + EPS(t, minLocalImmutability, maxLocalImmutability), + isFinal, + result + ) + } + } +} + +trait L1ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability, FieldImmutability) //TypeImmutability, //XXX + + override type InitializationData = TraversableOnce[ClassFile] + + private[this] def setResultsAndComputeEntities( + project: SomeProject, + propertyStore: PropertyStore + ): TraversableOnce[ClassFile] = { + val classHierarchy = project.classHierarchy + import classHierarchy.allSubtypes + import classHierarchy.rootClassTypesIterator + import propertyStore.set + implicit val logContext: LogContext = project.logContext + + // 1.1 + // java.lang.Object is by definition deep immutable. + set(ObjectType.Object, DeepImmutableClass) //ImmutableObject) + + // 1.2 + // All (instances of) interfaces are (by their very definition) also immutable. + val allInterfaces = project.allClassFiles.filter(cf ⇒ cf.isInterfaceDeclaration) + allInterfaces.foreach(cf ⇒ set(cf.thisType, DeepImmutableClass)) + + // 2. + // All classes that do not have complete superclass information are mutable + // due to the lack of knowledge. + // But, for classes that directly inherit from Object, but which also + // implement unknown interface types it is possible to compute the class + // immutability + val unexpectedRootClassTypes = rootClassTypesIterator.filter(rt ⇒ rt ne ObjectType.Object) + + unexpectedRootClassTypes foreach { rt ⇒ + allSubtypes(rt, reflexive = true) foreach { ot ⇒ + project.classFile(ot) foreach { cf ⇒ + set(cf.thisType, MutableClass) //MutableObjectDueToUnknownSupertypes) + } + } + } + + // 3. + // Compute the initial set of classes for which we want to determine the mutability. + var cfs: List[ClassFile] = Nil + classHierarchy + .directSubclassesOf(ObjectType.Object) + .toIterator + .map(ot ⇒ (ot, project.classFile(ot))) + .foreach { + case (_, Some(cf)) ⇒ cfs ::= cf + case (t, None) ⇒ + // This handles the case where the class hierarchy is at least partially + // based on a pre-configured class hierarchy (*.ths file). + // E.g., imagine that you analyze a lib which contains a class that inherits + // from java.lang.Exception, but you have no knowledge about this respective + // class... + OPALLogger.warn( + "project configuration - object immutability analysis", + s"${t.toJava}'s class file is not available" + ) + allSubtypes(t, reflexive = true).foreach(project.classFile(_).foreach { cf ⇒ + set(cf.thisType, MutableClass) //MutableObjectDueToUnknownSupertypes) + }) + } + cfs + } + + override def init(p: SomeProject, ps: PropertyStore): InitializationData = { + setResultsAndComputeEntities(p, ps) + } + + 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 = {} +} + +/** + * Scheduler to run the class immutability analysis eagerly. + * @author Tobias Roth + * @author Michael Eichberg + */ +object EagerL1ClassImmutabilityAnalysis extends L1ClassImmutabilityAnalysisScheduler + with FPCFEagerAnalysisScheduler { + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + + override def start(p: SomeProject, ps: PropertyStore, cfs: InitializationData): FPCFAnalysis = { + val analysis = new L1ClassImmutabilityAnalysis(p) + ps.scheduleEagerComputationsForEntities(cfs)( + analysis.determineL1ClassImmutability( + superClassType = null, + FinalEP(ObjectType.Object, DeepImmutableClass), + superClassMutabilityIsFinal = true, + lazyComputation = false + ) + ) + analysis + } +} + +/** + * Scheduler to run the class immutability analysis lazily. + * @author Michael Eichberg + */ +object LazyL1ClassImmutabilityAnalysis extends L1ClassImmutabilityAnalysisScheduler + with FPCFLazyAnalysisScheduler { + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + override def register( + p: SomeProject, + ps: PropertyStore, + unused: InitializationData + ): FPCFAnalysis = { + val analysis = new L1ClassImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + ClassImmutability.key, + analysis.doDetermineL1ClassImmutability + ) + analysis + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L1TypeImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L1TypeImmutabilityAnalysis.scala new file mode 100644 index 0000000000..479f2e65ec --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L1TypeImmutabilityAnalysis.scala @@ -0,0 +1,303 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package tac +package fpcf +package analyses +package immutability + +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.cg.TypeExtensibilityKey +import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler +import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.fpcf.properties.ClassImmutability +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.DependentImmutableClass +import org.opalj.br.fpcf.properties.DependentImmutableType +import org.opalj.br.fpcf.properties.MutableClass +import org.opalj.br.fpcf.properties.MutableType +import org.opalj.br.fpcf.properties.ShallowImmutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.fpcf.ELUBP +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.EPS +import org.opalj.fpcf.Entity +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.InterimEUBP +import org.opalj.fpcf.InterimLUBP +import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.ProperOnUpdateContinuation +import org.opalj.fpcf.ProperPropertyComputation +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.Result +import org.opalj.fpcf.SomeEPS +import org.opalj.fpcf.UBP + +/** + * Determines the immutability of a specific type by checking if all subtypes of a specific + * type are immutable and checking that the set of types is closed. + * + * @author Michael Eichberg + * @author Tobias Roth + */ +class L1TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnalysis { + + def doDetermineTypeImmutability_new( + typeExtensibility: ObjectType ⇒ Answer + )( + e: Entity + ): ProperPropertyComputationResult = e match { + case t: ObjectType ⇒ step1(typeExtensibility)(t) + case _ ⇒ throw new IllegalArgumentException(s"$e is not an ObjectType") + } + + /** + * @param t An object type which is not `java.lang.Object`. + */ + def step1( + typeExtensibility: ObjectType ⇒ Answer + )( + t: ObjectType + ): ProperPropertyComputationResult = + { + val te = typeExtensibility(t) + te match { + case Yes | Unknown ⇒ + Result(t, MutableType) + case No ⇒ step2(t) + } + } + + def step2(t: ObjectType): ProperPropertyComputationResult = { + val directSubtypes = classHierarchy.directSubtypesOf(t) + + val cf = project.classFile(t) + if (cf.exists(_.isFinal) || directSubtypes.isEmpty /*... the type is not extensible*/ ) { + + val c = new ProperOnUpdateContinuation { c ⇒ + def apply(eps: SomeEPS): ProperPropertyComputationResult = { + eps match { + case ELUBP(_, lb: ClassImmutability, ub: ClassImmutability) ⇒ + val thisLB = lb.correspondingTypeImmutability + val thisUB = ub.correspondingTypeImmutability + if (eps.isFinal) + Result(t, thisUB) + else + InterimResult(t, thisLB, thisUB, Set(eps), c) + } + } + } + + ps(t, ClassImmutability.key) match { + + case FinalP(p) ⇒ + Result(t, p.correspondingTypeImmutability); + + case eps @ InterimLUBP(lb, ub) ⇒ + val thisUB = ub.correspondingTypeImmutability + val thisLB = lb.correspondingTypeImmutability + InterimResult(t, thisLB, thisUB, Set(eps), c) + + case epk ⇒ + InterimResult(t, MutableType, DeepImmutableType, Set(epk), c) + } + } else { + var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] + var joinedImmutability: TypeImmutability = DeepImmutableType //this may become "Mutable..." + var maxImmutability: TypeImmutability = DeepImmutableType + + val resultToMatch2 = ps(t, ClassImmutability.key) + resultToMatch2 match { + case FinalP(DeepImmutableClass) ⇒ + case FinalP(MutableClass) ⇒ + return Result(t, MutableType); + case FinalP(ShallowImmutableClass) ⇒ + joinedImmutability = ShallowImmutableType + maxImmutability = ShallowImmutableType + case FinalP(DependentImmutableClass) ⇒ + joinedImmutability = DependentImmutableType + if (maxImmutability != ShallowImmutableType) + maxImmutability = DependentImmutableType + + case eps @ InterimLUBP(lb, ub) ⇒ + joinedImmutability = lb.correspondingTypeImmutability + maxImmutability = ub.correspondingTypeImmutability + dependencies += (t -> eps) + + case eOptP ⇒ + joinedImmutability = MutableType + dependencies += (t -> eOptP) + } + + directSubtypes foreach { subtype ⇒ + ps(subtype, TypeImmutability.key) match { + case FinalP(DeepImmutableType) ⇒ + case UBP(MutableType) ⇒ + return Result(t, MutableType); + + case FinalP(ShallowImmutableType) ⇒ + joinedImmutability = joinedImmutability.meet(ShallowImmutableType) + maxImmutability = ShallowImmutableType + + case FinalP(DependentImmutableType) ⇒ + joinedImmutability = joinedImmutability.meet(DependentImmutableType) + if (maxImmutability != ShallowImmutableType) + maxImmutability = DependentImmutableType + + case eps @ InterimLUBP(subtypeLB, subtypeUB) ⇒ + joinedImmutability = joinedImmutability.meet(subtypeLB) + maxImmutability = maxImmutability.meet(subtypeUB) + dependencies += ((subtype, eps)) + + case epk ⇒ + joinedImmutability = MutableType + dependencies += ((subtype, epk)) + } + } + + if (dependencies.isEmpty) { + Result(t, maxImmutability) + } else if (joinedImmutability == maxImmutability) { + // E.g., as soon as one subtype is shallow immutable, we are at most + // shallow immutable, even if all other subtype may even be deep immutable! + Result(t, joinedImmutability) + } else { + // when we reach this point, we have dependencies to types for which + // we have non-final information; joinedImmutability is either MutableType + // or ImmutableContainer + def c(eps: EPS[Entity, Property]): ProperPropertyComputationResult = { + + ///*debug*/ val previousDependencies = dependencies + ///*debug*/ val previousJoinedImmutability = joinedImmutability + + def nextResult(): ProperPropertyComputationResult = { + if (dependencies.isEmpty) { + Result(t, maxImmutability) + } else { + joinedImmutability = maxImmutability + val depIt = dependencies.valuesIterator + var continue = true + while (continue && depIt.hasNext) { + val n = depIt.next() + if (n.hasLBP) + n.lb match { + case lb: TypeImmutability ⇒ + joinedImmutability = joinedImmutability.meet(lb) + case lb: ClassImmutability ⇒ + joinedImmutability = + joinedImmutability.meet(lb.correspondingTypeImmutability) + } + else { + joinedImmutability = MutableType + continue = false + } + } + if (joinedImmutability == maxImmutability) { + assert(maxImmutability == ShallowImmutableType) + Result(t, maxImmutability) + } else { + InterimResult( + t, + joinedImmutability, + maxImmutability, + dependencies.values.toSet, + c + ) + } + } + } + + (eps: @unchecked) match { + case FinalEP(e, DeepImmutableType | DeepImmutableClass) ⇒ + dependencies = dependencies - e + nextResult() + + case UBP(x) if x == MutableType || x == MutableClass ⇒ + Result(t, MutableType) //MutableType) + + case FinalEP(e, x) if x == ShallowImmutableType || x == ShallowImmutableClass ⇒ + maxImmutability = ShallowImmutableType + dependencies = dependencies - e + nextResult() + + case FinalEP(e, DependentImmutableClass | DependentImmutableType) ⇒ + if (maxImmutability != ShallowImmutableType) + maxImmutability = DependentImmutableType + dependencies = dependencies - e + nextResult() + + case eps @ InterimEUBP(e, subtypeP) ⇒ + dependencies = dependencies.updated(e, eps) + subtypeP match { + case subtypeP: TypeImmutability ⇒ + maxImmutability = maxImmutability.meet(subtypeP) + case subtypeP: ClassImmutability ⇒ + maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) + } + nextResult() + } + } + InterimResult(t, joinedImmutability, maxImmutability, dependencies.values.toSet, c) + } + } + } +} + +trait L1TypeImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability, TypeImmutability) + +} + +/** + * Starter for the '''type immutability analysis'''. + * + * @author Michael Eichberg + */ +object EagerL1TypeImmutabilityAnalysis extends L1TypeImmutabilityAnalysisScheduler + with BasicFPCFEagerAnalysisScheduler { + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + + override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val typeExtensibility = project.get(TypeExtensibilityKey) + val analysis = new L1TypeImmutabilityAnalysis(project) + val types = project.allClassFiles.iterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) + ps.scheduleEagerComputationsForEntities(types) { + analysis.step1(typeExtensibility) + } + analysis + } +} + +object LazyL1TypeImmutabilityAnalysis extends L1TypeImmutabilityAnalysisScheduler + with BasicFPCFLazyAnalysisScheduler { + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + /** + * Registers the analysis as a lazy computation, that is, the method + * will call `ProperytStore.scheduleLazyComputation`. + */ + override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val typeExtensibility = p.get(TypeExtensibilityKey) + val analysis = new L1TypeImmutabilityAnalysis(p) + val analysisRunner: ProperPropertyComputation[Entity] = + analysis.doDetermineTypeImmutability_new(typeExtensibility) + ps.registerLazyPropertyComputation(TypeImmutability.key, analysisRunner) + analysis + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L3FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L3FieldImmutabilityAnalysis.scala new file mode 100755 index 0000000000..cd0fed5c3a --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L3FieldImmutabilityAnalysis.scala @@ -0,0 +1,1064 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package tac +package fpcf +package analyses +package immutability + +/* +import org.opalj.br.ClassFile +import org.opalj.br.FormalTypeParameter +import org.opalj.br.Attribute +import org.opalj.br.ClassSignature */ +import org.opalj.br.ClassTypeSignature +import org.opalj.br.Field +import org.opalj.br.ObjectType +import org.opalj.br.ProperTypeArgument +import org.opalj.br.RuntimeInvisibleAnnotationTable +import org.opalj.br.SimpleClassTypeSignature +import org.opalj.br.SourceFile +import org.opalj.br.TypeVariableSignature +import org.opalj.br.fpcf.properties.DeepImmutableField +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.DependentImmutableField +import org.opalj.br.fpcf.properties.DependentImmutableType +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.ImmutableFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeFieldReference +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.MutableFieldReference +import org.opalj.br.fpcf.properties.MutableType +import org.opalj.br.fpcf.properties.FieldReferenceImmutability +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Result +import org.opalj.fpcf.SomeEPS +import org.opalj.br.Method +import org.opalj.br.fpcf.properties.EscapeProperty +import org.opalj.tac.fpcf.analyses.AbstractIFDSAnalysis.V +import org.opalj.collection.immutable.IntTrieSet +import org.opalj.br.fpcf.properties.NoEscape +import org.opalj.tac.common.DefinitionSite +import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.br.PCs +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler +import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.DependentImmutableClass +import org.opalj.br.fpcf.properties.MutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableClass +import org.opalj.br.fpcf.properties.AtMost +import org.opalj.br.fpcf.properties.EscapeInCallee +import org.opalj.br.fpcf.properties.EscapeViaReturn +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyStore +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.FieldAccessInformationKey +import org.opalj.br.analyses.cg.ClosedPackagesKey +import org.opalj.br.analyses.cg.TypeExtensibilityKey +import org.opalj.fpcf.PropertyComputationResult +import org.opalj.fpcf.UBP +import org.opalj.tac.common.DefinitionSitesKey +import org.opalj.fpcf.InterimUBP +import org.opalj.br.fpcf.properties.ClassImmutability +import scala.collection.mutable +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.LBP +import org.opalj.fpcf.EUBP + +/** + * Analysis that determines the immutability of org.opalj.br.Field + * @author Tobias Roth + */ +class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) + extends FPCFAnalysis { + + /** + * Describes the different kinds of dependent immutable fields, that depend on the concrete types that replace + * the generic parameters. + * + * [[NotDependentImmutable]] Shallow or mutable types could still exist. + * Example: Foo f + * + * [[NotShallowOrMutable]] There are no shallow and no mutable types. + * Example: Foo f + * + * [[OnlyDeepImmutable]] There are only deep immutable types and no generic parameters left. + * Example: Foo f + */ + sealed trait DependentImmutabilityTypes + case object NotDependentImmutable extends DependentImmutabilityTypes + case object NotShallowOrMutable extends DependentImmutabilityTypes + case object OnlyDeepImmutable extends DependentImmutabilityTypes + + sealed trait ImmutabilityTypes + case object Mutable extends ImmutabilityTypes + case object DependentImmutable extends ImmutabilityTypes + case object DeepImmutable extends ImmutabilityTypes + + case class State( + field: Field, + var typeImmutability: ImmutabilityTypes = DeepImmutable, + var classTypeImmutability: ImmutabilityTypes = DeepImmutable, + var referenceIsImmutable: Option[Boolean] = None, + var noEscapePossibilityViaReference: Boolean = true, + var dependentImmutability: DependentImmutabilityTypes = OnlyDeepImmutable, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], (PCs, PCs))] = Map.empty, + var dependees: Set[EOptionP[Entity, Property]] = Set.empty, + var innerArrayTypes: Set[ObjectType] = Set.empty, + var concreteGenericTypes: Set[ObjectType] = Set.empty, + var escapesStillDetermined: Boolean = false, + var concreteClassTypeIsKnown: Boolean = false, + var totalNumberOfFieldWrites: Int = 0, + var fieldTypeIsDependentImmutable: Boolean = false + ) { + def hasDependees: Boolean = dependees.nonEmpty || tacDependees.nonEmpty + def getDependees: Set[EOptionP[Entity, Property]] = + dependees ++ tacDependees.valuesIterator.map(_._1) + } + + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) + final val definitionSites = project.get(DefinitionSitesKey) + implicit final val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + + var considerEscape = true; //TODO true /*project.config.getBoolean( //TODO due to unsoundness + /* "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape" + )*/ + + val considerGenericity = + project.config.getBoolean( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity" + ) + + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field ⇒ determineFieldImmutability(field) + case _ ⇒ + throw new IllegalArgumentException(s"${entity.getClass.getName} is not an org.opalj.br.Field") + } + + private[analyses] def determineFieldImmutability( + field: Field + ): ProperPropertyComputationResult = { + + //TODO determine bounds + /** + * Returns the formal type parameters from the class' and outer classes' signature + */ + /* + def getFormalTypeParameters: Set[String] = { + + /** + * + * Extract the formal type parameters if it exists of a class' attribute + */ + def collectFormalTypeParameterFromClassAttribute(attribute: Attribute): Iterator[String] = + attribute match { + + case ClassSignature(typeParameters, _, _) ⇒ + typeParameters.iterator.map { case FormalTypeParameter(identifier, _, _) ⇒ identifier } + + case _ ⇒ Iterator.empty + } + + /** + * If the genericity is nested in an inner class + * collect the generic type parameters from the field's outer classes + */ + def getAllFormalParameters(classFile: ClassFile): Iterator[String] = { + classFile.attributes.iterator.flatMap(collectFormalTypeParameterFromClassAttribute(_)) ++ { + if (classFile.outerType.isDefined) { + val outerClassFile = project.classFile(classFile.outerType.get._1) + if (outerClassFile.isDefined && outerClassFile.get != classFile) { + getAllFormalParameters(outerClassFile.get) + } else + Iterator.empty + } else + Iterator.empty + } + } + getAllFormalParameters(field.classFile).toSet + } */ + + //val classFormalTypeParameters: Set[String] = getFormalTypeParameters + + /** + * Returns, if a generic parameter like e.g. 'T' is in the class' or an outer class' signature + * @param string The generic type parameter that should be looked for + */ + // def isInClassesGenericTypeParameters(string: String): Boolean = + // classFormalTypeParameters.contains(string) + + /** + * Determines the immutability of a field's type. Adjusts the state and registers the dependencies if necessary. + */ + def handleTypeImmutability(state: State): Unit = { + val objectType = field.fieldType.asFieldType + if (objectType == ObjectType.Object) { + state.typeImmutability = Mutable //false //handling generic fields + } else if (objectType.isBaseType || objectType == ObjectType.String) { + // base types are by design deep immutable + //state.typeImmutability = true // true is default + } else if (objectType.isArrayType) { + // Because the entries of an array can be reassigned we state it as not being deep immutable + state.typeImmutability = Mutable //false + } else { + propertyStore(objectType, TypeImmutability.key) match { + + case LBP(DeepImmutableType) ⇒ // deep immutable type is set as default + case FinalEP(t, DependentImmutableType) ⇒ + state.typeImmutability = DependentImmutable //false + if (t == field.fieldType) + state.fieldTypeIsDependentImmutable = true + case UBP(MutableType | ShallowImmutableType) ⇒ + state.typeImmutability = Mutable + case epk ⇒ state.dependees += epk + } + } + } + + def handleGenericity()(implicit state: State): Unit = { + var noShallowOrMutableTypesInGenericTypeFound = true + var onlyDeepImmutableTypesInGenericTypeFound = true + var genericParameters: List[ObjectType] = List() + var noRelevantAttributesFound = true + + state.field.attributes.foreach { + + case RuntimeInvisibleAnnotationTable(_) | SourceFile(_) ⇒ // no including generic parameter + + case TypeVariableSignature(t) ⇒ + noRelevantAttributesFound = false + onlyDeepImmutableTypesInGenericTypeFound = false + state.fieldTypeIsDependentImmutable = true //generic type T is dependent immutable + // if (!isInClassesGenericTypeParameters(t)) + // noShallowOrMutableTypesInGenericTypeFound = false + + case ClassTypeSignature(_, SimpleClassTypeSignature(_, typeArguments), _) ⇒ + noRelevantAttributesFound = false + + typeArguments.foreach { + + case ProperTypeArgument(_, TypeVariableSignature(identifier)) ⇒ + onlyDeepImmutableTypesInGenericTypeFound = false + // if (!isInClassesGenericTypeParameters(identifier)) + // noShallowOrMutableTypesInGenericTypeFound = false + + case ProperTypeArgument( + _, + ClassTypeSignature( + outerPackageIdentifier, + SimpleClassTypeSignature(innerPackageIdentifier, _), + _ + ) + ) ⇒ + val objectPath = outerPackageIdentifier match { + case Some(prepackageIdentifier) ⇒ prepackageIdentifier + innerPackageIdentifier + case _ ⇒ innerPackageIdentifier + } + genericParameters ::= ObjectType(objectPath) + + case _ ⇒ + noShallowOrMutableTypesInGenericTypeFound = false + onlyDeepImmutableTypesInGenericTypeFound = false + } + case _ ⇒ + noShallowOrMutableTypesInGenericTypeFound = false + onlyDeepImmutableTypesInGenericTypeFound = false + } + + genericParameters.foreach(objectType ⇒ { + propertyStore(objectType, TypeImmutability.key) match { + + case FinalP(DeepImmutableType) ⇒ //nothing to do here: default value is deep immutable + + //no nested generic classes are allowed + case UBP(DependentImmutableType | ShallowImmutableType | MutableType) ⇒ + noShallowOrMutableTypesInGenericTypeFound = false + onlyDeepImmutableTypesInGenericTypeFound = false + + case ep ⇒ + state.concreteGenericTypes += ep.e + state.dependees += ep + } + }) + + //Prevents the case of keeping the default values of these + // flags only because of no relevant attribute has been found + if (!noRelevantAttributesFound) { + if (onlyDeepImmutableTypesInGenericTypeFound) { + //nothing to do... + } else if (noShallowOrMutableTypesInGenericTypeFound && + state.dependentImmutability != NotDependentImmutable) { + state.dependentImmutability = NotShallowOrMutable + } else { + state.dependentImmutability = NotDependentImmutable + } + } else { + state.dependentImmutability = NotDependentImmutable + } + } + + /** + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + def getTACAI( + method: Method, + pcs: PCs, + isRead: Boolean + )(implicit state: State): Option[TACode[TACMethodParameter, V]] = { + propertyStore(method, TACAI.key) match { + + case finalEP: FinalEP[Method, TACAI] ⇒ finalEP.ub.tac + + case epk ⇒ + val (reads, writes) = + if (state.tacDependees.contains(method)) + (state.tacDependees(method)._2._1, state.tacDependees(method)._2._2) + else + (IntTrieSet.empty, IntTrieSet.empty) + if (isRead) state.tacDependees += method -> ((epk, (reads ++ pcs, writes))) + if (!isRead) state.tacDependees += method -> ((epk, (reads, writes ++ pcs))) + None + } + } + + /** + * Handles the influence of an escape property on the field mutability. + * @return true if the object - on which a field write occurred - escapes, false otherwise. + * @note (Re-)Adds dependees as necessary. + */ + def doesEscapeViaMethod( + ep: EOptionP[DefinitionSite, EscapeProperty] + )(implicit state: State): Boolean = { + ep match { + case FinalP(NoEscape) ⇒ false + + case InterimUBP(NoEscape) ⇒ + state.dependees += ep + false + + case UBP(EscapeInCallee | EscapeViaReturn) ⇒ true + case FinalP(AtMost(_)) ⇒ true + case _: FinalEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return + case InterimUBP(AtMost(_)) ⇒ true + + case epk ⇒ + state.dependees += epk + false + } + } + + /** + * Determines if the referenced object can escape either via field reads or writes. + */ + def determineEscapeOfReferencedObjectOrValue()(implicit state: State): Unit = { + state.escapesStillDetermined = true + //val tmp = state.noEscapePossibilityViaReference + if (state.noEscapePossibilityViaReference) { + val writes = fieldAccessInformation.writeAccesses(state.field) + + //has to be determined before the following foreach loop because in this the information is still needed + state.totalNumberOfFieldWrites = writes.foldLeft(0)(_ + _._2.size) + + writes.foreach { write ⇒ + val (method, pcs) = write + val taCodeOption = getTACAI(method, pcs, isRead = false) + if (taCodeOption.isDefined) + determineEscapeViaFieldWritesWithKnownTAC(method, pcs, taCodeOption.get) + } + //TODO state.noEscapePossibilityViaReference = true + /*if (state.noEscapePossibilityViaReference) { + fieldAccessInformation.readAccesses(state.field).foreach { read ⇒ + val (method, pcs) = read + val taCodeOption = getTACAI(method, pcs, isRead = true) + if (taCodeOption.isDefined) + determineEscapeViaFieldReadsWithKnownTAC(method, pcs, taCodeOption.get) + } + } */ + } + } + + /** + * Determine if the referenced object can escape via field reads. + */ + /* + def determineEscapeViaFieldReadsWithKnownTAC( + method: Method, + pcs: PCs, + taCode: TACode[TACMethodParameter, V] + )(implicit state: State): Unit = { + if (pcs.exists { pc ⇒ + val readIndex = taCode.pcToIndex(pc) + // This if-statement is necessary, because there are -1 elements in the array + if (readIndex != -1) { + val stmt = taCode.stmts(readIndex) + if (stmt.isAssignment) { + val assignment = stmt.asAssignment + assignment.targetVar.usedBy.exists(useSite ⇒ { + + val fieldsUseSiteStmt = taCode.stmts(useSite) + + if (fieldsUseSiteStmt.isAssignment) { + + val assignment = fieldsUseSiteStmt.asAssignment + if (assignment.expr.isVirtualFunctionCall || + assignment.expr.isStaticFunctionCall) { + val functionCall = assignment.expr.asFunctionCall + field.fieldType.isObjectType && functionCall.params.exists(!_.isConst) || + doesEscapeViaMethod( + propertyStore( + definitionSites(method, assignment.pc), + EscapeProperty.key + ) + ) + } else if (assignment.expr.isArrayLoad) { + val arrayLoad = assignment.expr.asArrayLoad + arrayLoad.arrayRef.asVar.value.toCanonicalForm match { + case value: ASArrayValue ⇒ + val innerArrayType = value.theUpperTypeBound.componentType + if (innerArrayType.isBaseType) { + false // nothing to do, because it can not be mutated + } else if (innerArrayType.isArrayType) { + true // to be sound + } else if (innerArrayType == ObjectType.String) { + false + } else if (innerArrayType.isObjectType) { + //If a deep immutable object escapes, it can not be mutated + val result = propertyStore(innerArrayType, TypeImmutability.key) + result match { //TODO unnecessary + case LBP(DeepImmutableType) ⇒ false //nothing to do + case UBP( + DependentImmutableType | ShallowImmutableType | MutableType + ) ⇒ + true + + case ep ⇒ + state.innerArrayTypes += innerArrayType.asObjectType + state.dependees += ep + false + } + } else true + case _ ⇒ true + } + } else true + } else + !fieldsUseSiteStmt.isMonitorEnter && + !fieldsUseSiteStmt.isMonitorExit && + !fieldsUseSiteStmt.isIf + }) + } else { + //there can occur other cases; see issue #44 + //TODO make it more precise when issue #44 is fixed + !(stmt.isExprStmt || stmt.isNop) + } + } else false //nothing to do; -1 means NOTHING as a placeholder + }) { + state.noEscapePossibilityViaReference = false + } + } */ //// + + /** + * Determine if the referenced object can escape via field writes + */ + def determineEscapeViaFieldWritesWithKnownTAC( + method: Method, + pcs: PCs, + taCode: TACode[TACMethodParameter, V] + )(implicit state: State): Unit = { + + //Required because of cyclic calls of the inner functions - to prevent infinite cycles + val seen: mutable.Set[Stmt[V]] = mutable.Set.empty + + /** + * Checks if the parameters of a static function call are no parameters from an outer + * function and are constants + */ + def doesStaticFunctionCallEnableEscape( + staticFunctionCall: StaticFunctionCall[V] + ): Boolean = + staticFunctionCall.params.exists( + _.asVar.definedBy.exists( + definedByIndex ⇒ + definedByIndex < 0 || + !taCode.stmts(definedByIndex).asAssignment.expr.isConst + ) + ) + + /** + * In case of the concrete assigned classtype is known this method handles the immutability of it. + * @note [[state.concreteClassTypeIsKnown]] must be set to true, when calling this function + */ + def handleKnownClassType(objectType: ObjectType): Unit = { + + propertyStore(objectType, ClassImmutability.key) match { + case LBP(DeepImmutableClass) ⇒ //nothing to do ; true ist default + + case FinalEP(t, DependentImmutableClass) ⇒ + state.classTypeImmutability = DependentImmutable + if (t == field.fieldType) + state.fieldTypeIsDependentImmutable = true + case UBP(MutableClass | ShallowImmutableClass) ⇒ state.classTypeImmutability = Mutable + + case eps ⇒ + state.dependees += eps + } + } + + /** + * Checks if the referenced object or elements from it can escape via the nonvirtualmethod-call + */ + def doesNonVirtualMethodCallEnablesEscape( + nonVirtualMethodCall: NonVirtualMethodCall[V] + )(implicit state: State): Boolean = + nonVirtualMethodCall.params.exists { param ⇒ + param.asVar.definedBy.exists { paramDefinedByIndex ⇒ + if (paramDefinedByIndex < 0) { + true + } else { + val paramDefinitionStmt = taCode.stmts(paramDefinedByIndex) + if (paramDefinitionStmt.isAssignment) { + val assignmentExpression = paramDefinitionStmt.asAssignment.expr + if (assignmentExpression.isGetField || + assignmentExpression.isGetStatic) { + val assignedField: Option[Field] = + if (assignmentExpression.isGetField) + assignmentExpression.asGetField.resolveField + else if (assignmentExpression.isGetStatic) + assignmentExpression.asGetStatic.resolveField + else + throw new Error(s"$assignmentExpression is not an assigned field") + + if (assignedField.isDefined && assignedField.get != state.field) { + propertyStore(assignedField.get, FieldImmutability.key) match { + + case FinalP(DeepImmutableField) ⇒ false //nothing to do here + case UBP(MutableField | ShallowImmutableField | DependentImmutableField) ⇒ + true + case ep ⇒ + state.dependees += ep + false + } + } else false + } else if (assignmentExpression.isVirtualFunctionCall) { + val virtualFunctionCall = assignmentExpression.asVirtualFunctionCall + virtualFunctionCall.params.exists( + param ⇒ + param.asVar.definedBy.head < 0 || + !taCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst + ) + } else if (assignmentExpression.isStaticFunctionCall) { + doesStaticFunctionCallEnableEscape(assignmentExpression.asStaticFunctionCall) + } else if (assignmentExpression.isNew) { + val newStmt = assignmentExpression.asNew + if (field.fieldType.isObjectType && + newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType /*&& + state.totalNumberOfFieldWrites == 1*/ ) { + state.concreteClassTypeIsKnown = true + handleKnownClassType(newStmt.tpe.mostPreciseObjectType) + } + /* + paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy.exists { usedSiteIndex ⇒ + val stmt = taCode.stmts(usedSiteIndex) + if (stmt.isNonVirtualMethodCall) { + if (!seen.contains(stmt)) { + seen += stmt + doesNonVirtualMethodCallEnablesEscape(stmt.asNonVirtualMethodCall) + } else false + } else true + } */ + true + } else + assignmentExpression.isConst + } else { + val definitionSitesOfParam = definitionSites(method, paramDefinedByIndex) + val stmt = taCode.stmts(taCode.pcToIndex(definitionSitesOfParam.pc)) + if (stmt.isNonVirtualMethodCall) { + if (!seen.contains(stmt)) { + seen += stmt + doesNonVirtualMethodCallEnablesEscape(stmt.asNonVirtualMethodCall) + } else false + } else if (stmt.isPutField || stmt.isPutStatic) { + if (!seen.contains(stmt)) { + seen += stmt + doesPutEnableEscape(stmt) + } else false + } else if (stmt.isArrayStore) { + !stmt.asArrayStore.value.asVar.isConst + // true //TODO handling that case more precise + } else { // other cases that the purity analysis can not handle + doesEscapeViaMethod(propertyStore(definitionSitesOfParam, EscapeProperty.key)) + } + } //TODO go further + } + } + } + + /** + * Checks if a reference object can escape via a given putfield or putstatic + */ + def doesPutEnableEscape(putStmt: Stmt[V]): Boolean = { + + val (putDefinitionSites, putValue) = { + if (putStmt.isPutField) { + val putField = putStmt.asPutField + (putField.value.asVar.definedBy, putField.value.asVar) + } else if (putStmt.isPutStatic) { + val putStatic = putStmt.asPutStatic + (putStatic.value.asVar.definedBy, putStatic.value.asVar) + } else + throw new Exception(s"$putStmt is not a putStmt") + } + + if (putValue.value.isArrayValue.isYes) { + if (putValue.definedBy.forall(_ >= 0)) { //necessary + putValue.definedBy.exists { putValueDefinedByIndex ⇒ + taCode.stmts(putValueDefinedByIndex).asAssignment.targetVar.usedBy.exists { + usedByIndex ⇒ + val arrayStmt = taCode.stmts(usedByIndex) + if (arrayStmt != putStmt) { + if (arrayStmt.isArrayStore) { + + val arrayStore = arrayStmt.asArrayStore + val arrayStoreIndex = arrayStore.index + val isArrayIndexConst = + taCode.stmts(arrayStoreIndex.asVar.definedBy.head).asAssignment.expr.isConst + val assignedValue = arrayStore.value + if (assignedValue.asVar.definedBy.size == 1 && + assignedValue.asVar.definedBy.head >= 0) { + val valueAssignment = + taCode.stmts(assignedValue.asVar.definedBy.head).asAssignment + val assignedExpr = valueAssignment.expr + val useSites = valueAssignment.targetVar.usedBy.map(taCode.stmts(_)) + if (assignedExpr.isConst) + false + else if (useSites.exists { useSite ⇒ + if (useSite.isNonVirtualMethodCall) { + val nonVirtualMethodCall = useSite.asNonVirtualMethodCall + nonVirtualMethodCall.params.exists( + param ⇒ + !param.isConst && param.asVar.definedBy + .forall( + i ⇒ i >= 0 && taCode.stmts(i).asAssignment.expr.isConst + ) + ) + } else if (useSite == arrayStore) { + false + } else if (useSite.isReturnValue) { + //assigned array-element escapes + true + } else { + true //to be sound + } + }) + true + else if (!isArrayIndexConst) { + true + } else if (assignedExpr.isStaticFunctionCall) { + doesStaticFunctionCallEnableEscape(assignedExpr.asStaticFunctionCall) + } else if (assignedExpr.isNew) { + val newStmt = assignedExpr.asNew + + if (field.fieldType.isObjectType && + newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && + state.totalNumberOfFieldWrites == 1) { + state.concreteClassTypeIsKnown = true + handleKnownClassType(newStmt.tpe.mostPreciseObjectType) + } + + /*valueAssignment.targetVar.asVar.usedBy.exists { index ⇒ + val tmpStmt = taCode.stmts(index) + if (tmpStmt.isArrayStore) { + false // can be ingored + } else if (tmpStmt.isNonVirtualMethodCall) { + val nonVirtualMethodcall = tmpStmt.asNonVirtualMethodCall + if (!seen.contains(tmpStmt)) { + seen += tmpStmt + doesNonVirtualMethodCallEnablesEscape(nonVirtualMethodcall) + } + false //nothing to do in the else case. Stmt has still been handled + } else true + } */ true + } else true + } else true + } else true + } else false + } + } + } else true + } else { + + putDefinitionSites.exists { putDefinitionSite ⇒ + if (putDefinitionSite >= 0) { //necessary + val definitionSiteStatement = taCode.stmts(putDefinitionSite) + val definitionSiteAssignment = definitionSiteStatement.asAssignment + if (definitionSiteAssignment.expr.isStaticFunctionCall) { + doesStaticFunctionCallEnableEscape( + definitionSiteAssignment.expr.asStaticFunctionCall + ) + + } else if (definitionSiteAssignment.expr.isVar) { + val definitionSiteVar = definitionSiteAssignment.expr.asVar + definitionSiteVar.usedBy.exists { definitionSiteVarUseSite ⇒ + val definitionSiteVarUseSiteStmt = taCode.stmts(definitionSiteVarUseSite) + if (definitionSiteVarUseSiteStmt.isNonVirtualMethodCall) { + val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall + doesNonVirtualMethodCallEnablesEscape(nonVirtualMethodCall) + } else true + //TODO handle all cases + } + } else if (definitionSiteAssignment.expr.isNew) { + + val newStmt = definitionSiteAssignment.expr.asNew + if (field.fieldType.isObjectType && + newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType /*&& + state.totalNumberOfFieldWrites == 1*/ ) { + state.concreteClassTypeIsKnown = true + handleKnownClassType(newStmt.tpe.mostPreciseObjectType) + } + if (!method.isConstructor && !method.isStaticInitializer) { + definitionSiteAssignment.targetVar.asVar.usedBy.exists { x ⇒ + val tmpStmt = taCode.stmts(x) + if (tmpStmt.isPutStatic || tmpStmt.isPutField) { + if (!seen.contains(tmpStmt)) { + seen += tmpStmt + doesPutEnableEscape(tmpStmt) + } else false + } else if (tmpStmt.isNonVirtualMethodCall) { + if (!seen.contains(tmpStmt)) { + seen += tmpStmt + doesNonVirtualMethodCallEnablesEscape(tmpStmt.asNonVirtualMethodCall) + } else false + } else true + } + } else { //TODO check + definitionSiteAssignment.targetVar.usedBy.exists { useSite ⇒ + val useSiteStmt = taCode.stmts(useSite) + if (useSiteStmt.isNonVirtualMethodCall) { + doesNonVirtualMethodCallEnablesEscape(useSiteStmt.asNonVirtualMethodCall) + } else if (useSiteStmt.isPutStatic || useSiteStmt.isPutField) { + if (!seen.contains(useSiteStmt)) { + seen += useSiteStmt + doesPutEnableEscape(useSiteStmt) + } else false + } else if (useSiteStmt.isAssignment) { + true //conservative handling to reduce complexity + } else { + true + } + } + } + //TODO handle all cases + } else !definitionSiteAssignment.expr.isConst + } else true + } + } + } + + if (pcs.exists { pc ⇒ + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = taCode.stmts(index) + if (!seen.contains(stmt)) { + seen += stmt + doesPutEnableEscape(stmt) + } else false + } else true + }) + state.noEscapePossibilityViaReference = false + } + + /** + * If there are no dependencies left, this method can be called to create the result. + */ + def createResult()(implicit state: State): ProperPropertyComputationResult = { + + if (state.noEscapePossibilityViaReference && state.escapesStillDetermined && !state.field.isPrivate) + state.noEscapePossibilityViaReference = false + + if (state.hasDependees) { + val lowerBound = + if (state.referenceIsImmutable.isDefined && state.referenceIsImmutable.get) + ShallowImmutableField + else + MutableField + val upperBound = + if (state.referenceIsImmutable.isEmpty) DeepImmutableField + else { + state.referenceIsImmutable match { + + case Some(false) ⇒ MutableField + + case Some(true) | None ⇒ + if (state.tacDependees.isEmpty && !state.concreteClassTypeIsKnown) { + if (state.typeImmutability == DeepImmutable /*|| + state.noEscapePossibilityViaReference*/ ) { + DeepImmutableField + } else if (state.typeImmutability == Mutable && + !state.fieldTypeIsDependentImmutable) { + ShallowImmutableField + } else { + state.dependentImmutability match { + case NotShallowOrMutable ⇒ DependentImmutableField + case OnlyDeepImmutable ⇒ DeepImmutableField + case _ ⇒ ShallowImmutableField + } + } + } else + DeepImmutableField + } + } + if (lowerBound == upperBound) + Result(field, lowerBound) + else + InterimResult( + field, + lowerBound, + upperBound, + state.getDependees, + c + ) + } else { + if (!state.escapesStillDetermined) + state.noEscapePossibilityViaReference = false + + state.referenceIsImmutable match { + + case Some(false) | None ⇒ Result(field, MutableField) + + case Some(true) ⇒ + if (!state.concreteClassTypeIsKnown && state.typeImmutability == DeepImmutable || + state.concreteClassTypeIsKnown && state.classTypeImmutability == DeepImmutable) { + Result(field, DeepImmutableField) + } else { + /*if (state.noEscapePossibilityViaReference) { + Result(field, DeepImmutableField) + } else */ { + if (state.fieldTypeIsDependentImmutable && field.fieldType == ObjectType.Object || + state.classTypeImmutability == DependentImmutable || + state.typeImmutability == DependentImmutable) { + state.dependentImmutability match { + case OnlyDeepImmutable ⇒ Result(field, DeepImmutableField) + case NotShallowOrMutable ⇒ Result(field, DependentImmutableField) + case NotDependentImmutable ⇒ Result(field, ShallowImmutableField) + } + } else { + Result(field, ShallowImmutableField) + } + } + } + } + } + } + + def handleMutableType(tpe: ObjectType)(implicit state: State): Unit = { + if (state.innerArrayTypes.contains(tpe)) + state.noEscapePossibilityViaReference = false + + if (state.concreteGenericTypes.contains(tpe)) { + state.dependentImmutability = NotDependentImmutable + } + } + + def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { + + if (eps.asEPS.pk != TACAI.key) + state.dependees = state.dependees.filter(ep ⇒ (ep.e != eps.e) || (ep.pk != eps.pk)) + eps match { + case LBP(DeepImmutableType) ⇒ //nothing to do -> is default + + case FinalEP(t, DependentImmutableType) ⇒ + import org.opalj.br.FieldType + if (t.asInstanceOf[FieldType] == state.field.fieldType) { + state.fieldTypeIsDependentImmutable = true + state.typeImmutability = DependentImmutable + } + val tpe = t.asInstanceOf[ObjectType] + handleMutableType(tpe) + + case EUBP(t, MutableType | ShallowImmutableType) ⇒ + import org.opalj.br.FieldType + if (t.asInstanceOf[FieldType] == state.field.fieldType) + state.typeImmutability = Mutable + + val tpe = t.asInstanceOf[ObjectType] + handleMutableType(tpe) + + case UBP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ + state.referenceIsImmutable = Some(false) + return Result(state.field, MutableField); + + case LBP( + ImmutableFieldReference | LazyInitializedThreadSafeFieldReference | + LazyInitializedNotThreadSafeButDeterministicFieldReference + ) ⇒ + state.referenceIsImmutable = Some(true) + + case LBP(DeepImmutableField) ⇒ // nothing to do + + case UBP(DependentImmutableField | ShallowImmutableField | MutableField) ⇒ + state.noEscapePossibilityViaReference = false + + case epk if epk.asEPS.pk == TACAI.key ⇒ + val newEP = epk.asInstanceOf[EOptionP[Method, TACAI]] + val method = newEP.e + val pcs = state.tacDependees(method)._2 + state.tacDependees -= method + if (epk.isFinal) { + val tac = epk.asInstanceOf[FinalEP[Method, TACAI]].p.tac.get + determineEscapeViaFieldWritesWithKnownTAC(method, pcs._2, tac)(state) + //determineEscapeViaFieldReadsWithKnownTAC(method, pcs._1, tac)(state) + } else { + state.tacDependees += method -> ((newEP, pcs)) + } + + case epk if epk.isFinal && epk.asEPS.pk == EscapeProperty.key ⇒ + if (doesEscapeViaMethod(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])) + state.noEscapePossibilityViaReference = false + + case LBP(DeepImmutableClass) ⇒ //nothing to do + + case UBP(MutableClass | ShallowImmutableClass) ⇒ state.classTypeImmutability = Mutable + + case FinalEP(t, DependentImmutableClass) ⇒ + import org.opalj.br.FieldType + state.classTypeImmutability = DependentImmutable + if (t.asInstanceOf[FieldType] == field.fieldType) + state.fieldTypeIsDependentImmutable = true + + case epk ⇒ + state.dependees += epk + } + createResult + } + + implicit val state: State = State(field) + + /** + * Determines the immutability of the field reference and registers the dependees if necessary + */ + propertyStore(field, FieldReferenceImmutability.key) match { + + case FinalP( + ImmutableFieldReference | LazyInitializedThreadSafeFieldReference | + LazyInitializedNotThreadSafeButDeterministicFieldReference + ) ⇒ + state.referenceIsImmutable = Some(true) + + case FinalP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ + return Result(field, MutableField); + + case ep ⇒ state.dependees += ep + } + + /** + * Determines whether the field is dependent immutable if the flag [[considerGenericity]] is set + */ + if (considerGenericity) + handleGenericity() + else { + state.dependentImmutability = NotDependentImmutable + } + + /** + * Determines whether the reference object escapes or can be mutated if the flag [[considerEscape]] is set + */ + if (considerEscape) { + state.noEscapePossibilityViaReference = true //state.field.isPrivate + if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) + determineEscapeOfReferencedObjectOrValue() + if (state.escapesStillDetermined && !state.field.isPrivate) + state.noEscapePossibilityViaReference = false + + } else { + state.noEscapePossibilityViaReference = false + state.concreteClassTypeIsKnown = false + state.escapesStillDetermined = true + } + + /** + * In cases where we know the concrete class type assigned to the field we could use the immutability of this. + */ + if (!state.concreteClassTypeIsKnown) + handleTypeImmutability(state) + else { + state.typeImmutability = Mutable + } + + createResult() + } +} + +trait L3FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { + + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.ub(TACAI), + PropertyBounds.ub(EscapeProperty), + PropertyBounds.ub(FieldReferenceImmutability), + PropertyBounds.lub(TypeImmutability), + PropertyBounds.lub(FieldImmutability) + ) + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) +} + +/** + * + * Executor for the eager field immutability analysis. + * + */ +object EagerL3FieldImmutabilityAnalysis + extends L3FieldImmutabilityAnalysisScheduler + with BasicFPCFEagerAnalysisScheduler { + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L3FieldImmutabilityAnalysis(p) + val fields = p.allFields // p.allProjectClassFiles.flatMap(classfile ⇒ classfile.fields) //p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) + analysis + } +} + +/** + * + * Executor for the lazy field immutability analysis. + * + */ +object LazyL3FieldImmutabilityAnalysis + extends L3FieldImmutabilityAnalysisScheduler + with BasicFPCFLazyAnalysisScheduler { + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L3FieldImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldImmutability.key, + analysis.determineFieldImmutability + ) + analysis + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/AbstractFieldReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/AbstractFieldReferenceImmutabilityAnalysis.scala new file mode 100644 index 0000000000..9e5ea7f95b --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/AbstractFieldReferenceImmutabilityAnalysis.scala @@ -0,0 +1,203 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package tac +package fpcf +package analyses +package immutability +package fieldreference + +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.InterimUBP +import org.opalj.br.DeclaredMethod +import org.opalj.br.Field +import org.opalj.br.Method +import org.opalj.br.ObjectType +import org.opalj.br.PC +import org.opalj.br.PCs +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.FieldAccessInformationKey +import org.opalj.br.analyses.cg.ClosedPackagesKey +import org.opalj.br.analyses.cg.TypeExtensibilityKey +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.properties.EscapeProperty +import org.opalj.br.fpcf.properties.FieldPrematurelyRead +import org.opalj.br.fpcf.properties.ImmutableFieldReference +import org.opalj.br.fpcf.properties.MutableFieldReference +import org.opalj.br.fpcf.properties.NotPrematurelyReadField +import org.opalj.br.fpcf.properties.PrematurelyReadField +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.properties.FieldReferenceImmutability +import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.Entity +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.LBP +import org.opalj.fpcf.Property +import org.opalj.fpcf.UBP +import org.opalj.tac.common.DefinitionSite +import org.opalj.tac.common.DefinitionSitesKey +import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.value.ValueInformation +import org.opalj.br.fpcf.properties.cg.Callers + +/** + * + * Encompasses the base functions for determining field reference immutability. + * + * @author Tobias Roth + * @author Dominik Helm + * @author Florian Kübler + * @author Michael Eichberg + */ +trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { + + type V = DUVar[ValueInformation] + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) + final val definitionSites = project.get(DefinitionSitesKey) + implicit final val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + + case class State( + field: Field, + var referenceImmutability: FieldReferenceImmutability = ImmutableFieldReference, + var prematurelyReadDependee: Option[EOptionP[Field, FieldPrematurelyRead]] = None, + var purityDependees: Set[EOptionP[DeclaredMethod, Purity]] = Set.empty, + var lazyInitInvocation: Option[(DeclaredMethod, PC)] = None, + var calleesDependee: Map[DeclaredMethod, (EOptionP[DeclaredMethod, Callees], List[PC])] = Map.empty, + var referenceImmutabilityDependees: Set[EOptionP[Field, FieldReferenceImmutability]] = Set.empty, + var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty, + var typeDependees: Set[EOptionP[ObjectType, TypeImmutability]] = Set.empty, + var calledMethodsDependees: Map[Method, (EOptionP[Method, Callers], (TACode[TACMethodParameter, V], PCs))] = Map.empty + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || prematurelyReadDependee.isDefined || + purityDependees.nonEmpty || calleesDependee.nonEmpty || referenceImmutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty || typeDependees.nonEmpty + } + + def dependees: Set[EOptionP[Entity, Property]] = { + (prematurelyReadDependee ++ purityDependees ++ referenceImmutabilityDependees ++ escapeDependees ++ + calleesDependee.valuesIterator.map(_._1) ++ tacDependees.valuesIterator.map(_._1) ++ typeDependees).toSet + } + } + + /** + * Returns the TACode for a method if available and registers dependencies if necessary. + */ + def getTACAI( + method: Method, + pcs: PCs + )(implicit state: State): Option[TACode[TACMethodParameter, V]] = { + propertyStore(method, TACAI.key) match { + + case finalEP: FinalEP[Method, TACAI] ⇒ finalEP.ub.tac + + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + /** + * Checks whether the method that defines the value assigned to a (potentially) lazily initialized + * field is deterministic, ensuring that the same value is written even for concurrent + * executions. + */ + def isNonDeterministic(eop: EOptionP[DeclaredMethod, Purity])(implicit state: State): Boolean = eop match { + + case LBP(p: Purity) if p.isDeterministic ⇒ false + case UBP(p: Purity) if !p.isDeterministic ⇒ true + + case _ ⇒ + state.purityDependees += eop + false + } + + def lazyInitializerIsDeterministic( + method: Method + )(implicit state: State): Boolean = { + //over-approximates that the lazy initialization can not be influenced via a parameter + if (method.descriptor.parametersCount > 0) false + else !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + /** + * Checks whether the field the value assigned to a (potentially) lazily initialized field is final, + * ensuring that the same value is written even for concurrent executions. + */ + def isImmutableReference(eop: EOptionP[Field, FieldReferenceImmutability])(implicit state: State): Boolean = + eop match { + + case LBP(ImmutableFieldReference) ⇒ true + case UBP(MutableFieldReference) ⇒ false + + case _ ⇒ + state.referenceImmutabilityDependees += eop + true + } + + /** + * Checks whether the field is prematurely read, i.e. read before it is initialized in the + * constructor, using the corresponding property. + */ + def isPrematurelyRead( + eop: EOptionP[Field, FieldPrematurelyRead] + )(implicit state: State): Boolean = { + eop match { + + case LBP(NotPrematurelyReadField) ⇒ + state.prematurelyReadDependee = None + false + + case UBP(PrematurelyReadField) ⇒ + state.prematurelyReadDependee = None + true + + case eps ⇒ + state.prematurelyReadDependee = Some(eps) + false + } + } + + /** + * Checks whether the calls at a given pc within a given method introduce non determinism. + * @return if the calls introduce nondeterminism + */ + def doCallsIntroduceNonDeterminism( + calleesEOP: EOptionP[DeclaredMethod, Callees], + pc: PC + )(implicit state: State): Boolean = { + calleesEOP match { + + case FinalP(callees) ⇒ isNonDeterministicCallee(callees, pc) + case InterimUBP(callees) ⇒ isNonDeterministicCallee(callees.asInstanceOf[Callees], pc) + + case _ ⇒ + state.calleesDependee += calleesEOP.e → ((calleesEOP, pc :: state.calleesDependee(calleesEOP.e)._2)) + false + } + } + + /** + * Checks whether a callee is nondeterministic and sets the [[State.referenceImmutability]] to + * MutableFieldReference if it is not deterministic + * @return if the callee is nondeterministic + */ + def isNonDeterministicCallee(callees: Callees, pc: PC)(implicit state: State): Boolean = { + if (callees.isIncompleteCallSite(pc)) { + state.referenceImmutability = MutableFieldReference + true + } else { + val targets = callees.callees(pc) + if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { + state.referenceImmutability = MutableFieldReference + true + } else + false + } + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/AbstractFieldReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/AbstractFieldReferenceImmutabilityAnalysisLazyInitialization.scala new file mode 100644 index 0000000000..4b38eba1b8 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/AbstractFieldReferenceImmutabilityAnalysisLazyInitialization.scala @@ -0,0 +1,822 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package tac +package fpcf +package analyses +package immutability +package fieldreference + +import scala.annotation.tailrec +import scala.collection.mutable + +import org.opalj.RelationalOperators.EQ +import org.opalj.RelationalOperators.NE +//import org.opalj.br.ComputationalTypeFloat +//import org.opalj.br.ComputationalTypeInt +import org.opalj.br.Method +import org.opalj.br.cfg.BasicBlock +import org.opalj.br.cfg.CFGNode +import org.opalj.br.fpcf.FPCFAnalysis +//import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeFieldReference +import org.opalj.br.fpcf.properties.MutableFieldReference +import org.opalj.br.fpcf.properties.FieldReferenceImmutability +import org.opalj.collection.immutable.IntTrieSet +import org.opalj.br.ObjectType +import scala.annotation.switch +import org.opalj.br.fpcf.properties.cg.Callees +//import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.FieldType + +/** + * + * Encompasses the base functions for determining lazy initialization of a field reference. + * + * @author Tobias Roth + * @author Dominik Helm + * @author Florian Kübler + * @author Michael Eichberg + * + */ +trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization + extends AbstractFieldReferenceImmutabilityAnalysis + with FPCFAnalysis { + + /** + * handles the lazy initialization determination for a field write in a given method + * @author Tobias Roth + * @return true if there is no thread safe or deterministic lazy initialization + */ + def handleLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + taCode: TACode[TACMethodParameter, V] + )(implicit state: State): Boolean = { + val lazyInitializationResult: FieldReferenceImmutability = + determineLazyInitialization(writeIndex, defaultValue, method, taCode) + + state.referenceImmutability = lazyInitializationResult + lazyInitializationResult == MutableFieldReference + } + + /** + * Determines whether the basic block of a given index dominates the basic block of the other or is executed + * before the other when the basic blocks are the same + */ + def dominates( + potentiallyDominator: Int, + potentiallyDominated: Int, + taCode: TACode[TACMethodParameter, V] + ): Boolean = { + val bbPotentiallyDominator = taCode.cfg.bb(potentiallyDominator) + val bbPotentiallyDominated = taCode.cfg.bb(potentiallyDominated) + + taCode.cfg.dominatorTree + .strictlyDominates(bbPotentiallyDominator.nodeId, bbPotentiallyDominated.nodeId) || + bbPotentiallyDominator == bbPotentiallyDominated && potentiallyDominator < potentiallyDominated + } + + /** + * Determines whether a given field is lazy initialized in the given method through a given field write. + * @author Tobias Roth + */ + def determineLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + taCode: TACode[TACMethodParameter, V] + )(implicit state: State): FieldReferenceImmutability = { + val code = taCode.stmts + val cfg = taCode.cfg + + val write = code(writeIndex).asFieldWriteAccessStmt + val writeBB = cfg.bb(writeIndex).asBasicBlock + val resultCatchesAndThrows = findCatchesAndThrows(taCode) + + /** + * Determines whether all exceptions that are caught are thrown + */ + def noInterferingExceptions(): Boolean = { + resultCatchesAndThrows._1.forall { + case (catchPC, originsCaughtException) ⇒ + resultCatchesAndThrows._2.exists { + case (throwPC, throwDefinitionSites) ⇒ + dominates(taCode.pcToIndex(catchPC), taCode.pcToIndex(throwPC), taCode) && + throwDefinitionSites == originsCaughtException //throwing and catching same exceptions + } + } + } + + val findGuardResult = findGuards(method, writeIndex, defaultValue, taCode) + + val (readIndex, _ /* guardIndex */ , defaultCaseIndex, elseCaseIndex) = //guardIndex: for debugging purposes + if (findGuardResult.nonEmpty) + ( + findGuardResult.head._1, + findGuardResult.head._2, + findGuardResult.head._3, + findGuardResult.head._4 + ) + else + return MutableFieldReference; + + if (!dominates(defaultCaseIndex, writeIndex, taCode)) + return MutableFieldReference; + + val elseBB = cfg.bb(elseCaseIndex) + + // prevents wrong control flow + if (isTransitivePredecessor(elseBB, writeBB)) + return MutableFieldReference; + + if (method.returnType == state.field.fieldType) { + // prevents that another value than the field value is returned with the same type + if (!isFieldValueReturned(write, writeIndex, readIndex, taCode, findGuardResult)) + return MutableFieldReference; + //prevents that the field is seen with another value + if ( // potentially unsound with method.returnType == state.field.fieldType + // TODO comment it out and look at appearing cases + taCode.stmts.exists( + stmt ⇒ + stmt.isReturnValue && + !isTransitivePredecessor(writeBB, cfg.bb(taCode.pcToIndex(stmt.pc))) && + findGuardResult.forall { + case (indexOfFieldRead, _, _, _) ⇒ + !isTransitivePredecessor( + cfg.bb(indexOfFieldRead), + cfg.bb(taCode.pcToIndex(stmt.pc)) + ) + } + )) + return MutableFieldReference; + } + + val reads = fieldAccessInformation.readAccesses(state.field) + + // prevents reads outside the method + if (reads.exists(_._1 ne method)) + return MutableFieldReference; + + val writes = fieldAccessInformation.writeAccesses(state.field) + + // prevents writes outside the method + // and guarantees that the field is only once written within the method or the constructor + if (writes.exists( + methodAndPCs ⇒ + methodAndPCs._2.size > 1 || + ((methodAndPCs._1 ne method) && !methodAndPCs._1.isInitializer) + )) + return MutableFieldReference; + + // if the method is synchronized the monitor within the method doesn't have to be searched + if (method.isSynchronized) { + if (dominates(defaultCaseIndex, writeIndex, taCode) && noInterferingExceptions()) + LazyInitializedThreadSafeFieldReference + else + MutableFieldReference + } else { + + val (indexMonitorEnter, indexMonitorExit) = findMonitors(writeIndex, taCode) + + val monitorResultsDefined = indexMonitorEnter.isDefined && indexMonitorExit.isDefined + + if (monitorResultsDefined && //dominates(indexMonitorEnter.get, trueCaseIndex) && + dominates(indexMonitorEnter.get, readIndex, taCode)) { + if (noInterferingExceptions()) + LazyInitializedThreadSafeFieldReference + else + MutableFieldReference + } else { + if (write.value.asVar.definedBy.forall { defSite ⇒ //TODO check time consumption + defSite >= 0 /*&& checkWriteIsDeterministic( + code(defSite).asAssignment, + method, + code, + taCode + )*/ + } + && noInterferingExceptions()) { + //val computationalFieldType = state.field.fieldType.computationalType + //if (computationalFieldType != ComputationalTypeInt && + // computationalFieldType != ComputationalTypeFloat) { + LazyInitializedNotThreadSafeFieldReference + //} /*else + //LazyInitializedNotThreadSafeButDeterministicFieldReference + } else MutableFieldReference + } + } + } + + /** + * + * This method returns the information about catch blocks, throw statements and return nodes + * + * @note It requires still determined taCode + * + * @return The first element of the tuple returns: + * the caught exceptions (the pc of the catch, the exception type, the origin of the caught exception, + * the bb of the caughtException) + * @return The second element of the tuple returns: + * The throw statements: (the pc, the definitionSites, the bb of the throw statement) + * @author Tobias Roth + */ + def findCatchesAndThrows( + tacCode: TACode[TACMethodParameter, V] + ): (List[(Int, IntTrieSet)], List[(Int, IntTrieSet)]) = { + var caughtExceptions: List[(Int, IntTrieSet)] = List.empty + var throwStatements: List[(Int, IntTrieSet)] = List.empty + for (stmt ← tacCode.stmts) { + if (!stmt.isNop) { // to prevent the handling of partially negative pcs of nops + (stmt.astID: @switch) match { + + case CaughtException.ASTID ⇒ + val caughtException = stmt.asCaughtException + caughtExceptions = (caughtException.pc, caughtException.origins) :: caughtExceptions + + case Throw.ASTID ⇒ + val throwStatement = stmt.asThrow + val throwStatementDefinedBys = throwStatement.exception.asVar.definedBy + throwStatements = (throwStatement.pc, throwStatementDefinedBys) :: throwStatements + + case _ ⇒ + } + } + } + (caughtExceptions, throwStatements) + } + + /** + * Searches the closest monitor enter and exit to the field write. + * @return the index of the monitor enter and exit + * @author Tobias Roth + */ + def findMonitors( + fieldWrite: Int, + tacCode: TACode[TACMethodParameter, V] + )(implicit state: State): (Option[Int], Option[Int]) = { + + var result: (Option[Int], Option[Int]) = (None, None) + val startBB = tacCode.cfg.bb(fieldWrite) + var monitorExitQueuedBBs: Set[CFGNode] = startBB.successors + var worklistMonitorExit = getSuccessors(startBB, Set.empty) + + /** + * checks that a given monitor supports a thread safe lazy initialization. + * Supports two ways of synchronized blocks. + * + * When determining the lazy initialization of a static field, + * it allows only global locks on Foo.class. Independent of which class Foo is. + * + * When determining the lazy initialization of an instance fields, it allows + * synchronized(this) and synchronized(Foo.class). Independent of which class Foo is. + * In case of an instance field the second case is even stronger than synchronized(this). + * + */ + def checkMonitor(v: V)(implicit state: State): Boolean = { + v.definedBy.forall(definedByIndex ⇒ { + if (definedByIndex >= 0) { + tacCode.stmts(definedByIndex) match { + + //synchronized(Foo.class) + case Assignment(_, _, _: ClassConst) ⇒ true + case _ ⇒ false + } + } else { + //synchronized(this) + state.field.isNotStatic && IntTrieSet(definedByIndex) == SelfReferenceParameter + } + }) + } + + var monitorEnterQueuedBBs: Set[CFGNode] = startBB.predecessors + var worklistMonitorEnter = getPredecessors(startBB, Set.empty) + + //find monitorenter + while (worklistMonitorEnter.nonEmpty) { + val curBB = worklistMonitorEnter.head + worklistMonitorEnter = worklistMonitorEnter.tail + val startPC = curBB.startPC + val endPC = curBB.endPC + var hasNotFoundAnyMonitorYet = true + for (i ← startPC to endPC) { + (tacCode.stmts(i).astID: @switch) match { + case MonitorEnter.ASTID ⇒ + val monitorEnter = tacCode.stmts(i).asMonitorEnter + if (checkMonitor(monitorEnter.objRef.asVar)) { + result = (Some(tacCode.pcToIndex(monitorEnter.pc)), result._2) + hasNotFoundAnyMonitorYet = false + } + case _ ⇒ + } + } + if (hasNotFoundAnyMonitorYet) { + val predecessor = getPredecessors(curBB, monitorEnterQueuedBBs) + worklistMonitorEnter ++= predecessor + monitorEnterQueuedBBs ++= predecessor + } + } + //find monitorexit + while (worklistMonitorExit.nonEmpty) { + val curBB = worklistMonitorExit.head + + worklistMonitorExit = worklistMonitorExit.tail + val endPC = curBB.endPC + + val cfStmt = tacCode.stmts(endPC) + (cfStmt.astID: @switch) match { + + case MonitorExit.ASTID ⇒ + val monitorExit = cfStmt.asMonitorExit + if (checkMonitor(monitorExit.objRef.asVar)) { + result = (result._1, Some(tacCode.pcToIndex(monitorExit.pc))) + } + + case _ ⇒ + val successors = getSuccessors(curBB, monitorExitQueuedBBs) + worklistMonitorExit ++= successors + monitorExitQueuedBBs ++= successors + } + } + result + } + + /** + * Finds the index of the guarding if-Statement for a lazy initialization, the index of the + * first statement executed if the field does not have its default value and the index of the + * field read used for the guard and the index of the field-read. + */ + def findGuards( + method: Method, + fieldWrite: Int, + defaultValue: Any, + taCode: TACode[TACMethodParameter, V] + )(implicit state: State): List[(Int, Int, Int, Int)] = { + val cfg = taCode.cfg + val code = taCode.stmts + + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + var seen: Set[BasicBlock] = Set.empty + var result: List[(Int, Int, Int)] = List.empty + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + if (!seen.contains(curBB)) { + seen += curBB + + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + + case If.ASTID ⇒ + val ifStmt = cfStmt.asIf + if (ifStmt.condition.equals(EQ) && curBB != startBB && isGuard( + ifStmt, + defaultValue, + code, + taCode, + method + )) { + result = (endPC, ifStmt.targetStmt, endPC + 1) :: result + } else if (ifStmt.condition.equals(NE) && curBB != startBB && isGuard( + ifStmt, + defaultValue, + code, + taCode, + method + )) { + result = (endPC, endPC + 1, ifStmt.targetStmt) :: result + } else { + if ((cfg.bb(fieldWrite) != cfg.bb(ifStmt.target) || fieldWrite < ifStmt.target) && + isTransitivePredecessor(cfg.bb(fieldWrite), cfg.bb(ifStmt.target))) { + return List.empty //in cases where other if-statements destroy + } + } + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + } + + var finalResult: List[(Int, Int, Int, Int)] = List.empty + var fieldReadIndex = 0 + result.foreach(result ⇒ { + // The field read that defines the value checked by the guard must be used only for the + // guard or directly if the field's value was not the default value + val ifStmt = code(result._1).asIf + + val expr = + if (ifStmt.leftExpr.isConst) ifStmt.rightExpr + else ifStmt.leftExpr + + val definitions = expr.asVar.definedBy + if (definitions.forall(_ >= 0)) { + + fieldReadIndex = definitions.head + + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + + val fieldReadUsedCorrectly = + fieldReadUses.forall(use ⇒ use == result._1 || use == result._3) + + if (definitions.size == 1 && definitions.head >= 0 && fieldReadUsedCorrectly) { + // Found proper guard + finalResult = (fieldReadIndex, result._1, result._2, result._3) :: finalResult + } + } + }) + finalResult + } + + /** + * Returns all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + def getPredecessorsInternal(node: CFGNode, visited: Set[CFGNode]): Iterator[BasicBlock] = { + node.predecessors.iterator.flatMap { currentNode ⇒ + if (currentNode.isBasicBlock) + if (visited.contains(currentNode)) None + else Some(currentNode.asBasicBlock) + else getPredecessorsInternal(currentNode, visited) + } + } + getPredecessorsInternal(node, visited).toList + } + + def isTransitivePredecessor(possiblePredecessor: CFGNode, node: CFGNode): Boolean = { + + val visited: mutable.Set[CFGNode] = mutable.Set.empty + + def isTransitivePredecessorInternal(possiblePredecessor: CFGNode, node: CFGNode): Boolean = { + if (possiblePredecessor == node) true + else if (visited.contains(node)) false + else { + visited += node + node.predecessors.exists( + currentNode ⇒ isTransitivePredecessorInternal(possiblePredecessor, currentNode) + ) + } + } + isTransitivePredecessorInternal(possiblePredecessor, node) + } + + /** + * Returns all successors BasicBlocks of a CFGNode + */ + def getSuccessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + def getSuccessorsInternal(node: CFGNode, visited: Set[CFGNode]): Iterator[BasicBlock] = { + node.successors.iterator flatMap { currentNode ⇒ + if (currentNode.isBasicBlock) + if (visited.contains(currentNode)) None + else Some(currentNode.asBasicBlock) + else getSuccessors(currentNode, visited) + } + } + getSuccessorsInternal(node, visited).toList + } + + /** + * Checks if the value written to the field is guaranteed to be always the same. + * This is true if the value is constant or originates from a deterministic call of a method + * without non-constant parameters. Alternatively, if the initialization method itself is + * deterministic and has no parameters, the value is also always the same. + */ + /* def checkWriteIsDeterministic( + origin: Assignment[V], + method: Method, + code: Array[Stmt[V]], + taCode: TACode[TACMethodParameter, V] + )(implicit state: State): Boolean = { + + def isConstant(uvar: Expr[V]): Boolean = { + val defSites = uvar.asVar.definedBy + + def isConstantDef(index: Int) = { + if (index < 0) false + else if (code(index).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + + case Some(field) ⇒ + state.field == field || + isImmutableReference(propertyStore(field, FieldReferenceImmutability.key)) + + case _ ⇒ false // Unknown field + }) + } + } + defSites == SelfReferenceParameter || defSites.size == 1 && isConstantDef(defSites.head) + } + + val value = origin.expr + + def isNonConstDeterministic( + value: Expr[V], + taCode: TACode[TACMethodParameter, V] + )(implicit state: State): Boolean = { + (value.astID: @switch) match { + case BinaryExpr.ASTID ⇒ + isConstant(value.asBinaryExpr.left) && isConstant(value.asBinaryExpr.right) + + case GetStatic.ASTID | GetField.ASTID ⇒ + value.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + state.field == field || + isImmutableReference(propertyStore(field, FieldReferenceImmutability.key)) + + case _ ⇒ false // Unknown field + } + + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + // If the value originates from a call, that call must be deterministic and may not + // have any non constant parameters to guarantee that it is the same on every + // invocation. The receiver object must be the 'this' self reference for the same + // reason. + if (value.asFunctionCall.allParams.forall(isConstant)) { + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true + } else false + + case NewArray.ASTID ⇒ true //TODO look at it + + case Var.ASTID ⇒ + val varValue = value.asVar + varValue.definedBy.size == 1 && //no different values due to different control flows + varValue.definedBy.forall( + i ⇒ i >= 0 && isNonConstDeterministic(code(i).asAssignment.expr, taCode) + ) + + case New.ASTID ⇒ + val nonVirtualMethodCallIndexes = + origin.asAssignment.targetVar.usedBy.iterator.filter(i ⇒ code(i).isNonVirtualMethodCall) + nonVirtualMethodCallIndexes.forall { nonVirtualMethodCallIndex ⇒ + val callTargetResult = + taCode + .stmts(nonVirtualMethodCallIndex) + .asNonVirtualMethodCall + .resolveCallTarget( + state.field.classFile.thisType + ) + !callTargetResult.isEmpty && (!callTargetResult.value.isConstructor || + //if the constructor is called it must be deterministic + !isNonDeterministic(propertyStore(declaredMethods(callTargetResult.value), Purity.key))) + } + + case _ ⇒ + // The value neither is a constant nor originates from a call, but if the + // current method does not take parameters and is deterministic, the value is + // guaranteed to be the same on every invocation. + lazyInitializerIsDeterministic(method) + } + } + value.isConst || isNonConstDeterministic(value, taCode) + } */ + + /** + * Checks if an expression is a field read of the currently analyzed field. + * For instance fields, the read must be on the `this` reference. + */ + def isReadOfCurrentField( + expr: Expr[V], + tacCode: TACode[TACMethodParameter, V], + index: Int + )(implicit state: State): Boolean = { + def isExprReadOfCurrentField: Int ⇒ Boolean = + exprIndex ⇒ + exprIndex == index || + exprIndex >= 0 && isReadOfCurrentField( + tacCode.stmts(exprIndex).asAssignment.expr, + tacCode, + exprIndex + ) + (expr.astID: @switch) match { + case GetField.ASTID ⇒ + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) false + else expr.asGetField.resolveField(project).contains(state.field) + + case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project).contains(state.field) + case PrimitiveTypecastExpr.ASTID ⇒ false + + case Compare.ASTID ⇒ + val leftExpr = expr.asCompare.left + val rightExpr = expr.asCompare.right + leftExpr.asVar.definedBy + .forall(index ⇒ index >= 0 && tacCode.stmts(index).asAssignment.expr.isConst) && + rightExpr.asVar.definedBy.forall(isExprReadOfCurrentField) || + rightExpr.asVar.definedBy + .forall(index ⇒ index >= 0 && tacCode.stmts(index).asAssignment.expr.isConst) && + leftExpr.asVar.definedBy.forall(isExprReadOfCurrentField) + + case VirtualFunctionCall.ASTID ⇒ + val functionCall = expr.asVirtualFunctionCall + val fieldType = state.field.fieldType + functionCall.params.isEmpty && ( + fieldType match { + case ObjectType.Byte ⇒ functionCall.name == "byteValue" + case ObjectType.Short ⇒ functionCall.name == "shortValue" + case ObjectType.Integer ⇒ functionCall.name == "intValue" + case ObjectType.Long ⇒ functionCall.name == "longValue" + case ObjectType.Float ⇒ functionCall.name == "floatValue" + case ObjectType.Double ⇒ functionCall.name == "doubleValue" + case _ ⇒ false + } + ) && functionCall.receiver.asVar.definedBy.forall(isExprReadOfCurrentField) + + case _ ⇒ false + } + } + + /** + * Determines if an if-Statement is actually a guard for the current field, i.e. it compares + * the current field to the default value. + */ + def isGuard( + ifStmt: If[V], + defaultValue: Any, + code: Array[Stmt[V]], + tacCode: TACode[TACMethodParameter, V], + method: Method + )(implicit state: State): Boolean = { + + /** + * Checks if an expression + */ + @tailrec + def isDefaultConst(expr: Expr[V]): Boolean = { + + if (expr.isVar) { + val defSites = expr.asVar.definedBy + val head = defSites.head + defSites.size == 1 && head >= 0 && isDefaultConst(code(head).asAssignment.expr) + } else { + expr.isIntConst && defaultValue == expr.asIntConst.value || //defaultValue == expr.asIntConst.value || + expr.isFloatConst && defaultValue == expr.asFloatConst.value || + expr.isDoubleConst && defaultValue == expr.asDoubleConst.value || + expr.isLongConst && defaultValue == expr.asLongConst.value || + expr.isNullExpr && defaultValue == null + } + } + + /** + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. + */ + def isGuardInternal( + expr: V, + tacCode: TACode[TACMethodParameter, V], + method: Method + ): Boolean = { + expr.definedBy forall { index ⇒ + if (index < 0) + false // If the value is from a parameter, this can not be the guard + else { + val isStaticFunctionCall = code(index).asAssignment.expr.isStaticFunctionCall + val isVirtualFunctionCall = code(index).asAssignment.expr.isVirtualFunctionCall + if (isStaticFunctionCall || isVirtualFunctionCall) { + //in case of Integer etc.... .initValue() + + val calleesResult = propertyStore(declaredMethods(method), Callees.key) + if (doCallsIntroduceNonDeterminism(calleesResult, code(index).asAssignment.pc)) + return false; + + if (isVirtualFunctionCall) { + val virtualFunctionCall = code(index).asAssignment.expr.asVirtualFunctionCall + virtualFunctionCall.receiver.asVar.definedBy.forall( + receiverDefSite ⇒ + receiverDefSite >= 0 && + isReadOfCurrentField(code(receiverDefSite).asAssignment.expr, tacCode, index) + ) + } else { + isReadOfCurrentField(code(index).asAssignment.expr, tacCode, index) + } + } else { + isReadOfCurrentField(code(index).asAssignment.expr, tacCode, index) + } + } + } + } + + //Special handling for these types needed because of compare function in bytecode + def hasFloatDoubleOrLongType(fieldType: FieldType): Boolean = + fieldType.isDoubleType || fieldType.isFloatType || fieldType.isLongType + + if (ifStmt.rightExpr.isVar && hasFloatDoubleOrLongType(state.field.fieldType) && + ifStmt.rightExpr.asVar.definedBy.head > 0 && + tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.isCompare) { + + val left = + tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.asCompare.left.asVar + val right = + tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.asCompare.right.asVar + val leftExpr = tacCode.stmts(left.definedBy.head).asAssignment.expr + val rightExpr = tacCode.stmts(right.definedBy.head).asAssignment.expr + + if (leftExpr.isGetField || leftExpr.isGetStatic) isDefaultConst(rightExpr) + else (rightExpr.isGetField || rightExpr.isGetStatic) && isDefaultConst(leftExpr) + + } else if (ifStmt.leftExpr.isVar && ifStmt.rightExpr.isVar && ifStmt.leftExpr.asVar.definedBy.head >= 0 && + ifStmt.rightExpr.asVar.definedBy.head >= 0 && + hasFloatDoubleOrLongType(state.field.fieldType) && tacCode + .stmts(ifStmt.leftExpr.asVar.definedBy.head) + .asAssignment + .expr + .isCompare && + ifStmt.leftExpr.isVar && ifStmt.rightExpr.isVar) { + + val left = + tacCode.stmts(ifStmt.leftExpr.asVar.definedBy.head).asAssignment.expr.asCompare.left.asVar + val right = + tacCode.stmts(ifStmt.leftExpr.asVar.definedBy.head).asAssignment.expr.asCompare.right.asVar + val leftExpr = tacCode.stmts(left.definedBy.head).asAssignment.expr + val rightExpr = tacCode.stmts(right.definedBy.head).asAssignment.expr + + if (leftExpr.isGetField || leftExpr.isGetStatic) isDefaultConst(rightExpr) + else (rightExpr.isGetField || rightExpr.isGetStatic) && isDefaultConst(leftExpr) + + } else if (ifStmt.rightExpr.isVar && isDefaultConst(ifStmt.leftExpr)) { + isGuardInternal(ifStmt.rightExpr.asVar, tacCode, method) + } else if (ifStmt.leftExpr.isVar && isDefaultConst(ifStmt.rightExpr)) { + isGuardInternal(ifStmt.leftExpr.asVar, tacCode, method) + } else false + } + + /** + * Checks that the returned value is definitely read from the field. + */ + def isFieldValueReturned( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + taCode: TACode[TACMethodParameter, V], + guardIndexes: List[(Int, Int, Int, Int)] + )(implicit state: State): Boolean = { + + def isSimpleReadOfField(expr: Expr[V]) = { + expr.astID match { + case GetField.ASTID ⇒ + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) + false + else + expr.asGetField.resolveField(project).contains(state.field) + + case GetStatic.ASTID ⇒ + expr.asGetStatic.resolveField(project).contains(state.field) + + case _ ⇒ false + } + } + + taCode.stmts.forall { stmt ⇒ + !stmt.isReturnValue || { + + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + val assignedValueDefSite = write.value.asVar.definedBy + returnValueDefs.forall(_ >= 0) && { + if (returnValueDefs.size == 1 && returnValueDefs.head != readIndex) { + val expr = taCode.stmts(returnValueDefs.head).asAssignment.expr + isSimpleReadOfField(expr) && guardIndexes.exists { + case (_, guardIndex, defaultCase, _) ⇒ + dominates(guardIndex, returnValueDefs.head, taCode) && + (!dominates(defaultCase, returnValueDefs.head, taCode) || + dominates(writeIndex, returnValueDefs.head, taCode)) + } + } //The field is either read before the guard and returned or + // the value assigned to the field is returned + else { + returnValueDefs.size == 2 && assignedValueDefSite.size == 1 && + returnValueDefs.contains(readIndex) && { + returnValueDefs.contains(assignedValueDefSite.head) || { + val potentiallyReadIndex = returnValueDefs.filter(_ != readIndex).head + val expr = taCode.stmts(potentiallyReadIndex).asAssignment.expr + isSimpleReadOfField(expr) && + guardIndexes.exists { + case (_, guardIndex, defaultCase, _) ⇒ + dominates(guardIndex, potentiallyReadIndex, taCode) && + (!dominates(defaultCase, returnValueDefs.head, taCode) || + dominates(writeIndex, returnValueDefs.head, taCode)) + } + } + } + } + } + } + } + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/L0FieldReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/L0FieldReferenceImmutabilityAnalysis.scala new file mode 100644 index 0000000000..2f23d8f6e2 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/L0FieldReferenceImmutabilityAnalysis.scala @@ -0,0 +1,470 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package tac +package fpcf +package analyses +package immutability +package fieldreference + +import scala.annotation.switch + +import org.opalj.br.BooleanType +//import org.opalj.br.ClassFile +import org.opalj.br.DeclaredMethod +import org.opalj.br.Field +import org.opalj.br.Method +import org.opalj.br.PCs +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler +import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.fpcf.properties.AtMost +import org.opalj.br.fpcf.properties.EscapeInCallee +import org.opalj.br.fpcf.properties.EscapeProperty +import org.opalj.br.fpcf.properties.EscapeViaReturn +import org.opalj.br.fpcf.properties.FieldPrematurelyRead +import org.opalj.br.fpcf.properties.ImmutableFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicFieldReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeFieldReference +import org.opalj.br.fpcf.properties.MutableFieldReference +import org.opalj.br.fpcf.properties.NoEscape +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.properties.FieldReferenceImmutability +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.Entity +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.InterimEP +import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.InterimUBP +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyComputationResult +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.Result +import org.opalj.fpcf.SomeEPS +import org.opalj.tac.common.DefinitionSite +import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.br.ReferenceType +import org.opalj.br.ByteType +import org.opalj.br.CharType +import org.opalj.br.DoubleType +import org.opalj.br.FloatType +import org.opalj.br.IntegerType +import org.opalj.br.LongType +import org.opalj.br.ObjectType +import org.opalj.br.ShortType + +/** + * + * Determines the immutability of the reference of a class' field. + * A field reference can either refer to an reference object or have a value. + * + * Examples: + * class ... { + * ... + * final Object o; + * int n; + * ... + * } + * + * In both cases we consider o and n as a field reference. + * o refers to a reference object with type [[Object]] and n is storing an a value with type int. + * + * @note Requires that the 3-address code's expressions are not deeply nested. + * + * @author Tobias Roth + * @author Dominik Helm + * @author Florian Kübler + * @author Michael Eichberg + * + */ +class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeProject) + extends AbstractFieldReferenceImmutabilityAnalysisLazyInitialization + with AbstractFieldReferenceImmutabilityAnalysis + with FPCFAnalysis { + + val considerLazyInitialization: Boolean = + project.config.getBoolean( + "org.opalj.fpcf.analyses.L0FieldReferenceImmutabilityAnalysis.considerLazyInitialization" + ) + + def doDetermineFieldReferenceImmutability(entity: Entity): PropertyComputationResult = { + entity match { + + case field: Field ⇒ + determineFieldReferenceImmutability(field) + + case _ ⇒ + val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + } + + /** + * Analyzes the immutability fields references. + * + * This analysis is only ''soundy'' if the class file does not contain native methods. + * If the analysis is schedulued using its companion object all class files with + * native methods are filtered. + */ + private[analyses] def determineFieldReferenceImmutability( + field: Field + ): ProperPropertyComputationResult = { + import org.opalj.br.ClassFile + + if (field.isFinal) + return Result(field, ImmutableFieldReference); + + //if (field.isPublic) + // return Result(field, MutableFieldReference); + + implicit val state: State = State(field) + + val thisType = field.classFile.thisType + + // Fields are not final if they are read prematurely! + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) + return Result(field, MutableFieldReference); + + // Collect all classes that have access to the field, i.e. the declaring class and possibly + // classes in the same package as well as subclasses + // Give up if the set of classes having access to the field is not closed + val initialClasses = + if (field.isProtected || field.isPackagePrivate) { + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } + val classesHavingAccess: Iterator[ClassFile] = + if (field.isPublic) { + if (typeExtensibility(ObjectType.Object).isYesOrUnknown) { + return Result(field, MutableFieldReference); + } + project.allClassFiles.iterator + } else if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown) { + return Result(field, MutableFieldReference); + } + val subclassesIterator: Iterator[ClassFile] = + classHierarchy.allSubclassTypes(thisType, reflexive = false).flatMap { ot ⇒ + project.classFile(ot).filter(cf ⇒ !initialClasses.contains(cf)) + } + initialClasses.iterator ++ subclassesIterator + } else { + initialClasses.iterator + } + + // If there are native methods, we give up + if (classesHavingAccess.exists(_.methods.exists(_.isNative))) { //TODO flag for comparison...reim + if (!field.isFinal) + return Result(field, MutableFieldReference); + } + + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode ← getTACAI(method, pcs) //TODO field accesses via this + } { + if (methodUpdatesField(method, taCode, pcs)) + return Result(field, MutableFieldReference); + } + if (state.lazyInitInvocation.isDefined) { + val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + doCallsIntroduceNonDeterminism(calleesEOP, state.lazyInitInvocation.get._2) + } + createResult() + } + + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Any = { + + state.field.fieldType match { + case FloatType | ObjectType.Float ⇒ 0.0f + case DoubleType | ObjectType.Double ⇒ 0.0d + case LongType | ObjectType.Long ⇒ 0L + case CharType | ObjectType.Character ⇒ '\u0000' + case BooleanType | ObjectType.Boolean ⇒ false + case IntegerType | ObjectType.Integer | ByteType | ObjectType.Byte | ShortType | + ObjectType.Short ⇒ + 0 + case _: ReferenceType ⇒ null + } + } + + /** + * Prepares the PropertyComputation result, either as IntermediateResult if there are still + * dependees or as Result otherwise. + */ + def createResult()(implicit state: State): ProperPropertyComputationResult = { + + if (state.hasDependees && (state.referenceImmutability ne MutableFieldReference)) { + InterimResult( + state.field, + MutableFieldReference, + state.referenceImmutability, + state.dependees, + c + ) + } else { + Result(state.field, state.referenceImmutability) + } + } + + /** + * Continuation function handling updates to the FieldPrematurelyRead property or to the purity + * property of the method that initializes a (potentially) lazy initialized field. + */ + def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { + + var isNotFinal = false + + eps.pk match { + + case EscapeProperty.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.iterator.filter(_.e ne newEP.e).toSet + isNotFinal = handleEscapeProperty(newEP) + + case TACAI.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Method, TACAI]] + val method = newEP.e + val pcs = state.tacDependees(method)._2 + state.tacDependees -= method + if (eps.isRefinable) + state.tacDependees += method -> ((newEP, pcs)) + isNotFinal = methodUpdatesField(method, newEP.ub.tac.get, pcs) + + case Callees.key ⇒ + val newEPS = eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]] + val pcs = state.calleesDependee(newEPS.e)._2 + isNotFinal = pcs.forall(pc ⇒ doCallsIntroduceNonDeterminism(newEPS, pc)) + + case FieldPrematurelyRead.key ⇒ + isNotFinal = isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + + case Purity.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.iterator.filter(_.e ne newEP.e).toSet + val nonDeterministicResult = isNonDeterministic(newEP) + isNotFinal = nonDeterministicResult + + case FieldReferenceImmutability.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Field, FieldReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.iterator.filter(_.e ne newEP.e).toSet + isNotFinal = !isImmutableReference(newEP) + } + + if (isNotFinal) + state.referenceImmutability = MutableFieldReference + createResult() + } + + /** + * Analyzes field writes for a single method, returning false if the field may still be + * effectively final and true otherwise. + */ + def methodUpdatesField( + method: Method, + taCode: TACode[TACMethodParameter, V], + pcs: PCs + )(implicit state: State): Boolean = { + val field = state.field + val stmts = taCode.stmts + pcs.iterator.exists { pc ⇒ + val index = taCode.pcToIndex(pc) + if (index > -1) { //TODO actually, unnecessary but required because there are '-1'; dead + val stmt = stmts(index) + if (stmt.pc == pc) { + (stmt.astID: @switch) match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + if (field.isStatic) { + method.isConstructor + } else { + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + receiverDefs != SelfReferenceParameter + } + } else { + if (field.isStatic || + stmt.asPutField.objRef.asVar.definedBy == SelfReferenceParameter) { + // We consider lazy initialization if there is only single write + // outside an initializer, so we can ignore synchronization + state.referenceImmutability == LazyInitializedThreadSafeFieldReference || + state.referenceImmutability == + LazyInitializedNotThreadSafeButDeterministicFieldReference || + // A lazily initialized instance field must be initialized only + // by its owning instance + !field.isStatic && + stmt.asPutField.objRef.asVar.definedBy != SelfReferenceParameter || + // A field written outside an initializer must be lazily + // initialized or it is non-final + { + if (considerLazyInitialization) { + handleLazyInitialization( + index, + getDefaultValue(), + method, + taCode + ) + } else + true + } + // + + // + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // println("reference has escaped") + // note that here we assume real three address code (flat hierarchy) + + // for instance fields it is okay if they are written in the + // constructor (w.r.t. the currently initialized object!) + + // If the field that is written is not the one referred to by the + // self reference, it is not effectively final. + + // However, a method (e.g. clone) may instantiate a new object and + // write the field as long as that new object did not yet escape. + true + } else { + false + /*println( + s""" + | class: ${state.field.classFile.thisType} + | field: ${state.field} + | method: $method + |""".stripMargin + ) + throw new Exception("else case in methodUpdatesField")*/ + } + + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead + false + } + } else false + } + //false + } + + /** + * + * Checks whether the object reference of a PutField does escape (except for being returned). + */ + def referenceHasEscaped( + ref: V, + stmts: Array[Stmt[V]], + method: Method + )(implicit state: State): Boolean = { + ref.definedBy.forall { defSite ⇒ + if (defSite < 0) true + else { // Must be locally created + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else + handleEscapeProperty( + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + ) + } + } + } + + /** + * Handles the influence of an escape property on the field mutability. + * @return true if the object - on which a field write occurred - escapes, false otherwise. + * @note (Re-)Adds dependees as necessary. + */ + def handleEscapeProperty( + ep: EOptionP[DefinitionSite, EscapeProperty] + )(implicit state: State): Boolean = { + + val result = ep match { + case FinalP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ false + case FinalP(AtMost(_)) ⇒ true + case _: FinalEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return + + case InterimUBP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ + state.escapeDependees += ep + false + + case InterimUBP(AtMost(_)) ⇒ true + case _: InterimEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return + + case _ ⇒ + state.escapeDependees += ep + false + } + result + } + +} + +trait L0FieldReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { + + import org.opalj.br.fpcf.properties.FieldImmutability + + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.ub(EscapeProperty), + PropertyBounds.ub(FieldReferenceImmutability), + PropertyBounds.ub(FieldImmutability) + ) + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldReferenceImmutability) +} + +/** + * Executor for the eager field reference immutability analysis. + */ +object EagerL0FieldReferenceImmutabilityAnalysis + extends L0FieldReferenceImmutabilityAnalysisScheduler + with BasicFPCFEagerAnalysisScheduler { + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0FieldReferenceImmutabilityAnalysis(p) + val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) // p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldReferenceImmutability) + analysis + } +} + +/** + * Executor for the lazy field reference immutability analysis. + */ +object LazyL0FieldReferenceImmutabilityAnalysis + extends L0FieldReferenceImmutabilityAnalysisScheduler + with BasicFPCFLazyAnalysisScheduler { + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0FieldReferenceImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldReferenceImmutability.key, + analysis.determineFieldReferenceImmutability + ) + analysis + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/AbstractPointsToAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/AbstractPointsToAnalysis.scala index 96aa091f19..6142dd478b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/AbstractPointsToAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/AbstractPointsToAnalysis.scala @@ -453,7 +453,7 @@ trait AbstractPointsToAnalysis extends PointsToAnalysisBase with ReachableMethod if (state.hasCalleesDepenedee) { val calleesDependee = state.calleesDependee results += InterimPartialResult( - Some(calleesDependee), + Set(calleesDependee), continuationForCallees( calleesDependee, new PointsToAnalysisState[ElementType, PointsToSet](state.method, state.tacDependee) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ArraycopyPointsToAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ArraycopyPointsToAnalysis.scala index 0f9fc75a14..8adf5b643f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ArraycopyPointsToAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ArraycopyPointsToAnalysis.scala @@ -35,7 +35,7 @@ import org.opalj.tac.fpcf.analyses.cg.V import org.opalj.tac.fpcf.properties.TheTACAI /** - * Handles the effect of [[java.lang.System.arraycopy*]] to points-to sets. + * Handles the effect of `java.lang.System.arraycopy*` to points-to sets. * * @author Dominik Helm */ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/PointsToAnalysisBase.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/PointsToAnalysisBase.scala index 3e508c5574..f1e6f715f5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/PointsToAnalysisBase.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/PointsToAnalysisBase.scala @@ -317,7 +317,7 @@ trait PointsToAnalysisBase extends AbstractPointsToBasedAnalysis { } if (newDependees.nonEmpty) { results ::= InterimPartialResult( - newDependees.values.map(_._1), + newDependees.valuesIterator.map(_._1).toSet, continuationForNewAllocationSitesAtPutField( knownPointsTo, rhsDefSitesEPS, fieldOpt, newDependees ) @@ -358,7 +358,7 @@ trait PointsToAnalysisBase extends AbstractPointsToBasedAnalysis { } if (newDependees.nonEmpty) { results ::= InterimPartialResult( - newDependees.values.map(_._1), + newDependees.valuesIterator.map(_._1).toSet, continuationForNewAllocationSitesAtArrayStore( knownPointsTo, rhsDefSitesEPS, arrayType, newDependees ) @@ -401,7 +401,7 @@ trait PointsToAnalysisBase extends AbstractPointsToBasedAnalysis { if (newDependees.nonEmpty) { results +:= InterimPartialResult( - newDependees.values.map(_._1), + newDependees.valuesIterator.map(_._1).toSet, continuationForNewAllocationSitesAtGetField( defSiteObject, fieldOpt, filter, newDependees ) @@ -445,7 +445,7 @@ trait PointsToAnalysisBase extends AbstractPointsToBasedAnalysis { if (newDependees.nonEmpty) { results +:= InterimPartialResult( - newDependees.values.map(_._1), + newDependees.valuesIterator.map(_._1).toSet, continuationForNewAllocationSitesAtArrayLoad( defSiteObject, arrayType, filter, newDependees ) @@ -496,7 +496,7 @@ trait PointsToAnalysisBase extends AbstractPointsToBasedAnalysis { if (newDependees.nonEmpty) { results +:= InterimPartialResult( - newDependees.values.map(_._1), + newDependees.valuesIterator.map(_._1).toSet, continuationForShared(e, newDependees) ) } @@ -553,7 +553,7 @@ trait PointsToAnalysisBase extends AbstractPointsToBasedAnalysis { val (defSite, fieldOpt, filter) = fakeEntity val dependees = state.dependeesOf(fakeEntity) results += InterimPartialResult( - dependees.values.map(_._1), + dependees.valuesIterator.map(_._1).toSet, continuationForNewAllocationSitesAtGetField( defSite, fieldOpt, filter, dependees ) @@ -578,7 +578,7 @@ trait PointsToAnalysisBase extends AbstractPointsToBasedAnalysis { val dependees = state.dependeesOf(fakeEntity) if (defSitesEPSs.nonEmpty || (knownPointsTo ne emptyPointsToSet)) results += InterimPartialResult( - dependees.values.map(_._1), + dependees.valuesIterator.map(_._1).toSet, continuationForNewAllocationSitesAtPutField( knownPointsTo, defSitesEPSs, fieldOpt, dependees ) @@ -591,7 +591,7 @@ trait PointsToAnalysisBase extends AbstractPointsToBasedAnalysis { val (defSite, arrayType, filter) = fakeEntity val dependees = state.dependeesOf(fakeEntity) results += InterimPartialResult( - dependees.values.map(_._1), + dependees.valuesIterator.map(_._1).toSet, continuationForNewAllocationSitesAtArrayLoad( defSite, arrayType, filter, dependees ) @@ -616,7 +616,7 @@ trait PointsToAnalysisBase extends AbstractPointsToBasedAnalysis { val dependees = state.dependeesOf(fakeEntity) if (defSitesEPSs.nonEmpty || (knownPointsTo ne emptyPointsToSet)) results += InterimPartialResult( - dependees.values.map(_._1), + dependees.valuesIterator.map(_._1).toSet, continuationForNewAllocationSitesAtArrayStore( knownPointsTo, defSitesEPSs, arrayType, dependees ) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/UnsafePointsToAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/UnsafePointsToAnalysis.scala index dae3adf5e5..b32cb70714 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/UnsafePointsToAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/UnsafePointsToAnalysis.scala @@ -38,7 +38,7 @@ import org.opalj.tac.fpcf.analyses.cg.V import org.opalj.tac.fpcf.properties.TheTACAI /** - * Models effects of [[sun.misc.Unsafe]] to points-to sets. + * Models effects of `sun.misc.Unsafe` to points-to sets. * * @author Dominik Helm */ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis.scala index 3831cb7691..677c478de1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis.scala @@ -25,17 +25,11 @@ import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.UBPS import org.opalj.value.ValueInformation -import org.opalj.br.fpcf.properties.ClassImmutability import org.opalj.br.fpcf.properties.CompileTimePure -import org.opalj.br.fpcf.properties.FieldMutability -import org.opalj.br.fpcf.properties.FinalField -import org.opalj.br.fpcf.properties.ImmutableObject -import org.opalj.br.fpcf.properties.ImmutableType import org.opalj.br.fpcf.properties.ImpureByAnalysis import org.opalj.br.fpcf.properties.ImpureByLackOfInformation import org.opalj.br.fpcf.properties.Pure import org.opalj.br.fpcf.properties.SideEffectFree -import org.opalj.br.fpcf.properties.TypeImmutability import org.opalj.br.ComputationalTypeReference import org.opalj.br.DeclaredMethod import org.opalj.br.DefinedMethod @@ -53,6 +47,14 @@ import org.opalj.ai.ValueOrigin import org.opalj.ai.isImmediateVMException import org.opalj.tac.fpcf.analyses.cg.uVarForDefSites import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.DeepImmutableField +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.DependentImmutableField +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.br.fpcf.properties.ClassImmutability +import org.opalj.br.fpcf.properties.TypeImmutability /** * Base trait for analyses that analyze the purity of methods. @@ -371,10 +373,10 @@ trait AbstractPurityAnalysis extends FPCFAnalysis { if (state.ubPurity.isDeterministic) { fieldRef.asFieldRead.resolveField match { case Some(field) if field.isStatic ⇒ - checkFieldMutability(propertyStore(field, FieldMutability.key), None) + checkFieldMutability(propertyStore(field, FieldImmutability.key), None) case Some(field) ⇒ checkFieldMutability( - propertyStore(field, FieldMutability.key), Some(fieldRef.asGetField.objRef) + propertyStore(field, FieldImmutability.key), Some(fieldRef.asGetField.objRef) ) case _ ⇒ // Unknown field if (fieldRef.isGetField) isLocal(fieldRef.asGetField.objRef, SideEffectFree) @@ -387,11 +389,13 @@ trait AbstractPurityAnalysis extends FPCFAnalysis { * Examines the influence that a given field mutability has on the method's purity. */ def checkFieldMutability( - ep: EOptionP[Field, FieldMutability], + ep: EOptionP[Field, FieldImmutability], objRef: Option[Expr[V]] )(implicit state: StateType): Unit = ep match { - case LBP(_: FinalField) ⇒ // Final fields don't impede purity - case _: FinalEP[Field, FieldMutability] ⇒ // Mutable field + case LBP(ShallowImmutableField | + DependentImmutableField | + DeepImmutableField) ⇒ // Immutable fields don't impede purity + case _: FinalEP[Field, FieldImmutability] ⇒ // Mutable field if (objRef.isDefined) { if (state.ubPurity.isDeterministic) isLocal(objRef.get, SideEffectFree) @@ -407,7 +411,7 @@ trait AbstractPurityAnalysis extends FPCFAnalysis { * Analyses must implement this method with the behavior they need, e.g. registering dependees. */ def handleUnknownFieldMutability( - ep: EOptionP[Field, FieldMutability], + ep: EOptionP[Field, FieldImmutability], objRef: Option[Expr[V]] )(implicit state: StateType): Unit @@ -478,7 +482,7 @@ trait AbstractPurityAnalysis extends FPCFAnalysis { returnValue: Expr[V] )(implicit state: StateType): Boolean = ep match { // Returning immutable object is pure - case LBP(ImmutableType | ImmutableObject) ⇒ true + case LBP(DeepImmutableType | DeepImmutableClass) ⇒ true case _: FinalEP[ObjectType, Property] ⇒ atMost(Pure) // Can not be compile time pure if mutable object is returned if (state.ubPurity.isDeterministic) @@ -601,9 +605,9 @@ trait AbstractPurityAnalysis extends FPCFAnalysis { def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { case FinalP(p) ⇒ Result(dm, p) case ep @ InterimLUBP(lb, ub) ⇒ - InterimResult.create(dm, lb, ub, Seq(ep), c) + InterimResult.create(dm, lb, ub, Set(ep), c) case epk ⇒ - InterimResult(dm, ImpureByAnalysis, CompileTimePure, Seq(epk), c) + InterimResult(dm, ImpureByAnalysis, CompileTimePure, Set(epk), c) } c(propertyStore(declaredMethods(dm.definedMethod), Purity.key)) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L1PurityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L1PurityAnalysis.scala index e1d426832a..f4199b4cf1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L1PurityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L1PurityAnalysis.scala @@ -33,15 +33,9 @@ import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.cfg.CFG import org.opalj.br.fpcf.properties.ClassifiedImpure -import org.opalj.br.fpcf.properties.ClassImmutability -import org.opalj.br.fpcf.properties.FieldMutability -import org.opalj.br.fpcf.properties.FinalField -import org.opalj.br.fpcf.properties.ImmutableObject -import org.opalj.br.fpcf.properties.ImmutableType import org.opalj.br.fpcf.properties.ImpureByAnalysis import org.opalj.br.fpcf.properties.Pure import org.opalj.br.fpcf.properties.SideEffectFree -import org.opalj.br.fpcf.properties.TypeImmutability import org.opalj.br.fpcf.properties.VirtualMethodPurity import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.properties.Purity @@ -54,6 +48,13 @@ import org.opalj.br.fpcf.properties.cg.Callers import org.opalj.br.fpcf.properties.cg.NoCallers import org.opalj.ai.isImmediateVMException import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.br.fpcf.properties.DeepImmutableField +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.DependentImmutableField +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.br.fpcf.properties.ClassImmutability +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.TypeImmutability /** * An inter-procedural analysis to determine a method's purity. @@ -201,7 +202,7 @@ class L1PurityAnalysis private[analyses] (val project: SomeProject) extends Abst * known yet. */ override def handleUnknownFieldMutability( - ep: EOptionP[Field, FieldMutability], + ep: EOptionP[Field, FieldImmutability], objRef: Option[Expr[V]] )(implicit state: State): Unit = { if (objRef.isEmpty || !isLocal(objRef.get, Pure)) state.dependees += ep @@ -277,12 +278,16 @@ class L1PurityAnalysis private[analyses] (val project: SomeProject) extends Abst return Result(state.definedMethod, ImpureByAnalysis) // Cases that are pure - case FinalP(_: FinalField) ⇒ // Reading eff. final fields - case FinalP(ImmutableType | ImmutableObject) ⇒ // Returning immutable reference + case FinalP(ShallowImmutableField | + DependentImmutableField | + DeepImmutableField) ⇒ // Reading eff. final fields + case FinalP(DeepImmutableType | + DeepImmutableType) ⇒ // Returning immutable reference // Cases resulting in side-effect freeness - case FinalP(_: FieldMutability | // Reading non-final field - _: TypeImmutability | _: ClassImmutability) ⇒ // Returning mutable reference + case FinalP(_: FieldImmutability | // Reading non-final field + _: TypeImmutability | + _: ClassImmutability) ⇒ // Returning mutable reference atMost(SideEffectFree) case _: SomeInterimEP ⇒ state.dependees += eps @@ -427,7 +432,7 @@ trait L1PurityAnalysisScheduler extends FPCFAnalysisScheduler { Set( PropertyBounds.ub(TACAI), PropertyBounds.ub(Callees), - PropertyBounds.lub(FieldMutability), + PropertyBounds.lub(FieldImmutability), PropertyBounds.lub(ClassImmutability), PropertyBounds.lub(TypeImmutability), PropertyBounds.lub(Purity) @@ -456,7 +461,8 @@ object EagerL1PurityAnalysis extends L1PurityAnalysisScheduler with FPCFEagerAna ): FPCFAnalysis = { val dms = p.get(DeclaredMethodsKey).declaredMethods val methods = dms.collect { - case dm if dm.hasSingleDefinedMethod && dm.definedMethod.body.isDefined && !analysis.configuredPurity.wasSet(dm) && ps(dm, Callers.key).ub != NoCallers ⇒ + case dm if dm.hasSingleDefinedMethod && dm.definedMethod.body.isDefined && + !analysis.configuredPurity.wasSet(dm) && ps(dm, Callers.key).ub != NoCallers ⇒ dm.asDefinedMethod } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis.scala index 84678b3dba..30129d72b3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis.scala @@ -6,14 +6,11 @@ package analyses package purity import scala.annotation.switch - import scala.collection.immutable.IntMap - import net.ceedubs.ficus.Ficus._ import org.opalj.collection.immutable.EmptyIntTrieSet import org.opalj.collection.immutable.IntTrieSet -import org.opalj.fpcf.Entity import org.opalj.fpcf.EOptionP import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimResult @@ -45,7 +42,7 @@ import org.opalj.br.fpcf.properties.ExtensibleGetter import org.opalj.br.fpcf.properties.ExtensibleLocalField import org.opalj.br.fpcf.properties.ExtensibleLocalFieldWithGetter import org.opalj.br.fpcf.properties.FieldLocality -import org.opalj.br.fpcf.properties.FieldMutability +import org.opalj.br.fpcf.properties.FieldImmutability import org.opalj.br.fpcf.properties.FreshReturnValue import org.opalj.br.fpcf.properties.Getter import org.opalj.br.fpcf.properties.ImpureByAnalysis @@ -117,24 +114,27 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst ) extends AnalysisState { var fieldLocalityDependees: Map[Field, (EOptionP[Field, FieldLocality], Set[(Expr[V], Purity)])] = Map.empty - var fieldMutabilityDependees: Map[Field, (EOptionP[Field, FieldMutability], Set[Option[Expr[V]]])] = Map.empty + var fieldMutabilityDependees: Map[Field, (EOptionP[Field, FieldImmutability], Set[Option[Expr[V]]])] = Map.empty var classImmutabilityDependees: Map[ObjectType, (EOptionP[ObjectType, ClassImmutability], Set[Expr[V]])] = Map.empty + var typeImmutabilityDependees: Map[ObjectType, (EOptionP[ObjectType, TypeImmutability], Set[Expr[V]])] = Map.empty var purityDependees: Map[DeclaredMethod, (EOptionP[DeclaredMethod, Purity], Set[Seq[Expr[V]]])] = Map.empty var calleesDependee: Option[EOptionP[DeclaredMethod, Callees]] = None + var callees: Option[Callees] = None var rvfDependees: Map[DeclaredMethod, (EOptionP[DeclaredMethod, ReturnValueFreshness], Set[(Option[Expr[V]], Purity)])] = Map.empty + var rvfCallSites: IntMap[(Option[Expr[V]], Purity)] = IntMap.empty var staticDataUsage: Option[EOptionP[DeclaredMethod, StaticDataUsage]] = None var tacai: Option[EOptionP[Method, TACAI]] = None - def dependees: Traversable[EOptionP[Entity, Property]] = + def dependees: Set[SomeEOptionP] = (fieldLocalityDependees.valuesIterator.map(_._1) ++ fieldMutabilityDependees.valuesIterator.map(_._1) ++ classImmutabilityDependees.valuesIterator.map(_._1) ++ @@ -143,7 +143,7 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst calleesDependee ++ rvfDependees.valuesIterator.map(_._1) ++ staticDataUsage ++ - tacai).toTraversable + tacai).toSet def addFieldLocalityDependee( f: Field, @@ -160,7 +160,7 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst def addFieldMutabilityDependee( f: Field, - eop: EOptionP[Field, FieldMutability], + eop: EOptionP[Field, FieldImmutability], owner: Option[Expr[V]] ): Unit = { if (fieldMutabilityDependees.contains(f)) { @@ -303,8 +303,11 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst treatParamsAsFresh: Boolean, excludedDefSites: IntTrieSet = EmptyIntTrieSet )(implicit state: State): Boolean = { - if (expr eq null) // Expression is unknown due to an indirect call (e.g. reflection) - return false; + if (expr eq null) { + // Expression is unknown due to an indirect call (e.g. reflection) + atMost(otherwise) + return false + }; if (expr.isConst) return true; @@ -583,7 +586,7 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst * Adds the dependee necessary if a field mutability is not known yet. */ override def handleUnknownFieldMutability( - ep: EOptionP[Field, FieldMutability], + ep: EOptionP[Field, FieldImmutability], objRef: Option[Expr[V]] )(implicit state: State): Unit = { state.addFieldMutabilityDependee(ep.e, ep, objRef) @@ -644,6 +647,7 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst } var newFieldLocalityDependees: Map[Field, (EOptionP[Field, FieldLocality], Set[(Expr[V], Purity)])] = Map.empty + for ((dependee, (eop, data)) ← state.fieldLocalityDependees) { val newData = data.filter(_._2 meet state.ubPurity ne state.ubPurity) if (newData.nonEmpty) newFieldLocalityDependees += ((dependee, (eop, newData))) @@ -657,6 +661,7 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst state.rvfCallSites = newRVFCallsites var newRVFDependees: Map[DeclaredMethod, (EOptionP[DeclaredMethod, ReturnValueFreshness], Set[(Option[Expr[V]], Purity)])] = Map.empty + for ((dependee, (eop, data)) ← state.rvfDependees) { val newData = data.filter(_._2 meet state.ubPurity ne state.ubPurity) if (newData.nonEmpty) newRVFDependees += ((dependee, (eop, newData))) @@ -664,6 +669,7 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst state.rvfDependees = newRVFDependees var newPurityDependees: Map[DeclaredMethod, (EOptionP[DeclaredMethod, Purity], Set[Seq[Expr[V]]])] = Map.empty + for ((dependee, eAndD) ← state.purityDependees) { if (eAndD._1.isEPK || (eAndD._1.lb meet state.ubPurity ne state.ubPurity)) newPurityDependees += ((dependee, eAndD)) @@ -738,12 +744,12 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst dependees._2.foreach { e ⇒ checkMethodPurity(eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]], e) } - case FieldMutability.key ⇒ + case FieldImmutability.key ⇒ val e = eps.e.asInstanceOf[Field] val dependees = state.fieldMutabilityDependees(e) state.removeFieldMutabilityDependee(e) dependees._2.foreach { e ⇒ - checkFieldMutability(eps.asInstanceOf[EOptionP[Field, FieldMutability]], e) + checkFieldMutability(eps.asInstanceOf[EOptionP[Field, FieldImmutability]], e) } case ClassImmutability.key ⇒ val e = eps.e.asInstanceOf[ObjectType] @@ -852,8 +858,10 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst } val callees = propertyStore(state.definedMethod, Callees.key) - if (!checkPurityOfCallees(callees)) + if (!checkPurityOfCallees(callees)) { + assert(state.ubPurity.isInstanceOf[ClassifiedImpure]) return Result(state.definedMethod, state.ubPurity) + } if (callees.hasUBP) state.rvfCallSites.foreach { @@ -944,7 +952,7 @@ trait L2PurityAnalysisScheduler extends FPCFAnalysisScheduler { override def uses: Set[PropertyBounds] = { Set( - PropertyBounds.lub(FieldMutability), + PropertyBounds.lub(FieldImmutability), PropertyBounds.lub(ClassImmutability), PropertyBounds.lub(TypeImmutability), PropertyBounds.lub(StaticDataUsage), @@ -981,7 +989,8 @@ object EagerL2PurityAnalysis extends L2PurityAnalysisScheduler with FPCFEagerAna val dms = p.get(DeclaredMethodsKey).declaredMethods val methods = dms.collect { // todo querying ps is quiet expensive - case dm if dm.hasSingleDefinedMethod && dm.definedMethod.body.isDefined && !analysis.configuredPurity.wasSet(dm) && ps(dm, Callers.key).ub != NoCallers ⇒ + case dm if dm.hasSingleDefinedMethod && dm.definedMethod.body.isDefined && + !analysis.configuredPurity.wasSet(dm) && ps(dm, Callers.key).ub != NoCallers ⇒ dm.asDefinedMethod } ps.scheduleEagerComputationsForEntities(methods)(