From 3a445412ffe2655cac484de0255790dd91fcaefd Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 23 Oct 2019 22:36:25 +0200 Subject: [PATCH 001/327] First proposal of the lattice for field immutability --- .../fpcf/properties/FieldMutability_new.scala | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala new file mode 100644 index 0000000000..b0f5c3e421 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala @@ -0,0 +1,37 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.properties + +import org.opalj.fpcf._ + +sealed trait FieldMutabilityPropertyMetaInformation_new extends PropertyMetaInformation { + + type Self = FieldMutability_new + +} +/** + * @author Tobias Peter Roth + */ +sealed trait FieldMutability_new extends Property with FieldMutabilityPropertyMetaInformation_new { + + final def key: PropertyKey[FieldMutability_new] = FieldMutability_new.key +} + +object FieldMutability_new extends FieldMutabilityPropertyMetaInformation_new { + + final val PropertyKeyName = "opalj.FieldMutability_new" + + final val key: PropertyKey[FieldMutability_new] = { + PropertyKey.create( + PropertyKeyName, + MutableField + ) + } +} + +sealed trait ImmutableField extends FieldMutability_new + +case object ShallowImmutableField extends ImmutableField + +case object DeepImmutableField extends ImmutableField + +case object MutableField extends FieldMutability_new \ No newline at end of file From c6c39aac59984856b7aa2314a8f6c559a1b946dd Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 23 Oct 2019 22:39:54 +0200 Subject: [PATCH 002/327] Start of the new analysis of field immutability using the new lattice --- .../L3FieldMutabilityAnalysis_new.scala | 339 ++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala new file mode 100644 index 0000000000..48b7af0a5e --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala @@ -0,0 +1,339 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.analyses + +import org.opalj.br.{Field, Method, PCs} +import org.opalj.br.analyses.{FieldAccessInformationKey, SomeProject} +import org.opalj.br.analyses.cg.{ClosedPackagesKey, TypeExtensibilityKey} +import org.opalj.br.fpcf.{BasicFPCFEagerAnalysisScheduler, BasicFPCFLazyAnalysisScheduler, FPCFAnalysis, FPCFAnalysisScheduler} +import org.opalj.br.fpcf.properties._ +import org.opalj.fpcf._ +import org.opalj.tac.fpcf.analyses.escape.EagerInterProceduralEscapeAnalysis.V +import org.opalj.tac._ +import org.opalj.tac.fpcf.properties.TACAI + +/** + * New implementation of the fieldimmutability analysis + * @author Tobias Peter Roth + */ +class L3FieldMutabilityAnalysis_new private[analyses] (val project: SomeProject) extends FPCFAnalysis { + + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) + + case class State() { + var hasImmutableType: Boolean = false + var isFinal = false + var effectivelyFinal = true + var influencedByMethod = false + var escapesViaMethod = false + } + + def doDetermineFieldMutability_new(entity: Entity): PropertyComputationResult = { + entity match { + case field: Field ⇒ determineFieldMutability_new(field) + case _ ⇒ + val m = entity.getClass.getName+"is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + } + + + private[analyses] def determineFieldMutability_new(field: Field): PropertyComputationResult = { + val state: State = new State() + + //Reference Immutablity + if (field.isProtected || field.isPackagePrivate || field.isPublic) { + return Result(field, MutableField) + } + state.isFinal = field.isFinal + + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + dependencies += EPK(field.fieldType, TypeImmutability.key) + + /** + * def isSetByConstructor(): Boolean = { + * fieldAccessInformation.writeAccesses(field). + * filter(_._1.isConstructor). + * foreach(x ⇒ + * ( + * x._1.asMethod.descriptor.parameterTypes.foreach(x ⇒ + * if (x == field.fieldType) { + * return true + * }) + * )) + * false + * }* + */ + + // if constructor sets the field it could escape if it is not immutable + def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { + //dependencies = dependencies.filter(_.e ne eps.e) + (eps: @unchecked) match { + //case _: InterimEP[_, _] ⇒ + // dependencies += eps + // InterimResult(InterimEP(field, MutableField), dependencies, c(state)) + case FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) ⇒ + if (!state.influencedByMethod & (state.isFinal || state.effectivelyFinal)) { + Result(field, DeepImmutableField) + } else { + + return InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + } + case FinalEP(e, MutableType) ⇒ { + if ((state.isFinal || state.effectivelyFinal) && !state.influencedByMethod) { + return Result(field, ShallowImmutableField) + } + return Result(field, MutableField) + } + } + } + + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode = getTACAI(method, pcs) + } { + if (methodInfluencesField(method, taCode.get, pcs, field)) { + state.influencedByMethod = true + } + if (escapesViaMethod(method, taCode.get, pcs, field)) { + state.escapesViaMethod = true + } + } + + //determine field Type immutability + val result = propertyStore(field.fieldType, TypeImmutability.key) + println("Result "+result) + + result match { + case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) ⇒ { + println("has immutable type") + state.hasImmutableType = true + } + case x: InterimEP[_, _] ⇒ return InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + case _ ⇒ {} + } + println("Escapes Via Method: "+state.escapesViaMethod) + println("Effectively Final: "+state.effectivelyFinal) + println("is final: "+state.isFinal) + println("Has ImmutableType : "+state.hasImmutableType) + println("influenced by method: "+state.influencedByMethod) + println("State"+state.toString) + + if (field.isPublic || field.isPackagePrivate) { + return Result(field, MutableField) + } + + if (((state.isFinal) || state.effectivelyFinal) && state.escapesViaMethod && !state.hasImmutableType) { + return Result(field, MutableField) + } + if (((state.isFinal) || state.effectivelyFinal) && state.escapesViaMethod && state.hasImmutableType) { + return Result(field, DeepImmutableField) + } + if (state.influencedByMethod) { + return Result(field, MutableField) + } + if (((state.isFinal) || state.effectivelyFinal) && !state.hasImmutableType) { + return Result(field, ShallowImmutableField) + } + if (((state.isFinal) || state.effectivelyFinal) && state.hasImmutableType) { + return Result(field, DeepImmutableField) + } + if (((state.isFinal) || state.effectivelyFinal) && !state.influencedByMethod) { + if (state.hasImmutableType) { + return Result(field, DeepImmutableField) + } else { + return Result(field, ShallowImmutableField) + } + } + if (((state.isFinal) || state.effectivelyFinal) && state.escapesViaMethod && state.hasImmutableType) { + + return Result(field, DeepImmutableField) + } + + // not final or eff final reference and/or not influenced by a method + return Result(field, MutableField) + } + /** + * Analyzes field writes for a single method, returning false if the field may still be + * effectively final and true otherwise. + */ + + def escapesViaMethod( + method: Method, + taCode: TACode[TACMethodParameter, V], + pcs: PCs, + field: Field + ): Boolean = + { + if (method.isConstructor) { + method.asMethod.descriptor.parameterTypes.foreach(x ⇒ if (x == field.fieldType) return true) + } + println("method: "+method.toJava.toString) + val stmts = taCode.stmts + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + println(stmt.astID) + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + println("field.isStatic "+field.isStatic) + if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + return true + } + if (field.isStatic) { + } + } else { + + if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + + return true; + } + return false + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead + } + } + } + false + } + + def methodInfluencesField( + method: Method, + taCode: TACode[TACMethodParameter, V], + pcs: PCs, + field: Field + ): Boolean = { + if (method.isConstructor) { + method.asMethod.descriptor.parameterTypes.foreach(x ⇒ if (x == field.fieldType) return true) + } + println("method: "+method.toJava.toString) + val stmts = taCode.stmts + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + println(stmt.astID) + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + println("field.isStatic "+field.isStatic) + if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + return false + } + if (field.isStatic) { + } + } else { + + if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + return false; + } + return true + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead + } + } + } + false + } + + def referenceHasEscaped( + reference: V, + stmts: Array[Stmt[V]], + method: Method + ): Boolean = { + reference.definedBy.forall { defSite ⇒ + { + //TODO + false + } + } + } + + /** + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + + def getTACAI( + method: Method, + pcs: PCs + ): Option[TACode[TACMethodParameter, V]] = { + propertyStore(method, TACAI.key) match { + case finalEP: FinalEP[Method, TACAI] ⇒ + finalEP.ub.tac + case eps: InterimEP[Method, TACAI] ⇒ + eps.ub.tac + case epk ⇒ + None + } + } + +} + +trait L3FieldMutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { + + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(FieldMutability), + PropertyBounds.lub(TypeImmutability), + PropertyBounds.lub(FieldMutability_new), + PropertyBounds.ub(EscapeProperty) + ) + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability_new) + +} + +/** + * Executor for the new field mutability analysis. + */ +object EagerL3FieldMutabilityAnalysis_new + extends L3FieldMutabilityAnalysisScheduler_new + with BasicFPCFEagerAnalysisScheduler { + + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L3FieldMutabilityAnalysis_new(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldMutability_new) + analysis + } + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty +} + +/** + * Executor for the new lazy field mutability analysis. + */ +object LazyL3FieldMutabilityAnalysis_new + extends L3FieldMutabilityAnalysisScheduler_new + with BasicFPCFLazyAnalysisScheduler { + + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L3FieldMutabilityAnalysis_new(p) + //TODO + //ps.registerLazyPropertyComputation( + //FieldMutability_new.key, analysis.determineFieldMutability_new + //) + analysis + } + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + +} From 7cba8bc610d16a1142a1f7b4d04402c0d10ecc1a Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 23 Oct 2019 22:44:44 +0200 Subject: [PATCH 003/327] Runner for the new field immutable analysis implementation --- .../RunFieldMutabilityAnalysisNew.scala | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala new file mode 100644 index 0000000000..ae28589d93 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala @@ -0,0 +1,45 @@ +/* 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.ProjectAnalysisApplication +import org.opalj.br.analyses.Project +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.br.fpcf.analyses.{EagerClassImmutabilityAnalysis, EagerL3FieldMutabilityAnalysis_new, EagerTypeImmutabilityAnalysis} +import org.opalj.br.fpcf.properties.{DeepImmutableField, MutableField, ShallowImmutableField} +import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis + +/** + * Runs the new field mutability implementation + * + * @author Tobias Peter Roth + */ +object RunFieldMutabilityAnalysisNew extends ProjectAnalysisApplication { + + override def title: String = "run new field immutability analysis" + + override def description: String = "run new field immutability analysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + //project. + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll(EagerTypeImmutabilityAnalysis, EagerL3FieldMutabilityAnalysis_new, EagerL1FieldMutabilityAnalysis, EagerClassImmutabilityAnalysis); + return "Mutable Fields: "+propertyStore.finalEntities(MutableField).toList.toString()+"\n"+ + "Shallow Immutable Fields: "+propertyStore.finalEntities(ShallowImmutableField).toList.toString()+"\n"+ + "Deep Immutable Fields: "+propertyStore.finalEntities(DeepImmutableField).toList.toString() + } +} From 5e156bf9d2ec0cb7d717875704d13f0e73061906 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 6 Nov 2019 16:19:21 +0100 Subject: [PATCH 004/327] Introduced Reference Immutability. A whole new lattice, the analysis which is partly taken from the field mutability analysis and mapped to the new lattice and the demo-class for running the reference immutability analysis. --- .../ReferenceImmutabilityAnalysisDemo.scala | 56 + .../properties/ReferenceImmutability.scala | 52 + .../L0ReferenceImmutabilityAnalysis.scala | 1109 +++++++++++++++++ 3 files changed, 1217 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala new file mode 100644 index 0000000000..1b8292afa4 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -0,0 +1,56 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.EagerL0PurityAnalysis +import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis + +/** + * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll( + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerL0ReferenceImmutabilityAnalysis + ); + + "Mutable References: "+propertyStore + .finalEntities(MutableReference) + .toList + .toString()+"\n"+ + "Immutable References: "+propertyStore + .finalEntities(ImmutableReference) + .toList + .toString() + } +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala new file mode 100644 index 0000000000..54e86d106d --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -0,0 +1,52 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.properties + +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaInformation { + + type Self = ReferenceImmutability + +} + +/** + * Describes the reference immutability of org.opalj.br.Field. + * The analysis is used from the old implementation of the L2FieldMutabilityAnalysis. + * + * [[MutableReference]] A reference through which the object it points to could be changed. + * + * [[LazyInitializedReference]] A reference through which the object it points to is changed in a initial like phase + * and afterwards never changed through this reference + * + * [[ImmutableReference]] A reference through which the object it leads to is set in the constructor and afterwards + * never changed. And there is no possibility to change it afterwards through this reference.. + * + * @author Tobias Peter Roth + */ +sealed trait ReferenceImmutability + extends Property + with ReferenceImmutabilityPropertyMetaInformation { + final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key +} + +object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { + + final val PropertyKeyName = "opalj.ReferenceImmutability" + + final val key: PropertyKey[ReferenceImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableReference + ) + } +} + +sealed trait RefImm extends ReferenceImmutability + +case object MutableReference extends RefImm + +case object LazyInitializedReference extends RefImm + +case object ImmutableReference extends RefImm diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala new file mode 100644 index 0000000000..4d2590fb19 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -0,0 +1,1109 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package tac +package fpcf +package analyses + +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._ +import org.opalj.fpcf._ +import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler +import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler +import org.opalj.br._ +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.FieldAccessInformationKey +import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.cg.ClosedPackagesKey +import org.opalj.br.analyses.cg.TypeExtensibilityKey +import org.opalj.br.cfg.BasicBlock +import org.opalj.br.cfg.CFG +import org.opalj.br.cfg.CFGNode +import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.fpcf.properties.EscapeProperty +import org.opalj.br.fpcf.properties.FieldPrematurelyRead +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.ai.isImmediateVMException +import org.opalj.ai.pcOfImmediateVMException +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.value.ValueInformation + +/** + * + * Implementation is used from the old L2FieldMutability implementation + * but the lattice is mapped to the new reference immutability lattice. + * + * @note Requires that the 3-address code's expressions are not deeply nested. + * + * @author Dominik Helm + * @author Florian Kübler + * @author Michael Eichberg + * @author Tobias Peter Roth + */ +class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProject) + extends FPCFAnalysis { + + case class State( + field: Field, + var referenceImmutability: ReferenceImmutability = ImmutableReference, + 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, + var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty + } + + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + } + } + + 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) + + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field ⇒ determineReferenceImmutability(field) + case _ ⇒ + val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + + /** + * Analyzes the mutability of private non-final fields. + * + * 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 determineReferenceImmutability( + field: Field + ): ProperPropertyComputationResult = { + implicit val state: State = State(field) + println( + "field.isPrematurelyRead "+isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key)) + ) + // Fields are not final if they are read prematurely! + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) + return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); + println("field.isFinal "+field.isFinal) + if (field.isFinal) + return createResult(); + + state.referenceImmutability = ImmutableReference //EffectivelyFinalField + + val thisType = field.classFile.thisType + + if (field.isPublic) + return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) + + // 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) { + if (!closedPackages.isClosed(thisType.packageName)) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } + + val classesHavingAccess: Iterator[ClassFile] = + if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + 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))) + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode ← getTACAI(method, pcs) + } { + if (methodUpdatesField(method, taCode, pcs)) { + println("method updates field: true") + return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); + } else + println("method updates field: false") + + } + + if (state.lazyInitInvocation.isDefined) { + val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + handleCalls(calleesEOP) + } + + createResult() + } + + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: State + ): Boolean = { + calleesEOP match { + case FinalP(callees) ⇒ + state.calleesDependee = None + handleCallees(callees) + case InterimUBP(callees) ⇒ + state.calleesDependee = Some(calleesEOP) + handleCallees(callees) + case _ ⇒ + state.calleesDependee = Some(calleesEOP) + false + } + } + + def handleCallees(callees: Callees)(implicit state: State): Boolean = { + val pc = state.lazyInitInvocation.get._2 + if (callees.isIncompleteCallSite(pc)) { + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else { + val targets = callees.callees(pc).toTraversable + if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else false + } + } + + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + * The code below can be used to identify such value, but is not yet adapted to using the + * TACAI property */ + /* + var constantVal: Option[Any] = None + var allInitializeConstant = true + + val field = state.field + var constructors: Set[Method] = + if(field.isStatic) Set.empty else field.classFile.constructors.toSet + + val writesIterator = fieldAccessInformation.writeAccesses(field).iterator + while (writesIterator.hasNext && allInitializeConstant) { + val (method, pc) = writesIterator.next() + constructors -= method + val code = tacai(method).stmts + + val index = pcToIndex(pc) + val stmt = code(index) + if (stmt.astID == PutStatic.ASTID || + stmt.asPutField.objRef.asVar.definedBy == SelfReferenceParameter) { + val write = stmt.asFieldWriteAccessStmt + if (write.resolveField(p).contains(state.field)) { + val defs = write.value.asVar.definedBy + if (defs.size == 1 && defs.head >= 0) { + val defSite = code(defs.head).asAssignment.expr + val const = if (defSite.isIntConst) + Some(defSite.asIntConst.value) + else if (defSite.isFloatConst) + Some(defSite.asFloatConst.value) + else None + if (const.isDefined) { + if (constantVal.isDefined) { + if (constantVal != const) { + allInitializeConstant = false + constantVal = None + } + } else constantVal = const + } else { + allInitializeConstant = false + constantVal = None + } + } + } + } + } + + for (constructor ← constructors) { + // TODO iterate all statements + val NonVirtualMethodCall(_, declClass, _, name, _, rcvr, _) = stmt + // Consider calls to other constructors as initializations as either + // the called constructor will initialize the field or delegate to yet + // another constructor + if (declClass != state.field.classFile.thisType || name != "" || + rcvr.asVar.definedBy != SelfReferenceParameter) { + if (constantVal.isDefined) allInitializeConstant = false + else constantVal = Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + } + } + + constantVal */ + } + + /** + * 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 NonFinalFieldByAnalysis)) + InterimResult( + state.field, + MutableReference, //NonFinalFieldByAnalysis, + 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 = { + val isNotFinal = eps.pk match { + case EscapeProperty.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + methodUpdatesField(method, newEP.ub.tac.get, pcs) + case Callees.key ⇒ + handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key ⇒ + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + isNonDeterministic(newEP) + case ReferenceImmutability.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + !isImmutableReference(newEP) + } + + if (isNotFinal) + Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) + else + createResult() + } + + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + )(implicit state: State): Boolean = { + val write = code(writeIndex).asFieldWriteAccessStmt + + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + return false; + } + + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method + } + + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + case None ⇒ return false; + } + + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) + return false; + + println( + "Check writes: "+checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg) + ) + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + + true + } + + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val definitions = write.value.asVar.definedBy + + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + // The field write must be guarded correctly + isDeterministic && + checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID ⇒ + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID ⇒ + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ ⇒ return false; // neither a field read nor a return + } + index += 1 + } + false + } + + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + /** + * 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 = { + println("xxxxxx") + val field = state.field + val stmts = taCode.stmts + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + if (field.isStatic) { + if (method.isConstructor) + return true; + } else { + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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.referenceImmutability == LazyInitializedReference) //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; + + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + // A field written outside an initializer must be lazily + // initialized or it is non-final + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )) + return true; + + state.referenceImmutability = LazyInitializedReference //LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead + } + } + } + false + } + + /** + * 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 eps: InterimEP[Method, TACAI] ⇒ + * state.tacDependees += method → ((eps, pcs)) + * eps.ub.tac + * case epk ⇒ + * state.tacDependees += method → ((epk, pcs)) + * None + * } + * } + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) + } + } + } + } + + /** + * 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 = 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 + } + + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt ⇒ + stmt.astID == CaughtException.ASTID + } flatMap { exception ⇒ + exception.asCaughtException.origins.map { origin: Int ⇒ + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator + } + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement + + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + true + } + + /** + * 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]] + )(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(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + false + }) + } + } + + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) + } + + val value = origin.expr + + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID ⇒ + value.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + false + } + 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.exists(!isConstant(_))) { + false + } else { + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true + } + 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, code) + } + + value.isConst || isNonConstDeterministic + } + + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) + } + } + + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + true + } + + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode ⇒ + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) + } + result.toList + } + + /** + * 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. + */ + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID ⇒ + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + } + + None + } + + /** + * 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])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID ⇒ + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) + case _ ⇒ None + } + field.contains(state.field) + } + + /** + * 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]] + )(implicit state: State): Boolean = { + + /** + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + */ + 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } + + /** + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. + */ + def isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index ⇒ + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } + } + + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else false + } + + /** + * Checks if 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) ⇒ true + case eps ⇒ + state.prematurelyReadDependee = Some(eps) + false + } + + /** + * Checkes 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. + */ + 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 + } + + /** + * Checkes if 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, ReferenceImmutability] + )(implicit state: State): Boolean = eop match { + case FinalEP(e, ImmutableReference) ⇒ true + case FinalEP(e, MutableReference) ⇒ false + + /** + * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ + * true + * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * + */ + case _ ⇒ + state.referenceImmutabilityDependees += eop + true + } +} + +trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { + + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.ub(EscapeProperty), + PropertyBounds.lub(ReferenceImmutability) + ) + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) + +} + +/** + * Executor for the field mutability analysis. + */ +object EagerL0ReferenceImmutabilityAnalysis + extends L0ReferenceImmutabilityAnalysisScheduler + with BasicFPCFEagerAnalysisScheduler { + + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) + analysis + } + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty +} + +/** + * Executor for the lazy field mutability analysis. + */ +object LazyL0ReferenceImmutabilityAnalysis + extends L0ReferenceImmutabilityAnalysisScheduler + with BasicFPCFLazyAnalysisScheduler { + + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + ReferenceImmutability.key, + analysis.determineReferenceImmutability + ) + analysis + } + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) +} From ce6ee0f2f7fe181773efa39afd8888a14d6d8e69 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 6 Nov 2019 16:54:38 +0100 Subject: [PATCH 005/327] clean up --- .../RunFieldMutabilityAnalysisNew.scala | 45 --- .../fpcf/properties/FieldMutability_new.scala | 37 -- .../L3FieldMutabilityAnalysis_new.scala | 339 ------------------ 3 files changed, 421 deletions(-) delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala delete mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala deleted file mode 100644 index ae28589d93..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala +++ /dev/null @@ -1,45 +0,0 @@ -/* 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.ProjectAnalysisApplication -import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.analyses.{EagerClassImmutabilityAnalysis, EagerL3FieldMutabilityAnalysis_new, EagerTypeImmutabilityAnalysis} -import org.opalj.br.fpcf.properties.{DeepImmutableField, MutableField, ShallowImmutableField} -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis - -/** - * Runs the new field mutability implementation - * - * @author Tobias Peter Roth - */ -object RunFieldMutabilityAnalysisNew extends ProjectAnalysisApplication { - - override def title: String = "run new field immutability analysis" - - override def description: String = "run new field immutability analysis" - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(project: Project[URL]): String = { - //project. - val analysesManager = project.get(FPCFAnalysesManagerKey) - - val (propertyStore, _) = analysesManager.runAll(EagerTypeImmutabilityAnalysis, EagerL3FieldMutabilityAnalysis_new, EagerL1FieldMutabilityAnalysis, EagerClassImmutabilityAnalysis); - return "Mutable Fields: "+propertyStore.finalEntities(MutableField).toList.toString()+"\n"+ - "Shallow Immutable Fields: "+propertyStore.finalEntities(ShallowImmutableField).toList.toString()+"\n"+ - "Deep Immutable Fields: "+propertyStore.finalEntities(DeepImmutableField).toList.toString() - } -} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala deleted file mode 100644 index b0f5c3e421..0000000000 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala +++ /dev/null @@ -1,37 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.br.fpcf.properties - -import org.opalj.fpcf._ - -sealed trait FieldMutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - - type Self = FieldMutability_new - -} -/** - * @author Tobias Peter Roth - */ -sealed trait FieldMutability_new extends Property with FieldMutabilityPropertyMetaInformation_new { - - final def key: PropertyKey[FieldMutability_new] = FieldMutability_new.key -} - -object FieldMutability_new extends FieldMutabilityPropertyMetaInformation_new { - - final val PropertyKeyName = "opalj.FieldMutability_new" - - final val key: PropertyKey[FieldMutability_new] = { - PropertyKey.create( - PropertyKeyName, - MutableField - ) - } -} - -sealed trait ImmutableField extends FieldMutability_new - -case object ShallowImmutableField extends ImmutableField - -case object DeepImmutableField extends ImmutableField - -case object MutableField extends FieldMutability_new \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala deleted file mode 100644 index 48b7af0a5e..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala +++ /dev/null @@ -1,339 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.br.fpcf.analyses - -import org.opalj.br.{Field, Method, PCs} -import org.opalj.br.analyses.{FieldAccessInformationKey, SomeProject} -import org.opalj.br.analyses.cg.{ClosedPackagesKey, TypeExtensibilityKey} -import org.opalj.br.fpcf.{BasicFPCFEagerAnalysisScheduler, BasicFPCFLazyAnalysisScheduler, FPCFAnalysis, FPCFAnalysisScheduler} -import org.opalj.br.fpcf.properties._ -import org.opalj.fpcf._ -import org.opalj.tac.fpcf.analyses.escape.EagerInterProceduralEscapeAnalysis.V -import org.opalj.tac._ -import org.opalj.tac.fpcf.properties.TACAI - -/** - * New implementation of the fieldimmutability analysis - * @author Tobias Peter Roth - */ -class L3FieldMutabilityAnalysis_new private[analyses] (val project: SomeProject) extends FPCFAnalysis { - - final val typeExtensibility = project.get(TypeExtensibilityKey) - final val closedPackages = project.get(ClosedPackagesKey) - final val fieldAccessInformation = project.get(FieldAccessInformationKey) - - case class State() { - var hasImmutableType: Boolean = false - var isFinal = false - var effectivelyFinal = true - var influencedByMethod = false - var escapesViaMethod = false - } - - def doDetermineFieldMutability_new(entity: Entity): PropertyComputationResult = { - entity match { - case field: Field ⇒ determineFieldMutability_new(field) - case _ ⇒ - val m = entity.getClass.getName+"is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } - } - - - private[analyses] def determineFieldMutability_new(field: Field): PropertyComputationResult = { - val state: State = new State() - - //Reference Immutablity - if (field.isProtected || field.isPackagePrivate || field.isPublic) { - return Result(field, MutableField) - } - state.isFinal = field.isFinal - - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - dependencies += EPK(field.fieldType, TypeImmutability.key) - - /** - * def isSetByConstructor(): Boolean = { - * fieldAccessInformation.writeAccesses(field). - * filter(_._1.isConstructor). - * foreach(x ⇒ - * ( - * x._1.asMethod.descriptor.parameterTypes.foreach(x ⇒ - * if (x == field.fieldType) { - * return true - * }) - * )) - * false - * }* - */ - - // if constructor sets the field it could escape if it is not immutable - def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - //dependencies = dependencies.filter(_.e ne eps.e) - (eps: @unchecked) match { - //case _: InterimEP[_, _] ⇒ - // dependencies += eps - // InterimResult(InterimEP(field, MutableField), dependencies, c(state)) - case FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) ⇒ - if (!state.influencedByMethod & (state.isFinal || state.effectivelyFinal)) { - Result(field, DeepImmutableField) - } else { - - return InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) - } - case FinalEP(e, MutableType) ⇒ { - if ((state.isFinal || state.effectivelyFinal) && !state.influencedByMethod) { - return Result(field, ShallowImmutableField) - } - return Result(field, MutableField) - } - } - } - - for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode = getTACAI(method, pcs) - } { - if (methodInfluencesField(method, taCode.get, pcs, field)) { - state.influencedByMethod = true - } - if (escapesViaMethod(method, taCode.get, pcs, field)) { - state.escapesViaMethod = true - } - } - - //determine field Type immutability - val result = propertyStore(field.fieldType, TypeImmutability.key) - println("Result "+result) - - result match { - case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) ⇒ { - println("has immutable type") - state.hasImmutableType = true - } - case x: InterimEP[_, _] ⇒ return InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) - case _ ⇒ {} - } - println("Escapes Via Method: "+state.escapesViaMethod) - println("Effectively Final: "+state.effectivelyFinal) - println("is final: "+state.isFinal) - println("Has ImmutableType : "+state.hasImmutableType) - println("influenced by method: "+state.influencedByMethod) - println("State"+state.toString) - - if (field.isPublic || field.isPackagePrivate) { - return Result(field, MutableField) - } - - if (((state.isFinal) || state.effectivelyFinal) && state.escapesViaMethod && !state.hasImmutableType) { - return Result(field, MutableField) - } - if (((state.isFinal) || state.effectivelyFinal) && state.escapesViaMethod && state.hasImmutableType) { - return Result(field, DeepImmutableField) - } - if (state.influencedByMethod) { - return Result(field, MutableField) - } - if (((state.isFinal) || state.effectivelyFinal) && !state.hasImmutableType) { - return Result(field, ShallowImmutableField) - } - if (((state.isFinal) || state.effectivelyFinal) && state.hasImmutableType) { - return Result(field, DeepImmutableField) - } - if (((state.isFinal) || state.effectivelyFinal) && !state.influencedByMethod) { - if (state.hasImmutableType) { - return Result(field, DeepImmutableField) - } else { - return Result(field, ShallowImmutableField) - } - } - if (((state.isFinal) || state.effectivelyFinal) && state.escapesViaMethod && state.hasImmutableType) { - - return Result(field, DeepImmutableField) - } - - // not final or eff final reference and/or not influenced by a method - return Result(field, MutableField) - } - /** - * Analyzes field writes for a single method, returning false if the field may still be - * effectively final and true otherwise. - */ - - def escapesViaMethod( - method: Method, - taCode: TACode[TACMethodParameter, V], - pcs: PCs, - field: Field - ): Boolean = - { - if (method.isConstructor) { - method.asMethod.descriptor.parameterTypes.foreach(x ⇒ if (x == field.fieldType) return true) - } - println("method: "+method.toJava.toString) - val stmts = taCode.stmts - for (pc ← pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - println(stmt.astID) - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID ⇒ - if (method.isInitializer) { - println("field.isStatic "+field.isStatic) - if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - return true - } - if (field.isStatic) { - } - } else { - - if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - - return true; - } - return false - } - case _ ⇒ throw new RuntimeException("unexpected field access"); - } - } else { - // nothing to do as the put field is dead - } - } - } - false - } - - def methodInfluencesField( - method: Method, - taCode: TACode[TACMethodParameter, V], - pcs: PCs, - field: Field - ): Boolean = { - if (method.isConstructor) { - method.asMethod.descriptor.parameterTypes.foreach(x ⇒ if (x == field.fieldType) return true) - } - println("method: "+method.toJava.toString) - val stmts = taCode.stmts - for (pc ← pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - println(stmt.astID) - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID ⇒ - if (method.isInitializer) { - println("field.isStatic "+field.isStatic) - if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - return false - } - if (field.isStatic) { - } - } else { - - if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - return false; - } - return true - } - case _ ⇒ throw new RuntimeException("unexpected field access"); - } - } else { - // nothing to do as the put field is dead - } - } - } - false - } - - def referenceHasEscaped( - reference: V, - stmts: Array[Stmt[V]], - method: Method - ): Boolean = { - reference.definedBy.forall { defSite ⇒ - { - //TODO - false - } - } - } - - /** - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - - def getTACAI( - method: Method, - pcs: PCs - ): Option[TACode[TACMethodParameter, V]] = { - propertyStore(method, TACAI.key) match { - case finalEP: FinalEP[Method, TACAI] ⇒ - finalEP.ub.tac - case eps: InterimEP[Method, TACAI] ⇒ - eps.ub.tac - case epk ⇒ - None - } - } - -} - -trait L3FieldMutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { - - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(FieldMutability), - PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(FieldMutability_new), - PropertyBounds.ub(EscapeProperty) - ) - - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability_new) - -} - -/** - * Executor for the new field mutability analysis. - */ -object EagerL3FieldMutabilityAnalysis_new - extends L3FieldMutabilityAnalysisScheduler_new - with BasicFPCFEagerAnalysisScheduler { - - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L3FieldMutabilityAnalysis_new(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldMutability_new) - analysis - } - - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty -} - -/** - * Executor for the new lazy field mutability analysis. - */ -object LazyL3FieldMutabilityAnalysis_new - extends L3FieldMutabilityAnalysisScheduler_new - with BasicFPCFLazyAnalysisScheduler { - - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L3FieldMutabilityAnalysis_new(p) - //TODO - //ps.registerLazyPropertyComputation( - //FieldMutability_new.key, analysis.determineFieldMutability_new - //) - analysis - } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - -} From 4245579371585a8db7deb504bb6e5944961e1489 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 6 Nov 2019 17:47:54 +0100 Subject: [PATCH 006/327] Introduces the field immutability analysis that is based on the reference immutability analysis together with the type immutability analysis together with a new lattice. --- .../FieldImmutabilityAnalysisDemo.scala | 72 +++++ .../fpcf/properties/FieldImmutability.scala | 47 ++++ .../L0FieldImmutabilityAnalysis.scala | 259 ++++++++++++++++++ 3 files changed, 378 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo.scala create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldImmutability.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala 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..e8d71a009f --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo.scala @@ -0,0 +1,72 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.EagerClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis +import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.DeepImmutableField +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.TACAITransformer +import org.opalj.tac.fpcf.analyses.escape.EagerSimpleEscapeAnalysis + +/** + * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0FieldImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0FieldImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll( + EagerTypeImmutabilityAnalysis, + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerClassImmutabilityAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerSimpleEscapeAnalysis, + TACAITransformer, + EagerL0FieldImmutabilityAnalysis + ); + + "Mutable Fields: " + propertyStore + .finalEntities(MutableField) + .toList + .toString() + "\n" + + "Shallow Immutable Fields: " + propertyStore + .finalEntities(ShallowImmutableField) + .toList + .toString() + "\n" + + "Deep Immutable Fields: " + propertyStore + .finalEntities(DeepImmutableField) + .toList + .toString() + } +} 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..4b7dc9cf75 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldImmutability.scala @@ -0,0 +1,47 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.properties + +import org.opalj.fpcf.Property +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 an immutable reference + * + * [[ShallowImmutableField]] A field with an immutable reference and a shallow immutable or mutable data type + * + * [[DeepImmutableField]] A field with an immutable reference and a deep immutable field type + * + * @author Tobias Peter Roth + */ +sealed trait FieldImmutability extends Property 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 MutableField extends FieldImmutability + +sealed trait ImmutableField extends FieldImmutability + +case object ShallowImmutableField extends ImmutableField + +case object DeepImmutableField extends ImmutableField diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala new file mode 100644 index 0000000000..213460beef --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -0,0 +1,259 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.analyses + +import org.opalj.br.Field +import org.opalj.br.analyses.FieldAccessInformationKey +import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.cg.ClosedPackagesKey +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.DeepImmutableField +import org.opalj.br.fpcf.properties.EscapeProperty +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.FieldMutability +import org.opalj.br.fpcf.properties.FieldPrematurelyRead +import org.opalj.br.fpcf.properties.ImmutableContainerType +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.ImmutableType +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.br.fpcf.properties.MutableType +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.properties.ReferenceImmutability +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.Entity +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.InterimEP +import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Property +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.fpcf.properties.TACAI + +case class State() { + var typeImmutability: Option[Boolean] = None + var referenceImmutability: Option[Boolean] = None +} + +/** + * Analyses that determines the mutability of org.opalj.br.Field + * Because it depends on the Field Immutability Lattice it combines the immutability of the fields reference and + * it's type. Thus needs the information of the reference of the field from the [[L0ReferenceImmutabilityAnalysis]] + * and the information of the type immutability determined by the type immutability analysis. + * Till now it uses the old type immutability analysis. + * + * @author Tobias Peter Roth + */ +class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) + extends FPCFAnalysis { + + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) + + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { + entity match { + case field: Field ⇒ determineFieldImmutability(field) + case _ ⇒ + val m = entity.getClass.getName+"is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + } + private[analyses] def determineFieldImmutability( + field: Field + ): ProperPropertyComputationResult = { + + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + + def hasImmutableType(field: Field): Option[Boolean] = { + val result = propertyStore(field.fieldType, TypeImmutability.key) + result match { + case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) ⇒ { + println("has immutable type") + Some(true) + } + case FinalEP(e, t) if (t == MutableType) ⇒ { + println("has mutable type") + Some(false) + } + case x @ _ ⇒ { + dependencies += x + println(x) + println("immutability of type couldn't be determined") + None + } + } + } + def hasImmutableReference(field: Field): Option[Boolean] = { + + propertyStore(field, ReferenceImmutability.key) match { + case FinalEP(_, MutableReference) ⇒ { + println("has mutable reference") + Some(false) + } + case FinalEP(_, ImmutableReference) ⇒ { + println("has immutable Reference") + Some(true) + } + case x @ _ ⇒ { + dependencies += x + println(x) + println("immutability of reference couldn't be determined") + None + } + } + + } + + val state: State = new State() + + def createResult(state: State): ProperPropertyComputationResult = { + state.referenceImmutability match { + case Some(false) ⇒ Result(field, MutableField) + case Some(true) ⇒ { + state.typeImmutability match { + case Some(false) ⇒ Result(field, ShallowImmutableField) + case Some(true) ⇒ Result(field, DeepImmutableField) + case None ⇒ { + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } + } + } + case None ⇒ { + + println("first interim") + println(dependencies) + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } + + } + } + def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { + println("c function called") + dependencies = dependencies.filter(_.e ne eps.e) + (eps: @unchecked) match { + case x: InterimEP[_, _] ⇒ { + println("interim in continuation function") + println(x) + dependencies += eps + InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + } + + case x @ FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) ⇒ { + println(x) + println("has immutable type. Determined by continuation function.") + state.typeImmutability = Some(true) + } + + case x @ FinalEP(_, MutableType) ⇒ { + println(x) + println("has mutable type. Determined by continuation function.") + state.typeImmutability = Some(false) + } + + case x @ FinalEP(_, MutableReference) ⇒ { + println(x) + println("has mutable reference. Determined by continuation function.") + state.referenceImmutability = Some(false) + } + + case x @ FinalEP(_, ImmutableReference) ⇒ { + println(x) + println("has immutable reference. Determined by continuation function.") + state.referenceImmutability = Some(true) + } + + case _ ⇒ {} + } + createResult(state) + } + + //-- + state.referenceImmutability = hasImmutableReference(field) + state.typeImmutability = hasImmutableType(field); + println("after type immutability determination") + createResult(state) + } + +} + +trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { + + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(FieldMutability), + PropertyBounds.lub(EscapeProperty), + PropertyBounds.lub(TypeImmutability), + PropertyBounds.lub(FieldImmutability), + PropertyBounds.ub(EscapeProperty) + ) + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) + +} + +/** + * Executor for the field immutability analysis. + */ +object EagerL0FieldImmutabilityAnalysis + extends L0FieldImmutabilityAnalysisScheduler + with BasicFPCFEagerAnalysisScheduler { + + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) + analysis + } + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty +} + +/** + * Executor for the lazy field immutability analysis. + */ +object LazyL0FieldImmutabilityAnalysis + extends L0FieldImmutabilityAnalysisScheduler + with BasicFPCFLazyAnalysisScheduler { + + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldImmutability.key, + analysis.determineFieldImmutability + ) + analysis + } + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + +} From 25ed3c49a6dfff26acf16e4997cfb30a2151468a Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 6 Nov 2019 18:04:27 +0100 Subject: [PATCH 007/327] Introduces a new lattice for class immutability analysis. Takes to given class immutability analysis and adapts it to the new lattice as well as the new field immutability analysis. --- .../ClassImmutabilityAnalysisDemo.scala | 74 +++ .../properties/ClassImmutability_new.scala | 50 ++ .../LxClassImmutabilityAnalysis_new.scala | 556 ++++++++++++++++++ 3 files changed, 680 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo.scala create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala 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..f8fe01cf43 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo.scala @@ -0,0 +1,74 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.analyses + +import java.net.URL + +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new +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.EagerClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis +import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +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.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis + +/** + * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result + * + * @author Tobias Peter Roth + */ +object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll( + EagerClassImmutabilityAnalysis, + EagerTypeImmutabilityAnalysis, + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + EagerLxClassImmutabilityAnalysis_new + ) + "Mutable Class: "+propertyStore + .finalEntities(MutableClass) + .toList + .toString()+"\n"+ + "Dependent Immutable Class: "+propertyStore + .finalEntities(DependentImmutableClass) + .toList + .toString()+"\n"+ + "Shallow Immutable Class: "+propertyStore + .finalEntities(ShallowImmutableClass) + .toList + .toString()+"\n"+ + "Deep Immutable Class: "+propertyStore + .finalEntities(DeepImmutableClass) + .toList + .toString()+"\n" + } +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala new file mode 100644 index 0000000000..7a4602aedc --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -0,0 +1,50 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.properties + +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { + final type Self = ClassImmutability_new +} + +/** + * Describes the class immutability of org.opalj.br.ClassFile + * + * [[MutableClass]] A class with minimum 1 mutable field + * + * [[DependentImmutableClass]] A class with no mutable field but with at least one generic field where the + * immutability depends on the generic type of the field + * + * [[ShallowImmutableClass]] A class with no mutable field, no field with generic type but with at least one + * shallow immutable field + * + * [[DeepImmutableClass]] A class with only deep immutable fields + * + * @author Tobias Peter Roth + */ +sealed trait ClassImmutability_new + extends Property + with ClassImmutabilityPropertyMetaInformation_new { + final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key +} + +object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { + + /** + * The key associated with every [[ClassImmutability_new]] property. + */ + final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( + "opalj.ClassImmutability_new", + MutableClass + ) +} + +case object MutableClass extends ClassImmutability_new + +case object DependentImmutableClass extends ClassImmutability_new + +case object ShallowImmutableClass extends ClassImmutability_new + +case object DeepImmutableClass extends ClassImmutability_new diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala new file mode 100644 index 0000000000..81a2bba0bb --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -0,0 +1,556 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses + +import org.opalj.br.ClassFile +import org.opalj.br.ObjectType +import org.opalj.log.LogContext +import org.opalj.log.OPALLogger +import org.opalj.fpcf.ELBP +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.Entity +import org.opalj.fpcf.EPK +import org.opalj.fpcf.EPS +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.Result +import org.opalj.fpcf.Results +import org.opalj.fpcf.IncrementalResult +import org.opalj.fpcf.InterimE +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.SomeEPS +import org.opalj.fpcf.UBP +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_new +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.FieldImmutability +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.ImmutableType +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.TypeImmutability + +/** + * This code mainly taken from the old ClassImmutabilityAnalysis adapted to the + * new class immutability analysis lattice and uses the new field immutability analysis. + * + * 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 + * those fields which could be final, but which are not declared as final. + * + * 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 Peter Roth + * + */ +class LxClassImmutabilityAnalysis_new(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_new //MutableObject + ): 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 ::= ( + ( + determineClassImmutability_new(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, MutableClass) //MutableObjectDueToUnknownSupertypes) + } + } + IncrementalResult(Results(results), nextComputations.iterator) + } + + def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { + e match { + case t: ObjectType => + //this is safe + classHierarchy.superclassType(t) match { + case None => Result(t, MutableClass) //MutableObjectDueToUnknownSupertypes) + case Some(superClassType) => + val cf = project.classFile(t) match { + case None => + return Result(t, MutableClass) // MutableObjectByAnalysis) //TODO consider other lattice element + case Some(cf) => cf + } + + propertyStore(superClassType, ClassImmutability_new.key) match { + case UBP(MutableClass) => //MutableObject) ⇒ + Result(t, MutableClass) + case eps: EPS[ObjectType, ClassImmutability_new] => + determineClassImmutability_new( + superClassType, + eps, + eps.isFinal, + lazyComputation = true + )(cf) + case epk => + determineClassImmutability_new( + 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 determineClassImmutability_new( + superClassType: ObjectType, + superClassInformation: EOptionP[Entity, Property], + superClassMutabilityIsFinal: Boolean, + lazyComputation: Boolean + )( + cf: ClassFile + ): ProperPropertyComputationResult = { + // assert(superClassMutability.isMutable.isNoOrUnknown) + 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.filter { f => + !f.isStatic + } + dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { + case FinalP(MutableField) => //NonFinalField) ⇒ + // <=> The class is definitively mutable and therefore also all subclasses. + if (lazyComputation) + return Result(t, MutableClass); //MutableObjectByAnalysis); // + else + return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); + case ep @ InterimE(e) => + hasFieldsWithUnknownMutability = true + (e, ep) + case epk @ EPK(e: Entity, _) => + // <=> The mutability information is not yet available. + hasFieldsWithUnknownMutability = true + (e, epk) + + // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields + }).toMap + + var minLocalImmutability: ClassImmutability_new = + if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) + MutableClass //MutableObjectByAnalysis + else + ShallowImmutableClass //ImmutableContainer + + // NOTE: maxLocalImmutability does not take the super classes' mutability into account! + var maxLocalImmutability: ClassImmutability_new = superClassInformation match { + case UBP(ImmutableContainer) => ShallowImmutableClass //ImmutableContainer + case _ => DeepImmutableClass // ImmutableObject + } + + 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 //ImmutableContainer + } + + var fieldTypes: Set[ObjectType] = Set.empty + if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { + fieldTypes = // IMPROVE Use the precise type of the field (if available)! + cf.fields.collect { + case f if !f.isStatic && f.fieldType.isObjectType => f.fieldType.asObjectType + }.toSet + } + + // For each dependent class file we have to determine the mutability + // of instances of the respective type to determine the immutability + // of instances of this class. + // Basically, we have to distinguish the following cases: + // - A field's type is mutable or conditionally immutable=> + // This class is conditionally immutable. + // - A field's type is immutable => + // The field is ignored. + // (This class is as immutable as its superclass.) + // - A field's type is at least conditionally mutable or + // The immutability of the field's type is not yet known => + // This type is AtLeastConditionallyImmutable + // We have to declare a dependency on the respective type's immutability. + // + // Additional handling is required w.r.t. the supertype: + // If the supertype is Immutable => + // Nothing special to do. + // If the supertype is AtLeastConditionallyImmutable => + // We have to declare a dependency on the supertype. + // If the supertype is ConditionallyImmutable => + // This type is also at most conditionally immutable. + // + // We furthermore have to take the mutability of the fields into consideration: + // If a field is not effectively final => + // This type is mutable. + // If a field is effectively final => + // Nothing special to do. + + val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) + val hasMutableOrConditionallyImmutableField = + // IMPROVE Use the precise type of the field (if available)! + fieldTypesImmutability.exists { eOptP => + eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) + } + + if (hasMutableOrConditionallyImmutableField) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + } 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 + } + fieldTypesWithUndecidedMutability.foreach { eOptP => + dependees += (eOptP.e -> eOptP) + } + } + + 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 + someEPS match { + // Superclass related dependencies: + // + case UBP(MutableClass) => // MutableObject) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); + + case LBP(DeepImmutableClass) => //_:ImmutableObject) ⇒ // the super class + dependees -= SuperClassKey + + case UBP(DeepImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container + if (someEPS.isFinal) dependees -= SuperClassKey + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + + case LBP(DeepImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container + if (minLocalImmutability != DeepImmutableClass && //ImmutableContainer && + !dependees.valuesIterator.exists(_.pk == FieldMutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + + case LUBP(MutableClass, DeepImmutableClass) => //_: MutableObject, ImmutableObject) ⇒ // No information about superclass + + // Properties related to the type of the class' fields. + // + case UBP(ShallowImmutableClass | MutableClass) => //ImmutableContainerType | MutableType) ⇒ + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + + case ELBP(e, ImmutableType) => // Immutable field type, no influence on mutability + dependees -= e + + case UBP(ImmutableType) => // No information about field type + + // Field Mutability related dependencies: + // + case UBP(MutableField) => //_: NonFinalField) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); + + case ELBP(e, _: FinalField) => + dependees -= e + if (minLocalImmutability != ImmutableContainer && + !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + + case UBP(_: FinalField) => // no information about field mutability + + } + + if (someEPS.isRefinable) { + val entity = if (someEPS.pk == ClassImmutability_new.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" + ) + */ + + // Lift lower bound once no dependencies other than field type mutabilities are left + if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && + dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer + + 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, c) + } + } + + //[DEBUG] assert(initialImmutability.isRefinable) + val result = + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + if (lazyComputation) + result + else { + val isFinal = dependees.isEmpty + createIncrementalResult( + t, + EPS(t, minLocalImmutability, maxLocalImmutability), + isFinal, + result + ) + } + } +} + +trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability, FieldImmutability) + + 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 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.map(cf => set(cf.thisType, DeepImmutableClass)) //ImmutableObject)) + + // 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 immutability analysis eagerly. + * @author Tobias Peter Roth + * @author Michael Eichberg + */ +object EagerLxClassImmutabilityAnalysis_new + extends ClassImmutabilityAnalysisScheduler_new + 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 LxClassImmutabilityAnalysis_new(p) + ps.scheduleEagerComputationsForEntities(cfs)( + analysis.determineClassImmutability_new( + superClassType = null, + FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), + superClassMutabilityIsFinal = true, + lazyComputation = false + ) + ) + analysis + } +} + +/** + * Scheduler to run the immutability analysis lazily. + * @author Tobias Peter Roth + * @author Michael Eichberg + */ +object LazyLxClassImmutabilityAnalysis_new + extends ClassImmutabilityAnalysisScheduler_new + with FPCFLazyAnalysisScheduler { + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + override def register( + p: SomeProject, + ps: PropertyStore, + unused: InitializationData + ): FPCFAnalysis = { + val analysis = new LxClassImmutabilityAnalysis_new(p) + ps.registerLazyPropertyComputation( + ClassImmutability_new.key, + analysis.doDetermineClassImmutability_new + ) + analysis + } +} From c7d8837a3cdc122c8c387ca6ec49ab133df02edf Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 6 Nov 2019 19:25:27 +0100 Subject: [PATCH 008/327] introducing tests - not final, still errors --- .../class_immutability/FinalEmptyClass.java | 7 ++ .../TrivialMutableClass.java | 16 ++++ .../field_immutability/FinalEmptyClass.java | 7 ++ .../fixtures/field_immutability/Test.java | 5 ++ .../TrivialMutableClass.java | 6 ++ .../privateFieldNotBlank_deep.java | 6 ++ .../privateFieldNotBlank_shallow.java | 6 ++ ...FinalFieldBlank_costructorEscape_deep.java | 10 +++ ...alFieldBlank_costructorEscape_shallow.java | 10 +++ .../privateFinalFieldNotBlank_deep.java | 5 ++ .../privateFinalFieldNotBlank_shallow.java | 5 ++ .../private_getterEscape_deep.java | 18 ++++ .../private_getterEscape_shallow.java | 10 +++ .../private_setter_deep.java | 14 ++++ .../private_setter_shallow.java | 14 ++++ .../privatefinal_getterEscape_deep.java | 10 +++ .../privatefinal_getterEscape_shallow.java | 9 ++ .../protectedClass_deep.java | 8 ++ .../protectedClass_shallow.java | 8 ++ .../reference_immutability/Singleton.java | 37 +++++++++ .../DeepImmutableClassAnnotation.java | 24 ++++++ .../DependentImmutableClassAnnotation.java | 25 ++++++ .../MutableClassAnnotation.java | 25 ++++++ .../ShallowImmutableClassAnnotation.java | 25 ++++++ .../DeepImmutableFieldAnnotation.java | 30 +++++++ .../MutableFieldAnnotation.java | 29 +++++++ .../ShallowImmutableFieldAnnotation.java | 29 +++++++ .../ImmutableReferenceAnnotation.java | 28 +++++++ .../LazyInitializedReferenceAnnotation.java | 29 +++++++ .../MutableReferenceAnnotation.java | 29 +++++++ .../opalj/fpcf/ClassImmutabilityTests.scala | 81 ++++++++++++++++++ .../opalj/fpcf/FieldImmutabilityTests.scala | 83 +++++++++++++++++++ .../fpcf/ReferenceImmutabilityTests.scala | 74 +++++++++++++++++ .../ClassImmutabilityMatcher.scala | 56 +++++++++++++ .../FieldImmutabilityMatcher.scala | 53 ++++++++++++ .../ReferenceImmutabilityMatcher.scala | 59 +++++++++++++ 36 files changed, 890 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java new file mode 100644 index 0000000000..09f556d812 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java @@ -0,0 +1,7 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; + +@DeepImmutableClassAnnotation("trivial empty") +public final class FinalEmptyClass { +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java new file mode 100644 index 0000000000..4127e70979 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java @@ -0,0 +1,16 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +@MutableClassAnnotation("because of public fields") +public class TrivialMutableClass { + @MutableReferenceAnnotation("public") + @MutableFieldAnnotation("mutable reference") + public int n = 0; + + @MutableReferenceAnnotation("public") + @MutableFieldAnnotation("mutable reference") + public String name = "name"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java new file mode 100644 index 0000000000..eefbc17fa1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java @@ -0,0 +1,7 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; + +@DeepImmutableClassAnnotation("because it is empty") +public final class FinalEmptyClass { +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java new file mode 100644 index 0000000000..08edf8f91e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java @@ -0,0 +1,5 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class Test { + private String name = "name"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java new file mode 100644 index 0000000000..ea9dbaabb8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java @@ -0,0 +1,6 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class TrivialMutableClass { + public int n = 0; + public String name = "name"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java new file mode 100644 index 0000000000..f57aea697a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java @@ -0,0 +1,6 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privateFieldNotBlank_deep { + + private FinalEmptyClass name = new FinalEmptyClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java new file mode 100644 index 0000000000..ef894f276b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java @@ -0,0 +1,6 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privateFieldNotBlank_shallow { + + private TrivialMutableClass tmc = new TrivialMutableClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java new file mode 100644 index 0000000000..7bd19b235f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privateFinalFieldBlank_costructorEscape_deep { + + + private final FinalEmptyClass fec; + public privateFinalFieldBlank_costructorEscape_deep(FinalEmptyClass fec) { + this.fec = fec; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java new file mode 100644 index 0000000000..7e81db2a26 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privateFinalFieldBlank_costructorEscape_shallow { + + private final TrivialMutableClass tmc; + + public privateFinalFieldBlank_costructorEscape_shallow(TrivialMutableClass tmc) { + this.tmc = tmc; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java new file mode 100644 index 0000000000..28cde37ff6 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java @@ -0,0 +1,5 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privateFinalFieldNotBlank_deep { + private final FinalEmptyClass fec = new FinalEmptyClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java new file mode 100644 index 0000000000..350f8366dd --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java @@ -0,0 +1,5 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privateFinalFieldNotBlank_shallow { + private final TrivialMutableClass tmc = new TrivialMutableClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java new file mode 100644 index 0000000000..a6f6d75b7a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java @@ -0,0 +1,18 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("") +public class private_getterEscape_deep { + + public FinalEmptyClass getFec() { + return fec; + } + + @ImmutableReferenceAnnotation("") + @MutableFieldAnnotation("") + private FinalEmptyClass fec = new FinalEmptyClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java new file mode 100644 index 0000000000..64f799ccda --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class private_getterEscape_shallow { + public TrivialMutableClass getTmc() { + return tmc; + } + + private TrivialMutableClass tmc = new TrivialMutableClass(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java new file mode 100644 index 0000000000..a6b38fdeaa --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java @@ -0,0 +1,14 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class private_setter_deep { + + public void setFec(FinalEmptyClass fec) { + this.fec = fec; + } + + private FinalEmptyClass fec = new FinalEmptyClass(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java new file mode 100644 index 0000000000..54a592857a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java @@ -0,0 +1,14 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class private_setter_shallow { + + public void setTmc(TrivialMutableClass tmc) { + this.tmc = tmc; + } + + private TrivialMutableClass tmc = new TrivialMutableClass(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java new file mode 100644 index 0000000000..738c10c689 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privatefinal_getterEscape_deep { + public FinalEmptyClass getFec() { + return fec; + } + + private final FinalEmptyClass fec = new FinalEmptyClass(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java new file mode 100644 index 0000000000..3cb21f4715 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java @@ -0,0 +1,9 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privatefinal_getterEscape_shallow { + public TrivialMutableClass getTmc() { + return tmc; + } + + private final TrivialMutableClass tmc = new TrivialMutableClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java new file mode 100644 index 0000000000..94f3ab5884 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java @@ -0,0 +1,8 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; + +public class protectedClass_deep { + protected FinalEmptyClass fec = new FinalEmptyClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java new file mode 100644 index 0000000000..177fbd12f1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java @@ -0,0 +1,8 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; + +public class protectedClass_shallow { + protected TrivialMutableClass tmc = new TrivialMutableClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java new file mode 100644 index 0000000000..4824dea4c4 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java @@ -0,0 +1,37 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.reference_immutability; +/* + * Test case from the old L2 Field Mutability tests + */ + +public class Singleton { + + private String name; + + + private Object mutex = new Object(); + + private Singleton() { + this.name = ""; + } + + public String getName() { + synchronized (mutex) { + return name; + } + } + + // STATIC FUNCTIONALITY + + 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/properties/class_immutability/DeepImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java new file mode 100644 index 0000000000..ad27d7d1c6 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.class_immutability; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated class is deep immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ClassImmutability_new",validator = DeepImmutableClassMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DeepImmutableClassAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java new file mode 100644 index 0000000000..c85be61a9c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.class_immutability; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated class is dependent immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ClassImmutability_new",validator = DependentImmutableClassMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DependentImmutableClassAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java new file mode 100644 index 0000000000..648a8431aa --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.class_immutability; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.class_mutability.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 Tobias Peter Roth + */ +@PropertyValidator(key = "ClassImmutability_new",validator = MutableClassMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface MutableClassAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java new file mode 100644 index 0000000000..dd5152b9ff --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.class_immutability; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated class is shallow immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ClassImmutability_new",validator = ShallowImmutableClassMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ShallowImmutableClassAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java new file mode 100644 index 0000000000..05c2833d38 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java @@ -0,0 +1,30 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.field_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is deep immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key="FieldImmutability",validator=DeepImmutableFieldMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DeepImmutableFieldAnnotation { + + /** + * A short reasoning of this property. + */ + String value() ; // default = "N/A"; + + Class[] analyses() default { + L0FieldImmutabilityAnalysis.class + }; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java new file mode 100644 index 0000000000..b62adbe54f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.field_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is mutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key="FieldImmutability",validator=MutableFieldMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface MutableFieldAnnotation { + + /** + * A short reasoning of this property. + */ + String value() ; // default = "N/A"; + + Class[] analyses() default { + L0FieldImmutabilityAnalysis.class + }; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java new file mode 100644 index 0000000000..c522fc6f5d --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.field_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is shallow immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key="FieldImmutability",validator=ShallowImmutableFieldMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ShallowImmutableFieldAnnotation { + + /** + * A short reasoning of this property. + */ + String value() ; // default = "N/A"; + + Class[] analyses() default { + L0FieldImmutabilityAnalysis.class + }; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java new file mode 100644 index 0000000000..c4b12d7c0a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated reference is immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutability",validator = ImmutableReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ImmutableReferenceAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java new file mode 100644 index 0000000000..45b58b85ab --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is lazy initialized + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface LazyInitializedReferenceAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java new file mode 100644 index 0000000000..822be18a0a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated reference is mutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutability",validator = MutableReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface MutableReferenceAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala new file mode 100644 index 0000000000..e9c5e67fc9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -0,0 +1,81 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.EagerClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis +import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new + +/** + * @author Tobias Peter Roth + */ +class ClassImmutabilityTests extends PropertiesTest { + + 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("ReferenceImmutability")) + } + + describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + val as = executeAnalyses( + Set( + EagerClassImmutabilityAnalysis, + EagerTypeImmutabilityAnalysis, + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + EagerLxClassImmutabilityAnalysis_new + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ + +} 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..64be6de388 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -0,0 +1,83 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.EagerClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis +import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.TACAITransformer +import org.opalj.tac.fpcf.analyses.escape.EagerSimpleEscapeAnalysis + +/** + * @author Tobias Peter Roth + */ +class FieldImmutabilityTests extends PropertiesTest { + + 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("ReferenceImmutability")) + } + + describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + val as = executeAnalyses( + Set( + EagerTypeImmutabilityAnalysis, + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerClassImmutabilityAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerSimpleEscapeAnalysis, + TACAITransformer, + EagerL0FieldImmutabilityAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala new file mode 100644 index 0000000000..8cfd18c1a9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -0,0 +1,74 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.{EagerL0PurityAnalysis, EagerUnsoundPrematurelyReadFieldsAnalysis} +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.{ + EagerL0ReferenceImmutabilityAnalysis, + EagerL1FieldMutabilityAnalysis +} + +/** + * @author Tobias Peter Roth + */ +class ReferenceImmutabilityTests extends PropertiesTest { + + 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("ReferenceImmutability")) + } + + describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { + val as = executeAnalyses( + Set( + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala new file mode 100644 index 0000000000..5b210c2997 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala @@ -0,0 +1,56 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.class_immutability + +import org.opalj.br.{AnnotationLike, ObjectType} +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties._ +import org.opalj.fpcf.{Entity, Property} +import org.opalj.fpcf.properties.AbstractPropertyMatcher + +/** + * @author Tobias Peter Roth + */ +class ClassImmutabilityMatcher(val property: ClassImmutability_new) + 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(MutableClass) + +class DependentImmutableClassMatcher extends ClassImmutabilityMatcher(DependentImmutableClass) + +class ShallowImmutableClassMatcher extends ClassImmutabilityMatcher(ShallowImmutableClass) + +class DeepImmutableClassMatcher extends ClassImmutabilityMatcher(DeepImmutableClass) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala new file mode 100644 index 0000000000..03298c1cc8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala @@ -0,0 +1,53 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.field_immutability + +import org.opalj.br.{AnnotationLike, ObjectType} +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties._ +import org.opalj.fpcf.{Entity, Property} +import org.opalj.fpcf.properties.AbstractPropertyMatcher + +/** + * @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(MutableField) + +class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(ShallowImmutableField) + +class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(DeepImmutableField) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala new file mode 100644 index 0000000000..de82d1030b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala @@ -0,0 +1,59 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.br.fpcf.properties.ReferenceImmutability +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher + +/** + * @author Tobias Peter Roth + */ +class ReferenceImmutabilityMatcher(val property: ReferenceImmutability) + 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 MutableReferenceMatcher extends ReferenceImmutabilityMatcher(MutableReference) + +class LazyInitializedReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedReference) + +class ImmutableReferenceMatcher extends ReferenceImmutabilityMatcher(ImmutableReference) From 7af896bec720e0a7954653befe03dbe7d4896580 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 7 Nov 2019 11:34:09 +0100 Subject: [PATCH 009/327] adjusted the behavior of the createResult function in case of empty dependencies --- .../L0FieldImmutabilityAnalysis.scala | 310 +++++++++--------- 1 file changed, 155 insertions(+), 155 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 213460beef..3b03a228c7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -40,8 +40,8 @@ import org.opalj.fpcf.SomeEPS import org.opalj.tac.fpcf.properties.TACAI case class State() { - var typeImmutability: Option[Boolean] = None - var referenceImmutability: Option[Boolean] = None + var typeImmutability: Option[Boolean] = None + var referenceImmutability: Option[Boolean] = None } /** @@ -56,162 +56,162 @@ case class State() { class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - final val typeExtensibility = project.get(TypeExtensibilityKey) - final val closedPackages = project.get(ClosedPackagesKey) - final val fieldAccessInformation = project.get(FieldAccessInformationKey) + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) - def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { - entity match { - case field: Field ⇒ determineFieldImmutability(field) - case _ ⇒ - val m = entity.getClass.getName+"is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { + entity match { + case field: Field => determineFieldImmutability(field) + case _ => + val m = entity.getClass.getName + "is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + } + private[analyses] def determineFieldImmutability( + field: Field + ): ProperPropertyComputationResult = { + + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + + def hasImmutableType(field: Field): Option[Boolean] = { + val result = propertyStore(field.fieldType, TypeImmutability.key) + result match { + case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) => { + println("has immutable type") + Some(true) + } + case FinalEP(e, t) if (t == MutableType) => { + println("has mutable type") + Some(false) + } + case x @ _ => { + dependencies += x + println(x) + println("immutability of type couldn't be determined") + None } + } } - private[analyses] def determineFieldImmutability( - field: Field - ): ProperPropertyComputationResult = { + def hasImmutableReference(field: Field): Option[Boolean] = { - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - - def hasImmutableType(field: Field): Option[Boolean] = { - val result = propertyStore(field.fieldType, TypeImmutability.key) - result match { - case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) ⇒ { - println("has immutable type") - Some(true) - } - case FinalEP(e, t) if (t == MutableType) ⇒ { - println("has mutable type") - Some(false) - } - case x @ _ ⇒ { - dependencies += x - println(x) - println("immutability of type couldn't be determined") - None - } - } + propertyStore(field, ReferenceImmutability.key) match { + case FinalEP(_, MutableReference) => { + println("has mutable reference") + Some(false) } - def hasImmutableReference(field: Field): Option[Boolean] = { - - propertyStore(field, ReferenceImmutability.key) match { - case FinalEP(_, MutableReference) ⇒ { - println("has mutable reference") - Some(false) - } - case FinalEP(_, ImmutableReference) ⇒ { - println("has immutable Reference") - Some(true) - } - case x @ _ ⇒ { - dependencies += x - println(x) - println("immutability of reference couldn't be determined") - None - } - } - + case FinalEP(_, ImmutableReference) => { + println("has immutable Reference") + Some(true) } + case x @ _ => { + dependencies += x + println(x) + println("immutability of reference couldn't be determined") + None + } + } - val state: State = new State() - - def createResult(state: State): ProperPropertyComputationResult = { - state.referenceImmutability match { - case Some(false) ⇒ Result(field, MutableField) - case Some(true) ⇒ { - state.typeImmutability match { - case Some(false) ⇒ Result(field, ShallowImmutableField) - case Some(true) ⇒ Result(field, DeepImmutableField) - case None ⇒ { - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } - } - } - case None ⇒ { - - println("first interim") - println(dependencies) - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } + } + val state: State = new State() + + def createResult(state: State): ProperPropertyComputationResult = { + state.referenceImmutability match { + case Some(false) => Result(field, MutableField) + case Some(true) => { + state.typeImmutability match { + case Some(true) => Result(field, DeepImmutableField) + case Some(false) => Result(field, ShallowImmutableField) + case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) + case None => { + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) } + } + } + case None => { + println("first interim") + println(dependencies) + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) } - def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - println("c function called") - dependencies = dependencies.filter(_.e ne eps.e) - (eps: @unchecked) match { - case x: InterimEP[_, _] ⇒ { - println("interim in continuation function") - println(x) - dependencies += eps - InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) - } - case x @ FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) ⇒ { - println(x) - println("has immutable type. Determined by continuation function.") - state.typeImmutability = Some(true) - } + } + } + def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { + println("c function called") + dependencies = dependencies.filter(_.e ne eps.e) + (eps: @unchecked) match { + case x: InterimEP[_, _] => { + println("interim in continuation function") + println(x) + dependencies += eps + InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + } - case x @ FinalEP(_, MutableType) ⇒ { - println(x) - println("has mutable type. Determined by continuation function.") - state.typeImmutability = Some(false) - } + case x @ FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) => { + println(x) + println("has immutable type. Determined by continuation function.") + state.typeImmutability = Some(true) + } - case x @ FinalEP(_, MutableReference) ⇒ { - println(x) - println("has mutable reference. Determined by continuation function.") - state.referenceImmutability = Some(false) - } + case x @ FinalEP(_, MutableType) => { + println(x) + println("has mutable type. Determined by continuation function.") + state.typeImmutability = Some(false) + } - case x @ FinalEP(_, ImmutableReference) ⇒ { - println(x) - println("has immutable reference. Determined by continuation function.") - state.referenceImmutability = Some(true) - } + case x @ FinalEP(_, MutableReference) => { + println(x) + println("has mutable reference. Determined by continuation function.") + state.referenceImmutability = Some(false) + } - case _ ⇒ {} - } - createResult(state) + case x @ FinalEP(_, ImmutableReference) => { + println(x) + println("has immutable reference. Determined by continuation function.") + state.referenceImmutability = Some(true) } - //-- - state.referenceImmutability = hasImmutableReference(field) - state.typeImmutability = hasImmutableType(field); - println("after type immutability determination") - createResult(state) + case x @ _ => println("default value: " + x) + } + createResult(state) } + //-- + state.referenceImmutability = hasImmutableReference(field) + state.typeImmutability = hasImmutableType(field); + println("after type immutability determination") + createResult(state) + } + } trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(FieldMutability), - PropertyBounds.lub(EscapeProperty), - PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(FieldImmutability), - PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(FieldMutability), + PropertyBounds.lub(EscapeProperty), + PropertyBounds.lub(TypeImmutability), + PropertyBounds.lub(FieldImmutability), + PropertyBounds.ub(EscapeProperty) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) } @@ -222,16 +222,16 @@ object EagerL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -241,19 +241,19 @@ object LazyL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - FieldImmutability.key, - analysis.determineFieldImmutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldImmutability.key, + analysis.determineFieldImmutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } From e476eb1ab33412f1e20c04f8dacf02bf807d7a4d Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 12 Nov 2019 15:20:29 +0100 Subject: [PATCH 010/327] New Lattice Element Dependent Immutable Field and the according handling of the Field Immutability Analysis and the new Class Immutability Analysis. --- .../ClassImmutabilityAnalysisDemo.scala | 78 ++++---- .../FieldImmutabilityAnalysisDemo.scala | 5 + .../properties/ClassImmutability_new.scala | 18 +- .../fpcf/properties/FieldImmutability.scala | 4 + .../L0FieldImmutabilityAnalysis.scala | 66 ++++--- .../LxClassImmutabilityAnalysis_new.scala | 167 ++++++++++++------ 6 files changed, 208 insertions(+), 130 deletions(-) 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 index f8fe01cf43..4fa1ac4f0c 100644 --- 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 @@ -8,7 +8,6 @@ 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.EagerClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis @@ -27,48 +26,47 @@ import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis */ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" - override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(project: Project[URL]): String = { + def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) + val analysesManager = project.get(FPCFAnalysesManagerKey) - val (propertyStore, _) = analysesManager.runAll( - EagerClassImmutabilityAnalysis, - EagerTypeImmutabilityAnalysis, - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerL0ReferenceImmutabilityAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - EagerLxClassImmutabilityAnalysis_new - ) - "Mutable Class: "+propertyStore - .finalEntities(MutableClass) - .toList - .toString()+"\n"+ - "Dependent Immutable Class: "+propertyStore - .finalEntities(DependentImmutableClass) - .toList - .toString()+"\n"+ - "Shallow Immutable Class: "+propertyStore - .finalEntities(ShallowImmutableClass) - .toList - .toString()+"\n"+ - "Deep Immutable Class: "+propertyStore - .finalEntities(DeepImmutableClass) - .toList - .toString()+"\n" - } + val (propertyStore, _) = analysesManager.runAll( + EagerTypeImmutabilityAnalysis, + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + EagerLxClassImmutabilityAnalysis_new + ) + "Mutable Class: " + propertyStore + .finalEntities(MutableClass) + .toList + .toString() + "\n" + + "Dependent Immutable Class: " + propertyStore + .finalEntities(DependentImmutableClass) + .toList + .toString() + "\n" + + "Shallow Immutable Class: " + propertyStore + .finalEntities(ShallowImmutableClass) + .toList + .toString() + "\n" + + "Deep Immutable Class: " + propertyStore + .finalEntities(DeepImmutableClass) + .toList + .toString() + "\n" + } } 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 index e8d71a009f..8a1fd29738 100644 --- 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 @@ -13,6 +13,7 @@ import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis 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.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis @@ -64,6 +65,10 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .finalEntities(ShallowImmutableField) .toList .toString() + "\n" + + "Dependet Immutable Fields:" + propertyStore + .finalEntities(DependentImmutableField) + .toList + .toString() + "\n" + "Deep Immutable Fields: " + propertyStore .finalEntities(DeepImmutableField) .toList diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index 7a4602aedc..8059b5d74e 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -6,7 +6,7 @@ import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = ClassImmutability_new + final type Self = ClassImmutability_new } /** @@ -27,18 +27,18 @@ sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaIn sealed trait ClassImmutability_new extends Property with ClassImmutabilityPropertyMetaInformation_new { - final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key + final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key } object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[ClassImmutability_new]] property. - */ - final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( - "opalj.ClassImmutability_new", - MutableClass - ) + /** + * The key associated with every [[ClassImmutability_new]] property. + */ + final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( + "opalj.ClassImmutability_new", + MutableClass + ) } case object MutableClass extends ClassImmutability_new 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 index 4b7dc9cf75..7c6fb4c654 100644 --- 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 @@ -17,6 +17,8 @@ sealed trait FieldImmutabilityPropertyMetaInformation extends PropertyMetaInform * * [[ShallowImmutableField]] A field with an immutable reference and a shallow immutable or mutable data type * + * [[DependentImmutableField]] A field which immutability depends on its type. + * * [[DeepImmutableField]] A field with an immutable reference and a deep immutable field type * * @author Tobias Peter Roth @@ -44,4 +46,6 @@ sealed trait ImmutableField extends FieldImmutability case object ShallowImmutableField extends ImmutableField +case object DependentImmutableField extends ImmutableField + case object DeepImmutableField extends ImmutableField diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 3b03a228c7..c0762cdd2c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -2,6 +2,7 @@ package org.opalj.br.fpcf.analyses import org.opalj.br.Field +import org.opalj.br.ObjectType import org.opalj.br.analyses.FieldAccessInformationKey import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.ClosedPackagesKey @@ -11,17 +12,15 @@ import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.DeepImmutableField -import org.opalj.br.fpcf.properties.EscapeProperty +import org.opalj.br.fpcf.properties.DependentImmutableField import org.opalj.br.fpcf.properties.FieldImmutability -import org.opalj.br.fpcf.properties.FieldMutability -import org.opalj.br.fpcf.properties.FieldPrematurelyRead import org.opalj.br.fpcf.properties.ImmutableContainerType import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.ImmutableType +import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableField import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.MutableType -import org.opalj.br.fpcf.properties.Purity import org.opalj.br.fpcf.properties.ReferenceImmutability import org.opalj.br.fpcf.properties.ShallowImmutableField import org.opalj.br.fpcf.properties.TypeImmutability @@ -75,7 +74,13 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var dependencies: Set[EOptionP[Entity, Property]] = Set.empty def hasImmutableType(field: Field): Option[Boolean] = { + println("Field:: " + field) + println(field.fieldType) + println(field.fieldType.isBaseType) + if (field.fieldType.isArrayType) return Some(true); //TODO + if (field.fieldType.isBaseType) return Some(true); val result = propertyStore(field.fieldType, TypeImmutability.key) + println("result: " + result) result match { case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) => { println("has immutable type") @@ -100,7 +105,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) println("has mutable reference") Some(false) } - case FinalEP(_, ImmutableReference) => { + case FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO println("has immutable Reference") Some(true) } @@ -111,32 +116,38 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) None } } - } val state: State = new State() def createResult(state: State): ProperPropertyComputationResult = { + println("reference Immutability: " + state.referenceImmutability) + println("type immutabiliy: " + state.typeImmutability) + state.referenceImmutability match { case Some(false) => Result(field, MutableField) case Some(true) => { - state.typeImmutability match { - case Some(true) => Result(field, DeepImmutableField) - case Some(false) => Result(field, ShallowImmutableField) - case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) - case None => { - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) + //If the field type is object. It is a generic field + if (field.fieldType == ObjectType("java/lang/Object")) + Result(field, DependentImmutableField) + else + state.typeImmutability match { + case Some(true) => Result(field, DeepImmutableField) + case Some(false) => Result(field, ShallowImmutableField) + case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) + case None => { + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } } - } } + case None if (dependencies.isEmpty) => Result(field, MutableField) case None => { - println("first interim") println(dependencies) InterimResult( field, @@ -178,7 +189,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.referenceImmutability = Some(false) } - case x @ FinalEP(_, ImmutableReference) => { + case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO println(x) println("has immutable reference. Determined by continuation function.") state.referenceImmutability = Some(true) @@ -201,14 +212,15 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), + //PropertyBounds.lub(Purity), + //PropertyBounds.lub(FieldPrematurelyRead), PropertyBounds.ub(TACAI), - PropertyBounds.lub(FieldMutability), - PropertyBounds.lub(EscapeProperty), + //PropertyBounds.lub(FieldMutability), + //PropertyBounds.lub(EscapeProperty), + PropertyBounds.lub(ReferenceImmutability), PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(FieldImmutability), - PropertyBounds.ub(EscapeProperty) + PropertyBounds.lub(FieldImmutability) + //PropertyBounds.ub(EscapeProperty) ) final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 81a2bba0bb..9e1c4c9995 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -34,19 +34,19 @@ import org.opalj.br.fpcf.FPCFEagerAnalysisScheduler import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler import org.opalj.br.fpcf.properties.ClassImmutability_new 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.FieldMutability -import org.opalj.br.fpcf.properties.FinalField -import org.opalj.br.fpcf.properties.ImmutableContainer import org.opalj.br.fpcf.properties.ImmutableType import org.opalj.br.fpcf.properties.MutableClass import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.MutableType import org.opalj.br.fpcf.properties.ShallowImmutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableField import org.opalj.br.fpcf.properties.TypeImmutability /** - * This code mainly taken from the old ClassImmutabilityAnalysis adapted to the - * new class immutability analysis lattice and uses the new field immutability analysis. * * 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 @@ -184,6 +184,12 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal cf: ClassFile ): ProperPropertyComputationResult = { // assert(superClassMutability.isMutable.isNoOrUnknown) + + //---generic classes handling + //printf(cf.asVirtualClass.asClassFile.classSignature.get.toString()) + //cf.asClassFile.classSignature.get.formalTypeParameters. + //--------------------------------------------- + val t = cf.thisType var dependees = Map.empty[Entity, EOptionP[Entity, Property]] @@ -198,24 +204,54 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal val instanceFields = cf.fields.filter { f => !f.isStatic } - dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { - case FinalP(MutableField) => //NonFinalField) ⇒ - // <=> The class is definitively mutable and therefore also all subclasses. - if (lazyComputation) - return Result(t, MutableClass); //MutableObjectByAnalysis); // - else - return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); - case ep @ InterimE(e) => - hasFieldsWithUnknownMutability = true - (e, ep) - case epk @ EPK(e: Entity, _) => - // <=> The mutability information is not yet available. - hasFieldsWithUnknownMutability = true - (e, epk) - - // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields - }).toMap + var hasShallowImmutableFields = false + var hasDependentImmutableFields = false + val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) + fieldsPropertyStoreInformation.foreach( + f => + f match { + 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 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); + } + ) + /** + * dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { + * case FinalP(MutableField) => //NonFinalField) ⇒ + * // <=> The class is definitively mutable and therefore also all subclasses. + * if (lazyComputation) + * return Result(t, MutableClass); //MutableObjectByAnalysis); // + * else + * return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); + * case ep @ InterimE(e) => + * hasFieldsWithUnknownMutability = true + * (e, ep) + * case epk @ EPK(e: Entity, _) => + * // <=> The mutability information is not yet available. + * hasFieldsWithUnknownMutability = true + * (e, epk) + * + * // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields + * }).toMap * + */ var minLocalImmutability: ClassImmutability_new = if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) MutableClass //MutableObjectByAnalysis @@ -224,8 +260,14 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability_new = superClassInformation match { - case UBP(ImmutableContainer) => ShallowImmutableClass //ImmutableContainer - case _ => DeepImmutableClass // ImmutableObject + case UBP(ShallowImmutableClass) => ShallowImmutableClass //ImmutableContainer + case _ => DeepImmutableClass // ImmutableObject + } + + if (hasDependentImmutableFields) { + maxLocalImmutability = DependentImmutableClass + } else if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } if (cf.fields.exists(f => !f.isStatic && f.fieldType.isArrayType)) { @@ -235,13 +277,13 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } - var fieldTypes: Set[ObjectType] = Set.empty - if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { - fieldTypes = // IMPROVE Use the precise type of the field (if available)! - cf.fields.collect { - case f if !f.isStatic && f.fieldType.isObjectType => f.fieldType.asObjectType - }.toSet - } + //--var fieldTypes: Set[ObjectType] = Set.empty + //--if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { + //-- fieldTypes = // IMPROVE Use the precise type of the field (if available)! + // cf.fields.collect { + // case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType + // }.toSet + //} // For each dependent class file we have to determine the mutability // of instances of the respective type to determine the immutability @@ -271,25 +313,27 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal // If a field is effectively final => // Nothing special to do. - val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) - val hasMutableOrConditionallyImmutableField = - // IMPROVE Use the precise type of the field (if available)! - fieldTypesImmutability.exists { eOptP => - eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) - } + //val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) + //val hasMutableOrConditionallyImmutableField = + // IMPROVE Use the precise type of the field (if available)! + //-- fieldTypesImmutability.exists { eOptP ⇒ + //-- eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) + //-- } - if (hasMutableOrConditionallyImmutableField) { + if (hasShallowImmutableFields) { maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - } 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 - } - fieldTypesWithUndecidedMutability.foreach { eOptP => - dependees += (eOptP.e -> eOptP) - } - } + } //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 + * } + * fieldTypesWithUndecidedMutability.foreach { eOptP => + * dependees += (eOptP.e -> eOptP) + * }* + */ + //} if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { // <=> the super classes' immutability is final @@ -310,6 +354,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal def c(someEPS: SomeEPS): ProperPropertyComputationResult = { //[DEBUG] val oldDependees = dependees + dependees = dependees.filter(_._1 ne someEPS.e) + println("someEPS: " + someEPS) someEPS match { // Superclass related dependencies: // @@ -319,14 +365,14 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case LBP(DeepImmutableClass) => //_:ImmutableObject) ⇒ // the super class dependees -= SuperClassKey - case UBP(DeepImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container + case UBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container if (someEPS.isFinal) dependees -= SuperClassKey maxLocalImmutability = ShallowImmutableClass //ImmutableContainer dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - case LBP(DeepImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container + case LBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container if (minLocalImmutability != DeepImmutableClass && //ImmutableContainer && - !dependees.valuesIterator.exists(_.pk == FieldMutability.key)) + !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible case LUBP(MutableClass, DeepImmutableClass) => //_: MutableObject, ImmutableObject) ⇒ // No information about superclass @@ -342,18 +388,29 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case UBP(ImmutableType) => // No information about field type + case FinalP(MutableType) => Result(t, MutableClass) //TODO check + + case FinalP(DependentImmutableField) => maxLocalImmutability = DependentImmutableClass // Field Mutability related dependencies: // + + case FinalP(ShallowImmutableField) => { + maxLocalImmutability = ShallowImmutableClass + } + case FinalP(MutableField) => return Result(t, MutableClass); + case UBP(MutableField) => //_: NonFinalField) ⇒ return Result(t, MutableClass); //MutableObjectByAnalysis); - case ELBP(e, _: FinalField) => + case ELBP(e, ShallowImmutableField | DeepImmutableField) => //FinalField) => dependees -= e - if (minLocalImmutability != ImmutableContainer && + if (minLocalImmutability != ShallowImmutableClass && // ImmutableContainer && !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - case UBP(_: FinalField) => // no information about field mutability + case UBP(ShallowImmutableField | DeepImmutableField) => //_: FinalField) ⇒ // no information about field mutability + + case _ => Result(t, MutableClass) //TODO check } @@ -402,6 +459,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } //[DEBUG] assert(initialImmutability.isRefinable) + println("minLocalImmutability: " + minLocalImmutability) + println("maxLocalImmutability: " + maxLocalImmutability) val result = InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) if (lazyComputation) From 991bf54b614a2fabdd0250bd2e63cd668058e2b6 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 12 Nov 2019 15:39:07 +0100 Subject: [PATCH 011/327] Improved test environment: ReferenceImmutability, FieldImmutability, and ClassImmutability_new tests should no work --- .../ReferenceImmutabilityAnalysisDemo.scala | 73 +- .../class_immutability/FinalEmptyClass.java | 3 +- .../class_immutability/Generic_class1.java | 33 + .../TrivialMutableClass.java | 2 +- .../field_immutability/FinalEmptyClass.java | 2 +- .../fixtures/field_immutability/Test.java | 8 + .../TrivialMutableClass.java | 12 + .../privateFieldNotBlank_deep.java | 8 +- .../privateFieldNotBlank_shallow.java | 7 + ...FinalFieldBlank_costructorEscape_deep.java | 8 +- ...alFieldBlank_costructorEscape_shallow.java | 7 + .../privateFinalFieldNotBlank_deep.java | 8 + .../privateFinalFieldNotBlank_shallow.java | 8 + .../private_getterEscape_deep.java | 15 +- .../private_getterEscape_shallow.java | 7 + .../private_setter_deep.java | 4 + .../private_setter_shallow.java | 4 + .../privatefinal_getterEscape_deep.java | 7 + .../privatefinal_getterEscape_shallow.java | 7 + .../protectedClass_deep.java | 4 + .../protectedClass_shallow.java | 4 + .../DeclaredFinalFields.java | 91 ++ .../LazyInitialization.java | 394 ++++++++ .../PrivateFieldUpdater.java | 39 + .../reference_immutability/Singleton.java | 15 +- .../DeepImmutableClassAnnotation.java | 5 + .../DependentImmutableClassAnnotation.java | 4 + .../MutableClassAnnotation.java | 4 + .../ShallowImmutableClassAnnotation.java | 4 + .../DependentImmutableFieldAnnotation.java | 31 + .../MutableReferenceAnnotation.java | 9 +- .../opalj/fpcf/ClassImmutabilityTests.scala | 49 +- .../opalj/fpcf/FieldImmutabilityTests.scala | 36 +- .../fpcf/ReferenceImmutabilityTests.scala | 17 +- .../ClassImmutabilityMatcher.scala | 20 +- .../FieldImmutabilityMatcher.scala | 12 +- .../FieldMutabilityMatcher.scala | 2 +- .../field_mutability/NonFinalMatcher.scala | 5 +- .../MutableReferenceMatcher.scala | 70 ++ .../ReferenceImmutabilityMatcher.scala | 4 - .../properties/ClassImmutability_new.scala | 18 +- .../fpcf/properties/FieldImmutability.scala | 18 +- .../L0FieldImmutabilityAnalysis.scala | 330 +++---- .../L0ReferenceImmutabilityAnalysis.scala | 64 +- .../LxClassImmutabilityAnalysis_new.scala | 882 +++++++++--------- 45 files changed, 1606 insertions(+), 748 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/DeclaredFinalFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/LazyInitialization.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/PrivateFieldUpdater.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/MutableReferenceMatcher.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index 1b8292afa4..eb2d7a4b80 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -2,6 +2,7 @@ package org.opalj.fpcf.analyses import java.net.URL + import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication @@ -9,6 +10,7 @@ import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis @@ -20,37 +22,42 @@ import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis */ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - - val (propertyStore, _) = analysesManager.runAll( - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerL0ReferenceImmutabilityAnalysis - ); - - "Mutable References: "+propertyStore - .finalEntities(MutableReference) - .toList - .toString()+"\n"+ - "Immutable References: "+propertyStore - .finalEntities(ImmutableReference) - .toList - .toString() - } + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll( + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerL0ReferenceImmutabilityAnalysis + ); + + "Mutable References: " + propertyStore + .finalEntities(MutableReference) + .toList + .toString() + "\n" + + "Lazy Initialized Reference: " + propertyStore + .finalEntities(LazyInitializedReference) + .toList + .toString() + "\n" + + "Immutable References: " + propertyStore + .finalEntities(ImmutableReference) + .toList + .toString() + } + } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java index 09f556d812..5152cb6190 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java @@ -2,6 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -@DeepImmutableClassAnnotation("trivial empty") +@DeepImmutableClassAnnotation("It has no fields and is final") public final class FinalEmptyClass { + } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java new file mode 100644 index 0000000000..5ec7f3f1c8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java @@ -0,0 +1,33 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@DependentImmutableClassAnnotation("Dependent Immutability of T1,...,T5") +public class Generic_class1 { + @DependentImmutableFieldAnnotation("T1") + @ImmutableReferenceAnnotation("effectively") + private T1 t1; + @DependentImmutableFieldAnnotation("T2") + @ImmutableReferenceAnnotation("effectively") + private T2 t2; + @DependentImmutableFieldAnnotation("T3") + @ImmutableReferenceAnnotation("effectively") + private T3 t3; + @DependentImmutableFieldAnnotation("T4") + @ImmutableReferenceAnnotation("effectively") + private T4 t4; + @DependentImmutableFieldAnnotation("T5") + @ImmutableReferenceAnnotation("effectively") + private T5 t5; + + public Generic_class1(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + } + +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java index 4127e70979..e79dc0e79f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java @@ -4,7 +4,7 @@ import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -@MutableClassAnnotation("because of public fields") +@MutableClassAnnotation("It has Mutable Fields") public class TrivialMutableClass { @MutableReferenceAnnotation("public") @MutableFieldAnnotation("mutable reference") diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java index eefbc17fa1..d59e446c4a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java @@ -2,6 +2,6 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -@DeepImmutableClassAnnotation("because it is empty") +@DeepImmutableClassAnnotation("Because of Emptiness") public final class FinalEmptyClass { } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java index 08edf8f91e..faebb9737e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java @@ -1,5 +1,13 @@ package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") public class Test { + @ShallowImmutableFieldAnnotation("Because of Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("Effectively immutable") private String name = "name"; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java index ea9dbaabb8..a29aefed9d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java @@ -1,6 +1,18 @@ package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +@MutableClassAnnotation("It has Mutable Fields") public class TrivialMutableClass { + + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Because it is public") public int n = 0; + + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Because it is public") public String name = "name"; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java index f57aea697a..e18e45d65c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java @@ -1,6 +1,12 @@ package org.opalj.fpcf.fixtures.field_immutability; -public class privateFieldNotBlank_deep { +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +@DeepImmutableClassAnnotation("Because it has only Deep Immutable Field Types") +public class privateFieldNotBlank_deep { + @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("Effectively Immutable Reference") private FinalEmptyClass name = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java index ef894f276b..cd937aafe1 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java @@ -1,6 +1,13 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("Because it has only Shallow Immutable Fields") public class privateFieldNotBlank_shallow { + @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") + @ImmutableReferenceAnnotation("Effectively Immutable Reference") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java index 7bd19b235f..6a4dd9da24 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java @@ -1,8 +1,14 @@ package org.opalj.fpcf.fixtures.field_immutability; -public class privateFinalFieldBlank_costructorEscape_deep { +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +@DeepImmutableClassAnnotation("It has only Deep Immutable Fields") +public class privateFinalFieldBlank_costructorEscape_deep { + @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("Declared final Reference") private final FinalEmptyClass fec; public privateFinalFieldBlank_costructorEscape_deep(FinalEmptyClass fec) { this.fec = fec; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java index 7e81db2a26..58207793b2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java @@ -1,7 +1,14 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") public class privateFinalFieldBlank_costructorEscape_shallow { + @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") + @ImmutableReferenceAnnotation("Declared final Field") private final TrivialMutableClass tmc; public privateFinalFieldBlank_costructorEscape_shallow(TrivialMutableClass tmc) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java index 28cde37ff6..81ce97c380 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java @@ -1,5 +1,13 @@ package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@DeepImmutableClassAnnotation("It has only Deep Immutable Fields") public class privateFinalFieldNotBlank_deep { + @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("Declared final Field") private final FinalEmptyClass fec = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java index 350f8366dd..c980ed2895 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java @@ -1,5 +1,13 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") public class privateFinalFieldNotBlank_shallow { + + @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") + @ImmutableReferenceAnnotation("Declared final Field") private final TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java index a6f6d75b7a..b3bf7e004f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java @@ -1,18 +1,15 @@ package org.opalj.fpcf.fixtures.field_immutability; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -@ShallowImmutableClassAnnotation("") +@DeepImmutableClassAnnotation("Only Deep Immutable Fields") public class private_getterEscape_deep { - public FinalEmptyClass getFec() { return fec; } - - @ImmutableReferenceAnnotation("") - @MutableFieldAnnotation("") + @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("It is effectively immutable") private FinalEmptyClass fec = new FinalEmptyClass(); -} +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java index 64f799ccda..801cc1b0cc 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java @@ -1,10 +1,17 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("Becaus it has only Shallow Immutable Fields") public class private_getterEscape_shallow { public TrivialMutableClass getTmc() { return tmc; } + @ShallowImmutableFieldAnnotation("Because of Immutable Reference and Mutable Field Type") + @ImmutableReferenceAnnotation("effectively immutable") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java index a6b38fdeaa..07cb969109 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java @@ -1,14 +1,18 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +@MutableClassAnnotation("Because it has Mutable Fields") public class private_setter_deep { public void setFec(FinalEmptyClass fec) { this.fec = fec; } + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("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/field_immutability/private_setter_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java index 54a592857a..7edcb57d35 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java @@ -1,14 +1,18 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +@MutableClassAnnotation("Because it has Mutable Fields") public class private_setter_shallow { public void setTmc(TrivialMutableClass tmc) { this.tmc = tmc; } + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Not final field could be set via setter") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java index 738c10c689..1c79a391c0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java @@ -1,10 +1,17 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@DeepImmutableClassAnnotation("It has only Deep Immutable Fields") public class privatefinal_getterEscape_deep { public FinalEmptyClass getFec() { return fec; } + @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("Declared final Field") private final FinalEmptyClass fec = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java index 3cb21f4715..922e39bf2f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java @@ -1,9 +1,16 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") public class privatefinal_getterEscape_shallow { public TrivialMutableClass getTmc() { return tmc; } + @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") + @ImmutableReferenceAnnotation("Declared final Reference") private final TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java index 94f3ab5884..21e1621fc6 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java @@ -1,8 +1,12 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +@MutableClassAnnotation("It has Mutable Fields") public class protectedClass_deep { + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Because it is declared as protected") protected FinalEmptyClass fec = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java index 177fbd12f1..a9d738b700 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java @@ -1,8 +1,12 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +@MutableClassAnnotation("It has Mutable Fields") public class protectedClass_shallow { + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Because it is declared as protected") protected TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/DeclaredFinalFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/DeclaredFinalFields.java new file mode 100644 index 0000000000..f32e29b2a5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/DeclaredFinalFields.java @@ -0,0 +1,91 @@ +/* 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.fixtures.reference_immutability; + +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +/** + * 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 fields that are declared final. Some of them are not strictly final because they can + * be observed uninitialized. + * + * @author Dominik Helm + */ +public class DeclaredFinalFields extends Super { + + @ImmutableReferenceAnnotation("Initialized directly") + private final int a = 1; + + @ImmutableReferenceAnnotation("Initialized through instance initializer") + private final int b; + + @ImmutableReferenceAnnotation("Initialized through constructor") + private final int c; + + @MutableReferenceAnnotation(value = "Prematurely read through super constructor", prematurelyRead = true) + private final int d; + + @MutableReferenceAnnotation(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/reference_immutability/LazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/LazyInitialization.java new file mode 100644 index 0000000000..2547759f2e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/LazyInitialization.java @@ -0,0 +1,394 @@ +/* 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.fixtures.reference_immutability; + +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.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; + +/** + * Test classes for simple lazy initialization patterns and anti-patterns. + * + * @author Dominik Helm + */ + +class Simple { + + @LazyInitializedReferenceAnnotation("Simple lazy initialization") + private int x; + + public int init() { + if (x == 0) { + x = 5; + } + return x; + } +} + +class Local { + + @LazyInitializedReferenceAnnotation("Lazy initialization with local") + private int x; + + public int init() { + int y = this.x; + if (y == 0) { + x = y = 5; + } + return y; + } +} + +class LocalWrong { + + @MutableReferenceAnnotation("Incorrect lazy initialization with local") + private int x; + + public int init() { + int y = this.x; + if (y == 0) { + x = 5; + } + return y; + } +} + +class LocalReversed { + + @LazyInitializedReferenceAnnotation("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 { + + @LazyInitializedReferenceAnnotation("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 { + + @LazyInitializedReferenceAnnotation("Simple lazy initialization (reversed)") + private int x; + + public int init() { + if (x != 0) + return x; + x = 5; + return x; + } +} + +class SimpleWithDifferentDefault { + + @LazyInitialized(value = "Simple lazy initialization, but different default value", + analyses = {}) + @MutableReferenceAnnotation(value = "Analysis doesn't recognize lazy initialization with different default") + //TODO + private int x; + + public SimpleWithDifferentDefault() { + x = -1; + } + + public SimpleWithDifferentDefault(int a) { + this(); + } + + public int init() { + if (x == -1) { + x = 5; + } + return x; + } +} + +class WrongDefault { + + @MutableReferenceAnnotation("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 { + + @LazyInitializedReferenceAnnotation("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 { + + @MutableReferenceAnnotation("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 { + + @LazyInitializedReferenceAnnotation("Lazy initialization with call to deterministic method on final field") + private int x; + + @ImmutableReferenceAnnotation("Declared final field") + private final Inner inner; + + public DeterministicCallOnFinalField(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 DeterministicCallOnNonFinalField { + + @MutableReferenceAnnotation("Wrong lazy initialization with call to non-deterministic method on final field") + private int x; + + @MutableReferenceAnnotation("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 { + + @MutableReferenceAnnotation("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 { + + @LazyInitializedReferenceAnnotation("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 { + + @MutableReferenceAnnotation("Field can be observed partially updated") + private int x; + + public int init() { + if (x == 0) { + x = 5; + x ^= -1; + } + return x; + } +} + +class VisibleInitialization { + + @MutableReferenceAnnotation("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. + */ + @ImmutableReferenceAnnotation(value = "Field is never initialized, so it stays on its default value") + 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 PossibleExceptionInInitialization { + + @MutableReferenceAnnotation("Incorrect because lazy initialization is may not happen due to exception") + private int x; + + public int init(int i) { + int y = this.x; + if (y == 0) { + int z = 10 / i; + y = x = 5; + } + return y; + } +} + +class CaughtExceptionInInitialization { + + @MutableReferenceAnnotation("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/reference_immutability/PrivateFieldUpdater.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/PrivateFieldUpdater.java new file mode 100644 index 0000000000..01b9c43677 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/PrivateFieldUpdater.java @@ -0,0 +1,39 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.reference_immutability; + +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.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; + +/** + * Simple demo class which updates the private field of another instance of this class. + */ +public class PrivateFieldUpdater { + + @ImmutableReferenceAnnotation("only initialized by the constructor") + private String name; + + @MutableReferenceAnnotation("incremented whenever `this` object is passed to another `NonFinal` object") + 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/reference_immutability/Singleton.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java index 4824dea4c4..47c79df0d4 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java @@ -1,14 +1,20 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.reference_immutability; -/* - * Test case from the old L2 Field Mutability tests - */ + +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.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; public class Singleton { + @MutableReferenceAnnotation("written by static initializer after the field becomes (indirectly) readable") private String name; - + @ImmutableReferenceAnnotation("only initialized once by the constructor") private Object mutex = new Object(); private Singleton() { @@ -23,6 +29,7 @@ public String getName() { // STATIC FUNCTIONALITY + @ImmutableReferenceAnnotation("only set in the static initializer") private static Singleton theInstance; static { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java index ad27d7d1c6..36557c4675 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java @@ -1,7 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability; +import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -21,4 +23,7 @@ * A short reasoning of this property. */ String value();// default = "N/A"; + + Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; + } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java index c85be61a9c..e1925a8bc5 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java @@ -1,8 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability; +import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; +import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -22,4 +24,6 @@ * A short reasoning of this property. */ String value();// default = "N/A"; + + Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java index 648a8431aa..73e6289a9d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java @@ -1,8 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability; +import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; +import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -22,4 +24,6 @@ * A short reasoning of this property. */ String value();// default = "N/A"; + + Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java index dd5152b9ff..92812b409f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java @@ -1,8 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability; +import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; +import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -22,4 +24,6 @@ * A short reasoning of this property. */ String value();// default = "N/A"; + + Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java new file mode 100644 index 0000000000..eb76bef20c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.field_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is shallow immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key="FieldImmutability",validator=DependentImmutableFieldMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DependentImmutableFieldAnnotation { + + /** + * A short reasoning of this property. + */ + String value() ; // default = "N/A"; + + Class[] analyses() default { + L0FieldImmutabilityAnalysis.class + }; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java index 822be18a0a..1adf72d4b7 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java @@ -19,10 +19,17 @@ @Retention(RetentionPolicy.CLASS) public @interface MutableReferenceAnnotation { + + /** + * True if the field is non-final because it is read prematurely. + * Tests may ignore @NonFinal annotations if the FieldPrematurelyRead property for the field + * did not identify the premature read. + */ + boolean prematurelyRead() default false; /** * A short reasoning of this property. */ - String value();// default = "N/A"; + String value() default "N/A"; Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala index e9c5e67fc9..a16da84d99 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -6,15 +6,14 @@ 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.EagerClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** * @author Tobias Peter Roth @@ -28,27 +27,32 @@ class ClassImmutabilityTests extends PropertiesTest { p.get(RTACallGraphKey) } - describe("no analysis is scheduled") { - val as = executeAnalyses(Set.empty) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) - } - - describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + /** + * describe("no analysis is scheduled") { + * val as = executeAnalyses(Set.empty) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) + * }* + */ + describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { + println(1) val as = executeAnalyses( Set( - EagerClassImmutabilityAnalysis, - EagerTypeImmutabilityAnalysis, - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerL0ReferenceImmutabilityAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, +/****/ + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, EagerLxClassImmutabilityAnalysis_new ) ) + println(2) as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) + println(3) + validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) //Set("ClassImmutability_new")) + println(4) } /** * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { @@ -77,5 +81,4 @@ class ClassImmutabilityTests extends PropertiesTest { * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) * } * */ - } 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 index 64be6de388..442fda7ab6 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -6,16 +6,15 @@ 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.EagerClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.TACAITransformer -import org.opalj.tac.fpcf.analyses.escape.EagerSimpleEscapeAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** * @author Tobias Peter Roth @@ -32,21 +31,22 @@ class FieldImmutabilityTests extends PropertiesTest { describe("no analysis is scheduled") { val as = executeAnalyses(Set.empty) as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) } describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { val as = executeAnalyses( Set( - EagerTypeImmutabilityAnalysis, - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerClassImmutabilityAnalysis, - EagerL0ReferenceImmutabilityAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerSimpleEscapeAnalysis, - TACAITransformer, - EagerL0FieldImmutabilityAnalysis + LazyTypeImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyClassImmutabilityAnalysis + // LazySimpleEscapeAnalysis, + // TACAITransformer ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index 8cfd18c1a9..3e5b2ea958 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -6,12 +6,12 @@ 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.{EagerL0PurityAnalysis, EagerUnsoundPrematurelyReadFieldsAnalysis} +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.{ - EagerL0ReferenceImmutabilityAnalysis, - EagerL1FieldMutabilityAnalysis -} +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** * @author Tobias Peter Roth @@ -34,10 +34,11 @@ class ReferenceImmutabilityTests extends PropertiesTest { describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { val as = executeAnalyses( Set( - EagerUnsoundPrematurelyReadFieldsAnalysis, EagerL0ReferenceImmutabilityAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala index 5b210c2997..188a95ec8d 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala @@ -1,10 +1,16 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability -import org.opalj.br.{AnnotationLike, ObjectType} +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties._ -import org.opalj.fpcf.{Entity, Property} +import org.opalj.br.fpcf.properties.ClassImmutability_new +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.fpcf.Entity +import org.opalj.fpcf.Property import org.opalj.fpcf.properties.AbstractPropertyMatcher /** @@ -28,6 +34,7 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) analyses.exists(as.contains) + } def validateProperty( @@ -37,10 +44,15 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) a: AnnotationLike, properties: Traversable[Property] ): Option[String] = { + println(11) if (!properties.exists(p ⇒ p == property)) { // ... when we reach this point the expected property was not found. - Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) + println(22) + val r = Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) + println(33) + r } else { + println(44) None } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala index 03298c1cc8..64e406fb58 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala @@ -3,8 +3,13 @@ package org.opalj.fpcf.properties.field_immutability import org.opalj.br.{AnnotationLike, ObjectType} import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties._ -import org.opalj.fpcf.{Entity, Property} +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.MutableField +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property import org.opalj.fpcf.properties.AbstractPropertyMatcher /** @@ -27,6 +32,7 @@ class FieldImmutabilityMatcher(val property: FieldImmutability) extends Abstract val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) analyses.exists(as.contains) + } def validateProperty( @@ -50,4 +56,6 @@ class MutableFieldMatcher extends FieldImmutabilityMatcher(MutableField) class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(ShallowImmutableField) +class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(DependentImmutableField) + class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(DeepImmutableField) 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/field_mutability/FieldMutabilityMatcher.scala index 2f04704357..d29dbffb92 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/field_mutability/FieldMutabilityMatcher.scala @@ -21,7 +21,7 @@ import org.opalj.br.fpcf.properties.LazyInitializedField */ class FieldMutabilityMatcher(val property: FieldMutability) extends AbstractPropertyMatcher { - private final val PropertyReasonID = 0 + final private val PropertyReasonID = 0 override def isRelevant( p: SomeProject, 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/field_mutability/NonFinalMatcher.scala index 08526a42b0..a53af488ff 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/field_mutability/NonFinalMatcher.scala @@ -39,7 +39,9 @@ class NonFinalMatcher extends AbstractPropertyMatcher { if (!analyses.exists(as.contains)) return false; - val prematurelyRead = getValue(p, annotationType, a.elementValuePairs, "prematurelyRead").asInstanceOf[BooleanValue].value + val prematurelyRead = getValue(p, annotationType, a.elementValuePairs, "prematurelyRead") + .asInstanceOf[BooleanValue] + .value if (prematurelyRead) { val propertyStore = p.get(PropertyStoreKey) @@ -65,5 +67,4 @@ class NonFinalMatcher extends AbstractPropertyMatcher { Some(a.elementValuePairs.head.value.toString) } } - } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/MutableReferenceMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/MutableReferenceMatcher.scala new file mode 100644 index 0000000000..18ccdeb426 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/MutableReferenceMatcher.scala @@ -0,0 +1,70 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability + +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.MutableReference +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 + +/** + * @author Tobias Peter Roth + */ +class MutableReferenceMatcher extends AbstractPropertyMatcher { + + val property = MutableReference + + 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/reference_immutability/ReferenceImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala index de82d1030b..fa412de678 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala @@ -6,7 +6,6 @@ import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference -import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.ReferenceImmutability import org.opalj.fpcf.Entity import org.opalj.fpcf.Property @@ -49,11 +48,8 @@ class ReferenceImmutabilityMatcher(val property: ReferenceImmutability) None } } - } -class MutableReferenceMatcher extends ReferenceImmutabilityMatcher(MutableReference) - class LazyInitializedReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedReference) class ImmutableReferenceMatcher extends ReferenceImmutabilityMatcher(ImmutableReference) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index 8059b5d74e..7a4602aedc 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -6,7 +6,7 @@ import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = ClassImmutability_new + final type Self = ClassImmutability_new } /** @@ -27,18 +27,18 @@ sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaIn sealed trait ClassImmutability_new extends Property with ClassImmutabilityPropertyMetaInformation_new { - final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key + final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key } object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[ClassImmutability_new]] property. - */ - final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( - "opalj.ClassImmutability_new", - MutableClass - ) + /** + * The key associated with every [[ClassImmutability_new]] property. + */ + final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( + "opalj.ClassImmutability_new", + MutableClass + ) } case object MutableClass extends ClassImmutability_new 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 index 7c6fb4c654..f6bbffb40b 100644 --- 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 @@ -7,7 +7,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait FieldImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - type Self = FieldImmutability + type Self = FieldImmutability } @@ -25,19 +25,19 @@ sealed trait FieldImmutabilityPropertyMetaInformation extends PropertyMetaInform */ sealed trait FieldImmutability extends Property with FieldImmutabilityPropertyMetaInformation { - final def key: PropertyKey[FieldImmutability] = FieldImmutability.key + final def key: PropertyKey[FieldImmutability] = FieldImmutability.key } object FieldImmutability extends FieldImmutabilityPropertyMetaInformation { - final val PropertyKeyName = "opalj.FieldImmutability" + final val PropertyKeyName = "opalj.FieldImmutability" - final val key: PropertyKey[FieldImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableField - ) - } + final val key: PropertyKey[FieldImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableField + ) + } } case object MutableField extends FieldImmutability diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index c0762cdd2c..1836727cfe 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -39,8 +39,8 @@ import org.opalj.fpcf.SomeEPS import org.opalj.tac.fpcf.properties.TACAI case class State() { - var typeImmutability: Option[Boolean] = None - var referenceImmutability: Option[Boolean] = None + var typeImmutability: Option[Boolean] = None + var referenceImmutability: Option[Boolean] = None } /** @@ -55,175 +55,175 @@ case class State() { class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - final val typeExtensibility = project.get(TypeExtensibilityKey) - final val closedPackages = project.get(ClosedPackagesKey) - final val fieldAccessInformation = project.get(FieldAccessInformationKey) + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) - def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { - entity match { - case field: Field => determineFieldImmutability(field) - case _ => - val m = entity.getClass.getName + "is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { + entity match { + case field: Field ⇒ determineFieldImmutability(field) + case _ ⇒ + val m = entity.getClass.getName+"is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } } - } - private[analyses] def determineFieldImmutability( - field: Field - ): ProperPropertyComputationResult = { + private[analyses] def determineFieldImmutability( + field: Field + ): ProperPropertyComputationResult = { - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - def hasImmutableType(field: Field): Option[Boolean] = { - println("Field:: " + field) - println(field.fieldType) - println(field.fieldType.isBaseType) - if (field.fieldType.isArrayType) return Some(true); //TODO - if (field.fieldType.isBaseType) return Some(true); - val result = propertyStore(field.fieldType, TypeImmutability.key) - println("result: " + result) - result match { - case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) => { - println("has immutable type") - Some(true) - } - case FinalEP(e, t) if (t == MutableType) => { - println("has mutable type") - Some(false) - } - case x @ _ => { - dependencies += x - println(x) - println("immutability of type couldn't be determined") - None + def hasImmutableType(field: Field): Option[Boolean] = { + println("Field:: "+field) + println(field.fieldType) + println(field.fieldType.isBaseType) + if (field.fieldType.isArrayType) return Some(true); //TODO + if (field.fieldType.isBaseType) return Some(true); + val result = propertyStore(field.fieldType, TypeImmutability.key) + println("result: "+result) + result match { + case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) ⇒ { + println("has immutable type") + Some(true) + } + case FinalEP(e, t) if (t == MutableType) ⇒ { + println("has mutable type") + Some(false) + } + case x @ _ ⇒ { + dependencies += x + println(x) + println("immutability of type couldn't be determined") + None + } + } } - } - } - def hasImmutableReference(field: Field): Option[Boolean] = { + def hasImmutableReference(field: Field): Option[Boolean] = { - propertyStore(field, ReferenceImmutability.key) match { - case FinalEP(_, MutableReference) => { - println("has mutable reference") - Some(false) - } - case FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO - println("has immutable Reference") - Some(true) - } - case x @ _ => { - dependencies += x - println(x) - println("immutability of reference couldn't be determined") - None + propertyStore(field, ReferenceImmutability.key) match { + case FinalEP(_, MutableReference) ⇒ { + println("has mutable reference") + Some(false) + } + case FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ { //TODO + println("has immutable Reference") + Some(true) + } + case x @ _ ⇒ { + dependencies += x + println(x) + println("immutability of reference couldn't be determined") + None + } + } } - } - } - val state: State = new State() + val state: State = new State() - def createResult(state: State): ProperPropertyComputationResult = { - println("reference Immutability: " + state.referenceImmutability) - println("type immutabiliy: " + state.typeImmutability) + def createResult(state: State): ProperPropertyComputationResult = { + println("reference Immutability: "+state.referenceImmutability) + println("type immutabiliy: "+state.typeImmutability) + + state.referenceImmutability match { + case Some(false) ⇒ Result(field, MutableField) + case Some(true) ⇒ { + //If the field type is object. It is a generic field + if (field.fieldType == ObjectType("java/lang/Object")) + Result(field, DependentImmutableField) + else + state.typeImmutability match { + case Some(true) ⇒ Result(field, DeepImmutableField) + case Some(false) ⇒ Result(field, ShallowImmutableField) + case None if (dependencies.isEmpty) ⇒ Result(field, ShallowImmutableField) + case None ⇒ { + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } + } + } + case None if (dependencies.isEmpty) ⇒ Result(field, MutableField) + case None ⇒ { + println(dependencies) + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } - state.referenceImmutability match { - case Some(false) => Result(field, MutableField) - case Some(true) => { - //If the field type is object. It is a generic field - if (field.fieldType == ObjectType("java/lang/Object")) - Result(field, DependentImmutableField) - else - state.typeImmutability match { - case Some(true) => Result(field, DeepImmutableField) - case Some(false) => Result(field, ShallowImmutableField) - case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) - case None => { - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } } } - case None if (dependencies.isEmpty) => Result(field, MutableField) - case None => { - println(dependencies) - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } + def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { + println("c function called") + dependencies = dependencies.filter(_.e ne eps.e) + (eps: @unchecked) match { + case x: InterimEP[_, _] ⇒ { + println("interim in continuation function") + println(x) + dependencies += eps + InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + } - } - } - def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - println("c function called") - dependencies = dependencies.filter(_.e ne eps.e) - (eps: @unchecked) match { - case x: InterimEP[_, _] => { - println("interim in continuation function") - println(x) - dependencies += eps - InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) - } + case x @ FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) ⇒ { + println(x) + println("has immutable type. Determined by continuation function.") + state.typeImmutability = Some(true) + } - case x @ FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) => { - println(x) - println("has immutable type. Determined by continuation function.") - state.typeImmutability = Some(true) - } + case x @ FinalEP(_, MutableType) ⇒ { + println(x) + println("has mutable type. Determined by continuation function.") + state.typeImmutability = Some(false) + } - case x @ FinalEP(_, MutableType) => { - println(x) - println("has mutable type. Determined by continuation function.") - state.typeImmutability = Some(false) - } + case x @ FinalEP(_, MutableReference) ⇒ { + println(x) + println("has mutable reference. Determined by continuation function.") + state.referenceImmutability = Some(false) + } - case x @ FinalEP(_, MutableReference) => { - println(x) - println("has mutable reference. Determined by continuation function.") - state.referenceImmutability = Some(false) - } + case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ { //TODO + println(x) + println("has immutable reference. Determined by continuation function.") + state.referenceImmutability = Some(true) + } - case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO - println(x) - println("has immutable reference. Determined by continuation function.") - state.referenceImmutability = Some(true) + case x @ _ ⇒ println("default value: "+x) + } + createResult(state) } - case x @ _ => println("default value: " + x) - } - createResult(state) + //-- + state.referenceImmutability = hasImmutableReference(field) + state.typeImmutability = hasImmutableType(field); + println("after type immutability determination") + createResult(state) } - //-- - state.referenceImmutability = hasImmutableReference(field) - state.typeImmutability = hasImmutableType(field); - println("after type immutability determination") - createResult(state) - } - } trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - //PropertyBounds.lub(Purity), - //PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - //PropertyBounds.lub(FieldMutability), - //PropertyBounds.lub(EscapeProperty), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(FieldImmutability) + final override def uses: Set[PropertyBounds] = Set( + //PropertyBounds.lub(Purity), + //PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + //PropertyBounds.lub(FieldMutability), + //PropertyBounds.lub(EscapeProperty), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.lub(TypeImmutability), + PropertyBounds.lub(FieldImmutability) //PropertyBounds.ub(EscapeProperty) - ) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) } @@ -234,16 +234,16 @@ object EagerL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -253,19 +253,19 @@ object LazyL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - FieldImmutability.key, - analysis.determineFieldImmutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldImmutability.key, + analysis.determineFieldImmutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index 4d2590fb19..6fc3cc9170 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -8,11 +8,8 @@ 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._ -import org.opalj.fpcf._ import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler -import org.opalj.br._ import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.FieldAccessInformationKey @@ -31,6 +28,41 @@ import org.opalj.br.fpcf.properties.cg.Callees import org.opalj.ai.isImmediateVMException import org.opalj.ai.pcOfImmediateVMException import org.opalj.ai.pcOfMethodExternalException +import org.opalj.br.ClassFile +import org.opalj.br.ComputationalTypeFloat +import org.opalj.br.ComputationalTypeInt +import org.opalj.br.DeclaredMethod +import org.opalj.br.Field +import org.opalj.br.FloatType +import org.opalj.br.Method +import org.opalj.br.PC +import org.opalj.br.PCs +import org.opalj.br.fpcf.properties.AtMost +import org.opalj.br.fpcf.properties.EscapeInCallee +import org.opalj.br.fpcf.properties.EscapeViaReturn +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.br.fpcf.properties.NoEscape +import org.opalj.br.fpcf.properties.NotPrematurelyReadField +import org.opalj.br.fpcf.properties.PrematurelyReadField +import org.opalj.br.fpcf.properties.ReferenceImmutability +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.LBP +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyComputationResult +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.SomeEPS +import org.opalj.fpcf.UBP +import org.opalj.fpcf.Result import org.opalj.tac.common.DefinitionSite import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.properties.TACAI @@ -315,7 +347,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * dependees or as Result otherwise. */ def createResult()(implicit state: State): ProperPropertyComputationResult = { - if (state.hasDependees && (state.referenceImmutability ne NonFinalFieldByAnalysis)) + if (state.hasDependees && (state.referenceImmutability ne MutableReference)) //NonFinalFieldByAnalysis)) InterimResult( state.field, MutableReference, //NonFinalFieldByAnalysis, @@ -500,8 +532,18 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec method: Method, code: Array[Stmt[V]] )(implicit state: State): Boolean = { - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + )) + println("paramsCount: "+method.descriptor.parametersCount == 0) + println( + "isnondeterministic-result: "+isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + ).toString + ) + println(propertyStore(declaredMethods(method), Purity.key)) + println("lazyInitializerIsDeterministic: "+result) + result } /** @@ -1023,8 +1065,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec def isNonDeterministic( eop: EOptionP[DeclaredMethod, Purity] )(implicit state: State): Boolean = eop match { - case LBP(p: Purity) if p.isDeterministic ⇒ - false + case LBP(p: Purity) if p.isDeterministic ⇒ false case UBP(p: Purity) if !p.isDeterministic ⇒ true case _ ⇒ state.purityDependees += eop @@ -1040,6 +1081,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec )(implicit state: State): Boolean = eop match { case FinalEP(e, ImmutableReference) ⇒ true case FinalEP(e, MutableReference) ⇒ false + // + case LBP(ImmutableReference) ⇒ true + case UBP(MutableReference) ⇒ false /** * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ @@ -1058,8 +1102,8 @@ trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { PropertyBounds.lub(Purity), PropertyBounds.lub(FieldPrematurelyRead), PropertyBounds.ub(TACAI), - PropertyBounds.ub(EscapeProperty), - PropertyBounds.lub(ReferenceImmutability) + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.ub(EscapeProperty) ) final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 9e1c4c9995..9779090536 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -72,7 +72,7 @@ import org.opalj.br.fpcf.properties.TypeImmutability * */ class LxClassImmutabilityAnalysis_new(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. @@ -81,358 +81,358 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal * 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_new //MutableObject - ): 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 ::= ( - ( - determineClassImmutability_new(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, MutableClass) //MutableObjectDueToUnknownSupertypes) - } + /** + * 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_new //MutableObject + ): MultiResult = { + val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) + val r = allSubtypes.map { st ⇒ + new FinalEP(st, immutability) + }.toSeq + MultiResult(r) } - IncrementalResult(Results(results), nextComputations.iterator) - } - - def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { - e match { - case t: ObjectType => - //this is safe - classHierarchy.superclassType(t) match { - case None => Result(t, MutableClass) //MutableObjectDueToUnknownSupertypes) - case Some(superClassType) => - val cf = project.classFile(t) match { - case None => - return Result(t, MutableClass) // MutableObjectByAnalysis) //TODO consider other lattice element - case Some(cf) => cf - } - propertyStore(superClassType, ClassImmutability_new.key) match { - case UBP(MutableClass) => //MutableObject) ⇒ - Result(t, MutableClass) - case eps: EPS[ObjectType, ClassImmutability_new] => - determineClassImmutability_new( - superClassType, - eps, - eps.isFinal, - lazyComputation = true - )(cf) - case epk => - determineClassImmutability_new( - superClassType, - epk, - superClassMutabilityIsFinal = false, - lazyComputation = true - )(cf) + @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 ::= ( + ( + determineClassImmutability_new(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, MutableClass) //MutableObjectDueToUnknownSupertypes) } - } - case _ => - val m = e.getClass.getSimpleName + " is not an org.opalj.br.ObjectType" - throw new IllegalArgumentException(m) + IncrementalResult(Results(results), nextComputations.iterator) } - } - - 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 determineClassImmutability_new( - superClassType: ObjectType, - superClassInformation: EOptionP[Entity, Property], - superClassMutabilityIsFinal: Boolean, - lazyComputation: Boolean - )( - cf: ClassFile - ): ProperPropertyComputationResult = { - // assert(superClassMutability.isMutable.isNoOrUnknown) - - //---generic classes handling - //printf(cf.asVirtualClass.asClassFile.classSignature.get.toString()) - //cf.asClassFile.classSignature.get.formalTypeParameters. - //--------------------------------------------- - - val t = cf.thisType - - var dependees = Map.empty[Entity, EOptionP[Entity, Property]] - - if (!superClassMutabilityIsFinal) { - dependees += (SuperClassKey -> superClassInformation) + def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { + e match { + case t: ObjectType ⇒ + //this is safe + classHierarchy.superclassType(t) match { + case None ⇒ Result(t, MutableClass) //MutableObjectDueToUnknownSupertypes) + case Some(superClassType) ⇒ + val cf = project.classFile(t) match { + case None ⇒ + return Result(t, MutableClass) // MutableObjectByAnalysis) //TODO consider other lattice element + case Some(cf) ⇒ cf + } + + propertyStore(superClassType, ClassImmutability_new.key) match { + case UBP(MutableClass) ⇒ //MutableObject) ⇒ + Result(t, MutableClass) + case eps: EPS[ObjectType, ClassImmutability_new] ⇒ + determineClassImmutability_new( + superClassType, + eps, + eps.isFinal, + lazyComputation = true + )(cf) + case epk ⇒ + determineClassImmutability_new( + superClassType, + epk, + superClassMutabilityIsFinal = false, + lazyComputation = true + )(cf) + } + + } + case _ ⇒ + val m = e.getClass.getSimpleName+" is not an org.opalj.br.ObjectType" + throw new IllegalArgumentException(m) + } } - // Collect all fields for which we need to determine the effective mutability! - var hasFieldsWithUnknownMutability = false - - val instanceFields = cf.fields.filter { f => - !f.isStatic - } - var hasShallowImmutableFields = false - var hasDependentImmutableFields = false - val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) - fieldsPropertyStoreInformation.foreach( - f => - f match { - 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 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); - } - ) + private[this] object SuperClassKey /** - * dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { - * case FinalP(MutableField) => //NonFinalField) ⇒ - * // <=> The class is definitively mutable and therefore also all subclasses. - * if (lazyComputation) - * return Result(t, MutableClass); //MutableObjectByAnalysis); // - * else - * return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); - * case ep @ InterimE(e) => - * hasFieldsWithUnknownMutability = true - * (e, ep) - * case epk @ EPK(e: Entity, _) => - * // <=> The mutability information is not yet available. - * hasFieldsWithUnknownMutability = true - * (e, epk) + * Determines the immutability of instances of the given class type `t`. * - * // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields - * }).toMap * + * @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. */ - var minLocalImmutability: ClassImmutability_new = - if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) - MutableClass //MutableObjectByAnalysis - else - ShallowImmutableClass //ImmutableContainer - - // NOTE: maxLocalImmutability does not take the super classes' mutability into account! - var maxLocalImmutability: ClassImmutability_new = superClassInformation match { - case UBP(ShallowImmutableClass) => ShallowImmutableClass //ImmutableContainer - case _ => DeepImmutableClass // ImmutableObject - } + def determineClassImmutability_new( + superClassType: ObjectType, + superClassInformation: EOptionP[Entity, Property], + superClassMutabilityIsFinal: Boolean, + lazyComputation: Boolean + )( + cf: ClassFile + ): ProperPropertyComputationResult = { + // assert(superClassMutability.isMutable.isNoOrUnknown) + + //---generic classes handling + //printf(cf.asVirtualClass.asClassFile.classSignature.get.toString()) + //cf.asClassFile.classSignature.get.formalTypeParameters. + //--------------------------------------------- + + val t = cf.thisType + + var dependees = Map.empty[Entity, EOptionP[Entity, Property]] + + if (!superClassMutabilityIsFinal) { + dependees += (SuperClassKey -> superClassInformation) + } - if (hasDependentImmutableFields) { - maxLocalImmutability = DependentImmutableClass - } else if (hasShallowImmutableFields) { - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - } + // Collect all fields for which we need to determine the effective mutability! + var hasFieldsWithUnknownMutability = false - 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 //ImmutableContainer - } + val instanceFields = cf.fields.filter { f ⇒ + !f.isStatic + } + var hasShallowImmutableFields = false + var hasDependentImmutableFields = false + val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) + fieldsPropertyStoreInformation.foreach( + f ⇒ + f match { + 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 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); + } + ) + + /** + * dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { + * case FinalP(MutableField) => //NonFinalField) ⇒ + * // <=> The class is definitively mutable and therefore also all subclasses. + * if (lazyComputation) + * return Result(t, MutableClass); //MutableObjectByAnalysis); // + * else + * return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); + * case ep @ InterimE(e) => + * hasFieldsWithUnknownMutability = true + * (e, ep) + * case epk @ EPK(e: Entity, _) => + * // <=> The mutability information is not yet available. + * hasFieldsWithUnknownMutability = true + * (e, epk) + * + * // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields + * }).toMap * + */ + var minLocalImmutability: ClassImmutability_new = + if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) + MutableClass //MutableObjectByAnalysis + else + ShallowImmutableClass //ImmutableContainer - //--var fieldTypes: Set[ObjectType] = Set.empty - //--if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { - //-- fieldTypes = // IMPROVE Use the precise type of the field (if available)! - // cf.fields.collect { - // case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType - // }.toSet - //} - - // For each dependent class file we have to determine the mutability - // of instances of the respective type to determine the immutability - // of instances of this class. - // Basically, we have to distinguish the following cases: - // - A field's type is mutable or conditionally immutable=> - // This class is conditionally immutable. - // - A field's type is immutable => - // The field is ignored. - // (This class is as immutable as its superclass.) - // - A field's type is at least conditionally mutable or - // The immutability of the field's type is not yet known => - // This type is AtLeastConditionallyImmutable - // We have to declare a dependency on the respective type's immutability. - // - // Additional handling is required w.r.t. the supertype: - // If the supertype is Immutable => - // Nothing special to do. - // If the supertype is AtLeastConditionallyImmutable => - // We have to declare a dependency on the supertype. - // If the supertype is ConditionallyImmutable => - // This type is also at most conditionally immutable. - // - // We furthermore have to take the mutability of the fields into consideration: - // If a field is not effectively final => - // This type is mutable. - // If a field is effectively final => - // Nothing special to do. - - //val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) - //val hasMutableOrConditionallyImmutableField = - // IMPROVE Use the precise type of the field (if available)! - //-- fieldTypesImmutability.exists { eOptP ⇒ - //-- eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) - //-- } - - if (hasShallowImmutableFields) { - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - } //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 - * } - * fieldTypesWithUndecidedMutability.foreach { eOptP => - * dependees += (eOptP.e -> eOptP) - * }* - */ - //} - - 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) - ); - } + // NOTE: maxLocalImmutability does not take the super classes' mutability into account! + var maxLocalImmutability: ClassImmutability_new = superClassInformation match { + case UBP(ShallowImmutableClass) ⇒ ShallowImmutableClass //ImmutableContainer + case _ ⇒ DeepImmutableClass // ImmutableObject + } + + if (hasDependentImmutableFields) { + maxLocalImmutability = DependentImmutableClass + } else if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + } + + 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 //ImmutableContainer + } - def c(someEPS: SomeEPS): ProperPropertyComputationResult = { - //[DEBUG] val oldDependees = dependees - dependees = dependees.filter(_._1 ne someEPS.e) - println("someEPS: " + someEPS) - someEPS match { - // Superclass related dependencies: + //--var fieldTypes: Set[ObjectType] = Set.empty + //--if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { + //-- fieldTypes = // IMPROVE Use the precise type of the field (if available)! + // cf.fields.collect { + // case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType + // }.toSet + //} + + // For each dependent class file we have to determine the mutability + // of instances of the respective type to determine the immutability + // of instances of this class. + // Basically, we have to distinguish the following cases: + // - A field's type is mutable or conditionally immutable=> + // This class is conditionally immutable. + // - A field's type is immutable => + // The field is ignored. + // (This class is as immutable as its superclass.) + // - A field's type is at least conditionally mutable or + // The immutability of the field's type is not yet known => + // This type is AtLeastConditionallyImmutable + // We have to declare a dependency on the respective type's immutability. + // + // Additional handling is required w.r.t. the supertype: + // If the supertype is Immutable => + // Nothing special to do. + // If the supertype is AtLeastConditionallyImmutable => + // We have to declare a dependency on the supertype. + // If the supertype is ConditionallyImmutable => + // This type is also at most conditionally immutable. // - case UBP(MutableClass) => // MutableObject) ⇒ - return Result(t, MutableClass); //MutableObjectByAnalysis); + // We furthermore have to take the mutability of the fields into consideration: + // If a field is not effectively final => + // This type is mutable. + // If a field is effectively final => + // Nothing special to do. + + //val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) + //val hasMutableOrConditionallyImmutableField = + // IMPROVE Use the precise type of the field (if available)! + //-- fieldTypesImmutability.exists { eOptP ⇒ + //-- eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) + //-- } + + if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + } //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 + * } + * fieldTypesWithUndecidedMutability.foreach { eOptP => + * dependees += (eOptP.e -> eOptP) + * }* + */ + //} + + 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) + ); + } - case LBP(DeepImmutableClass) => //_:ImmutableObject) ⇒ // the super class - dependees -= SuperClassKey + def c(someEPS: SomeEPS): ProperPropertyComputationResult = { + //[DEBUG] val oldDependees = dependees + dependees = dependees.filter(_._1 ne someEPS.e) + println("someEPS: "+someEPS) + someEPS match { + // Superclass related dependencies: + // + case UBP(MutableClass) ⇒ // MutableObject) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); - case UBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container - if (someEPS.isFinal) dependees -= SuperClassKey - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + case LBP(DeepImmutableClass) ⇒ //_:ImmutableObject) ⇒ // the super class + dependees -= SuperClassKey - case LBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container - if (minLocalImmutability != DeepImmutableClass && //ImmutableContainer && - !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + case UBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is at most immutable container + if (someEPS.isFinal) dependees -= SuperClassKey + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - case LUBP(MutableClass, DeepImmutableClass) => //_: MutableObject, ImmutableObject) ⇒ // No information about superclass + case LBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is a least immutable container + if (minLocalImmutability != DeepImmutableClass && //ImmutableContainer && + !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - // Properties related to the type of the class' fields. - // - case UBP(ShallowImmutableClass | MutableClass) => //ImmutableContainerType | MutableType) ⇒ - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + case LUBP(MutableClass, DeepImmutableClass) ⇒ //_: MutableObject, ImmutableObject) ⇒ // No information about superclass - case ELBP(e, ImmutableType) => // Immutable field type, no influence on mutability - dependees -= e + // Properties related to the type of the class' fields. + // + case UBP(ShallowImmutableClass | MutableClass) ⇒ //ImmutableContainerType | MutableType) ⇒ + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - case UBP(ImmutableType) => // No information about field type + case ELBP(e, ImmutableType) ⇒ // Immutable field type, no influence on mutability + dependees -= e - case FinalP(MutableType) => Result(t, MutableClass) //TODO check + case UBP(ImmutableType) ⇒ // No information about field type - case FinalP(DependentImmutableField) => maxLocalImmutability = DependentImmutableClass - // Field Mutability related dependencies: - // + case FinalP(MutableType) ⇒ Result(t, MutableClass) //TODO check - case FinalP(ShallowImmutableField) => { - maxLocalImmutability = ShallowImmutableClass - } - case FinalP(MutableField) => return Result(t, MutableClass); + case FinalP(DependentImmutableField) ⇒ maxLocalImmutability = DependentImmutableClass + // Field Mutability related dependencies: + // - case UBP(MutableField) => //_: NonFinalField) ⇒ - return Result(t, MutableClass); //MutableObjectByAnalysis); + case FinalP(ShallowImmutableField) ⇒ { + maxLocalImmutability = ShallowImmutableClass + } + case FinalP(MutableField) ⇒ return Result(t, MutableClass); - case ELBP(e, ShallowImmutableField | DeepImmutableField) => //FinalField) => - dependees -= e - if (minLocalImmutability != ShallowImmutableClass && // ImmutableContainer && - !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + case UBP(MutableField) ⇒ //_: NonFinalField) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); - case UBP(ShallowImmutableField | DeepImmutableField) => //_: FinalField) ⇒ // no information about field mutability + case ELBP(e, ShallowImmutableField | DeepImmutableField) ⇒ //FinalField) => + dependees -= e + if (minLocalImmutability != ShallowImmutableClass && // ImmutableContainer && + !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - case _ => Result(t, MutableClass) //TODO check + case UBP(ShallowImmutableField | DeepImmutableField) ⇒ //_: FinalField) ⇒ // no information about field mutability - } + case _ ⇒ Result(t, MutableClass) //TODO check + + } - if (someEPS.isRefinable) { - val entity = if (someEPS.pk == ClassImmutability_new.key) SuperClassKey else someEPS.e - dependees += (entity -> someEPS) - } + if (someEPS.isRefinable) { + val entity = if (someEPS.pk == ClassImmutability_new.key) SuperClassKey else someEPS.e + dependees += (entity -> someEPS) + } - /*[DEBUG] + /*[DEBUG] assert( oldDependees != dependees, s"dependees are not correctly updated $e($p)\n:old=$oldDependees\nnew=$dependees" ) */ - // Lift lower bound once no dependencies other than field type mutabilities are left - if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && - dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer + // Lift lower bound once no dependencies other than field type mutabilities are left + if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && + dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer - if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { - /*[DEBUG] + if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { + /*[DEBUG] assert( maxLocalImmutability == ConditionallyImmutableObject || maxLocalImmutability == ImmutableObject @@ -451,115 +451,115 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal ) */ - Result(t, maxLocalImmutability) + Result(t, maxLocalImmutability) - } else { - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - } - } + } else { + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + } + } - //[DEBUG] assert(initialImmutability.isRefinable) - println("minLocalImmutability: " + minLocalImmutability) - println("maxLocalImmutability: " + maxLocalImmutability) - val result = - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - if (lazyComputation) - result - else { - val isFinal = dependees.isEmpty - createIncrementalResult( - t, - EPS(t, minLocalImmutability, maxLocalImmutability), - isFinal, - result - ) + //[DEBUG] assert(initialImmutability.isRefinable) + println("minLocalImmutability: "+minLocalImmutability) + println("maxLocalImmutability: "+maxLocalImmutability) + val result = + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + if (lazyComputation) + result + else { + val isFinal = dependees.isEmpty + createIncrementalResult( + t, + EPS(t, minLocalImmutability, maxLocalImmutability), + isFinal, + result + ) + } } - } } trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) - - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, TypeImmutability, FieldImmutability) - - 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 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.map(cf => set(cf.thisType, DeepImmutableClass)) //ImmutableObject)) - - // 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) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability, FieldImmutability) + + 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 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.map(cf ⇒ set(cf.thisType, DeepImmutableClass)) //ImmutableObject)) + + // 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) } - // 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 = {} + 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 = {} } /** @@ -571,22 +571,22 @@ object EagerLxClassImmutabilityAnalysis_new extends ClassImmutabilityAnalysisScheduler_new 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 LxClassImmutabilityAnalysis_new(p) - ps.scheduleEagerComputationsForEntities(cfs)( - analysis.determineClassImmutability_new( - superClassType = null, - FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), - superClassMutabilityIsFinal = true, - lazyComputation = false - ) - ) - analysis - } + 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 LxClassImmutabilityAnalysis_new(p) + ps.scheduleEagerComputationsForEntities(cfs)( + analysis.determineClassImmutability_new( + superClassType = null, + FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), + superClassMutabilityIsFinal = true, + lazyComputation = false + ) + ) + analysis + } } /** @@ -598,18 +598,18 @@ object LazyLxClassImmutabilityAnalysis_new extends ClassImmutabilityAnalysisScheduler_new with FPCFLazyAnalysisScheduler { - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - - override def register( - p: SomeProject, - ps: PropertyStore, - unused: InitializationData - ): FPCFAnalysis = { - val analysis = new LxClassImmutabilityAnalysis_new(p) - ps.registerLazyPropertyComputation( - ClassImmutability_new.key, - analysis.doDetermineClassImmutability_new - ) - analysis - } + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + override def register( + p: SomeProject, + ps: PropertyStore, + unused: InitializationData + ): FPCFAnalysis = { + val analysis = new LxClassImmutabilityAnalysis_new(p) + ps.registerLazyPropertyComputation( + ClassImmutability_new.key, + analysis.doDetermineClassImmutability_new + ) + analysis + } } From 543369fc7c572b994bb21686f4544c23307ca839 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 12 Nov 2019 15:55:01 +0100 Subject: [PATCH 012/327] format optimization --- .../scala/org/opalj/fpcf/PropertiesTest.scala | 34 +- .../analyses/ClassImmutabilityAnalysis.scala | 781 ++++---- .../analyses/TypeImmutabilityAnalysis.scala | 403 ++-- .../analyses/L2FieldMutabilityAnalysis.scala | 1781 +++++++++-------- 4 files changed, 1514 insertions(+), 1485 deletions(-) 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 ee8db7b2d4..300c3c6317 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 @@ -76,18 +76,22 @@ abstract class PropertiesTest extends FunSpec with Matchers { val libraryClassFiles = (if (withRT) ClassFiles(RTJar) else List()) ++ propertiesClassFiles - 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) ) - implicit val config: Config = configForEntryPoints.withValue( - InitialInstantiatedTypesKey.ConfigKeyPrefix+"analysis", - ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllInstantiatedTypesFinder") - ).withValue( + implicit val config: Config = configForEntryPoints + .withValue( + InitialInstantiatedTypesKey.ConfigKeyPrefix+"analysis", + ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllInstantiatedTypesFinder") + ) + .withValue( InitialInstantiatedTypesKey.ConfigKeyPrefix+ "AllInstantiatedTypesFinder.projectClassesOnly", ConfigValueFactory.fromAnyRef(true) @@ -119,6 +123,7 @@ abstract class PropertiesTest extends FunSpec with Matchers { )( annotation: AnnotationLike ): Option[(AnnotationLike, String, Type /* type of the matcher */ )] = { + println("inside getPropertyMatcher") if (!annotation.annotationType.isObjectType) return None; @@ -157,6 +162,7 @@ abstract class PropertiesTest extends FunSpec with Matchers { for { (e, entityIdentifier, annotations) ← eas augmentedAnnotations = annotations.flatMap(getPropertyMatcher(p, propertyKinds)) + (annotation, propertyKind, matcherType) ← augmentedAnnotations } { val annotationTypeName = annotation.annotationType.asObjectType.simpleName @@ -271,8 +277,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 ) } @@ -297,8 +304,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 ) } @@ -332,7 +340,7 @@ abstract class PropertiesTest extends FunSpec with Matchers { new RecordAllPropertyStoreTracer, context.iterator.map(_.asTuple).toMap ) - */ + */ val ps = PKESequentialPropertyStore(context: _*) ps } 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/ClassImmutabilityAnalysis.scala index c116c173ac..aba6712bc7 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/ClassImmutabilityAnalysis.scala @@ -44,6 +44,7 @@ import org.opalj.br.fpcf.properties.NonFinalField import org.opalj.br.fpcf.properties.TypeImmutability /** + * * 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 @@ -66,301 +67,307 @@ import org.opalj.br.fpcf.properties.TypeImmutability * @author Dominik Helm */ class ClassImmutabilityAnalysis(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: MutableObject - ): MultiResult = { - val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) - val r = allSubtypes.map { st ⇒ new FinalEP(st, immutability) }.toSeq - MultiResult(r) + /* + * 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: MutableObject + ): 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 ::= ( + ( + determineClassImmutability(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) + } } + IncrementalResult(Results(results), nextComputations.iterator) + } + + def doDetermineClassImmutability(e: Entity): ProperPropertyComputationResult = { + e match { + case t: ObjectType => + //this is safe + classHierarchy.superclassType(t) match { + case None => Result(t, MutableObjectDueToUnknownSupertypes) + case Some(superClassType) => + val cf = project.classFile(t) match { + case None => + return Result(t, MutableObjectByAnalysis) //TODO consider other lattice element + case Some(cf) => cf + } - @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 ::= ( - (determineClassImmutability(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) + propertyStore(superClassType, ClassImmutability.key) match { + case UBP(p: MutableObject) => Result(t, p) + case eps: EPS[ObjectType, ClassImmutability] => + determineClassImmutability( + superClassType, + eps, + eps.isFinal, + lazyComputation = true + )(cf) + case epk => + determineClassImmutability( + superClassType, + epk, + superClassMutabilityIsFinal = false, + lazyComputation = true + )(cf) } - } - IncrementalResult(Results(results), nextComputations.iterator) - } - def doDetermineClassImmutability(e: Entity): ProperPropertyComputationResult = { - e match { - case t: ObjectType ⇒ - //this is safe - classHierarchy.superclassType(t) match { - case None ⇒ Result(t, MutableObjectDueToUnknownSupertypes) - case Some(superClassType) ⇒ - val cf = project.classFile(t) match { - case None ⇒ - return Result(t, MutableObjectByAnalysis) //TODO consider other lattice element - case Some(cf) ⇒ cf - } - - propertyStore(superClassType, ClassImmutability.key) match { - case UBP(p: MutableObject) ⇒ Result(t, p) - case eps: EPS[ObjectType, ClassImmutability] ⇒ - determineClassImmutability( - superClassType, - eps, - eps.isFinal, - lazyComputation = true - )(cf) - case epk ⇒ - determineClassImmutability( - superClassType, - epk, - superClassMutabilityIsFinal = false, - lazyComputation = true - )(cf) - } - - } - case _ ⇒ - val m = e.getClass.getSimpleName+" is not an org.opalj.br.ObjectType" - throw new IllegalArgumentException(m) } + 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 determineClassImmutability( + superClassType: ObjectType, + superClassInformation: EOptionP[Entity, Property], + superClassMutabilityIsFinal: Boolean, + lazyComputation: Boolean + )( + cf: ClassFile + ): ProperPropertyComputationResult = { + // assert(superClassMutability.isMutable.isNoOrUnknown) + val t = cf.thisType + + var dependees = Map.empty[Entity, EOptionP[Entity, Property]] + + if (!superClassMutabilityIsFinal) { + dependees += (SuperClassKey -> superClassInformation) } - 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 determineClassImmutability( - superClassType: ObjectType, - superClassInformation: EOptionP[Entity, Property], - superClassMutabilityIsFinal: Boolean, - lazyComputation: Boolean - )( - cf: ClassFile - ): ProperPropertyComputationResult = { - // assert(superClassMutability.isMutable.isNoOrUnknown) - 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.filter { f ⇒ !f.isStatic } - dependees ++= (propertyStore(instanceFields, FieldMutability) collect { - case FinalP(_: NonFinalField) ⇒ - // <=> The class is definitively mutable and therefore also all subclasses. - if (lazyComputation) - return Result(t, MutableObjectByAnalysis); - else - return createResultForAllSubtypes(t, MutableObjectByAnalysis); - case ep @ InterimE(e) ⇒ - hasFieldsWithUnknownMutability = true - (e, ep) - case epk @ EPK(e: Entity, _) ⇒ - // <=> The mutability information is not yet available. - hasFieldsWithUnknownMutability = true - (e, epk) - - // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields - }).toMap - - var minLocalImmutability: ClassImmutability = - if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) - MutableObjectByAnalysis - else - ImmutableContainer - - // NOTE: maxLocalImmutability does not take the super classes' mutability into account! - var maxLocalImmutability: ClassImmutability = superClassInformation match { - case UBP(ImmutableContainer) ⇒ ImmutableContainer - case _ ⇒ ImmutableObject - } + // Collect all fields for which we need to determine the effective mutability! + var hasFieldsWithUnknownMutability = false - 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 - } + val instanceFields = cf.fields.filter { f => + !f.isStatic + } + dependees ++= (propertyStore(instanceFields, FieldMutability) collect { + case FinalP(_: NonFinalField) => + // <=> The class is definitively mutable and therefore also all subclasses. + if (lazyComputation) + return Result(t, MutableObjectByAnalysis); + else + return createResultForAllSubtypes(t, MutableObjectByAnalysis); + case ep @ InterimE(e) => + hasFieldsWithUnknownMutability = true + (e, ep) + case epk @ EPK(e: Entity, _) => + // <=> The mutability information is not yet available. + hasFieldsWithUnknownMutability = true + (e, epk) + + // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields + }).toMap + + var minLocalImmutability: ClassImmutability = + if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) + MutableObjectByAnalysis + else + ImmutableContainer + + // NOTE: maxLocalImmutability does not take the super classes' mutability into account! + var maxLocalImmutability: ClassImmutability = superClassInformation match { + case UBP(ImmutableContainer) => ImmutableContainer + case _ => ImmutableObject + } - var fieldTypes: Set[ObjectType] = Set.empty - if (maxLocalImmutability == ImmutableObject) { - fieldTypes = - // IMPROVE Use the precise type of the field (if available)! - cf.fields.collect { - case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType - }.toSet - } + 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 + } - // For each dependent class file we have to determine the mutability - // of instances of the respective type to determine the immutability - // of instances of this class. - // Basically, we have to distinguish the following cases: - // - A field's type is mutable or conditionally immutable=> - // This class is conditionally immutable. - // - A field's type is immutable => - // The field is ignored. - // (This class is as immutable as its superclass.) - // - A field's type is at least conditionally mutable or - // The immutability of the field's type is not yet known => - // This type is AtLeastConditionallyImmutable - // We have to declare a dependency on the respective type's immutability. - // - // Additional handling is required w.r.t. the supertype: - // If the supertype is Immutable => - // Nothing special to do. - // If the supertype is AtLeastConditionallyImmutable => - // We have to declare a dependency on the supertype. - // If the supertype is ConditionallyImmutable => - // This type is also at most conditionally immutable. - // - // We furthermore have to take the mutability of the fields into consideration: - // If a field is not effectively final => - // This type is mutable. - // If a field is effectively final => - // Nothing special to do. - - val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) - val hasMutableOrConditionallyImmutableField = - // IMPROVE Use the precise type of the field (if available)! - fieldTypesImmutability.exists { eOptP ⇒ - eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) - } + var fieldTypes: Set[ObjectType] = Set.empty + if (maxLocalImmutability == ImmutableObject) { + fieldTypes = // IMPROVE Use the precise type of the field (if available)! + cf.fields.collect { + case f if !f.isStatic && f.fieldType.isObjectType => f.fieldType.asObjectType + }.toSet + } - if (hasMutableOrConditionallyImmutableField) { - maxLocalImmutability = ImmutableContainer - } 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 - } - fieldTypesWithUndecidedMutability.foreach { eOptP ⇒ - dependees += (eOptP.e → eOptP) - } + // For each dependent class file we have to determine the mutability + // of instances of the respective type to determine the immutability + // of instances of this class. + // Basically, we have to distinguish the following cases: + // - A field's type is mutable or conditionally immutable=> + // This class is conditionally immutable. + // - A field's type is immutable => + // The field is ignored. + // (This class is as immutable as its superclass.) + // - A field's type is at least conditionally mutable or + // The immutability of the field's type is not yet known => + // This type is AtLeastConditionallyImmutable + // We have to declare a dependency on the respective type's immutability. + // + // Additional handling is required w.r.t. the supertype: + // If the supertype is Immutable => + // Nothing special to do. + // If the supertype is AtLeastConditionallyImmutable => + // We have to declare a dependency on the supertype. + // If the supertype is ConditionallyImmutable => + // This type is also at most conditionally immutable. + // + // We furthermore have to take the mutability of the fields into consideration: + // If a field is not effectively final => + // This type is mutable. + // If a field is effectively final => + // Nothing special to do. + + val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) + val hasMutableOrConditionallyImmutableField = + // IMPROVE Use the precise type of the field (if available)! + fieldTypesImmutability.exists { eOptP => + eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) + } + + if (hasMutableOrConditionallyImmutableField) { + maxLocalImmutability = ImmutableContainer + } 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 } + fieldTypesWithUndecidedMutability.foreach { eOptP => + dependees += (eOptP.e -> eOptP) + } + } - 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) - ); - } + 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 - someEPS match { - // Superclass related dependencies: - // - case UBP(_: MutableObject) ⇒ return Result(t, MutableObjectByAnalysis); + def c(someEPS: SomeEPS): ProperPropertyComputationResult = { + //[DEBUG] val oldDependees = dependees + someEPS match { + // Superclass related dependencies: + // + case UBP(_: MutableObject) => return Result(t, MutableObjectByAnalysis); - case LBP(ImmutableObject) ⇒ // the super class - dependees -= SuperClassKey + case LBP(ImmutableObject) => // the super class + dependees -= SuperClassKey - case UBP(ImmutableContainer) ⇒ // super class is at most immutable container - if (someEPS.isFinal) dependees -= SuperClassKey - maxLocalImmutability = ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + case UBP(ImmutableContainer) => // super class is at most immutable container + if (someEPS.isFinal) dependees -= SuperClassKey + maxLocalImmutability = ImmutableContainer + 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(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 LUBP(_: MutableObject, ImmutableObject) ⇒ // No information about superclass + case LUBP(_: MutableObject, ImmutableObject) => // No information about superclass - // Properties related to the type of the class' fields. - // - case UBP(ImmutableContainerType | MutableType) ⇒ - maxLocalImmutability = ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + // Properties related to the type of the class' fields. + // + case UBP(ImmutableContainerType | MutableType) => + maxLocalImmutability = ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - case ELBP(e, ImmutableType) ⇒ // Immutable field type, no influence on mutability - dependees -= e + case ELBP(e, ImmutableType) => // Immutable field type, no influence on mutability + dependees -= e - case UBP(ImmutableType) ⇒ // No information about field type + case UBP(ImmutableType) => // No information about field type - // Field Mutability related dependencies: - // - case UBP(_: NonFinalField) ⇒ return Result(t, MutableObjectByAnalysis); + // Field Mutability related dependencies: + // + case UBP(_: NonFinalField) => return Result(t, MutableObjectByAnalysis); - case ELBP(e, _: FinalField) ⇒ - dependees -= e - if (minLocalImmutability != ImmutableContainer && - !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) - minLocalImmutability = ImmutableContainer // Lift lower bound when possible + case ELBP(e, _: FinalField) => + dependees -= e + if (minLocalImmutability != ImmutableContainer && + !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) + minLocalImmutability = ImmutableContainer // Lift lower bound when possible - case UBP(_: FinalField) ⇒ // no information about field mutability + case UBP(_: FinalField) => // no information about field mutability - } + } - if (someEPS.isRefinable) { - val entity = if (someEPS.pk == ClassImmutability.key) SuperClassKey else someEPS.e - dependees += (entity → someEPS) - } + if (someEPS.isRefinable) { + val entity = if (someEPS.pk == ClassImmutability.key) SuperClassKey else someEPS.e + dependees += (entity -> someEPS) + } - /*[DEBUG] + /*[DEBUG] assert( oldDependees != dependees, s"dependees are not correctly updated $e($p)\n:old=$oldDependees\nnew=$dependees" ) - */ + */ - // Lift lower bound once no dependencies other than field type mutabilities are left - if (minLocalImmutability != ImmutableContainer && - dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) - minLocalImmutability = ImmutableContainer + // Lift lower bound once no dependencies other than field type mutabilities are left + if (minLocalImmutability != ImmutableContainer && + dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) + minLocalImmutability = ImmutableContainer - if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { - /*[DEBUG] + if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { + /*[DEBUG] assert( maxLocalImmutability == ConditionallyImmutableObject || maxLocalImmutability == ImmutableObject @@ -377,110 +384,115 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { s"maxLocalImmutability=$maxLocalImmutability - "+ s"(old dependees: ${oldDependees.mkString(",")}" ) - */ + */ - Result(t, maxLocalImmutability) + Result(t, maxLocalImmutability) - } else { - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - } - } + } else { + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + } + } - //[DEBUG] assert(initialImmutability.isRefinable) - val result = - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - if (lazyComputation) - result - else { - val isFinal = dependees.isEmpty - createIncrementalResult( - t, EPS(t, minLocalImmutability, maxLocalImmutability), isFinal, result - ) - } + //[DEBUG] assert(initialImmutability.isRefinable) + val result = + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + if (lazyComputation) + result + else { + val isFinal = dependees.isEmpty + createIncrementalResult( + t, + EPS(t, minLocalImmutability, maxLocalImmutability), + isFinal, + result + ) } + } } trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability) - - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability, TypeImmutability, FieldMutability) - - 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 immutable. - set(ObjectType.Object, ImmutableObject) - - // 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)) - - // 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, MutableObjectDueToUnknownSupertypes) - } - } + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability, TypeImmutability, FieldMutability) + + 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 immutable. + set(ObjectType.Object, ImmutableObject) + + // 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)) + + // 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, 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, 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 = {} + // 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, 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 = {} } /** @@ -492,22 +504,22 @@ object EagerClassImmutabilityAnalysis extends ClassImmutabilityAnalysisScheduler 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 ClassImmutabilityAnalysis(p) - ps.scheduleEagerComputationsForEntities(cfs)( - analysis.determineClassImmutability( - superClassType = null, - FinalEP(ObjectType.Object, ImmutableObject), - superClassMutabilityIsFinal = true, - lazyComputation = false - ) - ) - analysis - } + 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 ClassImmutabilityAnalysis(p) + ps.scheduleEagerComputationsForEntities(cfs)( + analysis.determineClassImmutability( + superClassType = null, + FinalEP(ObjectType.Object, ImmutableObject), + superClassMutabilityIsFinal = true, + lazyComputation = false + ) + ) + analysis + } } /** @@ -519,17 +531,18 @@ object LazyClassImmutabilityAnalysis extends ClassImmutabilityAnalysisScheduler with FPCFLazyAnalysisScheduler { - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - - override def register( - p: SomeProject, - ps: PropertyStore, - unused: InitializationData - ): FPCFAnalysis = { - val analysis = new ClassImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - ClassImmutability.key, analysis.doDetermineClassImmutability - ) - analysis - } + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + override def register( + p: SomeProject, + ps: PropertyStore, + unused: InitializationData + ): FPCFAnalysis = { + val analysis = new ClassImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + ClassImmutability.key, + analysis.doDetermineClassImmutability + ) + analysis + } } 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/TypeImmutabilityAnalysis.scala index 499e0586aa..bcc0458eca 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/TypeImmutabilityAnalysis.scala @@ -39,196 +39,194 @@ import org.opalj.br.fpcf.properties.TypeImmutability * * @author Michael Eichberg */ -class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnalysis { - - def doDetermineTypeMutability( - typeExtensibility: ObjectType ⇒ Answer - )( - e: Entity - ): ProperPropertyComputationResult = e match { - case t: ObjectType ⇒ step1(typeExtensibility)(t) - case _ ⇒ throw new IllegalArgumentException(s"$e is not an ObjectType") +class TypeImmutabilityAnalysis(final val project: SomeProject) extends FPCFAnalysis { + + def doDetermineTypeMutability( + 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 = { + typeExtensibility(t) match { + case Yes | Unknown => Result(t, MutableType) + case No => step2(t) } - - /** - * @param t An object type which is not `java.lang.Object`. - */ - def step1( - typeExtensibility: ObjectType ⇒ Answer - )( - t: ObjectType - ): ProperPropertyComputationResult = { - typeExtensibility(t) 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, Seq(eps), c) + } } - } - - 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, Seq(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, Seq(eps), c) - case epk ⇒ - InterimResult(t, MutableType, ImmutableType, Seq(epk), c) - } - } else { - var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] - var joinedImmutability: TypeImmutability = ImmutableType // this may become "Mutable..." - var maxImmutability: TypeImmutability = ImmutableType - - ps(t, ClassImmutability.key) match { - case FinalP(ImmutableObject) ⇒ - - case FinalP(_: MutableObject) ⇒ - return Result(t, MutableType); - - case FinalP(ImmutableContainer) ⇒ - joinedImmutability = ImmutableContainerType - maxImmutability = ImmutableContainerType - - 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(ImmutableType) ⇒ - - case UBP(MutableType) ⇒ - return Result(t, MutableType); - - case FinalP(ImmutableContainerType) ⇒ - joinedImmutability = joinedImmutability.meet(ImmutableContainerType) - maxImmutability = ImmutableContainerType - - case eps @ InterimLUBP(subtypeLB, subtypeUB) ⇒ - joinedImmutability = joinedImmutability.meet(subtypeLB) - maxImmutability = maxImmutability.meet(subtypeUB) - dependencies += ((subtype, eps)) - - case epk ⇒ - joinedImmutability = MutableType - dependencies += ((subtype, epk)) - - } - } + } + + 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, Seq(eps), c) + case epk => + InterimResult(t, MutableType, ImmutableType, Seq(epk), c) + } + } else { + var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] + var joinedImmutability: TypeImmutability = ImmutableType // this may become "Mutable..." + var maxImmutability: TypeImmutability = ImmutableType + + ps(t, ClassImmutability.key) match { + case FinalP(ImmutableObject) => + case FinalP(_: MutableObject) => + return Result(t, MutableType); + + case FinalP(ImmutableContainer) => + joinedImmutability = ImmutableContainerType + maxImmutability = ImmutableContainerType + + 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(ImmutableType) => + case UBP(MutableType) => + return Result(t, MutableType); + + case FinalP(ImmutableContainerType) => + joinedImmutability = joinedImmutability.meet(ImmutableContainerType) + maxImmutability = ImmutableContainerType + + 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 an ImmutableContainer, we are at most + // ImmutableContainer, even if all other subtype may even be 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 if (joinedImmutability == maxImmutability) { - // E.g., as soon as one subtype is an ImmutableContainer, we are at most - // ImmutableContainer, even if all other subtype may even be immutable! - Result(t, joinedImmutability) + Result(t, maxImmutability) } 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 == ImmutableContainerType) - Result(t, maxImmutability) - } else { - InterimResult( - t, joinedImmutability, maxImmutability, - dependencies.values, c - ) - } - } - } - - (eps: @unchecked) match { - case FinalEP(e, ImmutableType | ImmutableObject) ⇒ - dependencies = dependencies - e - nextResult() - - case UBP(MutableType | _: MutableObject) ⇒ - Result(t, MutableType) - - case FinalEP(e, ImmutableContainerType | ImmutableContainer) ⇒ - maxImmutability = ImmutableContainerType - 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() - } + 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 } - - InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) + } + if (joinedImmutability == maxImmutability) { + assert(maxImmutability == ImmutableContainerType) + Result(t, maxImmutability) + } else { + InterimResult( + t, + joinedImmutability, + maxImmutability, + dependencies.values, + c + ) + } } + } + + (eps: @unchecked) match { + case FinalEP(e, ImmutableType | ImmutableObject) => + dependencies = dependencies - e + nextResult() + + case UBP(MutableType | _: MutableObject) => + Result(t, MutableType) + + case FinalEP(e, ImmutableContainerType | ImmutableContainer) => + maxImmutability = ImmutableContainerType + 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, c) + } } + } } trait TypeImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability) - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability, TypeImmutability) + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability, TypeImmutability) } @@ -241,41 +239,42 @@ object EagerTypeImmutabilityAnalysis extends TypeImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + 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 TypeImmutabilityAnalysis(project) - val allClassFilesIterator = project.allClassFiles.iterator - val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) - - ps.scheduleEagerComputationsForEntities(types) { - analysis.step1(typeExtensibility) - } + override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val typeExtensibility = project.get(TypeExtensibilityKey) + val analysis = new TypeImmutabilityAnalysis(project) + val allClassFilesIterator = project.allClassFiles.iterator + val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) - analysis + ps.scheduleEagerComputationsForEntities(types) { + analysis.step1(typeExtensibility) } + analysis + } + } object LazyTypeImmutabilityAnalysis extends TypeImmutabilityAnalysisScheduler 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 TypeImmutabilityAnalysis(p) - val analysisRunner: ProperPropertyComputation[Entity] = - analysis.doDetermineTypeMutability(typeExtensibility) - ps.registerLazyPropertyComputation(TypeImmutability.key, analysisRunner) - analysis - - } + 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 TypeImmutabilityAnalysis(p) + val analysisRunner: ProperPropertyComputation[Entity] = + analysis.doDetermineTypeMutability(typeExtensibility) + ps.registerLazyPropertyComputation(TypeImmutability.key, analysisRunner) + 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/L2FieldMutabilityAnalysis.scala index 7c54548b27..571fb9bb6f 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/L2FieldMutabilityAnalysis.scala @@ -14,20 +14,13 @@ 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 @@ -35,8 +28,6 @@ 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 @@ -95,182 +86,200 @@ import org.opalj.tac.fpcf.properties.TACAI */ class L2FieldMutabilityAnalysis 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 - ) { - def hasDependees: Boolean = { - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || fieldMutabilityDependees.nonEmpty || - escapeDependees.nonEmpty || tacDependees.nonEmpty - } - - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - fieldMutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) - } + 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 + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || fieldMutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty } - 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) - - def doDetermineFieldMutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field ⇒ determineFieldMutability(field) - case _ ⇒ - val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + fieldMutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) } - - /** - * Analyzes the mutability of private non-final fields. - * - * 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 determineFieldMutability( - field: Field - ): ProperPropertyComputationResult = { - 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 (field.isFinal) - return createResult(); - - state.fieldMutability = EffectivelyFinalField - - val thisType = field.classFile.thisType - - if (field.isPublic) - return Result(field, NonFinalFieldByLackOfInformation) - - // 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) { - if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, NonFinalFieldByLackOfInformation); - } - project.classesPerPackage(thisType.packageName) - } else { - Set(field.classFile) - } - - val classesHavingAccess: Iterator[ClassFile] = - if (field.isProtected) { - if (typeExtensibility(thisType).isYesOrUnknown) { - return Result(field, NonFinalFieldByLackOfInformation); - } - 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))) - return Result(field, NonFinalFieldByLackOfInformation); - - // 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 - // of the class after the reference to the class and (an indirect) reference to the - // field has become available. Consider the following example: - // class X implements Y{ - // - // private Object o; - // - // public Object getO() { return o; } - // - // private static X instance; - // static { - // instance = new X(); - // Z.register(instance); - // // when we reach this point o is now (via getO) publically accessible and - // // X is properly initialized! - // o = new Object(); // o is mutated... - // } - // } - - for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode ← getTACAI(method, pcs) - } { - if (methodUpdatesField(method, taCode, pcs)) - return Result(field, NonFinalFieldByAnalysis); + } + + 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) + + def doDetermineFieldMutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field => determineFieldMutability(field) + case _ => + val m = entity.getClass.getSimpleName + " is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + + /** + * Analyzes the mutability of private non-final fields. + * + * 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 determineFieldMutability( + field: Field + ): ProperPropertyComputationResult = { + 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 (field.isFinal) + return createResult(); + + state.fieldMutability = EffectivelyFinalField + + val thisType = field.classFile.thisType + + if (field.isPublic) + return Result(field, NonFinalFieldByLackOfInformation) + + // 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) { + if (!closedPackages.isClosed(thisType.packageName)) { + return Result(field, NonFinalFieldByLackOfInformation); } - - if (state.lazyInitInvocation.isDefined) { - val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - handleCalls(calleesEOP) + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } + + val classesHavingAccess: Iterator[ClassFile] = + if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown) { + return Result(field, NonFinalFieldByLackOfInformation); } + 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))) + return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } - createResult() + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] => + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk => + state.tacDependees += method -> ((epk, pcs)) + None + } } - def handleCalls( - calleesEOP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: State - ): Boolean = { - calleesEOP match { - case FinalP(callees) ⇒ - state.calleesDependee = None - handleCallees(callees) - case InterimUBP(callees) ⇒ - state.calleesDependee = Some(calleesEOP) - handleCallees(callees) - case _ ⇒ - state.calleesDependee = Some(calleesEOP) - false - } + for { + (method, pcs) <- fieldAccessInformation.writeAccesses(field) + taCode <- getTACAI(method, pcs) + } { + if (methodUpdatesField(method, taCode, pcs)) + return Result(field, NonFinalFieldByAnalysis); } - def handleCallees(callees: Callees)(implicit state: State): Boolean = { - val pc = state.lazyInitInvocation.get._2 - if (callees.isIncompleteCallSite(pc)) { - state.fieldMutability = NonFinalFieldByAnalysis - true - } else { - val targets = callees.callees(pc).toTraversable - if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { - state.fieldMutability = NonFinalFieldByAnalysis - true - } else false - } + if (state.lazyInitInvocation.isDefined) { + val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + handleCalls(calleesEOP) } - /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. - */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field - * The code below can be used to identify such value, but is not yet adapted to using the - * TACAI property */ - /* + createResult() + } + + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: State + ): Boolean = { + calleesEOP match { + case FinalP(callees) => + state.calleesDependee = None + handleCallees(callees) + case InterimUBP(callees) => + state.calleesDependee = Some(calleesEOP) + handleCallees(callees) + case _ => + state.calleesDependee = Some(calleesEOP) + false + } + } + + def handleCallees(callees: Callees)(implicit state: State): Boolean = { + val pc = state.lazyInitInvocation.get._2 + if (callees.isIncompleteCallSite(pc)) { + state.fieldMutability = NonFinalFieldByAnalysis + true + } else { + val targets = callees.callees(pc).toTraversable + if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { + state.fieldMutability = NonFinalFieldByAnalysis + true + } else false + } + } + + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + * The code below can be used to identify such value, but is not yet adapted to using the + * TACAI property */ + /* var constantVal: Option[Any] = None var allInitializeConstant = true @@ -314,6 +323,7 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } } + for (constructor ← constructors) { // TODO iterate all statements val NonVirtualMethodCall(_, declClass, _, name, _, rcvr, _) = stmt @@ -328,754 +338,752 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } constantVal */ + } + + /** + * 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.fieldMutability ne NonFinalFieldByAnalysis)) + InterimResult( + state.field, + NonFinalFieldByAnalysis, + state.fieldMutability, + state.dependees, + c + ) + else + Result(state.field, state.fieldMutability) + } + + /** + * 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 = { + val isNotFinal = eps.pk match { + case EscapeProperty.key => + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + methodUpdatesField(method, newEP.ub.tac.get, pcs) + case Callees.key => + handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key => + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key => + 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) + !isFinalField(newEP) } - /** - * 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.fieldMutability ne NonFinalFieldByAnalysis)) - InterimResult( - state.field, - NonFinalFieldByAnalysis, - state.fieldMutability, - state.dependees, - c - ) - else - Result(state.field, state.fieldMutability) - } - - /** - * 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 = { - val isNotFinal = eps.pk match { - case EscapeProperty.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - 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)) - methodUpdatesField(method, newEP.ub.tac.get, pcs) - case Callees.key ⇒ - handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key ⇒ - isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key ⇒ - 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) - !isFinalField(newEP) - } - - if (isNotFinal) - Result(state.field, NonFinalFieldByAnalysis) - else - createResult() - } - - /** - * Checks whether a field write may be a lazy initialization. - * - * We only consider simple cases of lazy initialization where the single field write is guarded - * so it is executed only if the field still has its default value and where the written value - * is guaranteed to be the same even if the write is executed multiple times (may happen because - * of the initializing method being executed more than once on concurrent threads). - * - * Also, all field reads must be used only for a guard or their uses have to be guarded - * themselves. - */ - def isLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - )(implicit state: State): Boolean = { - val write = code(writeIndex).asFieldWriteAccessStmt - - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - // Only handle lazy initialization of ints and floats as they are guaranteed to be - // written atomically - return false; - } - - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return false; // Reads outside the (single) lazy initialization method - } - - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val (guardIndex, guardedIndex, readIndex) = - findGuard(writeIndex, defaultValue, code, cfg) match { - case Some((guard, guarded, read)) ⇒ (guard, guarded, read) - case None ⇒ return false; - } - - // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) - return false; - - // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - return false; - - // Field reads (except for the guard) may only be executed if the field's value is not the - // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - return false; - - true + if (isNotFinal) + Result(state.field, NonFinalFieldByAnalysis) + else + createResult() + } + + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + )(implicit state: State): Boolean = { + val write = code(writeIndex).asFieldWriteAccessStmt + + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + return false; } - def checkWrites( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val definitions = write.value.asVar.definedBy - - val isDeterministic = - if (definitions.size == 1) { - // The value written must be computed deterministically - checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) - } else { - // More than one definition site for the value might lead to differences between - // invocations, but not if this method has no parameters and is deterministic - // (in this case, the definition reaching the write will always be the same) - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - - // The field write must be guarded correctly - isDeterministic && - checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - } - - /** - * Checks if the field write for a lazy initialization is immediately followed by a return of - * the written value (possibly loaded by another field read). - */ - def checkImmediateReturn( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 - - var load = -1 - - while (index < code.length) { - val stmt = code(index) - stmt.astID match { - case Assignment.ASTID ⇒ - if (isReadOfCurrentField(stmt.asAssignment.expr)) - load = index - else - return false; // No field read or a field read of a different field - case ReturnValue.ASTID ⇒ - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) - return true; // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) - return true; // return of field value loaded by field read - else - return false; // return of different value - case _ ⇒ return false; // neither a field read nor a return - } - index += 1 - } - false + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs => (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method } - def lazyInitializerIsDeterministic( - method: Method, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) => (guard, guarded, read) + case None => return false; + } + + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) + return false; + + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + + true + } + + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val definitions = write.value.asVar.definedBy + + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + // The field write must be guarded correctly + isDeterministic && + checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID => + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID => + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ => return false; // neither a field read nor a return + } + index += 1 } - - /** - * 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 - for (pc ← pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID ⇒ - if (method.isInitializer) { - if (field.isStatic) { - if (method.isConstructor) - return true; - } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - 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; - - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - // A field written outside an initializer must be lazily - // initialized or it is non-final - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - )) - return true; - - state.fieldMutability = LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - // 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. - return true; - } - } - case _ ⇒ throw new RuntimeException("unexpected field access"); - } + false + } + + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + /** + * 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 + for (pc <- pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID => + if (method.isInitializer) { + if (field.isStatic) { + if (method.isConstructor) + return true; } else { - // nothing to do as the put field is dead + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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; + + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + // A field written outside an initializer must be lazily + // initialized or it is non-final + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )) + return true; + + state.fieldMutability = LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ => throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead } - false + } } - - /** - * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method → ((eps, pcs)) - eps.ub.tac - case epk ⇒ - state.tacDependees += method → ((epk, pcs)) - None - } + false + } + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] => + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk => + state.tacDependees += method -> ((epk, pcs)) + None } - - /** - * 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 // Must be locally created - else { - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - handleEscapeProperty(escape) - } - } + } + + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) } + } } - - /** - * 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 = 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 + } + + /** + * 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 = 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 + } + + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt => + stmt.astID == CaughtException.ASTID + } flatMap { exception => + exception.asCaughtException.origins.map { origin: Int => + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator } - /** - * Checks if the field write is only executed if the field's value was still the default value. - * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization - * and the field write. - */ - def checkWriteIsGuarded( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val startBB = cfg.bb(writeIndex).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt ⇒ - stmt.astID == CaughtException.ASTID - } flatMap { exception ⇒ - exception.asCaughtException.origins.map { origin: Int ⇒ - if (isImmediateVMException(origin)) - pcOfImmediateVMException(origin) - else - pcOfMethodExternalException(origin) - }.iterator - } + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail + val startPC = curBB.startPC + val endPC = curBB.endPC - val startPC = curBB.startPC - val endPC = curBB.endPC + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement - if (startPC == 0 || startPC == guardedIndex) - return false; // Reached method start or wrong branch of guarding if-Statement + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors + true + } + + /** + * 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]] + )(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(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) => + isFinalField(propertyStore(field, FieldMutability.key)) + case _ => // Unknown field + false + }) } + } - true + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) } - /** - * 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]] - )(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(defSites.head).asAssignment.expr.isConst) true - else { - val expr = code(index).asAssignment.expr - expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - isFinalField(propertyStore(field, FieldMutability.key)) - case _ ⇒ // Unknown field - false - }) - } - } + val value = origin.expr - defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID => + value.asFieldRead.resolveField(p) match { + case Some(field) => + isFinalField(propertyStore(field, FieldMutability.key)) + case _ => // Unknown field + false } - - val value = origin.expr - - val isNonConstDeterministic = value.astID match { - case GetStatic.ASTID | GetField.ASTID ⇒ - value.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - isFinalField(propertyStore(field, FieldMutability.key)) - case _ ⇒ // Unknown field - false - } - 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.exists(!isConstant(_))) { - false - } else { - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true - } - 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, code) + 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.exists(!isConstant(_))) { + false + } else { + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true } - - value.isConst || isNonConstDeterministic + 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, code) } - /** - * Checks that all non-dead field reads that are not used for the guarding if-Statement of a - * lazy initialization are only executed if the field did not have its default value or after - * the (single) field write. - */ - def checkReads( - reads: Seq[(Method, PCs)], - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - ): Boolean = { - // There is only a single method with reads aside from initializers (checked by - // isLazilyInitialized), so we have to check only reads from that one method. - reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ - val index = pcToIndex(readPC) - index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) - } + value.isConst || isNonConstDeterministic + } + + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC => + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) } - - /** - * Checks that a field read is only executed if the field did not have its default value or - * after the (single) field write. - */ - def checkRead( - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Boolean = { - val startBB = cfg.bb(readIndex).asBasicBlock - val writeBB = cfg.bb(writeIndex) - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - - if (startPC == 0) - return false; // Reached the start of the method but not the guard or field write - - if ((curBB eq writeBB) && writeIndex > readIndex) - return false; // In the basic block of the write, but before the write - - if (startPC != guardedIndex && // Did not reach the guard - (curBB ne writeBB) /* Not the basic block of the write */ ) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - true + } + + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } } - /** - * Gets all predecessor BasicBlocks of a CFGNode. - */ - def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode ⇒ - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) - } - result.toList + true + } + + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode => + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) } - - /** - * 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. - */ - def findGuard( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Option[(Int, Int, Int)] = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int)] = None - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID ⇒ - val ifStmt = cfStmt.asIf - ifStmt.condition match { - case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1)) - } - - case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None; - } else { - result = Some((endPC, ifStmt.targetStmt)) - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard - } - - None + result.toList + } + + /** + * 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. + */ + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID => + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) => + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) => + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ => + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ => + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } } - /** - * 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])(implicit state: State): Boolean = { - val field = expr.astID match { - case GetField.ASTID ⇒ - val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) None - else expr.asGetField.resolveField(project) - case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) - case _ ⇒ None - } - field.contains(state.field) + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use => + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard } - /** - * 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]] - )(implicit state: State): Boolean = { - - /** - * Checks if an expression is an IntConst or FloatConst with the corresponding default value. - */ - 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 || - expr.isFloatConst && defaultValue == expr.asFloatConst.value - } - } - - /** - * Checks whether the non-constant expression of the if-Statement is a read of the current - * field. - */ - def isGuardInternal(expr: V): Boolean = { - expr.definedBy forall { index ⇒ - if (index < 0) false // If the value is from a parameter, this can not be the guard - else isReadOfCurrentField(code(index).asAssignment.expr) - } - } - - if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { - isGuardInternal(ifStmt.rightExpr.asVar) - } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { - isGuardInternal(ifStmt.leftExpr.asVar) - } else false + None + } + + /** + * 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])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID => + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID => expr.asGetStatic.resolveField(project) + case _ => None } + field.contains(state.field) + } + + /** + * 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]] + )(implicit state: State): Boolean = { /** - * Checks if the field is prematurely read, i.e. read before it is initialized in the - * constructor, using the corresponding property. + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. */ - def isPrematurelyRead( - eop: EOptionP[Field, FieldPrematurelyRead] - )(implicit state: State): Boolean = - eop match { - case LBP(NotPrematurelyReadField) ⇒ - state.prematurelyReadDependee = None - false - case UBP(PrematurelyReadField) ⇒ true - case eps ⇒ - state.prematurelyReadDependee = Some(eps) - false - } + 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } /** - * Checkes 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. + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. */ - 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 isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index => + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } } - /** - * Checkes if 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 isFinalField( - eop: EOptionP[Field, FieldMutability] - )(implicit state: State): Boolean = eop match { - case LBP(_: FinalField) ⇒ - true - case UBP(_: NonFinalField) ⇒ false - case _ ⇒ - state.fieldMutabilityDependees += eop - true + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else false + } + + /** + * Checks if 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) => true + case eps => + state.prematurelyReadDependee = Some(eps) + false } + + /** + * Checkes 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. + */ + 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 + } + + /** + * Checkes if 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 isFinalField( + eop: EOptionP[Field, FieldMutability] + )(implicit state: State): Boolean = eop match { + case LBP(_: FinalField) => + true + case UBP(_: NonFinalField) => false + case _ => + state.fieldMutabilityDependees += eop + true + } } trait L2FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(FieldMutability), - PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(FieldMutability), + PropertyBounds.ub(EscapeProperty) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability) } @@ -1086,16 +1094,16 @@ object EagerL2FieldMutabilityAnalysis extends L2FieldMutabilityAnalysisScheduler 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) - analysis - } + 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) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -1105,17 +1113,18 @@ object LazyL2FieldMutabilityAnalysis extends L2FieldMutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L2FieldMutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - FieldMutability.key, analysis.determineFieldMutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L2FieldMutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldMutability.key, + analysis.determineFieldMutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } From 4f03db94db890383d0c70fd5f33b69636ca783b3 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 12 Nov 2019 16:27:09 +0100 Subject: [PATCH 013/327] Removing unnecessary trait in ReferenceImmutability Lattice --- .../properties/ReferenceImmutability.scala | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala index 54e86d106d..72526610f0 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -7,7 +7,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - type Self = ReferenceImmutability + type Self = ReferenceImmutability } @@ -28,25 +28,23 @@ sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaIn sealed trait ReferenceImmutability extends Property with ReferenceImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key + final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key } object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { - final val PropertyKeyName = "opalj.ReferenceImmutability" + final val PropertyKeyName = "opalj.ReferenceImmutability" - final val key: PropertyKey[ReferenceImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableReference - ) - } + final val key: PropertyKey[ReferenceImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableReference + ) + } } -sealed trait RefImm extends ReferenceImmutability +case object MutableReference extends ReferenceImmutability -case object MutableReference extends RefImm +case object LazyInitializedReference extends ReferenceImmutability -case object LazyInitializedReference extends RefImm - -case object ImmutableReference extends RefImm +case object ImmutableReference extends ReferenceImmutability From ef53a557b1c5b43b8e192f3c7e54934fad10c875 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 14 Nov 2019 00:48:01 +0100 Subject: [PATCH 014/327] first approach for the type immutability analysis with the new lattice nomenclature including demo and tests --- .../TypeImmutabilityAnalysisDemo.scala | 69 +++++ .../type_immutability/FinalEmptyClass.java | 10 + .../TrivialMutableClass.java | 18 ++ .../WithMutableAndImmutableFieldType.java | 12 + .../DeepImmutableTypeAnnotation.java | 24 ++ .../MutableTypeAnnotation.java | 30 ++ .../ShallowImmutableTypeAnnotation.java | 24 ++ .../opalj/fpcf/TypeImmutabilityTests.scala | 56 ++++ .../NewTypeImmutabilityMatcher.scala | 57 ++++ .../properties/TypeImmutability_new.scala | 112 +++++++ .../LxTypeImmutabilityAnalysis_new.scala | 288 ++++++++++++++++++ 11 files changed, 700 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/FinalEmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/TrivialMutableClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala 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..b78a2c409a --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo.scala @@ -0,0 +1,69 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0PurityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.MutableType_new +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new + +/** + * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result + * + * @author Tobias Peter Roth + */ +object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" + + override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0PurityAnalysis, + LazyL1FieldMutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new + ) + "Mutable Type: "+propertyStore + .finalEntities(MutableType_new) + .toList + .toString()+"\n"+ + "Shallow Immutable Type: "+propertyStore + .finalEntities(ShallowImmutableType) + .toList + .toString()+"\n"+ + "Deep Immutable Type: "+propertyStore + .finalEntities(DeepImmutableType) + .toList + .toString()+"\n" + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/FinalEmptyClass.java new file mode 100644 index 0000000000..37f683f188 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/FinalEmptyClass.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.type_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableTypeAnnotation("It has no fields and is final") +@DeepImmutableClassAnnotation("It has no fields and is final") +public final class FinalEmptyClass { + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/TrivialMutableClass.java new file mode 100644 index 0000000000..554b76d5c4 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/TrivialMutableClass.java @@ -0,0 +1,18 @@ +package org.opalj.fpcf.fixtures.type_immutability; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("is has mutable fields") +@MutableClassAnnotation("It has Mutable Fields") +public class TrivialMutableClass { + @MutableReferenceAnnotation("public") + @MutableFieldAnnotation("mutable reference") + public int n = 0; + + @MutableReferenceAnnotation("public") + @MutableFieldAnnotation("mutable reference") + public String name = "name"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java new file mode 100644 index 0000000000..db687c72e3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java @@ -0,0 +1,12 @@ +package org.opalj.fpcf.fixtures.type_immutability; + +import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; + +@ShallowImmutableTypeAnnotation("has shallow mutable fields") +public class WithMutableAndImmutableFieldType { + + private FinalEmptyClass fec = new FinalEmptyClass(); + + private TrivialMutableClass tmc = new TrivialMutableClass(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java new file mode 100644 index 0000000000..795a018d57 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.type_immutability; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated type deep immutable. + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "TypeImmutability_new", validator = DeepImmutableTypeMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DeepImmutableTypeAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java new file mode 100644 index 0000000000..fa643933cf --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java @@ -0,0 +1,30 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.type_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.TypeImmutabilityAnalysis_new; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated type mutable. + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "TypeImmutability_new", validator = NewMutableTypeMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface MutableTypeAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default { + TypeImmutabilityAnalysis_new.class + }; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java new file mode 100644 index 0000000000..bd9ac4477c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.type_immutability; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated type shallow immutable. + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "TypeImmutability_new", validator = ShallowImmutableTypeMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ShallowImmutableTypeAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala new file mode 100644 index 0000000000..74a309ce90 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala @@ -0,0 +1,56 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis + +/** + * Tests the Type Immutability Analysis with the new lattice + * + * @author Tobias Peter Roth + */ +class TypeImmutabilityTests extends PropertiesTest { + + 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("ClassImmutability_new")) + } + + describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { + println(1) + val as = executeAnalyses( + Set( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala new file mode 100644 index 0000000000..0492c08aa2 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala @@ -0,0 +1,57 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.type_immutability + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.MutableType_new +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.br.fpcf.properties.TypeImmutability_new +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher + +/** + * @author Tobias Peter Roth + */ +class NewTypeImmutabilityMatcher(val property: TypeImmutability_new) + 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(MutableType_new) + +class ShallowImmutableTypeMatcher extends NewTypeImmutabilityMatcher(ShallowImmutableType) + +class DeepImmutableTypeMatcher extends NewTypeImmutabilityMatcher(DeepImmutableType) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala new file mode 100644 index 0000000000..fa1450bc81 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -0,0 +1,112 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.properties + +import org.opalj.fpcf.OrderedProperty +import org.opalj.fpcf.Entity +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +sealed trait TypeImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { + + final type Self = TypeImmutability_new +} + +/** + * 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]]. + * + * @author Tobias Peter Roth + */ +sealed trait TypeImmutability_new + extends OrderedProperty + with TypeImmutabilityPropertyMetaInformation_new { + + /** + * Returns the key used by all `TypeImmutability_new` properties. + */ + final def key = TypeImmutability_new.key + + def isDeepImmutable: Boolean + def isShallowImmutable: Boolean + + /** `true` if the immutability is unknown or if the type is mutable.*/ + def isMutable: Boolean + + def meet(other: TypeImmutability_new): TypeImmutability_new +} + +/** + * Common constants use by all [[TypeImmutability_new]] properties associated with methods. + */ +object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new { + + /** + * The key associated with every [[TypeImmutability_new]] property. + */ + final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( + "org.opalj.TypeImmutability_new", + MutableType_new + ) +} + +/** + * 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 DeepImmutableType extends TypeImmutability_new { + + override def isDeepImmutable: Boolean = true + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = false + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (this == that) + this + else + that + +} + +case object ShallowImmutableType extends TypeImmutability_new { + + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = true + override def isMutable: Boolean = false + + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (that == MutableType_new) + 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_new extends TypeImmutability_new { + + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = true + + def meet(other: TypeImmutability_new): this.type = this + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other != MutableType_new) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala new file mode 100644 index 0000000000..12d5ce8372 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -0,0 +1,288 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses + +import org.opalj.Answer +import org.opalj.No +import org.opalj.Unknown +import org.opalj.Yes +import org.opalj.br.ObjectType +import org.opalj.fpcf.ELUBP +import org.opalj.fpcf.Entity +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.EPS +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 +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_new +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.MutableClass +import org.opalj.br.fpcf.properties.MutableType_new +import org.opalj.br.fpcf.properties.ShallowImmutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.br.fpcf.properties.TypeImmutability_new + +/** + * Determines the mutability 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 + */ +class TypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPCFAnalysis { + + def doDetermineTypeMutability_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 = { + typeExtensibility(t) match { + case Yes | Unknown => Result(t, MutableType_new) // 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_new, ub: ClassImmutability_new) => + val thisLB = lb.correspondingTypeImmutability + val thisUB = ub.correspondingTypeImmutability + if (eps.isFinal) + Result(t, thisUB) + else + InterimResult(t, thisLB, thisUB, Seq(eps), c) + } + } + } + + ps(t, ClassImmutability_new.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, Seq(eps), c) + case epk => InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) + //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) + } + } else { + var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] + var joinedImmutability + : TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." + var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType + + ps(t, ClassImmutability_new.key) match { + case FinalP(DeepImmutableClass) => //ImmutableObject) => + case FinalP(MutableClass) => //(_: MutableObject) => + return Result(t, MutableType_new); //MutableType); + + case FinalP(ShallowImmutableClass) => //ImmutableContainer) => + joinedImmutability = ShallowImmutableType // ImmutableContainerType + maxImmutability = ShallowImmutableType //ImmutableContainerType + + case eps @ InterimLUBP(lb, ub) => + joinedImmutability = lb.correspondingTypeImmutability + maxImmutability = ub.correspondingTypeImmutability + dependencies += (t -> eps) + + case eOptP => + joinedImmutability = MutableType_new //MutableType + dependencies += (t -> eOptP) + } + + directSubtypes foreach { subtype => + ps(subtype, TypeImmutability_new.key) match { + case FinalP(DeepImmutableType) => //ImmutableType) => + case UBP(MutableType_new) => //MutableType) => + return Result(t, MutableType_new); //MutableType); + + case FinalP(ShallowImmutableType) => //ImmutableContainerType) => + joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) + maxImmutability = ShallowImmutableType //ImmutableContainerType + + case eps @ InterimLUBP(subtypeLB, subtypeUB) => + joinedImmutability = joinedImmutability.meet(subtypeLB) + maxImmutability = maxImmutability.meet(subtypeUB) + dependencies += ((subtype, eps)) + + case epk => + joinedImmutability = MutableType_new //MutableType + dependencies += ((subtype, epk)) + + } + } + + if (dependencies.isEmpty) { + Result(t, maxImmutability) + } else if (joinedImmutability == maxImmutability) { + // E.g., as soon as one subtype is an ImmutableContainer, we are at most + // ImmutableContainer, even if all other subtype may even be 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_new => + joinedImmutability = joinedImmutability.meet(lb) + case lb: ClassImmutability_new => + joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) + } else { + joinedImmutability = MutableType_new //MutableType + continue = false + } + } + if (joinedImmutability == maxImmutability) { + assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) + Result(t, maxImmutability) + } else { + InterimResult( + t, + joinedImmutability, + maxImmutability, + dependencies.values, + c + ) + } + } + } + + (eps: @unchecked) match { + case FinalEP(e, DeepImmutableType | DeepImmutableClass) => //ImmutableType | ImmutableObject) => + dependencies = dependencies - e + nextResult() + + case UBP(x) + if (x == MutableType_new || x == MutableClass) => //MutableType | _: MutableObject) => + Result(t, MutableType_new) //MutableType) + + case FinalEP(e, x) + if (x == ShallowImmutableType || x == ShallowImmutableClass) => //ImmutableContainerType | ImmutableContainer) => + maxImmutability = ShallowImmutableType //ImmutableContainerType + dependencies = dependencies - e + nextResult() + + case eps @ InterimEUBP(e, subtypeP) => + dependencies = dependencies.updated(e, eps) + subtypeP match { + case subtypeP: TypeImmutability_new => + maxImmutability = maxImmutability.meet(subtypeP) + case subtypeP: ClassImmutability_new => + maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) + } + nextResult() + } + } + + InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) + } + } + } +} + +trait TypeImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) + +} + +/** + * Starter for the '''type immutability analysis'''. + * + * @author Michael Eichberg + */ +object EagerLxTypeImmutabilityAnalysis_new + extends TypeImmutabilityAnalysisScheduler_new + 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 TypeImmutabilityAnalysis_new(project) + val allClassFilesIterator = project.allClassFiles.iterator + val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) + + ps.scheduleEagerComputationsForEntities(types) { + analysis.step1(typeExtensibility) + } + + analysis + } + +} + +object LazyLxTypeImmutabilityAnalysis_new + extends TypeImmutabilityAnalysisScheduler_new + 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 TypeImmutabilityAnalysis_new(p) + val analysisRunner: ProperPropertyComputation[Entity] = + analysis.doDetermineTypeMutability_new(typeExtensibility) + ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) + analysis + } +} From 37588cf802c18b336e58bdd1181597d9dbb2e540 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Mon, 18 Nov 2019 14:21:32 +0100 Subject: [PATCH 015/327] Type Immutability Analysis in the other new immutability Analysis integrated. With first approach of dependent classes and fields. 1. approach: A dependent class is always seen as a shallow immutable type --- .../ClassImmutabilityAnalysisDemo.scala | 92 +- .../FieldImmutabilityAnalysisDemo.scala | 44 +- .../ReferenceImmutabilityAnalysisDemo.scala | 80 +- .../TypeImmutabilityAnalysisDemo.scala | 9 +- .../WithMutableAndImmutableFieldType.java | 5 +- .../DeepImmutableTypeAnnotation.java | 4 + .../MutableTypeAnnotation.java | 6 +- .../ShallowImmutableTypeAnnotation.java | 4 + .../opalj/fpcf/ClassImmutabilityTests.scala | 126 +-- .../opalj/fpcf/FieldImmutabilityTests.scala | 6 +- .../opalj/fpcf/TypeImmutabilityTests.scala | 61 +- .../properties/ClassImmutability_new.scala | 36 +- .../properties/ReferenceImmutability.scala | 18 +- .../properties/TypeImmutability_new.scala | 92 +- .../L0FieldImmutabilityAnalysis.scala | 376 ++++---- .../LxClassImmutabilityAnalysis_new.scala | 912 +++++++++--------- .../LxTypeImmutabilityAnalysis_new.scala | 419 ++++---- 17 files changed, 1194 insertions(+), 1096 deletions(-) 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 index 4fa1ac4f0c..04946824b9 100644 --- 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 @@ -8,16 +8,18 @@ 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.EagerL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +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.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis; /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -26,47 +28,49 @@ import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis */ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" - override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(project: Project[URL]): String = { + def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) + val analysesManager = project.get(FPCFAnalysesManagerKey) - val (propertyStore, _) = analysesManager.runAll( - EagerTypeImmutabilityAnalysis, - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerL0ReferenceImmutabilityAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - EagerLxClassImmutabilityAnalysis_new - ) - "Mutable Class: " + propertyStore - .finalEntities(MutableClass) - .toList - .toString() + "\n" + - "Dependent Immutable Class: " + propertyStore - .finalEntities(DependentImmutableClass) - .toList - .toString() + "\n" + - "Shallow Immutable Class: " + propertyStore - .finalEntities(ShallowImmutableClass) - .toList - .toString() + "\n" + - "Deep Immutable Class: " + propertyStore - .finalEntities(DeepImmutableClass) - .toList - .toString() + "\n" - } + val (propertyStore, _) = analysesManager.runAll( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new + ) + "Mutable Class: "+propertyStore + .finalEntities(MutableClass) + .toList + .toString()+"\n"+ + "Dependent Immutable Class: "+propertyStore + .finalEntities(DependentImmutableClass) + .toList + .toString()+"\n"+ + "Shallow Immutable Class: "+propertyStore + .finalEntities(ShallowImmutableClass) + .toList + .toString()+"\n"+ + "Deep Immutable Class: "+propertyStore + .finalEntities(DeepImmutableClass) + .toList + .toString()+"\n" + } } 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 index 8a1fd29738..4da81148ed 100644 --- 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 @@ -7,19 +7,18 @@ 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.EagerClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +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.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.TACAITransformer -import org.opalj.tac.fpcf.analyses.escape.EagerSimpleEscapeAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. @@ -46,15 +45,26 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val analysesManager = project.get(FPCFAnalysesManagerKey) val (propertyStore, _) = analysesManager.runAll( - EagerTypeImmutabilityAnalysis, - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerClassImmutabilityAnalysis, - EagerL0ReferenceImmutabilityAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerSimpleEscapeAnalysis, - TACAITransformer, - EagerL0FieldImmutabilityAnalysis + /** + * LazyTypeImmutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyClassImmutabilityAnalysis, + * LazyL0ReferenceImmutabilityAnalysis, + * LazyL0PurityAnalysis, + * LazyL1FieldMutabilityAnalysis, + * LazySimpleEscapeAnalysis, + * TACAITransformer, + * LazyLxTypeImmutabilityAnalysis_new, + * EagerL0FieldImmutabilityAnalysis* + */ + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new ); "Mutable Fields: " + propertyStore diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index eb2d7a4b80..ba34ef0ad8 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -7,13 +7,13 @@ 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.EagerL0PurityAnalysis -import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.analyses.LazyL0PurityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.TACAITransformer /** * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. @@ -22,42 +22,42 @@ import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis */ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - - val (propertyStore, _) = analysesManager.runAll( - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerL0ReferenceImmutabilityAnalysis - ); - - "Mutable References: " + propertyStore - .finalEntities(MutableReference) - .toList - .toString() + "\n" + - "Lazy Initialized Reference: " + propertyStore - .finalEntities(LazyInitializedReference) - .toList - .toString() + "\n" + - "Immutable References: " + propertyStore - .finalEntities(ImmutableReference) - .toList - .toString() - } + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL0PurityAnalysis, + TACAITransformer, + EagerL0ReferenceImmutabilityAnalysis + ); + + "Mutable References: "+propertyStore + .finalEntities(MutableReference) + .toList + .toString()+"\n"+ + "Lazy Initialized Reference: "+propertyStore + .finalEntities(LazyInitializedReference) + .toList + .toString()+"\n"+ + "Immutable References: "+propertyStore + .finalEntities(ImmutableReference) + .toList + .toString() + } } 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 index b78a2c409a..bffe4c39e1 100644 --- 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 @@ -14,10 +14,11 @@ import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.DeepImmutableType import org.opalj.br.fpcf.properties.MutableType_new import org.opalj.br.fpcf.properties.ShallowImmutableType +//import org.opalj.br.fpcf.properties.ShallowImmutableType import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -48,9 +49,9 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL0ReferenceImmutabilityAnalysis, LazyL0PurityAnalysis, - LazyL1FieldMutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, EagerLxTypeImmutabilityAnalysis_new ) "Mutable Type: "+propertyStore diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java index db687c72e3..d9ddb71e0f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java @@ -1,12 +1,11 @@ package org.opalj.fpcf.fixtures.type_immutability; -import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@ShallowImmutableTypeAnnotation("has shallow mutable fields") +@MutableTypeAnnotation("has shallow mutable fields") public class WithMutableAndImmutableFieldType { private FinalEmptyClass fec = new FinalEmptyClass(); - private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java index 795a018d57..3400216ea3 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java @@ -1,7 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.type_immutability; +import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.LxTypeImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -21,4 +23,6 @@ * A short reasoning of this property. */ String value();// default = "N/A"; + + Class[] analyses() default {LxTypeImmutabilityAnalysis_new.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java index fa643933cf..a31dc77bcb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.TypeImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.LxTypeImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -24,7 +24,5 @@ */ String value();// default = "N/A"; - Class[] analyses() default { - TypeImmutabilityAnalysis_new.class - }; + Class[] analyses() default {LxTypeImmutabilityAnalysis_new.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java index bd9ac4477c..6bea537699 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java @@ -1,7 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.type_immutability; +import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.LxTypeImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -21,4 +23,6 @@ * A short reasoning of this property. */ String value();// default = "N/A"; + + Class[] analyses() default {LxTypeImmutabilityAnalysis_new.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala index a16da84d99..eaad2963d4 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -7,12 +7,14 @@ 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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** @@ -20,65 +22,71 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class ClassImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) - } - p.get(RTACallGraphKey) + 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("ClassImmutability_new")) - * }* - */ - describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { - println(1) - val as = executeAnalyses( - Set( -/****/ - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - EagerLxClassImmutabilityAnalysis_new - ) - ) - println(2) - as.propertyStore.shutdown() - println(3) - validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) //Set("ClassImmutability_new")) - println(4) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ + /** + * describe("no analysis is scheduled") { + * val as = executeAnalyses(Set.empty) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) + * }* + */ + describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { + println(1) + val as = executeAnalyses( + Set( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new + ) + ) + + println(2) + as.propertyStore.shutdown() + println(3) + validateProperties( + as, + classFilesWithAnnotations(as.project).map(tp => (tp._1.thisType, tp._2, tp._3)), + Set("ClassImmutability_new") + ) + println(4) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ } 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 index 442fda7ab6..a5cbceb83e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -13,6 +13,8 @@ import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis @@ -44,7 +46,9 @@ class FieldImmutabilityTests extends PropertiesTest { LazyL2PurityAnalysis, LazyInterProceduralEscapeAnalysis, EagerL0FieldImmutabilityAnalysis, - LazyClassImmutabilityAnalysis + LazyClassImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new // LazySimpleEscapeAnalysis, // TACAITransformer ) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala index 74a309ce90..85c3fd18b2 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala @@ -23,34 +23,41 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class TypeImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ => - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + } + p.get(RTACallGraphKey) } - p.get(RTACallGraphKey) - } - describe("no analysis is scheduled") { - val as = executeAnalyses(Set.empty) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) - } + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) + } - describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { - println(1) - val as = executeAnalyses( - Set( - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new - ) - ) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) - } + describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { + println(1) + val as = executeAnalyses( + Set( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new + ) + ) + as.propertyStore.shutdown() + val tmp = classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)) + print("===========================>>>>>>>>>>>>>>>>>>>>>>"+tmp) + validateProperties( + as, + tmp, + // fieldsWithAnnotations(as.project), + Set("TypeImmutability_new") + ) //TODO class files ... with annotation + } } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index 7a4602aedc..7605ac7268 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -6,7 +6,7 @@ import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = ClassImmutability_new + final type Self = ClassImmutability_new } /** @@ -27,24 +27,34 @@ sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaIn sealed trait ClassImmutability_new extends Property with ClassImmutabilityPropertyMetaInformation_new { - final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key + final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key + def correspondingTypeImmutability: TypeImmutability_new } object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[ClassImmutability_new]] property. - */ - final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( - "opalj.ClassImmutability_new", - MutableClass - ) + /** + * The key associated with every [[ClassImmutability_new]] property. + */ + final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( + "opalj.ClassImmutability_new", + MutableClass + ) } -case object MutableClass extends ClassImmutability_new +case object MutableClass extends ClassImmutability_new { + def correspondingTypeImmutability = MutableType_new +} -case object DependentImmutableClass extends ClassImmutability_new +case object DependentImmutableClass extends ClassImmutability_new { + override def correspondingTypeImmutability: TypeImmutability_new = + ShallowImmutableType //TODO check +} -case object ShallowImmutableClass extends ClassImmutability_new +case object ShallowImmutableClass extends ClassImmutability_new { + override def correspondingTypeImmutability: TypeImmutability_new = ShallowImmutableType +} -case object DeepImmutableClass extends ClassImmutability_new +case object DeepImmutableClass extends ClassImmutability_new { + override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala index 72526610f0..d4c57da5a7 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -7,7 +7,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - type Self = ReferenceImmutability + type Self = ReferenceImmutability } @@ -28,19 +28,19 @@ sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaIn sealed trait ReferenceImmutability extends Property with ReferenceImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key + final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key } object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { - final val PropertyKeyName = "opalj.ReferenceImmutability" + final val PropertyKeyName = "opalj.ReferenceImmutability" - final val key: PropertyKey[ReferenceImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableReference - ) - } + final val key: PropertyKey[ReferenceImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableReference + ) + } } case object MutableReference extends ReferenceImmutability diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala index fa1450bc81..3b4936ca3b 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -8,7 +8,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait TypeImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = TypeImmutability_new + final type Self = TypeImmutability_new } /** @@ -29,18 +29,18 @@ sealed trait TypeImmutability_new extends OrderedProperty with TypeImmutabilityPropertyMetaInformation_new { - /** - * Returns the key used by all `TypeImmutability_new` properties. - */ - final def key = TypeImmutability_new.key + /** + * Returns the key used by all `TypeImmutability_new` properties. + */ + final def key = TypeImmutability_new.key - def isDeepImmutable: Boolean - def isShallowImmutable: Boolean + def isDeepImmutable: Boolean + def isShallowImmutable: Boolean - /** `true` if the immutability is unknown or if the type is mutable.*/ - def isMutable: Boolean + /** `true` if the immutability is unknown or if the type is mutable.*/ + def isMutable: Boolean - def meet(other: TypeImmutability_new): TypeImmutability_new + def meet(other: TypeImmutability_new): TypeImmutability_new } /** @@ -48,13 +48,13 @@ sealed trait TypeImmutability_new */ object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[TypeImmutability_new]] property. - */ - final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( - "org.opalj.TypeImmutability_new", - MutableType_new - ) + /** + * The key associated with every [[TypeImmutability_new]] property. + */ + final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( + "org.opalj.TypeImmutability_new", + MutableType_new + ) } /** @@ -63,50 +63,50 @@ object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new */ case object DeepImmutableType extends TypeImmutability_new { - override def isDeepImmutable: Boolean = true - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = false + override def isDeepImmutable: Boolean = true + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = false - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (this == that) - this - else - that + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (this == that) + this + else + that } case object ShallowImmutableType extends TypeImmutability_new { - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = true - override def isMutable: Boolean = false + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = true + override def isMutable: Boolean = false - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (that == MutableType_new) - that - else - this + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (that == MutableType_new) + that + else + this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == ImmutableType) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other == DeepImmutableType) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } } - } } case object MutableType_new extends TypeImmutability_new { - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = true + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = true - def meet(other: TypeImmutability_new): this.type = this + def meet(other: TypeImmutability_new): this.type = this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other != MutableType_new) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other != MutableType_new) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } } - } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 1836727cfe..7ae9399292 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -12,18 +12,17 @@ import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.DeepImmutableField -import org.opalj.br.fpcf.properties.DependentImmutableField +import org.opalj.br.fpcf.properties.DeepImmutableType import org.opalj.br.fpcf.properties.FieldImmutability -import org.opalj.br.fpcf.properties.ImmutableContainerType import org.opalj.br.fpcf.properties.ImmutableReference -import org.opalj.br.fpcf.properties.ImmutableType import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableField import org.opalj.br.fpcf.properties.MutableReference -import org.opalj.br.fpcf.properties.MutableType +import org.opalj.br.fpcf.properties.MutableType_new import org.opalj.br.fpcf.properties.ReferenceImmutability import org.opalj.br.fpcf.properties.ShallowImmutableField -import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.br.fpcf.properties.TypeImmutability_new import org.opalj.fpcf.EOptionP import org.opalj.fpcf.Entity import org.opalj.fpcf.FinalEP @@ -39,8 +38,10 @@ import org.opalj.fpcf.SomeEPS import org.opalj.tac.fpcf.properties.TACAI case class State() { - var typeImmutability: Option[Boolean] = None - var referenceImmutability: Option[Boolean] = None + var typeImmutability: Option[Boolean] = None + var referenceImmutability: Option[Boolean] = None + var depencenciesTypes: scala.collection.mutable.Set[ObjectType] = + scala.collection.mutable.Set[ObjectType]() } /** @@ -55,175 +56,194 @@ case class State() { class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - final val typeExtensibility = project.get(TypeExtensibilityKey) - final val closedPackages = project.get(ClosedPackagesKey) - final val fieldAccessInformation = project.get(FieldAccessInformationKey) + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) - def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { - entity match { - case field: Field ⇒ determineFieldImmutability(field) - case _ ⇒ - val m = entity.getClass.getName+"is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { + entity match { + case field: Field => determineFieldImmutability(field) + case _ => + val m = entity.getClass.getName + "is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + } + + private[analyses] def determineFieldImmutability( + field: Field + ): ProperPropertyComputationResult = { + + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + + def handleTypeImmutability(objectType: ObjectType): Option[Boolean] = { + if (objectType.isArrayType) return Some(true); //TODO + if (objectType.isBaseType) return Some(true); + val result = propertyStore(objectType, TypeImmutability_new.key) + println("result: " + result) + result match { + case FinalEP(e, DeepImmutableType) => { //ImmutableType || t == ImmutableContainerType) ⇒ { + println("has deep immutable type") + Some(true) + } + case FinalEP(e, ShallowImmutableType) => { + println("has shallow immutable type") + Some(false) //TODO mindstorm if this approch is appropriate } + case FinalEP(e, t) if (t == MutableType_new) => { //MutableType) ⇒ { + println("has mutable type") + Some(false) + } + case x @ _ => { + dependencies += x + println(x) + println("immutability of type couldn't be determined") + None + } + } } - private[analyses] def determineFieldImmutability( - field: Field - ): ProperPropertyComputationResult = { - - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - - def hasImmutableType(field: Field): Option[Boolean] = { - println("Field:: "+field) - println(field.fieldType) - println(field.fieldType.isBaseType) - if (field.fieldType.isArrayType) return Some(true); //TODO - if (field.fieldType.isBaseType) return Some(true); - val result = propertyStore(field.fieldType, TypeImmutability.key) - println("result: "+result) - result match { - case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) ⇒ { - println("has immutable type") - Some(true) - } - case FinalEP(e, t) if (t == MutableType) ⇒ { - println("has mutable type") - Some(false) - } - case x @ _ ⇒ { - dependencies += x - println(x) - println("immutability of type couldn't be determined") - None - } + + def hasImmutableType(field: Field)(state: State): Option[Boolean] = { + val hasFieldImmutableType = handleTypeImmutability(field.fieldType.asObjectType) + hasFieldImmutableType match { + case Some(false) => return Some(false); + case _ => { + state.depencenciesTypes.foreach( + t => { + val isImmutable = handleTypeImmutability(t) + isImmutable match { + case Some(false) => return Some(false); + case _ => + } } + ) + Some(true) } - def hasImmutableReference(field: Field): Option[Boolean] = { - - propertyStore(field, ReferenceImmutability.key) match { - case FinalEP(_, MutableReference) ⇒ { - println("has mutable reference") - Some(false) - } - case FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ { //TODO - println("has immutable Reference") - Some(true) - } - case x @ _ ⇒ { - dependencies += x - println(x) - println("immutability of reference couldn't be determined") - None - } - } + } + + } + def hasImmutableReference(field: Field): Option[Boolean] = { + + propertyStore(field, ReferenceImmutability.key) match { + case FinalEP(_, MutableReference) => { + println("has mutable reference") + Some(false) + } + case FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO + println("has immutable Reference") + Some(true) } + case x @ _ => { + dependencies += x + println(x) + println("immutability of reference couldn't be determined") + None + } + } + } - val state: State = new State() - - def createResult(state: State): ProperPropertyComputationResult = { - println("reference Immutability: "+state.referenceImmutability) - println("type immutabiliy: "+state.typeImmutability) - - state.referenceImmutability match { - case Some(false) ⇒ Result(field, MutableField) - case Some(true) ⇒ { - //If the field type is object. It is a generic field - if (field.fieldType == ObjectType("java/lang/Object")) - Result(field, DependentImmutableField) - else - state.typeImmutability match { - case Some(true) ⇒ Result(field, DeepImmutableField) - case Some(false) ⇒ Result(field, ShallowImmutableField) - case None if (dependencies.isEmpty) ⇒ Result(field, ShallowImmutableField) - case None ⇒ { - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } - } - } - case None if (dependencies.isEmpty) ⇒ Result(field, MutableField) - case None ⇒ { - println(dependencies) - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } + val state: State = new State() + def createResult(state: State): ProperPropertyComputationResult = { + println("reference Immutability: " + state.referenceImmutability) + println("type immutabiliy: " + state.typeImmutability) + + state.referenceImmutability match { + case Some(false) => Result(field, MutableField) + case Some(true) => { + //If the field type is object. It is a generic field + //if (field.fieldType == ObjectType("java/lang/Object")) + // Result(field, DependentImmutableField) + //else + state.typeImmutability match { + case Some(true) => Result(field, DeepImmutableField) + case Some(false) => Result(field, ShallowImmutableField) + case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) + case None => { + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) } + } } - def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - println("c function called") - dependencies = dependencies.filter(_.e ne eps.e) - (eps: @unchecked) match { - case x: InterimEP[_, _] ⇒ { - println("interim in continuation function") - println(x) - dependencies += eps - InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) - } - - case x @ FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) ⇒ { - println(x) - println("has immutable type. Determined by continuation function.") - state.typeImmutability = Some(true) - } - - case x @ FinalEP(_, MutableType) ⇒ { - println(x) - println("has mutable type. Determined by continuation function.") - state.typeImmutability = Some(false) - } - - case x @ FinalEP(_, MutableReference) ⇒ { - println(x) - println("has mutable reference. Determined by continuation function.") - state.referenceImmutability = Some(false) - } - - case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ { //TODO - println(x) - println("has immutable reference. Determined by continuation function.") - state.referenceImmutability = Some(true) - } - - case x @ _ ⇒ println("default value: "+x) - } - createResult(state) + case None if (dependencies.isEmpty) => Result(field, MutableField) + case None => { + println(dependencies) + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) } - //-- - state.referenceImmutability = hasImmutableReference(field) - state.typeImmutability = hasImmutableType(field); - println("after type immutability determination") - createResult(state) + } } + def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { + println("c function called") + dependencies = dependencies.filter(_.e ne eps.e) + (eps: @unchecked) match { + case x: InterimEP[_, _] => { + println("interim in continuation function") + println(x) + dependencies += eps + InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + } + + case x @ FinalEP(_, t) + if (t == DeepImmutableType || t == ShallowImmutableType) => { // ImmutableContainerType || t == ImmutableType) ⇒ { + println(x) + println("has immutable type. Determined by continuation function.") + state.typeImmutability = Some(true) + } + + case x @ FinalEP(_, MutableType_new) => { //MutableType) ⇒ { + println(x) + println("has mutable type. Determined by continuation function.") + state.typeImmutability = Some(false) + } + + case x @ FinalEP(_, MutableReference) => { + println(x) + println("has mutable reference. Determined by continuation function.") + state.referenceImmutability = Some(false) + } + + case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO + println(x) + println("has immutable reference. Determined by continuation function.") + state.referenceImmutability = Some(true) + } + + case x @ _ => println("default value: " + x) + } + createResult(state) + } + + //-- + state.depencenciesTypes = scala.collection.mutable.Set[ObjectType]() + state.referenceImmutability = hasImmutableReference(field) + state.typeImmutability = hasImmutableType(field)(state); + println("after type immutability determination") + createResult(state) + } } trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - //PropertyBounds.lub(Purity), - //PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - //PropertyBounds.lub(FieldMutability), - //PropertyBounds.lub(EscapeProperty), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(FieldImmutability) - //PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.ub(TACAI), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.lub(TypeImmutability_new), + PropertyBounds.lub(FieldImmutability) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) } @@ -234,16 +254,16 @@ object EagerL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -253,19 +273,19 @@ object LazyL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - FieldImmutability.key, - analysis.determineFieldImmutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldImmutability.key, + analysis.determineFieldImmutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 9779090536..4791874a69 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -38,13 +38,11 @@ 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.ImmutableType import org.opalj.br.fpcf.properties.MutableClass import org.opalj.br.fpcf.properties.MutableField -import org.opalj.br.fpcf.properties.MutableType import org.opalj.br.fpcf.properties.ShallowImmutableClass import org.opalj.br.fpcf.properties.ShallowImmutableField -import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.br.fpcf.properties.TypeImmutability_new /** * @@ -72,7 +70,7 @@ import org.opalj.br.fpcf.properties.TypeImmutability * */ class LxClassImmutabilityAnalysis_new(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. @@ -81,358 +79,379 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal * 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_new //MutableObject - ): MultiResult = { - val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) - val r = allSubtypes.map { st ⇒ - new FinalEP(st, immutability) - }.toSeq - MultiResult(r) + /** + * 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_new //MutableObject + ): 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 ::= ( + ( + determineClassImmutability_new(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, MutableClass) //MutableObjectDueToUnknownSupertypes) + } } + IncrementalResult(Results(results), nextComputations.iterator) + } + + def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { + e match { + case t: ObjectType => + //this is safe + val a = classHierarchy.superclassType(t) + println("AAA::::" + a) + a match { + case None => Result(t, MutableClass); //MutableObjectDueToUnknownSupertypes) + case Some(superClassType) => + val cf = project.classFile(t) match { + case None => + return Result(t, MutableClass); // MutableObjectByAnalysis) //TODO consider other lattice element + case Some(cf) => cf + } - @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 ::= ( - ( - determineClassImmutability_new(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, MutableClass) //MutableObjectDueToUnknownSupertypes) + propertyStore(superClassType, ClassImmutability_new.key) match { + case UBP(MutableClass) => //MutableObject) ⇒ + Result(t, MutableClass) + case eps: EPS[ObjectType, ClassImmutability_new] => + determineClassImmutability_new( + superClassType, + eps, + eps.isFinal, + lazyComputation = true + )(cf) + case epk => + determineClassImmutability_new( + superClassType, + epk, + superClassMutabilityIsFinal = false, + lazyComputation = true + )(cf) } } - IncrementalResult(Results(results), nextComputations.iterator) + case _ => + val m = e.getClass.getSimpleName + " is not an org.opalj.br.ObjectType" + throw new IllegalArgumentException(m) } + } - def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { - e match { - case t: ObjectType ⇒ - //this is safe - classHierarchy.superclassType(t) match { - case None ⇒ Result(t, MutableClass) //MutableObjectDueToUnknownSupertypes) - case Some(superClassType) ⇒ - val cf = project.classFile(t) match { - case None ⇒ - return Result(t, MutableClass) // MutableObjectByAnalysis) //TODO consider other lattice element - case Some(cf) ⇒ cf - } - - propertyStore(superClassType, ClassImmutability_new.key) match { - case UBP(MutableClass) ⇒ //MutableObject) ⇒ - Result(t, MutableClass) - case eps: EPS[ObjectType, ClassImmutability_new] ⇒ - determineClassImmutability_new( - superClassType, - eps, - eps.isFinal, - lazyComputation = true - )(cf) - case epk ⇒ - determineClassImmutability_new( - 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 determineClassImmutability_new( + superClassType: ObjectType, + superClassInformation: EOptionP[Entity, Property], + superClassMutabilityIsFinal: Boolean, + lazyComputation: Boolean + )( + cf: ClassFile + ): ProperPropertyComputationResult = { + // assert(superClassMutability.isMutable.isNoOrUnknown) + + //---generic classes handling + //printf(cf.asVirtualClass.asClassFile.classSignature.get.toString()) + //cf.asClassFile.classSignature.get.formalTypeParameters. + //--------------------------------------------- + + val t = cf.thisType + + var dependees = Map.empty[Entity, EOptionP[Entity, Property]] + + if (!superClassMutabilityIsFinal) { + dependees += (SuperClassKey -> superClassInformation) } - private[this] object SuperClassKey + // Collect all fields for which we need to determine the effective mutability! + var hasFieldsWithUnknownMutability = false + + val instanceFields = cf.fields.filter { f => + !f.isStatic + } + var hasShallowImmutableFields = false + var hasDependentImmutableFields = false + val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) + fieldsPropertyStoreInformation.foreach( + f => { + println("f:::" + f) + f match { + case FinalP(MutableField) => { + if (lazyComputation) + return Result(t, MutableClass); + else + return createResultForAllSubtypes(t, MutableClass); + } + case FinalP(ShallowImmutableField) => hasShallowImmutableFields = true + case FinalEP(f, DependentImmutableField) => { + println("FieldType: " + f.asField.fieldType) + println("FieldTypeSignature: " + f.asField.fieldTypeSignature.get.toJVMSignature) + 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); + } + } + ) /** - * Determines the immutability of instances of the given class type `t`. + * dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { + * case FinalP(MutableField) => //NonFinalField) ⇒ + * // <=> The class is definitively mutable and therefore also all subclasses. + * if (lazyComputation) + * return Result(t, MutableClass); //MutableObjectByAnalysis); // + * else + * return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); + * case ep @ InterimE(e) => + * hasFieldsWithUnknownMutability = true + * (e, ep) + * case epk @ EPK(e: Entity, _) => + * // <=> The mutability information is not yet available. + * hasFieldsWithUnknownMutability = true + * (e, epk) * - * @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. + * // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields + * }).toMap * */ - def determineClassImmutability_new( - superClassType: ObjectType, - superClassInformation: EOptionP[Entity, Property], - superClassMutabilityIsFinal: Boolean, - lazyComputation: Boolean - )( - cf: ClassFile - ): ProperPropertyComputationResult = { - // assert(superClassMutability.isMutable.isNoOrUnknown) - - //---generic classes handling - //printf(cf.asVirtualClass.asClassFile.classSignature.get.toString()) - //cf.asClassFile.classSignature.get.formalTypeParameters. - //--------------------------------------------- - - val t = cf.thisType - - var dependees = Map.empty[Entity, EOptionP[Entity, Property]] - - if (!superClassMutabilityIsFinal) { - dependees += (SuperClassKey -> superClassInformation) - } + var minLocalImmutability: ClassImmutability_new = + if (!superClassMutabilityIsFinal) { + println("OnE!") + MutableClass //MutableObjectByAnalysis + + } else { + println("Two!!") + ShallowImmutableClass //ImmutableContainer + } + + // NOTE: maxLocalImmutability does not take the super classes' mutability into account! + var maxLocalImmutability: ClassImmutability_new = superClassInformation match { + case UBP(ShallowImmutableClass) => ShallowImmutableClass //ImmutableContainer + case _ => DeepImmutableClass // ImmutableObject + } - // Collect all fields for which we need to determine the effective mutability! - var hasFieldsWithUnknownMutability = false + if (hasDependentImmutableFields) { + maxLocalImmutability = DependentImmutableClass //TODO possibly here + } else if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + } - val instanceFields = cf.fields.filter { f ⇒ - !f.isStatic - } - var hasShallowImmutableFields = false - var hasDependentImmutableFields = false - val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) - fieldsPropertyStoreInformation.foreach( - f ⇒ - f match { - 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 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); - } - ) + 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 //ImmutableContainer + } - /** - * dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { - * case FinalP(MutableField) => //NonFinalField) ⇒ - * // <=> The class is definitively mutable and therefore also all subclasses. - * if (lazyComputation) - * return Result(t, MutableClass); //MutableObjectByAnalysis); // - * else - * return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); - * case ep @ InterimE(e) => - * hasFieldsWithUnknownMutability = true - * (e, ep) - * case epk @ EPK(e: Entity, _) => - * // <=> The mutability information is not yet available. - * hasFieldsWithUnknownMutability = true - * (e, epk) - * - * // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields - * }).toMap * - */ - var minLocalImmutability: ClassImmutability_new = - if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) - MutableClass //MutableObjectByAnalysis - else - ShallowImmutableClass //ImmutableContainer + //--var fieldTypes: Set[ObjectType] = Set.empty + //--if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { + //-- fieldTypes = // IMPROVE Use the precise type of the field (if available)! + // cf.fields.collect { + // case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType + // }.toSet + //} + + // For each dependent class file we have to determine the mutability + // of instances of the respective type to determine the immutability + // of instances of this class. + // Basically, we have to distinguish the following cases: + // - A field's type is mutable or conditionally immutable=> + // This class is conditionally immutable. + // - A field's type is immutable => + // The field is ignored. + // (This class is as immutable as its superclass.) + // - A field's type is at least conditionally mutable or + // The immutability of the field's type is not yet known => + // This type is AtLeastConditionallyImmutable + // We have to declare a dependency on the respective type's immutability. + // + // Additional handling is required w.r.t. the supertype: + // If the supertype is Immutable => + // Nothing special to do. + // If the supertype is AtLeastConditionallyImmutable => + // We have to declare a dependency on the supertype. + // If the supertype is ConditionallyImmutable => + // This type is also at most conditionally immutable. + // + // We furthermore have to take the mutability of the fields into consideration: + // If a field is not effectively final => + // This type is mutable. + // If a field is effectively final => + // Nothing special to do. + + //val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) + //val hasMutableOrConditionallyImmutableField = + // IMPROVE Use the precise type of the field (if available)! + //-- fieldTypesImmutability.exists { eOptP ⇒ + //-- eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) + //-- } + + if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + println("shallow immutable fields.......!!!!!!!!!!!!!!!!!!") + } //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 + * } + * fieldTypesWithUndecidedMutability.foreach { eOptP => + * dependees += (eOptP.e -> eOptP) + * }* + */ + //} + + 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) + ); + } - // NOTE: maxLocalImmutability does not take the super classes' mutability into account! - var maxLocalImmutability: ClassImmutability_new = superClassInformation match { - case UBP(ShallowImmutableClass) ⇒ ShallowImmutableClass //ImmutableContainer - case _ ⇒ DeepImmutableClass // ImmutableObject - } + def c(someEPS: SomeEPS): ProperPropertyComputationResult = { + //[DEBUG] val oldDependees = dependees + dependees = dependees.filter(_._1 ne someEPS.e) + println("someEPS: " + someEPS) + someEPS match { + // Superclass related dependencies: + // + case UBP(MutableClass) => // MutableObject) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); - if (hasDependentImmutableFields) { - maxLocalImmutability = DependentImmutableClass - } else if (hasShallowImmutableFields) { - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - } + case LBP(DeepImmutableClass) => //_:ImmutableObject) ⇒ // the super class + dependees -= SuperClassKey - 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 //ImmutableContainer - } + case UBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container + if (someEPS.isFinal) dependees -= SuperClassKey + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) //TypeImmutability.key) - //--var fieldTypes: Set[ObjectType] = Set.empty - //--if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { - //-- fieldTypes = // IMPROVE Use the precise type of the field (if available)! - // cf.fields.collect { - // case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType - // }.toSet - //} - - // For each dependent class file we have to determine the mutability - // of instances of the respective type to determine the immutability - // of instances of this class. - // Basically, we have to distinguish the following cases: - // - A field's type is mutable or conditionally immutable=> - // This class is conditionally immutable. - // - A field's type is immutable => - // The field is ignored. - // (This class is as immutable as its superclass.) - // - A field's type is at least conditionally mutable or - // The immutability of the field's type is not yet known => - // This type is AtLeastConditionallyImmutable - // We have to declare a dependency on the respective type's immutability. - // - // Additional handling is required w.r.t. the supertype: - // If the supertype is Immutable => - // Nothing special to do. - // If the supertype is AtLeastConditionallyImmutable => - // We have to declare a dependency on the supertype. - // If the supertype is ConditionallyImmutable => - // This type is also at most conditionally immutable. + case LBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container + if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && + !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + + case LUBP(MutableClass, DeepImmutableClass) => //_: MutableObject, ImmutableObject) ⇒ // No information about superclass + + // Properties related to the type of the class' fields. // - // We furthermore have to take the mutability of the fields into consideration: - // If a field is not effectively final => - // This type is mutable. - // If a field is effectively final => - // Nothing special to do. - - //val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) - //val hasMutableOrConditionallyImmutableField = - // IMPROVE Use the precise type of the field (if available)! - //-- fieldTypesImmutability.exists { eOptP ⇒ - //-- eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) - //-- } - - if (hasShallowImmutableFields) { - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - } //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 - * } - * fieldTypesWithUndecidedMutability.foreach { eOptP => - * dependees += (eOptP.e -> eOptP) - * }* + * case UBP(ShallowImmutableType | MutableType_new) ⇒ //ImmutableContainerType | MutableType) ⇒ + * maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + * dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) // TypeImmutability.key) + * + * case ELBP(e, DeepImmutableType) ⇒ // ImmutableType) ⇒ // Immutable field type, no influence on mutability + * dependees -= e + * + * case UBP(DeepImmutableType) ⇒ //ImmutableType) ⇒ // No information about field type + * + * case FinalP(MutableType_new) ⇒ //MutableType) + * Result(t, MutableClass) //TODO check */ - //} - - 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) - ); + case FinalP(DependentImmutableField) => { + println( + ".........................................kkk......................................" + ) + maxLocalImmutability = ShallowImmutableClass // DependentImmutableClass //TODO possibly here? } - def c(someEPS: SomeEPS): ProperPropertyComputationResult = { - //[DEBUG] val oldDependees = dependees - dependees = dependees.filter(_._1 ne someEPS.e) - println("someEPS: "+someEPS) - someEPS match { - // Superclass related dependencies: - // - case UBP(MutableClass) ⇒ // MutableObject) ⇒ - return Result(t, MutableClass); //MutableObjectByAnalysis); - - case LBP(DeepImmutableClass) ⇒ //_:ImmutableObject) ⇒ // the super class - dependees -= SuperClassKey - - case UBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is at most immutable container - if (someEPS.isFinal) dependees -= SuperClassKey - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - - case LBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is a least immutable container - if (minLocalImmutability != DeepImmutableClass && //ImmutableContainer && - !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - - case LUBP(MutableClass, DeepImmutableClass) ⇒ //_: MutableObject, ImmutableObject) ⇒ // No information about superclass - - // Properties related to the type of the class' fields. - // - case UBP(ShallowImmutableClass | MutableClass) ⇒ //ImmutableContainerType | MutableType) ⇒ - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - - case ELBP(e, ImmutableType) ⇒ // Immutable field type, no influence on mutability - dependees -= e - - case UBP(ImmutableType) ⇒ // No information about field type - - case FinalP(MutableType) ⇒ Result(t, MutableClass) //TODO check - - case FinalP(DependentImmutableField) ⇒ maxLocalImmutability = DependentImmutableClass - // Field Mutability related dependencies: - // + // Field Mutability related dependencies: + // - case FinalP(ShallowImmutableField) ⇒ { - maxLocalImmutability = ShallowImmutableClass - } - case FinalP(MutableField) ⇒ return Result(t, MutableClass); + case FinalP(ShallowImmutableField) => { + maxLocalImmutability = ShallowImmutableClass + } + case FinalP(MutableField) => return Result(t, MutableClass); - case UBP(MutableField) ⇒ //_: NonFinalField) ⇒ - return Result(t, MutableClass); //MutableObjectByAnalysis); + case UBP(MutableField) => //_: NonFinalField) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); - case ELBP(e, ShallowImmutableField | DeepImmutableField) ⇒ //FinalField) => - dependees -= e - if (minLocalImmutability != ShallowImmutableClass && // ImmutableContainer && - !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + case ELBP(e, ShallowImmutableField | DeepImmutableField) => //FinalField) => + dependees -= e + if (minLocalImmutability != ShallowImmutableClass && // ImmutableContainer && + !dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - case UBP(ShallowImmutableField | DeepImmutableField) ⇒ //_: FinalField) ⇒ // no information about field mutability + case UBP(ShallowImmutableField | DeepImmutableField) => //_: FinalField) ⇒ // no information about field mutability - case _ ⇒ Result(t, MutableClass) //TODO check + case _ => Result(t, MutableClass) //TODO check - } + } - if (someEPS.isRefinable) { - val entity = if (someEPS.pk == ClassImmutability_new.key) SuperClassKey else someEPS.e - dependees += (entity -> someEPS) - } + if (someEPS.isRefinable) { + val entity = if (someEPS.pk == ClassImmutability_new.key) SuperClassKey else someEPS.e + dependees += (entity -> someEPS) + } - /*[DEBUG] + /*[DEBUG] assert( oldDependees != dependees, s"dependees are not correctly updated $e($p)\n:old=$oldDependees\nnew=$dependees" ) */ - // Lift lower bound once no dependencies other than field type mutabilities are left - if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && - dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer - - if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { - /*[DEBUG] + // Lift lower bound once no dependencies other than field type mutabilities are left + if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && + dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer + println("minLocalImmutability: " + minLocalImmutability) + println("maxLocalImmutability: " + maxLocalImmutability) + if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { + /*[DEBUG] assert( maxLocalImmutability == ConditionallyImmutableObject || maxLocalImmutability == ImmutableObject @@ -451,115 +470,114 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal ) */ - Result(t, maxLocalImmutability) + Result(t, maxLocalImmutability) - } else { - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - } - } + } else { + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + } + } - //[DEBUG] assert(initialImmutability.isRefinable) - println("minLocalImmutability: "+minLocalImmutability) - println("maxLocalImmutability: "+maxLocalImmutability) - val result = - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - if (lazyComputation) - result - else { - val isFinal = dependees.isEmpty - createIncrementalResult( - t, - EPS(t, minLocalImmutability, maxLocalImmutability), - isFinal, - result - ) - } + //[DEBUG] assert(initialImmutability.isRefinable) + + val result = + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + if (lazyComputation) + result + else { + val isFinal = dependees.isEmpty + createIncrementalResult( + t, + EPS(t, minLocalImmutability, maxLocalImmutability), + isFinal, + result + ) } + } } trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) - - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, TypeImmutability, FieldImmutability) - - 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 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.map(cf ⇒ set(cf.thisType, DeepImmutableClass)) //ImmutableObject)) - - // 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) - } - } + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new, FieldImmutability) //TypeImmutability, FieldImmutability) + + 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 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.map(cf => set(cf.thisType, DeepImmutableClass)) //ImmutableObject)) + + // 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 = {} + // 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 = {} } /** @@ -571,22 +589,22 @@ object EagerLxClassImmutabilityAnalysis_new extends ClassImmutabilityAnalysisScheduler_new 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 LxClassImmutabilityAnalysis_new(p) - ps.scheduleEagerComputationsForEntities(cfs)( - analysis.determineClassImmutability_new( - superClassType = null, - FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), - superClassMutabilityIsFinal = true, - lazyComputation = false - ) - ) - analysis - } + 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 LxClassImmutabilityAnalysis_new(p) + ps.scheduleEagerComputationsForEntities(cfs)( + analysis.determineClassImmutability_new( + superClassType = null, + FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), + superClassMutabilityIsFinal = true, + lazyComputation = false + ) + ) + analysis + } } /** @@ -598,18 +616,18 @@ object LazyLxClassImmutabilityAnalysis_new extends ClassImmutabilityAnalysisScheduler_new with FPCFLazyAnalysisScheduler { - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - - override def register( - p: SomeProject, - ps: PropertyStore, - unused: InitializationData - ): FPCFAnalysis = { - val analysis = new LxClassImmutabilityAnalysis_new(p) - ps.registerLazyPropertyComputation( - ClassImmutability_new.key, - analysis.doDetermineClassImmutability_new - ) - analysis - } + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + override def register( + p: SomeProject, + ps: PropertyStore, + unused: InitializationData + ): FPCFAnalysis = { + val analysis = new LxClassImmutabilityAnalysis_new(p) + ps.registerLazyPropertyComputation( + ClassImmutability_new.key, + analysis.doDetermineClassImmutability_new + ) + analysis + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index 12d5ce8372..1143ec8aec 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -33,6 +33,7 @@ import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.ClassImmutability_new 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.MutableClass import org.opalj.br.fpcf.properties.MutableType_new import org.opalj.br.fpcf.properties.ShallowImmutableClass @@ -44,198 +45,209 @@ import org.opalj.br.fpcf.properties.TypeImmutability_new * type are immutable and checking that the set of types is closed. * * @author Michael Eichberg + * @author Tobias Peter Roth */ -class TypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPCFAnalysis { - - def doDetermineTypeMutability_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 = { - typeExtensibility(t) match { - case Yes | Unknown => Result(t, MutableType_new) // MutableType) - case No => step2(t) +class LxTypeImmutabilityAnalysis_new( 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") } - } - - 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_new, ub: ClassImmutability_new) => - val thisLB = lb.correspondingTypeImmutability - val thisUB = ub.correspondingTypeImmutability - if (eps.isFinal) - Result(t, thisUB) - else - InterimResult(t, thisLB, thisUB, Seq(eps), c) - } - } - } - - ps(t, ClassImmutability_new.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, Seq(eps), c) - case epk => InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) - //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) - } - } else { - var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] - var joinedImmutability - : TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." - var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType - - ps(t, ClassImmutability_new.key) match { - case FinalP(DeepImmutableClass) => //ImmutableObject) => - case FinalP(MutableClass) => //(_: MutableObject) => - return Result(t, MutableType_new); //MutableType); - - case FinalP(ShallowImmutableClass) => //ImmutableContainer) => - joinedImmutability = ShallowImmutableType // ImmutableContainerType - maxImmutability = ShallowImmutableType //ImmutableContainerType - - case eps @ InterimLUBP(lb, ub) => - joinedImmutability = lb.correspondingTypeImmutability - maxImmutability = ub.correspondingTypeImmutability - dependencies += (t -> eps) - - case eOptP => - joinedImmutability = MutableType_new //MutableType - dependencies += (t -> eOptP) - } - - directSubtypes foreach { subtype => - ps(subtype, TypeImmutability_new.key) match { - case FinalP(DeepImmutableType) => //ImmutableType) => - case UBP(MutableType_new) => //MutableType) => - return Result(t, MutableType_new); //MutableType); - - case FinalP(ShallowImmutableType) => //ImmutableContainerType) => - joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) - maxImmutability = ShallowImmutableType //ImmutableContainerType - - case eps @ InterimLUBP(subtypeLB, subtypeUB) => - joinedImmutability = joinedImmutability.meet(subtypeLB) - maxImmutability = maxImmutability.meet(subtypeUB) - dependencies += ((subtype, eps)) - - case epk => - joinedImmutability = MutableType_new //MutableType - dependencies += ((subtype, epk)) + /** + * @param t An object type which is not `java.lang.Object`. + */ + def step1( + typeExtensibility: ObjectType ⇒ Answer + )( + t: ObjectType + ): ProperPropertyComputationResult = { + typeExtensibility(t) match { + case Yes | Unknown ⇒ + println("Yes Unknown"); Result(t, MutableType_new) // MutableType) + case No ⇒ step2(t) } - } - - if (dependencies.isEmpty) { - Result(t, maxImmutability) - } else if (joinedImmutability == maxImmutability) { - // E.g., as soon as one subtype is an ImmutableContainer, we are at most - // ImmutableContainer, even if all other subtype may even be 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 = { + } + + 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 ⇒ + println("c function called") + def apply(eps: SomeEPS): ProperPropertyComputationResult = { + println("EPS: "+eps) + eps match { + case ELUBP(_, lb: ClassImmutability_new, ub: ClassImmutability_new) ⇒ + val thisLB = lb.correspondingTypeImmutability + val thisUB = ub.correspondingTypeImmutability + if (eps.isFinal) + Result(t, thisUB) + else + InterimResult(t, thisLB, thisUB, Seq(eps), c) + } + } + } + val resultToMatch = ps(t, ClassImmutability_new.key) + println("Result1: "+resultToMatch) + resultToMatch match { + case x @ FinalP(p) ⇒ { + println("x::: "+x.p.correspondingTypeImmutability) + Result(t, p.correspondingTypeImmutability); + } + + case eps @ InterimLUBP(lb, ub) ⇒ + val thisUB = ub.correspondingTypeImmutability + val thisLB = lb.correspondingTypeImmutability + InterimResult(t, thisLB, thisUB, Seq(eps), c) + case epk ⇒ { + println("here ") + InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) + } + //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) + } + } else { + var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] + var joinedImmutability: TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." + var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType + + val resultToMatch2 = ps(t, ClassImmutability_new.key) + println("Result2: "+resultToMatch2) + resultToMatch2 match { + case FinalP(DeepImmutableClass) ⇒ //ImmutableObject) => + case FinalP(MutableClass) ⇒ //(_: MutableObject) => + return Result(t, MutableType_new); //MutableType); + case FinalP(ShallowImmutableClass) ⇒ //ImmutableContainer) => + joinedImmutability = ShallowImmutableType // ImmutableContainerType + maxImmutability = ShallowImmutableType //ImmutableContainerType + case FinalP(DependentImmutableClass) ⇒ + joinedImmutability = ShallowImmutableType + maxImmutability = ShallowImmutableType + + case eps @ InterimLUBP(lb, ub) ⇒ + joinedImmutability = lb.correspondingTypeImmutability + maxImmutability = ub.correspondingTypeImmutability + dependencies += (t -> eps) + + case eOptP ⇒ + joinedImmutability = MutableType_new //MutableType + dependencies += (t -> eOptP) + } + + directSubtypes foreach { subtype ⇒ + ps(subtype, TypeImmutability_new.key) match { + case FinalP(DeepImmutableType) ⇒ //ImmutableType) => + case UBP(MutableType_new) ⇒ //MutableType) => + return Result(t, MutableType_new); //MutableType); + + case FinalP(ShallowImmutableType) ⇒ //ImmutableContainerType) => + joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) + maxImmutability = ShallowImmutableType //ImmutableContainerType + + case eps @ InterimLUBP(subtypeLB, subtypeUB) ⇒ + joinedImmutability = joinedImmutability.meet(subtypeLB) + maxImmutability = maxImmutability.meet(subtypeUB) + dependencies += ((subtype, eps)) + + case epk ⇒ + joinedImmutability = MutableType_new //MutableType + dependencies += ((subtype, epk)) + } + } + if (dependencies.isEmpty) { - Result(t, maxImmutability) + Result(t, maxImmutability) + } else if (joinedImmutability == maxImmutability) { + // E.g., as soon as one subtype is an ImmutableContainer, we are at most + // ImmutableContainer, even if all other subtype may even be immutable! + Result(t, joinedImmutability) } 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_new => - joinedImmutability = joinedImmutability.meet(lb) - case lb: ClassImmutability_new => - joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) - } else { - joinedImmutability = MutableType_new //MutableType - continue = false + // 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_new ⇒ + joinedImmutability = joinedImmutability.meet(lb) + case lb: ClassImmutability_new ⇒ + joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) + } + else { + joinedImmutability = MutableType_new //MutableType + continue = false + } + } + if (joinedImmutability == maxImmutability) { + assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) + Result(t, maxImmutability) + } else { + InterimResult( + t, + joinedImmutability, + maxImmutability, + dependencies.values, + c + ) + } + } + } + + (eps: @unchecked) match { + case FinalEP(e, DeepImmutableType | DeepImmutableClass) ⇒ //ImmutableType | ImmutableObject) => + dependencies = dependencies - e + nextResult() + + case UBP(x) if (x == MutableType_new || x == MutableClass) ⇒ //MutableType | _: MutableObject) => + Result(t, MutableType_new) //MutableType) + + case FinalEP(e, x) if (x == ShallowImmutableType || x == DependentImmutableClass || x == ShallowImmutableClass) ⇒ //ImmutableContainerType | ImmutableContainer) => + maxImmutability = ShallowImmutableType //ImmutableContainerType + dependencies = dependencies - e + nextResult() + + case eps @ InterimEUBP(e, subtypeP) ⇒ + dependencies = dependencies.updated(e, eps) + subtypeP match { + case subtypeP: TypeImmutability_new ⇒ + maxImmutability = maxImmutability.meet(subtypeP) + case subtypeP: ClassImmutability_new ⇒ + maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) + } + nextResult() + } } - } - if (joinedImmutability == maxImmutability) { - assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) - Result(t, maxImmutability) - } else { - InterimResult( - t, - joinedImmutability, - maxImmutability, - dependencies.values, - c - ) - } + InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) } - } - - (eps: @unchecked) match { - case FinalEP(e, DeepImmutableType | DeepImmutableClass) => //ImmutableType | ImmutableObject) => - dependencies = dependencies - e - nextResult() - - case UBP(x) - if (x == MutableType_new || x == MutableClass) => //MutableType | _: MutableObject) => - Result(t, MutableType_new) //MutableType) - - case FinalEP(e, x) - if (x == ShallowImmutableType || x == ShallowImmutableClass) => //ImmutableContainerType | ImmutableContainer) => - maxImmutability = ShallowImmutableType //ImmutableContainerType - dependencies = dependencies - e - nextResult() - - case eps @ InterimEUBP(e, subtypeP) => - dependencies = dependencies.updated(e, eps) - subtypeP match { - case subtypeP: TypeImmutability_new => - maxImmutability = maxImmutability.meet(subtypeP) - case subtypeP: ClassImmutability_new => - maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) - } - nextResult() - } } - - InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) - } } - } } trait TypeImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) } @@ -248,41 +260,40 @@ object EagerLxTypeImmutabilityAnalysis_new extends TypeImmutabilityAnalysisScheduler_new with BasicFPCFEagerAnalysisScheduler { - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val typeExtensibility = project.get(TypeExtensibilityKey) - val analysis = new TypeImmutabilityAnalysis_new(project) - val allClassFilesIterator = project.allClassFiles.iterator - val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty - ps.scheduleEagerComputationsForEntities(types) { - analysis.step1(typeExtensibility) - } + override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val typeExtensibility = project.get(TypeExtensibilityKey) + val analysis = new LxTypeImmutabilityAnalysis_new(project) + val allClassFilesIterator = project.allClassFiles.iterator + val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) - analysis - } + ps.scheduleEagerComputationsForEntities(types) { + analysis.step1(typeExtensibility) + } + analysis + } } object LazyLxTypeImmutabilityAnalysis_new extends TypeImmutabilityAnalysisScheduler_new 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 TypeImmutabilityAnalysis_new(p) - val analysisRunner: ProperPropertyComputation[Entity] = - analysis.doDetermineTypeMutability_new(typeExtensibility) - ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) - analysis - } + 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 LxTypeImmutabilityAnalysis_new(p) + val analysisRunner: ProperPropertyComputation[Entity] = + analysis.doDetermineTypeImmutability_new(typeExtensibility) + ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) + analysis + } } From 9c8dfcebdbe93bdd9c97464d73745d2a4e148e38 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Mon, 25 Nov 2019 14:57:39 +0100 Subject: [PATCH 016/327] Introducing dependent immutable type including tests --- .../ClassImmutabilityAnalysisDemo.scala | 3 + .../ReferenceImmutabilityAnalysisDemo.scala | 18 ++- .../TypeImmutabilityAnalysisDemo.scala | 5 + .../GenericAndDeepImmutableFields.java | 31 ++++++ .../GenericAndMutableFields.java | 24 ++++ .../GenericAndShallowImmutableFields.java | 27 +++++ .../class_immutability/Generic_class1.java | 4 +- .../WithMutableAndImmutableFieldType.java | 15 ++- .../DependentImmutableTypeAnnotation.java | 28 +++++ .../opalj/fpcf/TypeImmutabilityTests.scala | 70 ++++++------ .../NewTypeImmutabilityMatcher.scala | 3 + .../properties/ClassImmutability_new.scala | 2 +- .../properties/TypeImmutability_new.scala | 50 +++++++-- .../L0FieldImmutabilityAnalysis.scala | 104 +++++++++++------- .../LxClassImmutabilityAnalysis_new.scala | 4 +- .../LxTypeImmutabilityAnalysis_new.scala | 17 ++- 16 files changed, 306 insertions(+), 99 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java 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 index 04946824b9..d7ba9e7954 100644 --- 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 @@ -16,6 +16,7 @@ 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.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new @@ -45,6 +46,8 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + val (propertyStore, _) = analysesManager.runAll( LazyTypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index ba34ef0ad8..25dcf825c8 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -7,13 +7,15 @@ 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.LazyL0PurityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.TACAITransformer +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. @@ -38,12 +40,16 @@ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { def analyze(project: Project[URL]): String = { val analysesManager = project.get(FPCFAnalysesManagerKey) - + analysesManager.project.get(RTACallGraphKey) val (propertyStore, _) = analysesManager.runAll( + //EagerUnsoundPrematurelyReadFieldsAnalysis, + //EagerL2PurityAnalysis, + //EagerL2FieldMutabilityAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL0PurityAnalysis, - TACAITransformer, - EagerL0ReferenceImmutabilityAnalysis + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis ); "Mutable References: "+propertyStore 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 index bffe4c39e1..5b54015926 100644 --- 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 @@ -12,6 +12,7 @@ import org.opalj.br.fpcf.analyses.LazyL0PurityAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis 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_new import org.opalj.br.fpcf.properties.ShallowImmutableType //import org.opalj.br.fpcf.properties.ShallowImmutableType @@ -62,6 +63,10 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .finalEntities(ShallowImmutableType) .toList .toString()+"\n"+ + "Dependent Immutable Type: "+propertyStore + .finalEntities(DependentImmutableType) + .toList + .toString()+"\n"+ "Deep Immutable Type: "+propertyStore .finalEntities(DeepImmutableType) .toList diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java new file mode 100644 index 0000000000..4510b6b51c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java @@ -0,0 +1,31 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_mutability.MutableType; + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class GenericAndDeepImmutableFields { + + @DependentImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private T1 t1; + + @DependentImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private T2 t2; + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private FinalEmptyClass fec; + + GenericAndDeepImmutableFields(T1 t1, T2 t2, FinalEmptyClass fec){ + this.t1 = t1; + this.t2 = t2; + this.fec = fec; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java new file mode 100644 index 0000000000..05fcf78ccb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java @@ -0,0 +1,24 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@MutableClassAnnotation("Because of mutable field") +public class GenericAndMutableFields { + @MutableFieldAnnotation("Because of mutable reference") + @MutableReferenceAnnotation("Because of public field") + public T1 t1; + @DependentImmutableFieldAnnotation("Because of generic type") + @ImmutableReferenceAnnotation("Because of effectively immutable final") + private T2 t2; + GenericAndMutableFields(T1 t1, T2 t2){ + this.t1 = t1; + this.t2 = t2; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java new file mode 100644 index 0000000000..1905bbc540 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java @@ -0,0 +1,27 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +import org.opalj.fpcf.properties.type_mutability.MutableType; + +@MutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +public class GenericAndShallowImmutableFields { + + @DependentImmutableFieldAnnotation("") + private T1 t1; + @DependentImmutableFieldAnnotation("") + private T2 t2; + @ShallowImmutableFieldAnnotation("") + private TrivialMutableClass tmc; + GenericAndShallowImmutableFields(T1 t1, T2 t2, TrivialMutableClass tmc){ + this.t1 = t1; + this.t2 = t2; + this.tmc = tmc; + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java index 5ec7f3f1c8..0e7cfcf3d3 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java @@ -3,9 +3,11 @@ import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; +@DependentImmutableTypeAnnotation("Dependent Immutability of T1,...,T5") @DependentImmutableClassAnnotation("Dependent Immutability of T1,...,T5") -public class Generic_class1 { +public final class Generic_class1 { @DependentImmutableFieldAnnotation("T1") @ImmutableReferenceAnnotation("effectively") private T1 t1; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java index d9ddb71e0f..658f54d05d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java @@ -1,11 +1,22 @@ package org.opalj.fpcf.fixtures.type_immutability; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; -@MutableTypeAnnotation("has shallow mutable fields") -public class WithMutableAndImmutableFieldType { +@ShallowImmutableTypeAnnotation("has shallow mutable fields") +@ShallowImmutableClassAnnotation("has shallow imm fields") +public final class WithMutableAndImmutableFieldType { + @DeepImmutableFieldAnnotation("imm reference and deep immutable type") + @ImmutableReferenceAnnotation("private") private FinalEmptyClass fec = new FinalEmptyClass(); + + @ShallowImmutableFieldAnnotation("imm reference and mutable type") + @ImmutableReferenceAnnotation("private") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java new file mode 100644 index 0000000000..2781c70abb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.type_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.LxTypeImmutabilityAnalysis_new; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * 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 DependentImmutableTypeAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {LxTypeImmutabilityAnalysis_new.class}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala index 85c3fd18b2..f6531f392a 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala @@ -7,6 +7,7 @@ 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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey @@ -23,41 +24,42 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class TypeImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) - } - p.get(RTACallGraphKey) + 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("TypeImmutability_new")) - } + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) + } - describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { - println(1) - val as = executeAnalyses( - Set( - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new - ) - ) - as.propertyStore.shutdown() - val tmp = classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)) - print("===========================>>>>>>>>>>>>>>>>>>>>>>"+tmp) - validateProperties( - as, - tmp, - // fieldsWithAnnotations(as.project), - Set("TypeImmutability_new") - ) //TODO class files ... with annotation - } + describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { + println(1) + val as = executeAnalyses( + Set( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new + ) + ) + as.propertyStore.shutdown() + val tmp = classFilesWithAnnotations(as.project).map(tp => (tp._1.thisType, tp._2, tp._3)) + print("===========================>>>>>>>>>>>>>>>>>>>>>>" + tmp) + validateProperties( + as, + tmp, + // fieldsWithAnnotations(as.project), + Set("TypeImmutability_new") + ) //TODO class files ... with annotation + } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala index 0492c08aa2..2611624620 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala @@ -5,6 +5,7 @@ import org.opalj.br.AnnotationLike import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.DependentImmutableType import org.opalj.br.fpcf.properties.MutableType_new import org.opalj.br.fpcf.properties.ShallowImmutableType import org.opalj.br.fpcf.properties.TypeImmutability_new @@ -54,4 +55,6 @@ class NewMutableTypeMatcher extends NewTypeImmutabilityMatcher(MutableType_new) class ShallowImmutableTypeMatcher extends NewTypeImmutabilityMatcher(ShallowImmutableType) +class DependentImmutableTypeMatcher extends NewTypeImmutabilityMatcher(DependentImmutableType) + class DeepImmutableTypeMatcher extends NewTypeImmutabilityMatcher(DeepImmutableType) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index 7605ac7268..50239f389b 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -48,7 +48,7 @@ case object MutableClass extends ClassImmutability_new { case object DependentImmutableClass extends ClassImmutability_new { override def correspondingTypeImmutability: TypeImmutability_new = - ShallowImmutableType //TODO check + DependentImmutableType //TODO check } case object ShallowImmutableClass extends ClassImmutability_new { diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala index 3b4936ca3b..6b43e42c51 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -36,6 +36,7 @@ sealed trait TypeImmutability_new def isDeepImmutable: Boolean def isShallowImmutable: Boolean + def isDependentImmutable: Boolean /** `true` if the immutability is unknown or if the type is mutable.*/ def isMutable: Boolean @@ -66,6 +67,7 @@ case object DeepImmutableType extends TypeImmutability_new { 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 = {} @@ -82,6 +84,7 @@ case object ShallowImmutableType extends TypeImmutability_new { override def isDeepImmutable: Boolean = false override def isShallowImmutable: Boolean = true override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = false def meet(that: TypeImmutability_new): TypeImmutability_new = if (that == MutableType_new) @@ -89,11 +92,35 @@ case object ShallowImmutableType extends TypeImmutability_new { else this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == DeepImmutableType) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - } - } + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + /** + * if (other == DeepImmutableType) { + * throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + * } + * }* + */ +} + +case object DependentImmutableType extends TypeImmutability_new { + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = true + + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (that == MutableType_new) + that + else + this + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + /** + * if (other == DependentImmutableType || other == ShallowImmutableType) { + * throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + * } + * }* + */ + } case object MutableType_new extends TypeImmutability_new { @@ -101,12 +128,15 @@ case object MutableType_new extends TypeImmutability_new { override def isDeepImmutable: Boolean = false override def isShallowImmutable: Boolean = false override def isMutable: Boolean = true + override def isDependentImmutable: Boolean = false def meet(other: TypeImmutability_new): this.type = this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other != MutableType_new) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - } - } + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + /** + * if (other != MutableType_new) { + * throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + * } + * }* + */ } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 7ae9399292..994809a9cb 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -2,6 +2,7 @@ package org.opalj.br.fpcf.analyses import org.opalj.br.Field +import org.opalj.br.FieldType import org.opalj.br.ObjectType import org.opalj.br.analyses.FieldAccessInformationKey import org.opalj.br.analyses.SomeProject @@ -13,6 +14,8 @@ import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.FPCFAnalysisScheduler 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.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference @@ -42,6 +45,7 @@ case class State() { var referenceImmutability: Option[Boolean] = None var depencenciesTypes: scala.collection.mutable.Set[ObjectType] = scala.collection.mutable.Set[ObjectType]() + var dependentTypeImmutability: Option[Boolean] = None } /** @@ -75,7 +79,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - def handleTypeImmutability(objectType: ObjectType): Option[Boolean] = { + def handleTypeImmutability(objectType: FieldType)(state: State): Option[Boolean] = { if (objectType.isArrayType) return Some(true); //TODO if (objectType.isBaseType) return Some(true); val result = propertyStore(objectType, TypeImmutability_new.key) @@ -83,42 +87,52 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) result match { case FinalEP(e, DeepImmutableType) => { //ImmutableType || t == ImmutableContainerType) ⇒ { println("has deep immutable type") - Some(true) + return Some(true); + } + case FinalEP(e, DependentImmutableType) => { + println("has dependent immutable type") + state.dependentTypeImmutability = Some(true) + return Some(false); } + case FinalEP(e, ShallowImmutableType) => { println("has shallow immutable type") - Some(false) //TODO mindstorm if this approch is appropriate + return Some(false); //TODO mindstorm if this approch is appropriate } case FinalEP(e, t) if (t == MutableType_new) => { //MutableType) ⇒ { println("has mutable type") - Some(false) + return Some(false); } case x @ _ => { dependencies += x println(x) println("immutability of type couldn't be determined") - None + return None; } } } def hasImmutableType(field: Field)(state: State): Option[Boolean] = { - val hasFieldImmutableType = handleTypeImmutability(field.fieldType.asObjectType) - hasFieldImmutableType match { - case Some(false) => return Some(false); - case _ => { - state.depencenciesTypes.foreach( - t => { - val isImmutable = handleTypeImmutability(t) - isImmutable match { - case Some(false) => return Some(false); - case _ => - } - } - ) - Some(true) - } - } + //val hasFieldImmutableType = + handleTypeImmutability(field.fieldType.asFieldType)(state) + + /** + * hasFieldImmutableType match { + * case Some(false) => return Some(false); + * case _ => { + * state.depencenciesTypes.foreach( + * t => { + * val isImmutable = handleTypeImmutability(t)(state) + * isImmutable match { + * case Some(false) => return Some(false); + * case _ => + * } + * } + * ) + * Some(true) + * } + * } * + */ } def hasImmutableReference(field: Field): Option[Boolean] = { @@ -146,28 +160,34 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def createResult(state: State): ProperPropertyComputationResult = { println("reference Immutability: " + state.referenceImmutability) println("type immutabiliy: " + state.typeImmutability) + println("dependent immutability: " + state.dependentTypeImmutability) state.referenceImmutability match { case Some(false) => Result(field, MutableField) case Some(true) => { //If the field type is object. It is a generic field - //if (field.fieldType == ObjectType("java/lang/Object")) - // Result(field, DependentImmutableField) - //else - state.typeImmutability match { - case Some(true) => Result(field, DeepImmutableField) - case Some(false) => Result(field, ShallowImmutableField) - case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) - case None => { - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) + if (field.fieldType == ObjectType("java/lang/Object")) + Result(field, DependentImmutableField) + else + state.typeImmutability match { + case Some(true) => Result(field, DeepImmutableField) + case Some(false) => { + state.dependentTypeImmutability match { + case Some(true) => Result(field, DependentImmutableField) + case _ => Result(field, ShallowImmutableField) + } + } + case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) + case None => { + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } } - } } case None if (dependencies.isEmpty) => Result(field, MutableField) case None => { @@ -195,17 +215,23 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case x @ FinalEP(_, t) - if (t == DeepImmutableType || t == ShallowImmutableType) => { // ImmutableContainerType || t == ImmutableType) ⇒ { + if (t == DeepImmutableType) => { // || t == ShallowImmutableType) ⇒ { // ImmutableContainerType || t == ImmutableType) ⇒ { println(x) println("has immutable type. Determined by continuation function.") state.typeImmutability = Some(true) } - case x @ FinalEP(_, MutableType_new) => { //MutableType) ⇒ { + case x @ FinalEP(_, MutableType_new | ShallowImmutableType) => { //MutableType) ⇒ { println(x) println("has mutable type. Determined by continuation function.") state.typeImmutability = Some(false) } + case x @ FinalEP(_, DependentImmutableType) => { + println(x) + println("has dependent immutable type. Determined by continuation function.") + state.typeImmutability = Some(false) + state.dependentTypeImmutability = Some(true) + } case x @ FinalEP(_, MutableReference) => { println(x) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 4791874a69..13cf4ebc3f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -217,8 +217,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } case FinalP(ShallowImmutableField) => hasShallowImmutableFields = true case FinalEP(f, DependentImmutableField) => { - println("FieldType: " + f.asField.fieldType) - println("FieldTypeSignature: " + f.asField.fieldTypeSignature.get.toJVMSignature) + //println("FieldType: "+f.asField.fieldType) + //println("FieldTypeSignature: "+f.asField.fieldTypeSignature.get.toJVMSignature) hasDependentImmutableFields = true } case FinalP(DeepImmutableField) => diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index 1143ec8aec..848733f5aa 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -34,6 +34,7 @@ import org.opalj.br.fpcf.properties.ClassImmutability_new 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_new import org.opalj.br.fpcf.properties.ShallowImmutableClass @@ -127,8 +128,8 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP joinedImmutability = ShallowImmutableType // ImmutableContainerType maxImmutability = ShallowImmutableType //ImmutableContainerType case FinalP(DependentImmutableClass) ⇒ - joinedImmutability = ShallowImmutableType - maxImmutability = ShallowImmutableType + joinedImmutability = DependentImmutableType + maxImmutability = DependentImmutableType case eps @ InterimLUBP(lb, ub) ⇒ joinedImmutability = lb.correspondingTypeImmutability @@ -150,6 +151,10 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) maxImmutability = ShallowImmutableType //ImmutableContainerType + case FinalP(DependentImmutableType) ⇒ + joinedImmutability = joinedImmutability.meet(DependentImmutableType) + maxImmutability = DependentImmutableType + case eps @ InterimLUBP(subtypeLB, subtypeUB) ⇒ joinedImmutability = joinedImmutability.meet(subtypeLB) maxImmutability = maxImmutability.meet(subtypeUB) @@ -220,11 +225,15 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP case UBP(x) if (x == MutableType_new || x == MutableClass) ⇒ //MutableType | _: MutableObject) => Result(t, MutableType_new) //MutableType) - case FinalEP(e, x) if (x == ShallowImmutableType || x == DependentImmutableClass || x == ShallowImmutableClass) ⇒ //ImmutableContainerType | ImmutableContainer) => + case FinalEP(e, x) if (x == ShallowImmutableType || x == ShallowImmutableClass) ⇒ //ImmutableContainerType | ImmutableContainer) => maxImmutability = ShallowImmutableType //ImmutableContainerType dependencies = dependencies - e nextResult() - + case FinalEP(e, x) if (x == DependentImmutableClass || x == DependentImmutableType) ⇒ { + maxImmutability = DependentImmutableType + dependencies = dependencies - e + nextResult() + } case eps @ InterimEUBP(e, subtypeP) ⇒ dependencies = dependencies.updated(e, eps) subtypeP match { From d0e6661686c84670d46d0a3d9d7c88b616cca92b Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Mon, 25 Nov 2019 14:59:47 +0100 Subject: [PATCH 017/327] formatting respecting scalafmt --- .../opalj/fpcf/ComputationSpecification.scala | 295 +++++++++--------- 1 file changed, 145 insertions(+), 150 deletions(-) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala index 4f4ecc259c..cefd90111a 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala @@ -17,160 +17,155 @@ case class SpecificationViolation(message: String) extends Exception(message) */ trait ComputationSpecification[A] { - // - // PROPERTIES OF COMPUTATION SPECIFICATIONS - // - - /** - * Identifies this computation specification; typically the name of the class - * which implements the underlying analysis. - * - * The default name is the name of `this` class. - * - * '''This method should be overridden.''' - */ - def name: String = { - val nameCandidate = this.getClass.getSimpleName - if (nameCandidate.endsWith("$")) - nameCandidate.substring(0, nameCandidate.length() - 1) - else - nameCandidate - } - - /** - * Returns the kinds of properties which are queried by this analysis. - * - * @note This set consists only of property kinds which are directly used by the analysis. - * - * @note Self usages should also be documented. - * - * @note This method is called after - * [[org.opalj.fpcf.ComputationSpecification#init(ps:org\.opalj\.fpcf\.PropertyStore)*]] - * was called for all analyses belonging to an analysis scenario. - * (E.g., it can be used to collect the set of used property bounds based on the - * configuration choices made in other analyses.) - */ - def uses(ps: PropertyStore): Set[PropertyBounds] - - /** - * Returns the kind of the property that is lazily (on-demand) derived. - */ - def derivesLazily: Option[PropertyBounds] - - /** - * Returns the set of property kinds eagerly derived by the underlying analysis. - */ - def derivesEagerly: Set[PropertyBounds] - - def derivesCollaboratively: Set[PropertyBounds] - - def derives: Iterator[PropertyBounds] = { - derivesEagerly.iterator ++ derivesCollaboratively.iterator ++ derivesLazily.iterator - } - - require( - (derivesCollaboratively intersect derivesEagerly).isEmpty, - "a property either has to be derived eagerly or collaboratively, but not both: "+ - (derivesCollaboratively intersect derivesEagerly).mkString(", ") - ) - - require( - derivesLazily.isDefined || derivesEagerly.nonEmpty || derivesCollaboratively.nonEmpty, - "the computation does not derive any information" - ) - - require( - derivesLazily.isEmpty || (derivesEagerly.isEmpty && derivesCollaboratively.isEmpty), - "a lazy analysis cannot also derive information eagerly and/or collaboratively " - ) - - /** - * Specifies the kind of the computation that is performed. The kind restricts in which - * way the analysis is allowed to interact with the property store/other analyses. - */ - def computationType: ComputationType - - def toString(ps: PropertyStore): String = { - val uses = this.uses(ps).iterator.map(_.toSpecification).mkString("uses={", ", ", "}") - - val derivesLazily = - this.derivesLazily.iterator. - map(_.toSpecification). - mkString("derivesLazily={", ", ", "}") - val derivesEagerly = - this.derivesEagerly.iterator. - map(_.toSpecification). - mkString("derivesEagerly={", ", ", "}") - val derivesCollaboratively = - this.derivesCollaboratively.iterator. - map(_.toSpecification). - mkString("derivesCollaboratively={", ", ", "}") - - s"ComputationSpecification(name=$name,type=$computationType,"+ - s"$uses,$derivesLazily,$derivesEagerly,$derivesCollaboratively)" - - } - - override def toString: String = { - s"ComputationSpecification(name=$name,type=$computationType)" - } - - // - // LIFECYCLE RELATED METHODS - // - - /** - * The type of the data used by the analysis at initialization time. - * For analyses without special initialization requirements this type is `Null`. - */ - type InitializationData - - /** - * Called directly after the analysis is registered with an analysis scheduler; in particular - * before any analysis belonging to the same analysis scenario is scheduled – - * independent of the batch in which it will run. - * - * This enables further initialization of the computations that will eventually be executed. - * For example to initialize global configuration information. - * - * A computation specification does not have to call any methods of the property store that - * may trigger or schedule computations; i.e., it must – in particular – not call - * the methods `apply`, `schedule*`, `register*` or `waitOnPhaseCompletion`. - * - * @return The initialization data that is later on passed to schedule. - */ - def init(ps: PropertyStore): InitializationData - - /** - * Called directly before the analyses belonging to a phase are effectively scheduled. I.e., - * after phase setup, but potentially after other analyses' `beforeSchedule` method is called. - */ - def beforeSchedule(ps: PropertyStore): Unit - - /** - * Called by the scheduler to let the analysis register itself or to start execution. - */ - def schedule(ps: PropertyStore, i: InitializationData): A - - /** - * Called back after all analyses of a specific phase have been - * schedule (i.e., before calling waitOnPhaseCompletion). - */ - def afterPhaseScheduling(ps: PropertyStore, analysis: A): Unit - - /** - * Called after phase completion. - */ - def afterPhaseCompletion(ps: PropertyStore, analysis: A): Unit + // + // PROPERTIES OF COMPUTATION SPECIFICATIONS + // + + /** + * Identifies this computation specification; typically the name of the class + * which implements the underlying analysis. + * + * The default name is the name of `this` class. + * + * '''This method should be overridden.''' + */ + def name: String = { + val nameCandidate = this.getClass.getSimpleName + if (nameCandidate.endsWith("$")) + nameCandidate.substring(0, nameCandidate.length() - 1) + else + nameCandidate + } + + /** + * Returns the kinds of properties which are queried by this analysis. + * + * @note This set consists only of property kinds which are directly used by the analysis. + * + * @note Self usages should also be documented. + * + * @note This method is called after + * [[org.opalj.fpcf.ComputationSpecification#init(ps:org\.opalj\.fpcf\.PropertyStore)*]] + * was called for all analyses belonging to an analysis scenario. + * (E.g., it can be used to collect the set of used property bounds based on the + * configuration choices made in other analyses.) + */ + def uses(ps: PropertyStore): Set[PropertyBounds] + + /** + * Returns the kind of the property that is lazily (on-demand) derived. + */ + def derivesLazily: Option[PropertyBounds] + + /** + * Returns the set of property kinds eagerly derived by the underlying analysis. + */ + def derivesEagerly: Set[PropertyBounds] + + def derivesCollaboratively: Set[PropertyBounds] + + def derives: Iterator[PropertyBounds] = { + derivesEagerly.iterator ++ derivesCollaboratively.iterator ++ derivesLazily.iterator + } + + require( + (derivesCollaboratively intersect derivesEagerly).isEmpty, + "a property either has to be derived eagerly or collaboratively, but not both: " + + (derivesCollaboratively intersect derivesEagerly).mkString(", ") + ) + + require( + derivesLazily.isDefined || derivesEagerly.nonEmpty || derivesCollaboratively.nonEmpty, + "the computation does not derive any information" + ) + + require( + derivesLazily.isEmpty || (derivesEagerly.isEmpty && derivesCollaboratively.isEmpty), + "a lazy analysis cannot also derive information eagerly and/or collaboratively " + ) + + /** + * Specifies the kind of the computation that is performed. The kind restricts in which + * way the analysis is allowed to interact with the property store/other analyses. + */ + def computationType: ComputationType + + def toString(ps: PropertyStore): String = { + val uses = this.uses(ps).iterator.map(_.toSpecification).mkString("uses={", ", ", "}") + + val derivesLazily = + this.derivesLazily.iterator.map(_.toSpecification).mkString("derivesLazily={", ", ", "}") + val derivesEagerly = + this.derivesEagerly.iterator.map(_.toSpecification).mkString("derivesEagerly={", ", ", "}") + val derivesCollaboratively = + this.derivesCollaboratively.iterator + .map(_.toSpecification) + .mkString("derivesCollaboratively={", ", ", "}") + + s"ComputationSpecification(name=$name,type=$computationType," + + s"$uses,$derivesLazily,$derivesEagerly,$derivesCollaboratively)" + } + + override def toString: String = { + s"ComputationSpecification(name=$name,type=$computationType)" + } + + // + // LIFECYCLE RELATED METHODS + // + + /** + * The type of the data used by the analysis at initialization time. + * For analyses without special initialization requirements this type is `Null`. + */ + type InitializationData + + /** + * Called directly after the analysis is registered with an analysis scheduler; in particular + * before any analysis belonging to the same analysis scenario is scheduled – + * independent of the batch in which it will run. + * + * This enables further initialization of the computations that will eventually be executed. + * For example to initialize global configuration information. + * + * A computation specification does not have to call any methods of the property store that + * may trigger or schedule computations; i.e., it must – in particular – not call + * the methods `apply`, `schedule*`, `register*` or `waitOnPhaseCompletion`. + * + * @return The initialization data that is later on passed to schedule. + */ + def init(ps: PropertyStore): InitializationData + + /** + * Called directly before the analyses belonging to a phase are effectively scheduled. I.e., + * after phase setup, but potentially after other analyses' `beforeSchedule` method is called. + */ + def beforeSchedule(ps: PropertyStore): Unit + + /** + * Called by the scheduler to let the analysis register itself or to start execution. + */ + def schedule(ps: PropertyStore, i: InitializationData): A + + /** + * Called back after all analyses of a specific phase have been + * schedule (i.e., before calling waitOnPhaseCompletion). + */ + def afterPhaseScheduling(ps: PropertyStore, analysis: A): Unit + + /** + * Called after phase completion. + */ + def afterPhaseCompletion(ps: PropertyStore, analysis: A): Unit } trait SimpleComputationSpecification[A] extends ComputationSpecification[A] { - final override type InitializationData = Null - final override def init(ps: PropertyStore): Null = null - final override def beforeSchedule(ps: PropertyStore): Unit = {} - final override def afterPhaseScheduling(ps: PropertyStore, analysis: A): Unit = {} - final override def afterPhaseCompletion(ps: PropertyStore, analysis: A): Unit = {} + final override type InitializationData = Null + final override def init(ps: PropertyStore): Null = null + final override def beforeSchedule(ps: PropertyStore): Unit = {} + final override def afterPhaseScheduling(ps: PropertyStore, analysis: A): Unit = {} + final override def afterPhaseCompletion(ps: PropertyStore, analysis: A): Unit = {} } From be67d7b7288ea4159206392181f35c7df97f671e Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 26 Nov 2019 11:30:33 +0100 Subject: [PATCH 018/327] test error fix --- .../main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala index f6b55077e3..95537737ac 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala @@ -4,14 +4,13 @@ package br package fpcf import com.typesafe.config.Config - import org.opalj.log.LogContext import org.opalj.log.OPALLogger import org.opalj.concurrent.NumberOfThreadsForCPUBoundTasks import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.PropertyStoreContext -import org.opalj.fpcf.par.PKECPropertyStore -//import org.opalj.fpcf.seq.PKESequentialPropertyStore +//import org.opalj.fpcf.par.PKECPropertyStore +import org.opalj.fpcf.seq.PKESequentialPropertyStore import org.opalj.br.analyses.ProjectInformationKey import org.opalj.br.analyses.SomeProject @@ -60,8 +59,8 @@ object PropertyStoreKey ) psFactory(context) case None ⇒ - //val ps = PKESequentialPropertyStore(context: _*) - val ps = PKECPropertyStore(context: _*) + val ps = PKESequentialPropertyStore(context: _*) + //val ps = PKECPropertyStore(context: _*) ps } } From 661294486d3babb0ee459fa19665ed5a53441b58 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 5 Dec 2019 22:12:08 +0100 Subject: [PATCH 019/327] Dependent field and dependent class approach using signature --- .../ClassImmutabilityAnalysisDemo.scala | 85 +- .../FieldImmutabilityAnalysisDemo.scala | 23 +- .../TypeImmutabilityAnalysisDemo.scala | 32 +- .../GenericAndDeepImmutableFields.java | 4 +- .../GenericAndMutableFields.java | 2 +- .../GenericAndShallowImmutableFields.java | 4 +- .../class_immutability/Generic_class1.java | 10 +- .../DependentImmutableClassAnnotation.java | 3 +- .../DependentImmutableFieldAnnotation.java | 2 + .../opalj/fpcf/FieldImmutabilityTests.scala | 2 - .../ClassImmutabilityMatcher.scala | 18 +- .../FieldImmutabilityMatcher.scala | 10 +- .../fpcf/properties/ClassImmutability.scala | 67 +- .../properties/ClassImmutability_new.scala | 33 +- .../fpcf/properties/FieldImmutability.scala | 15 +- .../L0FieldImmutabilityAnalysis.scala | 26 +- .../L0ReferenceImmutabilityAnalysis.scala | 95 +- .../analyses/L2FieldMutabilityAnalysis.scala | 1780 ++++++++--------- .../LxClassImmutabilityAnalysis_new.scala | 55 +- .../LxTypeImmutabilityAnalysis_new.scala | 444 ++-- 20 files changed, 1450 insertions(+), 1260 deletions(-) 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 index d7ba9e7954..447d52c55d 100644 --- 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 @@ -12,8 +12,8 @@ import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.ClassImmutability_new 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 @@ -29,51 +29,52 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis; */ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" - override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(project: Project[URL]): String = { + def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) + val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + analysesManager.project.get(RTACallGraphKey) - val (propertyStore, _) = analysesManager.runAll( - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new - ) - "Mutable Class: "+propertyStore - .finalEntities(MutableClass) - .toList - .toString()+"\n"+ - "Dependent Immutable Class: "+propertyStore - .finalEntities(DependentImmutableClass) - .toList - .toString()+"\n"+ - "Shallow Immutable Class: "+propertyStore - .finalEntities(ShallowImmutableClass) - .toList - .toString()+"\n"+ - "Deep Immutable Class: "+propertyStore - .finalEntities(DeepImmutableClass) - .toList - .toString()+"\n" - } + val (propertyStore, _) = analysesManager.runAll( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new + ) + + "Mutable Class: " + propertyStore + .finalEntities(MutableClass) + .toList + .toString() + "\n" + + "Dependent Immutable Class: " + propertyStore + .entities(ClassImmutability_new.key) + .toList + .toString() + "\n" + + "Shallow Immutable Class: " + propertyStore + .finalEntities(ShallowImmutableClass) + .toList + .toString() + "\n" + + "Deep Immutable Class: " + propertyStore + .finalEntities(DeepImmutableClass) + .toList + .toString() + "\n" + } } 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 index 4da81148ed..dfc0b5aed7 100644 --- 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 @@ -13,10 +13,13 @@ import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis 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.FieldImmutability 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.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis @@ -44,6 +47,8 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { def analyze(project: Project[URL]): String = { val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + val (propertyStore, _) = analysesManager.runAll( /** * LazyTypeImmutabilityAnalysis, @@ -57,6 +62,8 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { * LazyLxTypeImmutabilityAnalysis_new, * EagerL0FieldImmutabilityAnalysis* */ + + LazyLxClassImmutabilityAnalysis_new, LazyTypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, @@ -76,12 +83,24 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .toList .toString() + "\n" + "Dependet Immutable Fields:" + propertyStore - .finalEntities(DependentImmutableField) + .finalEntities(DependentImmutableField(None)) .toList .toString() + "\n" + "Deep Immutable Fields: " + propertyStore .finalEntities(DeepImmutableField) .toList - .toString() + .toString() + "\n" + + propertyStore + .entities(FieldImmutability.key) + /** + * .entities(x ⇒ { + * x.asEPS.pk match { + * case DependentImmutableField(_) ⇒ true + * case _ ⇒ false + * } + * })* + */ + .toList + .toString } } 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 index 5b54015926..5793b66312 100644 --- 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 @@ -7,19 +7,20 @@ 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.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyL0PurityAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis 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_new import org.opalj.br.fpcf.properties.ShallowImmutableType -//import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +//import org.opalj.br.fpcf.properties.ShallowImmutableType import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -45,15 +46,28 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + val (propertyStore, _) = analysesManager.runAll( - LazyTypeImmutabilityAnalysis, + /** + * LazyTypeImmutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL0ReferenceImmutabilityAnalysis, + * LazyL0PurityAnalysis, + * LazyL2FieldMutabilityAnalysis, + * LazyL0FieldImmutabilityAnalysis, + * EagerLxClassImmutabilityAnalysis_new, + * EagerLxTypeImmutabilityAnalysis_new* + */ + //LazyTypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0PurityAnalysis, + LazyL2PurityAnalysis, LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, - EagerLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new ) "Mutable Type: "+propertyStore .finalEntities(MutableType_new) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java index 4510b6b51c..acd93a1b2d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java @@ -11,11 +11,11 @@ @DependentImmutableClassAnnotation("") public final class GenericAndDeepImmutableFields { - @DependentImmutableFieldAnnotation("") + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") @ImmutableReferenceAnnotation("") private T1 t1; - @DependentImmutableFieldAnnotation("") + @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") @ImmutableReferenceAnnotation("") private T2 t2; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java index 05fcf78ccb..d80fcf4ac2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java @@ -14,7 +14,7 @@ public class GenericAndMutableFields { @MutableFieldAnnotation("Because of mutable reference") @MutableReferenceAnnotation("Because of public field") public T1 t1; - @DependentImmutableFieldAnnotation("Because of generic type") + @DependentImmutableFieldAnnotation(value = "Because of generic type", genericString = "T2") @ImmutableReferenceAnnotation("Because of effectively immutable final") private T2 t2; GenericAndMutableFields(T1 t1, T2 t2){ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java index 1905bbc540..1ffd179a83 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java @@ -12,9 +12,9 @@ @ShallowImmutableClassAnnotation("") public class GenericAndShallowImmutableFields { - @DependentImmutableFieldAnnotation("") + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") private T1 t1; - @DependentImmutableFieldAnnotation("") + @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") private T2 t2; @ShallowImmutableFieldAnnotation("") private TrivialMutableClass tmc; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java index 0e7cfcf3d3..eef7c43847 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java @@ -8,19 +8,19 @@ @DependentImmutableTypeAnnotation("Dependent Immutability of T1,...,T5") @DependentImmutableClassAnnotation("Dependent Immutability of T1,...,T5") public final class Generic_class1 { - @DependentImmutableFieldAnnotation("T1") + @DependentImmutableFieldAnnotation(value = "T1",genericString = "T1") @ImmutableReferenceAnnotation("effectively") private T1 t1; - @DependentImmutableFieldAnnotation("T2") + @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") @ImmutableReferenceAnnotation("effectively") private T2 t2; - @DependentImmutableFieldAnnotation("T3") + @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") @ImmutableReferenceAnnotation("effectively") private T3 t3; - @DependentImmutableFieldAnnotation("T4") + @DependentImmutableFieldAnnotation(value = "T4", genericString = "T3") @ImmutableReferenceAnnotation("effectively") private T4 t4; - @DependentImmutableFieldAnnotation("T5") + @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") @ImmutableReferenceAnnotation("effectively") private T5 t5; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java index e1925a8bc5..d2f047a50b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java @@ -1,11 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability; +import javafx.util.Pair; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; - import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java index eb76bef20c..ed0bc13b0e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java @@ -24,6 +24,8 @@ */ String value() ; // default = "N/A"; + String genericString(); + Class[] analyses() default { L0FieldImmutabilityAnalysis.class }; 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 index a5cbceb83e..430b923e33 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -8,7 +8,6 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.Project import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis @@ -39,7 +38,6 @@ class FieldImmutabilityTests extends PropertiesTest { describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { val as = executeAnalyses( Set( - LazyTypeImmutabilityAnalysis, LazyL2FieldMutabilityAnalysis, LazyL0ReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala index 188a95ec8d..fac97feb4c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala @@ -44,24 +44,26 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) a: AnnotationLike, properties: Traversable[Property] ): Option[String] = { - println(11) - if (!properties.exists(p ⇒ p == property)) { + if (!properties.exists(p ⇒ { + p match { + case DependentImmutableClass(_) if property == DependentImmutableClass() ⇒ true + case _ if p == property ⇒ true + case _ ⇒ false + } + })) { //p == property)) { // ... when we reach this point the expected property was not found. - println(22) - val r = Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) - println(33) - r + Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) } else { - println(44) None } + } } class MutableClassMatcher extends ClassImmutabilityMatcher(MutableClass) -class DependentImmutableClassMatcher extends ClassImmutabilityMatcher(DependentImmutableClass) +class DependentImmutableClassMatcher extends ClassImmutabilityMatcher(DependentImmutableClass()) class ShallowImmutableClassMatcher extends ClassImmutabilityMatcher(ShallowImmutableClass) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala index 64e406fb58..5d27f81213 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala @@ -42,7 +42,13 @@ class FieldImmutabilityMatcher(val property: FieldImmutability) extends Abstract a: AnnotationLike, properties: Traversable[Property] ): Option[String] = { - if (!properties.exists(p ⇒ p == property)) { + if (!properties.exists(p ⇒ { + p match { + case DependentImmutableField(_) if property == DependentImmutableField() ⇒ true + case _ if p == property ⇒ true + case _ ⇒ false + } + })) { //p == property)) { // ... when we reach this point the expected property was not found. Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) } else { @@ -56,6 +62,6 @@ class MutableFieldMatcher extends FieldImmutabilityMatcher(MutableField) class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(ShallowImmutableField) -class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(DependentImmutableField) +class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(DependentImmutableField()) class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(DeepImmutableField) 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..3c085c8897 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 @@ -11,7 +11,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - final type Self = ClassImmutability + final type Self = ClassImmutability } @@ -86,25 +86,26 @@ sealed trait ClassImmutability extends OrderedProperty with ClassImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ClassImmutability] = ClassImmutability.key + final def key: PropertyKey[ClassImmutability] = ClassImmutability.key - def correspondingTypeImmutability: TypeImmutability + def correspondingTypeImmutability: TypeImmutability - /** `true` if instances of the class are mutable. */ - def isMutable: Boolean + /** `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 { - /** - * The key associated with every [[ClassImmutability]] property. - */ - final val key: PropertyKey[ClassImmutability] = PropertyKey.create( - "opalj.ClassImmutability", - MutableObjectDueToUnresolvableDependency - ) + /** + * The key associated with every [[ClassImmutability]] property. + */ + final val key: PropertyKey[ClassImmutability] = PropertyKey.create( + "opalj.ClassImmutability", + MutableObjectDueToUnresolvableDependency + ) } /** @@ -117,11 +118,11 @@ object ClassImmutability extends ClassImmutabilityPropertyMetaInformation { */ case object ImmutableObject extends ClassImmutability { - final val correspondingTypeImmutability = ImmutableType + final val correspondingTypeImmutability = ImmutableType - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - final def isMutable: Boolean = false + final def isMutable: Boolean = false } /** @@ -131,45 +132,45 @@ case object ImmutableObject extends ClassImmutability { */ case object ImmutableContainer extends ClassImmutability { - final val correspondingTypeImmutability = ImmutableContainerType + final val correspondingTypeImmutability = ImmutableContainerType - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == ImmutableObject) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - } + 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 + final def isMutable: Boolean = false } sealed trait MutableObject extends ClassImmutability { - def reason: String - final val correspondingTypeImmutability = MutableType + def reason: String + final val correspondingTypeImmutability = MutableType - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == ImmutableObject || other == ImmutableContainer) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") - } + 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 def isMutable: Boolean = true - final override def toString: String = s"MutableObject(reason=$reason)" + final override def toString: String = s"MutableObject(reason=$reason)" } case object MutableObjectDueToIncompleteAnalysis extends MutableObject { - final def reason = "analysis has not yet completed" + final def reason = "analysis has not yet completed" } case object MutableObjectByAnalysis extends MutableObject { - final def reason = "determined by analysis" + final def reason = "determined by analysis" } case object MutableObjectDueToUnknownSupertypes extends MutableObject { - final def reason = "the type hierarchy is upwards incomplete" + final def reason = "the type hierarchy is upwards incomplete" } case object MutableObjectDueToUnresolvableDependency extends MutableObject { - final def reason = "a dependency cannot be resolved" + final def reason = "a dependency cannot be resolved" } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index 50239f389b..b4977729a9 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -6,7 +6,7 @@ import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = ClassImmutability_new + final type Self = ClassImmutability_new } /** @@ -27,34 +27,35 @@ sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaIn sealed trait ClassImmutability_new extends Property with ClassImmutabilityPropertyMetaInformation_new { - final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key - def correspondingTypeImmutability: TypeImmutability_new + final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key + def correspondingTypeImmutability: TypeImmutability_new } object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[ClassImmutability_new]] property. - */ - final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( - "opalj.ClassImmutability_new", - MutableClass - ) + /** + * The key associated with every [[ClassImmutability_new]] property. + */ + final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( + "opalj.ClassImmutability_new", + MutableClass + ) } case object MutableClass extends ClassImmutability_new { - def correspondingTypeImmutability = MutableType_new + def correspondingTypeImmutability = MutableType_new } -case object DependentImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = - DependentImmutableType //TODO check +case class DependentImmutableClass(typeBounds: Set[(String, String)] = Set.empty) + extends ClassImmutability_new { + override def correspondingTypeImmutability: TypeImmutability_new = + DependentImmutableType //TODO check } case object ShallowImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = ShallowImmutableType + override def correspondingTypeImmutability: TypeImmutability_new = ShallowImmutableType } case object DeepImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType + override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType } 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 index f6bbffb40b..23b7699fad 100644 --- 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 @@ -46,6 +46,17 @@ sealed trait ImmutableField extends FieldImmutability case object ShallowImmutableField extends ImmutableField -case object DependentImmutableField extends ImmutableField - +case class DependentImmutableField(var genericString: Option[String] = None) + extends ImmutableField + with FieldImmutability //{ +// def genericString: Option[String] = None +//} +/** + * case object DependentImmutableField extends DependentImmutableField_ { + * def setGenericString(s: Option[String]) = { + * genericString = s + * this + * } + * }* + */ case object DeepImmutableField extends ImmutableField diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 994809a9cb..79b8f1f129 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -4,6 +4,7 @@ package org.opalj.br.fpcf.analyses import org.opalj.br.Field import org.opalj.br.FieldType import org.opalj.br.ObjectType +import org.opalj.br.TypeVariableSignature import org.opalj.br.analyses.FieldAccessInformationKey import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.ClosedPackagesKey @@ -77,6 +78,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) field: Field ): ProperPropertyComputationResult = { + println("Field Attributes: " + field.asField.attributes.toList.collectFirst({ + case TypeVariableSignature(t) => t + })) + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty def handleTypeImmutability(objectType: FieldType)(state: State): Option[Boolean] = { @@ -91,6 +96,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case FinalEP(e, DependentImmutableType) => { println("has dependent immutable type") + //TODO under construction state.dependentTypeImmutability = Some(true) return Some(false); } @@ -166,14 +172,26 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(false) => Result(field, MutableField) case Some(true) => { //If the field type is object. It is a generic field - if (field.fieldType == ObjectType("java/lang/Object")) - Result(field, DependentImmutableField) - else + //Test Dependent... //TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //if (field.fieldType == ObjectType("java/lang/Object")) { + val genericString = field.asField.attributes.toList.collectFirst({ + case TypeVariableSignature(t) => t + }) + if (!field.attributes.isEmpty && genericString != None) { + + //if(genericString!=None) + // println("Generic String: "+genericString) + // println( + // "test: "+DependentImmutableField(genericString).genericString + // } + Result(field, DependentImmutableField(genericString)) //genericString)) + + } else state.typeImmutability match { case Some(true) => Result(field, DeepImmutableField) case Some(false) => { state.dependentTypeImmutability match { - case Some(true) => Result(field, DependentImmutableField) + case Some(true) => Result(field, DependentImmutableField()) case _ => Result(field, ShallowImmutableField) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index 6fc3cc9170..c2ad3ac191 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -601,7 +601,14 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec taCode.stmts, taCode.cfg, taCode.pcToIndex - )) + ) /**&& !isDoubleCheckedLocking( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )**/ ) return true; state.referenceImmutability = LazyInitializedReference //LazyInitializedField @@ -910,15 +917,16 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec defaultValue: Any, code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Option[(Int, Int, Int)] = { + ): Option[(Int, Int, Int)] = { val startBB = cfg.bb(fieldWrite).asBasicBlock var enqueuedBBs: Set[CFGNode] = startBB.predecessors var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) var result: Option[(Int, Int)] = None - + println("worklist initial: "+worklist) while (worklist.nonEmpty) { + println("worklist "+worklist) val curBB = worklist.head worklist = worklist.tail @@ -929,8 +937,13 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec (cfStmt.astID: @switch) match { case If.ASTID ⇒ val ifStmt = cfStmt.asIf + println("ifstmt: "+ifStmt) + println("curBB: "+curBB) + println("startBB: "+startBB) + println("result.isDefined "+result.isDefined) + println("result: "+result) ifStmt.condition match { - case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + case EQ if curBB != startBB ⇒ //&& isGuard(ifStmt, defaultValue, code) ⇒ if (result.isDefined) { if (result.get._1 != endPC || result.get._2 != endPC + 1) return None; @@ -938,7 +951,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec result = Some((endPC, endPC + 1)) } - case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + case NE if curBB != startBB ⇒ //&& isGuard(ifStmt, defaultValue, code) ⇒ if (result.isDefined) { if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) return None; @@ -1037,7 +1050,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec isGuardInternal(ifStmt.rightExpr.asVar) } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { isGuardInternal(ifStmt.leftExpr.asVar) - } else false + } else + isGuardInternal(ifStmt.leftExpr.asVar) || isGuardInternal(ifStmt.rightExpr.asVar) // || false //TODO } /** @@ -1087,6 +1101,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec /** * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ + * * true * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * */ @@ -1094,8 +1109,74 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec state.referenceImmutabilityDependees += eop true } -} + //-------------------------------------------------- double checked locking + def isDoubleCheckedLocking( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + )(implicit state: State): Boolean = { + //val a = state.field + //val write = code(writeIndex).asFieldWriteAccessStmt + //println("Purity Information: "+propertyStore(declaredMethods(method), Purity.key)) + //println(isNonDeterministic(propertyStore(declaredMethods(method), Purity.key))) + if (!method.isStatic || !state.field.isStatic) { + false + } else { + //----------------------------- + /** + * val reads = fieldAccessInformation.readAccesses(state.field) + * if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + * return false; // Reads outside the (single) lazy initialization method + * } + * + * // There must be a guarding if-Statement + * // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + * // first statement that is executed after the if-Statement if the field's value was not the + * // default value + * val (guardIndex, guardedIndex, readIndex) = + * findGuard(writeIndex, defaultValue, code, cfg) match { + * case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + * case None ⇒ return false; + * } + * // Detect only simple patterns where the lazily initialized value is returned immediately + * // if (!checkImmediateReturn(write, writeIndex, readIndex, code)) + * // return false; + * + * println( + * "Check writes (is double checked locking): "+checkWrites( + * write, + * writeIndex, + * guardIndex, + * guardedIndex, + * method, + * code, + * cfg + * ) + * ) + * // The value written must be computed deterministically and the writes guarded correctly + * if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + * return false; + * + * // Field reads (except for the guard) may only be executed if the field's value is not the + * // default value + * if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + * return false; + * + * true + */ + //----------------------------- + + //} + + false + } + } + //-------------------------------------------------- double checked locking +} trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { final override def uses: Set[PropertyBounds] = Set( 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/L2FieldMutabilityAnalysis.scala index 571fb9bb6f..c703fb9427 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/L2FieldMutabilityAnalysis.scala @@ -86,200 +86,200 @@ import org.opalj.tac.fpcf.properties.TACAI */ class L2FieldMutabilityAnalysis 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 - ) { - def hasDependees: Boolean = { - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || fieldMutabilityDependees.nonEmpty || - escapeDependees.nonEmpty || tacDependees.nonEmpty + 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 + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || fieldMutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty + } + + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + fieldMutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + } } - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - fieldMutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + 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) + + def doDetermineFieldMutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field ⇒ determineFieldMutability(field) + case _ ⇒ + val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) } - } - - 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) - - def doDetermineFieldMutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field => determineFieldMutability(field) - case _ => - val m = entity.getClass.getSimpleName + " is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } - - /** - * Analyzes the mutability of private non-final fields. - * - * 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 determineFieldMutability( - field: Field - ): ProperPropertyComputationResult = { - 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 (field.isFinal) - return createResult(); - - state.fieldMutability = EffectivelyFinalField - - val thisType = field.classFile.thisType - - if (field.isPublic) - return Result(field, NonFinalFieldByLackOfInformation) - - // 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) { - if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, NonFinalFieldByLackOfInformation); - } - project.classesPerPackage(thisType.packageName) - } else { - Set(field.classFile) - } - - val classesHavingAccess: Iterator[ClassFile] = - if (field.isProtected) { - if (typeExtensibility(thisType).isYesOrUnknown) { - return Result(field, NonFinalFieldByLackOfInformation); - } - 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))) - return Result(field, NonFinalFieldByLackOfInformation); - - // 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 - // of the class after the reference to the class and (an indirect) reference to the - // field has become available. Consider the following example: - // class X implements Y{ - // - // private Object o; - // - // public Object getO() { return o; } - // - // private static X instance; - // static { - // instance = new X(); - // Z.register(instance); - // // when we reach this point o is now (via getO) publically accessible and - // // X is properly initialized! - // o = new Object(); // o is mutated... - // } - // } /** - * Returns the TACode for a method if available, registering dependencies as necessary. + * Analyzes the mutability of private non-final fields. + * + * 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. */ - 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 eps: InterimEP[Method, TACAI] => - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk => - state.tacDependees += method -> ((epk, pcs)) - None - } - } + private[analyses] def determineFieldMutability( + field: Field + ): ProperPropertyComputationResult = { + implicit val state: State = State(field) - for { - (method, pcs) <- fieldAccessInformation.writeAccesses(field) - taCode <- getTACAI(method, pcs) - } { - if (methodUpdatesField(method, taCode, pcs)) - return Result(field, NonFinalFieldByAnalysis); - } + // Fields are not final if they are read prematurely! + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) + return Result(field, NonFinalFieldByAnalysis); + + if (field.isFinal) + return createResult(); + + state.fieldMutability = EffectivelyFinalField + + val thisType = field.classFile.thisType + + if (field.isPublic) + return Result(field, NonFinalFieldByLackOfInformation) + + // 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) { + if (!closedPackages.isClosed(thisType.packageName)) { + return Result(field, NonFinalFieldByLackOfInformation); + } + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } + + val classesHavingAccess: Iterator[ClassFile] = + if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown) { + return Result(field, NonFinalFieldByLackOfInformation); + } + 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))) + return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode ← getTACAI(method, pcs) + } { + if (methodUpdatesField(method, taCode, pcs)) + return Result(field, NonFinalFieldByAnalysis); + } - if (state.lazyInitInvocation.isDefined) { - val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - handleCalls(calleesEOP) + if (state.lazyInitInvocation.isDefined) { + val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + handleCalls(calleesEOP) + } + + createResult() } - createResult() - } - - def handleCalls( - calleesEOP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: State - ): Boolean = { - calleesEOP match { - case FinalP(callees) => - state.calleesDependee = None - handleCallees(callees) - case InterimUBP(callees) => - state.calleesDependee = Some(calleesEOP) - handleCallees(callees) - case _ => - state.calleesDependee = Some(calleesEOP) - false + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: State + ): Boolean = { + calleesEOP match { + case FinalP(callees) ⇒ + state.calleesDependee = None + handleCallees(callees) + case InterimUBP(callees) ⇒ + state.calleesDependee = Some(calleesEOP) + handleCallees(callees) + case _ ⇒ + state.calleesDependee = Some(calleesEOP) + false + } } - } - - def handleCallees(callees: Callees)(implicit state: State): Boolean = { - val pc = state.lazyInitInvocation.get._2 - if (callees.isIncompleteCallSite(pc)) { - state.fieldMutability = NonFinalFieldByAnalysis - true - } else { - val targets = callees.callees(pc).toTraversable - if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { - state.fieldMutability = NonFinalFieldByAnalysis - true - } else false + + def handleCallees(callees: Callees)(implicit state: State): Boolean = { + val pc = state.lazyInitInvocation.get._2 + if (callees.isIncompleteCallSite(pc)) { + state.fieldMutability = NonFinalFieldByAnalysis + true + } else { + val targets = callees.callees(pc).toTraversable + if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { + state.fieldMutability = NonFinalFieldByAnalysis + true + } else false + } } - } - /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. - */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field * The code below can be used to identify such value, but is not yet adapted to using the * TACAI property */ - /* + /* var constantVal: Option[Any] = None var allInitializeConstant = true @@ -338,752 +338,752 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } constantVal */ - } - - /** - * 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.fieldMutability ne NonFinalFieldByAnalysis)) - InterimResult( - state.field, - NonFinalFieldByAnalysis, - state.fieldMutability, - state.dependees, - c - ) - else - Result(state.field, state.fieldMutability) - } - - /** - * 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 = { - val isNotFinal = eps.pk match { - case EscapeProperty.key => - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - 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)) - methodUpdatesField(method, newEP.ub.tac.get, pcs) - case Callees.key => - handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key => - isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key => - 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) - !isFinalField(newEP) } - if (isNotFinal) - Result(state.field, NonFinalFieldByAnalysis) - else - createResult() - } - - /** - * Checks whether a field write may be a lazy initialization. - * - * We only consider simple cases of lazy initialization where the single field write is guarded - * so it is executed only if the field still has its default value and where the written value - * is guaranteed to be the same even if the write is executed multiple times (may happen because - * of the initializing method being executed more than once on concurrent threads). - * - * Also, all field reads must be used only for a guard or their uses have to be guarded - * themselves. - */ - def isLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - )(implicit state: State): Boolean = { - val write = code(writeIndex).asFieldWriteAccessStmt - - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - // Only handle lazy initialization of ints and floats as they are guaranteed to be - // written atomically - return false; + /** + * 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.fieldMutability ne NonFinalFieldByAnalysis)) + InterimResult( + state.field, + NonFinalFieldByAnalysis, + state.fieldMutability, + state.dependees, + c + ) + else + Result(state.field, state.fieldMutability) + } + + /** + * 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 = { + val isNotFinal = eps.pk match { + case EscapeProperty.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + methodUpdatesField(method, newEP.ub.tac.get, pcs) + case Callees.key ⇒ + handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key ⇒ + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key ⇒ + 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) + !isFinalField(newEP) + } + + if (isNotFinal) + Result(state.field, NonFinalFieldByAnalysis) + else + createResult() + } + + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + )(implicit state: State): Boolean = { + val write = code(writeIndex).asFieldWriteAccessStmt + + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + return false; + } + + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method + } + + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + case None ⇒ return false; + } + + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) + return false; + + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + + true } - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs => (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return false; // Reads outside the (single) lazy initialization method + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val definitions = write.value.asVar.definedBy + + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + // The field write must be guarded correctly + isDeterministic && + checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID ⇒ + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID ⇒ + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ ⇒ return false; // neither a field read nor a return + } + index += 1 + } + false } - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val (guardIndex, guardedIndex, readIndex) = - findGuard(writeIndex, defaultValue, code, cfg) match { - case Some((guard, guarded, read)) => (guard, guarded, read) - case None => return false; - } - - // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) - return false; - - // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - return false; - - // Field reads (except for the guard) may only be executed if the field's value is not the - // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - return false; - - true - } - - def checkWrites( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val definitions = write.value.asVar.definedBy - - val isDeterministic = - if (definitions.size == 1) { - // The value written must be computed deterministically - checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) - } else { - // More than one definition site for the value might lead to differences between - // invocations, but not if this method has no parameters and is deterministic - // (in this case, the definition reaching the write will always be the same) + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - - // The field write must be guarded correctly - isDeterministic && - checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - } - - /** - * Checks if the field write for a lazy initialization is immediately followed by a return of - * the written value (possibly loaded by another field read). - */ - def checkImmediateReturn( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 - - var load = -1 - - while (index < code.length) { - val stmt = code(index) - stmt.astID match { - case Assignment.ASTID => - if (isReadOfCurrentField(stmt.asAssignment.expr)) - load = index - else - return false; // No field read or a field read of a different field - case ReturnValue.ASTID => - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) - return true; // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) - return true; // return of field value loaded by field read - else - return false; // return of different value - case _ => return false; // neither a field read nor a return - } - index += 1 + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) } - false - } - - def lazyInitializerIsDeterministic( - method: Method, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - - /** - * 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 - for (pc <- pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID => - if (method.isInitializer) { - if (field.isStatic) { - if (method.isConstructor) - return true; + + /** + * 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 + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + if (field.isStatic) { + if (method.isConstructor) + return true; + } else { + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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; + + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + // A field written outside an initializer must be lazily + // initialized or it is non-final + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )) + return true; + + state.fieldMutability = LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - if (receiverDefs != SelfReferenceParameter) - return true; + // nothing to do as the put field is dead } - } 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; - - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - // A field written outside an initializer must be lazily - // initialized or it is non-final - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - )) - return true; - - state.fieldMutability = LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - // 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. - return true; - } - } - case _ => throw new RuntimeException("unexpected field access"); - } - } else { - // nothing to do as the put field is dead + } } - } - } - false - } - - /** - * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] => - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk => - state.tacDependees += method -> ((epk, pcs)) - None + 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 // Must be locally created - else { - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - handleEscapeProperty(escape) + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, 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 handleEscapeProperty( - ep: EOptionP[DefinitionSite, EscapeProperty] - )(implicit state: State): Boolean = 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 - } - - /** - * Checks if the field write is only executed if the field's value was still the default value. - * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization - * and the field write. - */ - def checkWriteIsGuarded( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val startBB = cfg.bb(writeIndex).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt => - stmt.astID == CaughtException.ASTID - } flatMap { exception => - exception.asCaughtException.origins.map { origin: Int => - if (isImmediateVMException(origin)) - pcOfImmediateVMException(origin) - else - pcOfMethodExternalException(origin) - }.iterator + + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) + } + } + } } - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail + /** + * 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 = ep match { + case FinalP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ + false - val startPC = curBB.startPC - val endPC = curBB.endPC + case FinalP(AtMost(_)) ⇒ + true - if (startPC == 0 || startPC == guardedIndex) - return false; // Reached method start or wrong branch of guarding if-Statement + case _: FinalEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + case InterimUBP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ + state.escapeDependees += ep + false + + case InterimUBP(AtMost(_)) ⇒ + true - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + case _: InterimEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors + case _ ⇒ + state.escapeDependees += ep + false } - true - } - - /** - * 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]] - )(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(defSites.head).asAssignment.expr.isConst) true - else { - val expr = code(index).asAssignment.expr - expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { - case Some(field) => - isFinalField(propertyStore(field, FieldMutability.key)) - case _ => // Unknown field - false - }) + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt ⇒ + stmt.astID == CaughtException.ASTID + } flatMap { exception ⇒ + exception.asCaughtException.origins.map { origin: Int ⇒ + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator + } + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement + + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors } - } - defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) + true } - val value = origin.expr + /** + * 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]] + )(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(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isFinalField(propertyStore(field, FieldMutability.key)) + case _ ⇒ // Unknown field + false + }) + } + } - val isNonConstDeterministic = value.astID match { - case GetStatic.ASTID | GetField.ASTID => - value.asFieldRead.resolveField(p) match { - case Some(field) => - isFinalField(propertyStore(field, FieldMutability.key)) - case _ => // Unknown field - false + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) } - 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.exists(!isConstant(_))) { - false - } else { - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true + + val value = origin.expr + + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID ⇒ + value.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isFinalField(propertyStore(field, FieldMutability.key)) + case _ ⇒ // Unknown field + false + } + 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.exists(!isConstant(_))) { + false + } else { + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true + } + 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, code) } - 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, code) - } - value.isConst || isNonConstDeterministic - } - - /** - * Checks that all non-dead field reads that are not used for the guarding if-Statement of a - * lazy initialization are only executed if the field did not have its default value or after - * the (single) field write. - */ - def checkReads( - reads: Seq[(Method, PCs)], - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - ): Boolean = { - // There is only a single method with reads aside from initializers (checked by - // isLazilyInitialized), so we have to check only reads from that one method. - reads.filter(!_._1.isInitializer).head._2 forall { readPC => - val index = pcToIndex(readPC) - index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) - } - } - - /** - * Checks that a field read is only executed if the field did not have its default value or - * after the (single) field write. - */ - def checkRead( - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Boolean = { - val startBB = cfg.bb(readIndex).asBasicBlock - val writeBB = cfg.bb(writeIndex) - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - - if (startPC == 0) - return false; // Reached the start of the method but not the guard or field write - - if ((curBB eq writeBB) && writeIndex > readIndex) - return false; // In the basic block of the write, but before the write - - if (startPC != guardedIndex && // Did not reach the guard - (curBB ne writeBB) /* Not the basic block of the write */ ) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } + value.isConst || isNonConstDeterministic } - true - } - - /** - * Gets all predecessor BasicBlocks of a CFGNode. - */ - def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode => - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) + } } - result.toList - } - - /** - * 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. - */ - def findGuard( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Option[(Int, Int, Int)] = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int)] = None - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID => - val ifStmt = cfStmt.asIf - ifStmt.condition match { - case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) => - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1)) - } - - case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) => - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None; - } else { - result = Some((endPC, ifStmt.targetStmt)) - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ => - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ => - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } + + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + true } - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use => - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode ⇒ + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) + } + result.toList } - None - } - - /** - * 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])(implicit state: State): Boolean = { - val field = expr.astID match { - case GetField.ASTID => - val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) None - else expr.asGetField.resolveField(project) - case GetStatic.ASTID => expr.asGetStatic.resolveField(project) - case _ => None + /** + * 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. + */ + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID ⇒ + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + } + + None } - field.contains(state.field) - } - - /** - * 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]] - )(implicit state: State): Boolean = { /** - * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + * 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 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 || - expr.isFloatConst && defaultValue == expr.asFloatConst.value - } + def isReadOfCurrentField(expr: Expr[V])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID ⇒ + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) + case _ ⇒ None + } + field.contains(state.field) } /** - * Checks whether the non-constant expression of the if-Statement is a read of the current - * field. + * 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 isGuardInternal(expr: V): Boolean = { - expr.definedBy forall { index => - if (index < 0) false // If the value is from a parameter, this can not be the guard - else isReadOfCurrentField(code(index).asAssignment.expr) - } + def isGuard( + ifStmt: If[V], + defaultValue: Any, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + + /** + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + */ + 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } + + /** + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. + */ + def isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index ⇒ + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } + } + + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else false } - if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { - isGuardInternal(ifStmt.rightExpr.asVar) - } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { - isGuardInternal(ifStmt.leftExpr.asVar) - } else false - } - - /** - * Checks if 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) => true - case eps => - state.prematurelyReadDependee = Some(eps) - false + /** + * Checks if 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) ⇒ true + case eps ⇒ + state.prematurelyReadDependee = Some(eps) + false + } + + /** + * Checkes 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. + */ + 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 } - /** - * Checkes 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. - */ - 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 - } - - /** - * Checkes if 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 isFinalField( - eop: EOptionP[Field, FieldMutability] - )(implicit state: State): Boolean = eop match { - case LBP(_: FinalField) => - true - case UBP(_: NonFinalField) => false - case _ => - state.fieldMutabilityDependees += eop - true - } + /** + * Checkes if 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 isFinalField( + eop: EOptionP[Field, FieldMutability] + )(implicit state: State): Boolean = eop match { + case LBP(_: FinalField) ⇒ + true + case UBP(_: NonFinalField) ⇒ false + case _ ⇒ + state.fieldMutabilityDependees += eop + true + } } trait L2FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(FieldMutability), - PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(FieldMutability), + PropertyBounds.ub(EscapeProperty) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability) } @@ -1094,16 +1094,16 @@ object EagerL2FieldMutabilityAnalysis extends L2FieldMutabilityAnalysisScheduler 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) - analysis - } + 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) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -1113,18 +1113,18 @@ object LazyL2FieldMutabilityAnalysis extends L2FieldMutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L2FieldMutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - FieldMutability.key, - analysis.determineFieldMutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L2FieldMutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldMutability.key, + analysis.determineFieldMutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 13cf4ebc3f..395e0da63e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -2,7 +2,11 @@ package org.opalj.tac.fpcf.analyses 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.log.LogContext import org.opalj.log.OPALLogger import org.opalj.fpcf.ELBP @@ -122,6 +126,37 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal 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(identifier, classBound, interfaceBound) => ftp + }) + .foreach({ + case FormalTypeParameter(identifier, classBound, interfaceBound) => + classBound match { + case Some( + ClassTypeSignature( + packageIdentifier, + SimpleClassTypeSignature(simpleName, typeArguments), + classTypeSignatureSuffix + ) + ) => { + genericTypeBounds += ((identifier, simpleName)) //+ typeBounds + } + case _ => + } + + }) + }) + genericTypeBounds.toList.foreach({ x => + println("Bound: " + x) + }) + genericTypeBounds + } + def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { e match { case t: ObjectType => @@ -203,7 +238,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal !f.isStatic } var hasShallowImmutableFields = false - var hasDependentImmutableFields = false + var dependentImmutableFields: Set[Option[String]] = Set.empty val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) fieldsPropertyStoreInformation.foreach( f => { @@ -216,10 +251,10 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal return createResultForAllSubtypes(t, MutableClass); } case FinalP(ShallowImmutableField) => hasShallowImmutableFields = true - case FinalEP(f, DependentImmutableField) => { + case FinalEP(f, DependentImmutableField(x)) => { //println("FieldType: "+f.asField.fieldType) //println("FieldTypeSignature: "+f.asField.fieldTypeSignature.get.toJVMSignature) - hasDependentImmutableFields = true + dependentImmutableFields = dependentImmutableFields + x } case FinalP(DeepImmutableField) => case ep @ InterimE(e) => @@ -270,11 +305,11 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability_new = superClassInformation match { case UBP(ShallowImmutableClass) => ShallowImmutableClass //ImmutableContainer - case _ => DeepImmutableClass // ImmutableObject + case _ => DeepImmutableClass // ImmutableObject } - if (hasDependentImmutableFields) { - maxLocalImmutability = DependentImmutableClass //TODO possibly here + if (!dependentImmutableFields.isEmpty) { + maxLocalImmutability = DependentImmutableClass(determineGenericTypeBounds(cf)) //TODO possibly here } else if (hasShallowImmutableFields) { maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } @@ -402,11 +437,11 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal * case FinalP(MutableType_new) ⇒ //MutableType) * Result(t, MutableClass) //TODO check */ - case FinalP(DependentImmutableField) => { + case FinalP(DependentImmutableField(x)) => { println( - ".........................................kkk......................................" + ".........................................kkk......................................: " + x.get ) - maxLocalImmutability = ShallowImmutableClass // DependentImmutableClass //TODO possibly here? + maxLocalImmutability = DependentImmutableClass() // DependentImmutableClass //TODO possibly here? } // Field Mutability related dependencies: @@ -548,7 +583,7 @@ trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { .map(ot => (ot, project.classFile(ot))) .foreach { case (_, Some(cf)) => cfs ::= cf - case (t, None) => + 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 diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index 848733f5aa..0529ed6b85 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -48,215 +48,217 @@ import org.opalj.br.fpcf.properties.TypeImmutability_new * @author Michael Eichberg * @author Tobias Peter Roth */ -class LxTypeImmutabilityAnalysis_new( 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") +class LxTypeImmutabilityAnalysis_new(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 = { + typeExtensibility(t) match { + case Yes | Unknown => + println("Yes Unknown"); Result(t, MutableType_new) // MutableType) + case No => step2(t) } - - /** - * @param t An object type which is not `java.lang.Object`. - */ - def step1( - typeExtensibility: ObjectType ⇒ Answer - )( - t: ObjectType - ): ProperPropertyComputationResult = { - typeExtensibility(t) match { - case Yes | Unknown ⇒ - println("Yes Unknown"); Result(t, MutableType_new) // 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 => + println("c function called") + def apply(eps: SomeEPS): ProperPropertyComputationResult = { + println("EPS: " + eps) + eps match { + case ELUBP(_, lb: ClassImmutability_new, ub: ClassImmutability_new) => + val thisLB = lb.correspondingTypeImmutability + val thisUB = ub.correspondingTypeImmutability + if (eps.isFinal) + Result(t, thisUB) + else + InterimResult(t, thisLB, thisUB, Seq(eps), c) + } + } + } + val resultToMatch = ps(t, ClassImmutability_new.key) + println("Result1: " + resultToMatch) + resultToMatch match { + case x @ FinalP(p) => { + println("x::: " + x.p.correspondingTypeImmutability) + Result(t, p.correspondingTypeImmutability); } - } - - 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 ⇒ - println("c function called") - def apply(eps: SomeEPS): ProperPropertyComputationResult = { - println("EPS: "+eps) - eps match { - case ELUBP(_, lb: ClassImmutability_new, ub: ClassImmutability_new) ⇒ - val thisLB = lb.correspondingTypeImmutability - val thisUB = ub.correspondingTypeImmutability - if (eps.isFinal) - Result(t, thisUB) - else - InterimResult(t, thisLB, thisUB, Seq(eps), c) - } - } - } - val resultToMatch = ps(t, ClassImmutability_new.key) - println("Result1: "+resultToMatch) - resultToMatch match { - case x @ FinalP(p) ⇒ { - println("x::: "+x.p.correspondingTypeImmutability) - Result(t, p.correspondingTypeImmutability); - } - - case eps @ InterimLUBP(lb, ub) ⇒ - val thisUB = ub.correspondingTypeImmutability - val thisLB = lb.correspondingTypeImmutability - InterimResult(t, thisLB, thisUB, Seq(eps), c) - case epk ⇒ { - println("here ") - InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) - } - //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) - } - } else { - var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] - var joinedImmutability: TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." - var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType - - val resultToMatch2 = ps(t, ClassImmutability_new.key) - println("Result2: "+resultToMatch2) - resultToMatch2 match { - case FinalP(DeepImmutableClass) ⇒ //ImmutableObject) => - case FinalP(MutableClass) ⇒ //(_: MutableObject) => - return Result(t, MutableType_new); //MutableType); - case FinalP(ShallowImmutableClass) ⇒ //ImmutableContainer) => - joinedImmutability = ShallowImmutableType // ImmutableContainerType - maxImmutability = ShallowImmutableType //ImmutableContainerType - case FinalP(DependentImmutableClass) ⇒ - joinedImmutability = DependentImmutableType - maxImmutability = DependentImmutableType - - case eps @ InterimLUBP(lb, ub) ⇒ - joinedImmutability = lb.correspondingTypeImmutability - maxImmutability = ub.correspondingTypeImmutability - dependencies += (t -> eps) - - case eOptP ⇒ - joinedImmutability = MutableType_new //MutableType - dependencies += (t -> eOptP) - } - - directSubtypes foreach { subtype ⇒ - ps(subtype, TypeImmutability_new.key) match { - case FinalP(DeepImmutableType) ⇒ //ImmutableType) => - case UBP(MutableType_new) ⇒ //MutableType) => - return Result(t, MutableType_new); //MutableType); - - case FinalP(ShallowImmutableType) ⇒ //ImmutableContainerType) => - joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) - maxImmutability = ShallowImmutableType //ImmutableContainerType - - case FinalP(DependentImmutableType) ⇒ - joinedImmutability = joinedImmutability.meet(DependentImmutableType) - maxImmutability = DependentImmutableType - - case eps @ InterimLUBP(subtypeLB, subtypeUB) ⇒ - joinedImmutability = joinedImmutability.meet(subtypeLB) - maxImmutability = maxImmutability.meet(subtypeUB) - dependencies += ((subtype, eps)) - - case epk ⇒ - joinedImmutability = MutableType_new //MutableType - dependencies += ((subtype, epk)) - } - } + case eps @ InterimLUBP(lb, ub) => + val thisUB = ub.correspondingTypeImmutability + val thisLB = lb.correspondingTypeImmutability + InterimResult(t, thisLB, thisUB, Seq(eps), c) + case epk => { + println("here ") + InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) + } + //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) + } + } else { + var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] + var joinedImmutability + : TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." + var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType + + val resultToMatch2 = ps(t, ClassImmutability_new.key) + println("Result2: " + resultToMatch2) + resultToMatch2 match { + case FinalP(DeepImmutableClass) => //ImmutableObject) => + case FinalP(MutableClass) => //(_: MutableObject) => + return Result(t, MutableType_new); //MutableType); + case FinalP(ShallowImmutableClass) => //ImmutableContainer) => + joinedImmutability = ShallowImmutableType // ImmutableContainerType + maxImmutability = ShallowImmutableType //ImmutableContainerType + case FinalP(DependentImmutableClass(_)) => + joinedImmutability = DependentImmutableType + maxImmutability = DependentImmutableType + + case eps @ InterimLUBP(lb, ub) => + joinedImmutability = lb.correspondingTypeImmutability + maxImmutability = ub.correspondingTypeImmutability + dependencies += (t -> eps) + + case eOptP => + joinedImmutability = MutableType_new //MutableType + dependencies += (t -> eOptP) + } + + directSubtypes foreach { subtype => + ps(subtype, TypeImmutability_new.key) match { + case FinalP(DeepImmutableType) => //ImmutableType) => + case UBP(MutableType_new) => //MutableType) => + return Result(t, MutableType_new); //MutableType); + + case FinalP(ShallowImmutableType) => //ImmutableContainerType) => + joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) + maxImmutability = ShallowImmutableType //ImmutableContainerType + + case FinalP(DependentImmutableType) => + joinedImmutability = joinedImmutability.meet(DependentImmutableType) + maxImmutability = DependentImmutableType + + case eps @ InterimLUBP(subtypeLB, subtypeUB) => + joinedImmutability = joinedImmutability.meet(subtypeLB) + maxImmutability = maxImmutability.meet(subtypeUB) + dependencies += ((subtype, eps)) + + case epk => + joinedImmutability = MutableType_new //MutableType + dependencies += ((subtype, epk)) + } + } + + if (dependencies.isEmpty) { + Result(t, maxImmutability) + } else if (joinedImmutability == maxImmutability) { + // E.g., as soon as one subtype is an ImmutableContainer, we are at most + // ImmutableContainer, even if all other subtype may even be 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 if (joinedImmutability == maxImmutability) { - // E.g., as soon as one subtype is an ImmutableContainer, we are at most - // ImmutableContainer, even if all other subtype may even be immutable! - Result(t, joinedImmutability) + Result(t, maxImmutability) } 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_new ⇒ - joinedImmutability = joinedImmutability.meet(lb) - case lb: ClassImmutability_new ⇒ - joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) - } - else { - joinedImmutability = MutableType_new //MutableType - continue = false - } - } - if (joinedImmutability == maxImmutability) { - assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) - Result(t, maxImmutability) - } else { - InterimResult( - t, - joinedImmutability, - maxImmutability, - dependencies.values, - c - ) - } - } - } - - (eps: @unchecked) match { - case FinalEP(e, DeepImmutableType | DeepImmutableClass) ⇒ //ImmutableType | ImmutableObject) => - dependencies = dependencies - e - nextResult() - - case UBP(x) if (x == MutableType_new || x == MutableClass) ⇒ //MutableType | _: MutableObject) => - Result(t, MutableType_new) //MutableType) - - case FinalEP(e, x) if (x == ShallowImmutableType || x == ShallowImmutableClass) ⇒ //ImmutableContainerType | ImmutableContainer) => - maxImmutability = ShallowImmutableType //ImmutableContainerType - dependencies = dependencies - e - nextResult() - case FinalEP(e, x) if (x == DependentImmutableClass || x == DependentImmutableType) ⇒ { - maxImmutability = DependentImmutableType - dependencies = dependencies - e - nextResult() - } - case eps @ InterimEUBP(e, subtypeP) ⇒ - dependencies = dependencies.updated(e, eps) - subtypeP match { - case subtypeP: TypeImmutability_new ⇒ - maxImmutability = maxImmutability.meet(subtypeP) - case subtypeP: ClassImmutability_new ⇒ - maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) - } - nextResult() - } + 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_new => + joinedImmutability = joinedImmutability.meet(lb) + case lb: ClassImmutability_new => + joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) + } else { + joinedImmutability = MutableType_new //MutableType + continue = false } - InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) + } + if (joinedImmutability == maxImmutability) { + assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) + Result(t, maxImmutability) + } else { + InterimResult( + t, + joinedImmutability, + maxImmutability, + dependencies.values, + c + ) + } + } + } + + (eps: @unchecked) match { + case FinalEP(e, DeepImmutableType | DeepImmutableClass) => //ImmutableType | ImmutableObject) => + dependencies = dependencies - e + nextResult() + + case UBP(x) + if (x == MutableType_new || x == MutableClass) => //MutableType | _: MutableObject) => + Result(t, MutableType_new) //MutableType) + + case FinalEP(e, x) + if (x == ShallowImmutableType || x == ShallowImmutableClass) => //ImmutableContainerType | ImmutableContainer) => + maxImmutability = ShallowImmutableType //ImmutableContainerType + dependencies = dependencies - e + nextResult() + case FinalEP(e, x) if (x == DependentImmutableClass || x == DependentImmutableType) => { + maxImmutability = DependentImmutableType + dependencies = dependencies - e + nextResult() } + case eps @ InterimEUBP(e, subtypeP) => + dependencies = dependencies.updated(e, eps) + subtypeP match { + case subtypeP: TypeImmutability_new => + maxImmutability = maxImmutability.meet(subtypeP) + case subtypeP: ClassImmutability_new => + maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) + } + nextResult() + } } + InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) + } } + } } trait TypeImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) } @@ -269,40 +271,40 @@ object EagerLxTypeImmutabilityAnalysis_new extends TypeImmutabilityAnalysisScheduler_new with BasicFPCFEagerAnalysisScheduler { - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val typeExtensibility = project.get(TypeExtensibilityKey) - val analysis = new LxTypeImmutabilityAnalysis_new(project) - val allClassFilesIterator = project.allClassFiles.iterator - val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty - ps.scheduleEagerComputationsForEntities(types) { - analysis.step1(typeExtensibility) - } + override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val typeExtensibility = project.get(TypeExtensibilityKey) + val analysis = new LxTypeImmutabilityAnalysis_new(project) + val allClassFilesIterator = project.allClassFiles.iterator + val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) - analysis + ps.scheduleEagerComputationsForEntities(types) { + analysis.step1(typeExtensibility) } + + analysis + } } object LazyLxTypeImmutabilityAnalysis_new extends TypeImmutabilityAnalysisScheduler_new 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 LxTypeImmutabilityAnalysis_new(p) - val analysisRunner: ProperPropertyComputation[Entity] = - analysis.doDetermineTypeImmutability_new(typeExtensibility) - ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) - analysis - } + 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 LxTypeImmutabilityAnalysis_new(p) + val analysisRunner: ProperPropertyComputation[Entity] = + analysis.doDetermineTypeImmutability_new(typeExtensibility) + ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) + analysis + } } From 6a2fd8e67b0c69c2e61fe97444cab540694f36ae Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 11 Dec 2019 12:10:12 +0100 Subject: [PATCH 020/327] works with the current test suite for reference immutability without double checked locking with lazy instantiation --- .../L0ReferenceImmutabilityAnalysis.scala | 1915 ++++++++--------- 1 file changed, 917 insertions(+), 998 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index c2ad3ac191..c220646e22 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -83,206 +83,206 @@ import org.opalj.value.ValueInformation class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - case class State( - field: Field, - var referenceImmutability: ReferenceImmutability = ImmutableReference, - 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, - var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, - var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty - ) { - def hasDependees: Boolean = { - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || - escapeDependees.nonEmpty || tacDependees.nonEmpty - } - - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) - } + case class State( + field: Field, + var referenceImmutability: ReferenceImmutability = ImmutableReference, + 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, + var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty } - 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) - - def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field ⇒ determineReferenceImmutability(field) - case _ ⇒ - val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) } + } + + 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) + + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field => determineReferenceImmutability(field) + case _ => + val m = entity.getClass.getSimpleName + " is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + + /** + * Analyzes the mutability of private non-final fields. + * + * 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 determineReferenceImmutability( + field: Field + ): ProperPropertyComputationResult = { + implicit val state: State = State(field) + println( + "field.isPrematurelyRead " + isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key)) + ) + // Fields are not final if they are read prematurely! + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) + return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); + println("field.isFinal " + field.isFinal) + if (field.isFinal) + return createResult(); + + state.referenceImmutability = ImmutableReference //EffectivelyFinalField + + val thisType = field.classFile.thisType + + if (field.isPublic) + return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) + + // 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) { + if (!closedPackages.isClosed(thisType.packageName)) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } + + val classesHavingAccess: Iterator[ClassFile] = + if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + 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))) + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } /** - * Analyzes the mutability of private non-final fields. - * - * 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. + * Returns the TACode for a method if available, registering dependencies as necessary. */ - private[analyses] def determineReferenceImmutability( - field: Field - ): ProperPropertyComputationResult = { - implicit val state: State = State(field) - println( - "field.isPrematurelyRead "+isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key)) - ) - // Fields are not final if they are read prematurely! - if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) - return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); - println("field.isFinal "+field.isFinal) - if (field.isFinal) - return createResult(); - - state.referenceImmutability = ImmutableReference //EffectivelyFinalField - - val thisType = field.classFile.thisType - - if (field.isPublic) - return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) - - // 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) { - if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - } - project.classesPerPackage(thisType.packageName) - } else { - Set(field.classFile) - } - - val classesHavingAccess: Iterator[ClassFile] = - if (field.isProtected) { - if (typeExtensibility(thisType).isYesOrUnknown) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - } - 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))) - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - - // 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 - // of the class after the reference to the class and (an indirect) reference to the - // field has become available. Consider the following example: - // class X implements Y{ - // - // private Object o; - // - // public Object getO() { return o; } - // - // private static X instance; - // static { - // instance = new X(); - // Z.register(instance); - // // when we reach this point o is now (via getO) publically accessible and - // // X is properly initialized! - // o = new Object(); // o is mutated... - // } - // } - - /** - * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) - None - } - } + 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 eps: InterimEP[Method, TACAI] => + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk => + state.tacDependees += method -> ((epk, pcs)) + None + } + } - for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode ← getTACAI(method, pcs) - } { - if (methodUpdatesField(method, taCode, pcs)) { - println("method updates field: true") - return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); - } else - println("method updates field: false") + for { + (method, pcs) <- fieldAccessInformation.writeAccesses(field) + taCode <- getTACAI(method, pcs) + } { + if (methodUpdatesField(method, taCode, pcs)) { + println("method updates field: true") + return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); + } else + println("method updates field: false") - } - - if (state.lazyInitInvocation.isDefined) { - val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - handleCalls(calleesEOP) - } - - createResult() } - def handleCalls( - calleesEOP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: State - ): Boolean = { - calleesEOP match { - case FinalP(callees) ⇒ - state.calleesDependee = None - handleCallees(callees) - case InterimUBP(callees) ⇒ - state.calleesDependee = Some(calleesEOP) - handleCallees(callees) - case _ ⇒ - state.calleesDependee = Some(calleesEOP) - false - } + if (state.lazyInitInvocation.isDefined) { + val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + handleCalls(calleesEOP) } - def handleCallees(callees: Callees)(implicit state: State): Boolean = { - val pc = state.lazyInitInvocation.get._2 - if (callees.isIncompleteCallSite(pc)) { - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else { - val targets = callees.callees(pc).toTraversable - if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else false - } + createResult() + } + + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: State + ): Boolean = { + calleesEOP match { + case FinalP(callees) => + state.calleesDependee = None + handleCallees(callees) + case InterimUBP(callees) => + state.calleesDependee = Some(calleesEOP) + handleCallees(callees) + case _ => + state.calleesDependee = Some(calleesEOP) + false } + } + + def handleCallees(callees: Callees)(implicit state: State): Boolean = { + val pc = state.lazyInitInvocation.get._2 + if (callees.isIncompleteCallSite(pc)) { + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else { + val targets = callees.callees(pc).toTraversable + if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else false + } + } - /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. - */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field * The code below can be used to identify such value, but is not yet adapted to using the * TACAI property */ - /* + /* var constantVal: Option[Any] = None var allInitializeConstant = true @@ -340,854 +340,773 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } constantVal */ + } + + /** + * 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 MutableReference)) //NonFinalFieldByAnalysis)) + InterimResult( + state.field, + MutableReference, //NonFinalFieldByAnalysis, + 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 = { + val isNotFinal = eps.pk match { + case EscapeProperty.key => + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + methodUpdatesField(method, newEP.ub.tac.get, pcs) + case Callees.key => + handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key => + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key => + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + isNonDeterministic(newEP) + case ReferenceImmutability.key => + val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + !isImmutableReference(newEP) } - /** - * 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 MutableReference)) //NonFinalFieldByAnalysis)) - InterimResult( - state.field, - MutableReference, //NonFinalFieldByAnalysis, - state.referenceImmutability, - state.dependees, - c - ) - else - Result(state.field, state.referenceImmutability) + if (isNotFinal) + Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) + else + createResult() + } + + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + )(implicit state: State): Boolean = { + val write = code(writeIndex).asFieldWriteAccessStmt + + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + return false; } - /** - * 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 = { - val isNotFinal = eps.pk match { - case EscapeProperty.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - 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)) - methodUpdatesField(method, newEP.ub.tac.get, pcs) - case Callees.key ⇒ - handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key ⇒ - isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] - state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) - isNonDeterministic(newEP) - case ReferenceImmutability.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] - state.referenceImmutabilityDependees = - state.referenceImmutabilityDependees.filter(_.e ne newEP.e) - !isImmutableReference(newEP) - } - - if (isNotFinal) - Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) - else - createResult() + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs => (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method } - /** - * Checks whether a field write may be a lazy initialization. - * - * We only consider simple cases of lazy initialization where the single field write is guarded - * so it is executed only if the field still has its default value and where the written value - * is guaranteed to be the same even if the write is executed multiple times (may happen because - * of the initializing method being executed more than once on concurrent threads). - * - * Also, all field reads must be used only for a guard or their uses have to be guarded - * themselves. - */ - def isLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - )(implicit state: State): Boolean = { - val write = code(writeIndex).asFieldWriteAccessStmt - - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - // Only handle lazy initialization of ints and floats as they are guaranteed to be - // written atomically - return false; - } - - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return false; // Reads outside the (single) lazy initialization method - } - - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val (guardIndex, guardedIndex, readIndex) = - findGuard(writeIndex, defaultValue, code, cfg) match { - case Some((guard, guarded, read)) ⇒ (guard, guarded, read) - case None ⇒ return false; - } - - // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) - return false; - - println( - "Check writes: "+checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg) - ) - // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - return false; - - // Field reads (except for the guard) may only be executed if the field's value is not the - // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - return false; - - true - } - - def checkWrites( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val definitions = write.value.asVar.definedBy - - val isDeterministic = - if (definitions.size == 1) { - // The value written must be computed deterministically - checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) - } else { - // More than one definition site for the value might lead to differences between - // invocations, but not if this method has no parameters and is deterministic - // (in this case, the definition reaching the write will always be the same) - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - - // The field write must be guarded correctly - isDeterministic && - checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - } - - /** - * Checks if the field write for a lazy initialization is immediately followed by a return of - * the written value (possibly loaded by another field read). - */ - def checkImmediateReturn( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 - - var load = -1 - - while (index < code.length) { - val stmt = code(index) - stmt.astID match { - case Assignment.ASTID ⇒ - if (isReadOfCurrentField(stmt.asAssignment.expr)) - load = index - else - return false; // No field read or a field read of a different field - case ReturnValue.ASTID ⇒ - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) - return true; // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) - return true; // return of field value loaded by field read - else - return false; // return of different value - case _ ⇒ return false; // neither a field read nor a return - } - index += 1 - } - false - } - - def lazyInitializerIsDeterministic( - method: Method, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( - propertyStore(declaredMethods(method), Purity.key) - )) - println("paramsCount: "+method.descriptor.parametersCount == 0) - println( - "isnondeterministic-result: "+isNonDeterministic( - propertyStore(declaredMethods(method), Purity.key) - ).toString - ) - println(propertyStore(declaredMethods(method), Purity.key)) - println("lazyInitializerIsDeterministic: "+result) - result + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) => (guard, guarded, read) + case None => return false; + } + + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) + return false; + + println( + "Check writes: " + checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg) + ) + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + + true + } + + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val definitions = write.value.asVar.definedBy + + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + // The field write must be guarded correctly + isDeterministic && + checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID => + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID => + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ => return false; // neither a field read nor a return + } + index += 1 } - - /** - * 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 = { - println("xxxxxx") - val field = state.field - val stmts = taCode.stmts - for (pc ← pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID ⇒ - if (method.isInitializer) { - if (field.isStatic) { - if (method.isConstructor) - return true; - } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - 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.referenceImmutability == LazyInitializedReference) //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; - - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - // A field written outside an initializer must be lazily - // initialized or it is non-final - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - ) /**&& !isDoubleCheckedLocking( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - )**/ ) - return true; - - state.referenceImmutability = LazyInitializedReference //LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - // 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. - return true; - } - } - case _ ⇒ throw new RuntimeException("unexpected field access"); - } + false + } + + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + )) + println("paramsCount: " + method.descriptor.parametersCount == 0) + println( + "isnondeterministic-result: " + isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + ).toString + ) + println(propertyStore(declaredMethods(method), Purity.key)) + println("lazyInitializerIsDeterministic: " + result) + result + } + + /** + * 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 = { + println("xxxxxx") + val field = state.field + val stmts = taCode.stmts + for (pc <- pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID => + if (method.isInitializer) { + if (field.isStatic) { + if (method.isConstructor) + return true; } else { - // nothing to do as the put field is dead + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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.referenceImmutability == LazyInitializedReference) //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; + + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + // A field written outside an initializer must be lazily + // initialized or it is non-final + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )) + return true; + + state.referenceImmutability = LazyInitializedReference //LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ => throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead } - false + } } - - /** - * 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 eps: InterimEP[Method, TACAI] ⇒ - * state.tacDependees += method → ((eps, pcs)) - * eps.ub.tac - * case epk ⇒ - * state.tacDependees += method → ((epk, pcs)) - * None - * } - * } - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - /** - * 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 // Must be locally created - else { - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - handleEscapeProperty(escape) - } - } + false + } + + /** + * 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 eps: InterimEP[Method, TACAI] ⇒ + * state.tacDependees += method → ((eps, pcs)) + * eps.ub.tac + * case epk ⇒ + * state.tacDependees += method → ((epk, pcs)) + * None + * } + * } + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) } + } } - - /** - * 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 = 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 + } + + /** + * 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 = 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 + } + + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt => + stmt.astID == CaughtException.ASTID + } flatMap { exception => + exception.asCaughtException.origins.map { origin: Int => + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator } - /** - * Checks if the field write is only executed if the field's value was still the default value. - * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization - * and the field write. - */ - def checkWriteIsGuarded( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val startBB = cfg.bb(writeIndex).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt ⇒ - stmt.astID == CaughtException.ASTID - } flatMap { exception ⇒ - exception.asCaughtException.origins.map { origin: Int ⇒ - if (isImmediateVMException(origin)) - pcOfImmediateVMException(origin) - else - pcOfMethodExternalException(origin) - }.iterator - } + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail + val startPC = curBB.startPC + val endPC = curBB.endPC - val startPC = curBB.startPC - val endPC = curBB.endPC + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement - if (startPC == 0 || startPC == guardedIndex) - return false; // Reached method start or wrong branch of guarding if-Statement + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors + true + } + + /** + * 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]] + )(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(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) => + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ => // Unknown field + false + }) } + } - true + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) } - /** - * 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]] - )(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(defSites.head).asAssignment.expr.isConst) true - else { - val expr = code(index).asAssignment.expr - expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ ⇒ // Unknown field - false - }) - } - } + val value = origin.expr - defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID => + value.asFieldRead.resolveField(p) match { + case Some(field) => + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ => // Unknown field + false } - - val value = origin.expr - - val isNonConstDeterministic = value.astID match { - case GetStatic.ASTID | GetField.ASTID ⇒ - value.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ ⇒ // Unknown field - false - } - 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.exists(!isConstant(_))) { - false - } else { - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true - } - 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, code) + 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.exists(!isConstant(_))) { + false + } else { + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true } - - value.isConst || isNonConstDeterministic + 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, code) } - /** - * Checks that all non-dead field reads that are not used for the guarding if-Statement of a - * lazy initialization are only executed if the field did not have its default value or after - * the (single) field write. - */ - def checkReads( - reads: Seq[(Method, PCs)], - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - ): Boolean = { - // There is only a single method with reads aside from initializers (checked by - // isLazilyInitialized), so we have to check only reads from that one method. - reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ - val index = pcToIndex(readPC) - index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) - } + value.isConst || isNonConstDeterministic + } + + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC => + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) } - - /** - * Checks that a field read is only executed if the field did not have its default value or - * after the (single) field write. - */ - def checkRead( - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Boolean = { - val startBB = cfg.bb(readIndex).asBasicBlock - val writeBB = cfg.bb(writeIndex) - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - - if (startPC == 0) - return false; // Reached the start of the method but not the guard or field write - - if ((curBB eq writeBB) && writeIndex > readIndex) - return false; // In the basic block of the write, but before the write - - if (startPC != guardedIndex && // Did not reach the guard - (curBB ne writeBB) /* Not the basic block of the write */ ) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - true + } + + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } } - /** - * Gets all predecessor BasicBlocks of a CFGNode. - */ - def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode ⇒ - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) - } - result.toList + true + } + + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode => + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) } - - /** - * 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. - */ - def findGuard( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - ): Option[(Int, Int, Int)] = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int)] = None - println("worklist initial: "+worklist) - while (worklist.nonEmpty) { - println("worklist "+worklist) - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID ⇒ - val ifStmt = cfStmt.asIf - println("ifstmt: "+ifStmt) - println("curBB: "+curBB) - println("startBB: "+startBB) - println("result.isDefined "+result.isDefined) - println("result: "+result) - ifStmt.condition match { - case EQ if curBB != startBB ⇒ //&& isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1)) - } - - case NE if curBB != startBB ⇒ //&& isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None; - } else { - result = Some((endPC, ifStmt.targetStmt)) - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard - } - - None + result.toList + } + + /** + * 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. + */ + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID => + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) => + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) => + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ => + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ => + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } } - /** - * 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])(implicit state: State): Boolean = { - val field = expr.astID match { - case GetField.ASTID ⇒ - val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) None - else expr.asGetField.resolveField(project) - case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) - case _ ⇒ None - } - field.contains(state.field) + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use => + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard } - /** - * 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]] - )(implicit state: State): Boolean = { - - /** - * Checks if an expression is an IntConst or FloatConst with the corresponding default value. - */ - 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 || - expr.isFloatConst && defaultValue == expr.asFloatConst.value - } - } - - /** - * Checks whether the non-constant expression of the if-Statement is a read of the current - * field. - */ - def isGuardInternal(expr: V): Boolean = { - expr.definedBy forall { index ⇒ - if (index < 0) false // If the value is from a parameter, this can not be the guard - else isReadOfCurrentField(code(index).asAssignment.expr) - } - } - - if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { - isGuardInternal(ifStmt.rightExpr.asVar) - } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { - isGuardInternal(ifStmt.leftExpr.asVar) - } else - isGuardInternal(ifStmt.leftExpr.asVar) || isGuardInternal(ifStmt.rightExpr.asVar) // || false //TODO + None + } + + /** + * 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])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID => + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID => expr.asGetStatic.resolveField(project) + case _ => None } + field.contains(state.field) + } + + /** + * 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]] + )(implicit state: State): Boolean = { /** - * Checks if the field is prematurely read, i.e. read before it is initialized in the - * constructor, using the corresponding property. + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. */ - def isPrematurelyRead( - eop: EOptionP[Field, FieldPrematurelyRead] - )(implicit state: State): Boolean = - eop match { - case LBP(NotPrematurelyReadField) ⇒ - state.prematurelyReadDependee = None - false - case UBP(PrematurelyReadField) ⇒ true - case eps ⇒ - state.prematurelyReadDependee = Some(eps) - false - } + 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } /** - * Checkes 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. + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. */ - 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 isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index => + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } } - /** - * Checkes if 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, ReferenceImmutability] - )(implicit state: State): Boolean = eop match { - case FinalEP(e, ImmutableReference) ⇒ true - case FinalEP(e, MutableReference) ⇒ false - // - case LBP(ImmutableReference) ⇒ true - case UBP(MutableReference) ⇒ false - - /** - * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ - * - * true - * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * - */ - case _ ⇒ - state.referenceImmutabilityDependees += eop - true + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else false + } + + /** + * Checks if 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) => true + case eps => + state.prematurelyReadDependee = Some(eps) + false } - //-------------------------------------------------- double checked locking - def isDoubleCheckedLocking( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - )(implicit state: State): Boolean = { - //val a = state.field - //val write = code(writeIndex).asFieldWriteAccessStmt - //println("Purity Information: "+propertyStore(declaredMethods(method), Purity.key)) - //println(isNonDeterministic(propertyStore(declaredMethods(method), Purity.key))) - if (!method.isStatic || !state.field.isStatic) { - false - } else { - //----------------------------- - /** - * val reads = fieldAccessInformation.readAccesses(state.field) - * if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - * return false; // Reads outside the (single) lazy initialization method - * } - * - * // There must be a guarding if-Statement - * // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - * // first statement that is executed after the if-Statement if the field's value was not the - * // default value - * val (guardIndex, guardedIndex, readIndex) = - * findGuard(writeIndex, defaultValue, code, cfg) match { - * case Some((guard, guarded, read)) ⇒ (guard, guarded, read) - * case None ⇒ return false; - * } - * // Detect only simple patterns where the lazily initialized value is returned immediately - * // if (!checkImmediateReturn(write, writeIndex, readIndex, code)) - * // return false; - * - * println( - * "Check writes (is double checked locking): "+checkWrites( - * write, - * writeIndex, - * guardIndex, - * guardedIndex, - * method, - * code, - * cfg - * ) - * ) - * // The value written must be computed deterministically and the writes guarded correctly - * if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - * return false; - * - * // Field reads (except for the guard) may only be executed if the field's value is not the - * // default value - * if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - * return false; - * - * true - */ - //----------------------------- - - //} + /** + * Checkes 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. + */ + 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 + } + + /** + * Checkes if 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, ReferenceImmutability] + )(implicit state: State): Boolean = eop match { + case FinalEP(e, ImmutableReference) => true + case FinalEP(e, MutableReference) => false + // + case LBP(ImmutableReference) => true + case UBP(MutableReference) => false - false - } - } - //-------------------------------------------------- double checked locking + /** + * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ + * true + * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * + */ + case _ => + state.referenceImmutabilityDependees += eop + true + } } + trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.ub(EscapeProperty) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) } @@ -1198,16 +1117,16 @@ object EagerL0ReferenceImmutabilityAnalysis extends L0ReferenceImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -1217,18 +1136,18 @@ object LazyL0ReferenceImmutabilityAnalysis extends L0ReferenceImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - ReferenceImmutability.key, - analysis.determineReferenceImmutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + ReferenceImmutability.key, + analysis.determineReferenceImmutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } From 82e74e34b9b8ab4b34b5a3428c3b6cd4baf6bb13 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 18 Dec 2019 14:28:46 +0100 Subject: [PATCH 021/327] formatting, optimization and removal of printlns in tests and matcher --- .../opalj/fpcf/ClassImmutabilityTests.scala | 4 - .../opalj/fpcf/FieldImmutabilityTests.scala | 112 +++++++++--------- .../fpcf/ReferenceImmutabilityTests.scala | 100 ++++++++-------- .../ClassImmutabilityMatcher.scala | 5 +- .../FieldImmutabilityMatcher.scala | 6 +- 5 files changed, 110 insertions(+), 117 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala index eaad2963d4..3edc1ed830 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -51,16 +51,12 @@ class ClassImmutabilityTests extends PropertiesTest { EagerLxClassImmutabilityAnalysis_new ) ) - - println(2) as.propertyStore.shutdown() - println(3) validateProperties( as, classFilesWithAnnotations(as.project).map(tp => (tp._1.thisType, tp._2, tp._3)), Set("ClassImmutability_new") ) - println(4) } /** * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { 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 index 430b923e33..944ba41571 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -8,13 +8,13 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.Project import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** @@ -22,64 +22,62 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class FieldImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) - } - p.get(RTACallGraphKey) + 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")) - } + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } - describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { - val as = executeAnalyses( - Set( - LazyL2FieldMutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - EagerL0FieldImmutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new - // LazySimpleEscapeAnalysis, - // TACAITransformer - ) - ) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ + describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + val as = executeAnalyses( + Set( + LazyTypeImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index 3e5b2ea958..5cb5df6328 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -18,58 +18,58 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class ReferenceImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) - } - p.get(RTACallGraphKey) + 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("ReferenceImmutability")) - } + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + } - describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { - val as = executeAnalyses( - Set( - EagerL0ReferenceImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis - ) - ) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ + describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { + val as = executeAnalyses( + Set( + EagerL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala index fac97feb4c..4a15bb6dcd 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala @@ -46,9 +46,8 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) ): Option[String] = { if (!properties.exists(p ⇒ { p match { - case DependentImmutableClass(_) if property == DependentImmutableClass() ⇒ true - case _ if p == property ⇒ true - case _ ⇒ false + case DependentImmutableClass(_) ⇒ property == DependentImmutableClass() + case _ ⇒ p == property } })) { //p == property)) { // ... when we reach this point the expected property was not found. diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala index 5d27f81213..1871e2971d 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala @@ -44,9 +44,9 @@ class FieldImmutabilityMatcher(val property: FieldImmutability) extends Abstract ): Option[String] = { if (!properties.exists(p ⇒ { p match { - case DependentImmutableField(_) if property == DependentImmutableField() ⇒ true - case _ if p == property ⇒ true - case _ ⇒ false + case DependentImmutableField(_) ⇒ property == DependentImmutableField() //TODO optimize + case _ ⇒ p == property + } })) { //p == property)) { // ... when we reach this point the expected property was not found. From 8546ac8a94602a47c078d5cdd41defe1ee88ac52 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 18 Dec 2019 14:32:11 +0100 Subject: [PATCH 022/327] removed printlns in ref imm analysis --- .../L0ReferenceImmutabilityAnalysis.scala | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index c220646e22..95da9cdc5d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -132,13 +132,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec field: Field ): ProperPropertyComputationResult = { implicit val state: State = State(field) - println( - "field.isPrematurelyRead " + isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key)) - ) // Fields are not final if they are read prematurely! if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); - println("field.isFinal " + field.isFinal) if (field.isFinal) return createResult(); @@ -224,10 +220,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec taCode <- getTACAI(method, pcs) } { if (methodUpdatesField(method, taCode, pcs)) { - println("method updates field: true") return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); - } else - println("method updates field: false") + } } @@ -445,9 +439,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (!checkImmediateReturn(write, writeIndex, readIndex, code)) return false; - println( - "Check writes: " + checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg) - ) // The value written must be computed deterministically and the writes guarded correctly if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) return false; @@ -535,14 +526,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( propertyStore(declaredMethods(method), Purity.key) )) - println("paramsCount: " + method.descriptor.parametersCount == 0) - println( - "isnondeterministic-result: " + isNonDeterministic( - propertyStore(declaredMethods(method), Purity.key) - ).toString - ) - println(propertyStore(declaredMethods(method), Purity.key)) - println("lazyInitializerIsDeterministic: " + result) result } @@ -555,7 +538,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec taCode: TACode[TACMethodParameter, V], pcs: PCs )(implicit state: State): Boolean = { - println("xxxxxx") val field = state.field val stmts = taCode.stmts for (pc <- pcs) { From afdea1d24247aa64e0653f6264a730e9668281a9 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 18 Dec 2019 14:49:26 +0100 Subject: [PATCH 023/327] optimization of dependent immutability. Introducing the recognition of generic classes where the a field with a generic types is influenced by the generic type of the generic class. --- .../FieldImmutabilityAnalysisDemo.scala | 21 --- .../ClassWithGenericField_deep.java | 46 ++++++ .../ClassWithGenericField_mutable.java | 24 +++ .../ClassWithGenericField_shallow.java | 18 +++ .../DependentClassWithGenericField_deep1.java | 31 ++++ .../DependentClassWithGenericField_deep2.java | 25 +++ .../L0FieldImmutabilityAnalysis.scala | 152 ++++++++++++------ .../LxClassImmutabilityAnalysis_new.scala | 152 ++++-------------- .../LxTypeImmutabilityAnalysis_new.scala | 9 +- 9 files changed, 279 insertions(+), 199 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_mutable.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep1.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep2.java 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 index dfc0b5aed7..1efd2e07a6 100644 --- 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 @@ -50,19 +50,6 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { analysesManager.project.get(RTACallGraphKey) val (propertyStore, _) = analysesManager.runAll( - /** - * LazyTypeImmutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyClassImmutabilityAnalysis, - * LazyL0ReferenceImmutabilityAnalysis, - * LazyL0PurityAnalysis, - * LazyL1FieldMutabilityAnalysis, - * LazySimpleEscapeAnalysis, - * TACAITransformer, - * LazyLxTypeImmutabilityAnalysis_new, - * EagerL0FieldImmutabilityAnalysis* - */ - LazyLxClassImmutabilityAnalysis_new, LazyTypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, @@ -92,14 +79,6 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .toString() + "\n" + propertyStore .entities(FieldImmutability.key) - /** - * .entities(x ⇒ { - * x.asEPS.pk match { - * case DependentImmutableField(_) ⇒ true - * case _ ⇒ false - * } - * })* - */ .toList .toString } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_deep.java new file mode 100644 index 0000000000..d549417ad5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_deep.java @@ -0,0 +1,46 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public final class ClassWithGenericField_deep { + @DeepImmutableFieldAnnotation("deep imm field") + @ImmutableReferenceAnnotation("eff imm ref") + private Generic_class2 gc = + new Generic_class2 + (new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2()); +} + + +final class Generic_class2 { + + private T1 t1; + + private T2 t2; + + private T3 t3; + + private T4 t4; + + private T5 t5; + + public Generic_class2(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + } + +} + +final class FinalEmptyClass2 { + +} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_mutable.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_mutable.java new file mode 100644 index 0000000000..d550757bce --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_mutable.java @@ -0,0 +1,24 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +import org.opalj.fpcf.properties.type_mutability.MutableType; + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +public final class ClassWithGenericField_mutable { + @MutableFieldAnnotation("deep imm field") + @MutableReferenceAnnotation("eff imm ref") + public Generic_class1 gc = + new Generic_class1 + (new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); +} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_shallow.java new file mode 100644 index 0000000000..2edd749fea --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_shallow.java @@ -0,0 +1,18 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; + +@ShallowImmutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +public final class ClassWithGenericField_shallow { + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Generic_class1 gc = + new Generic_class1 + (new TrivialMutableClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); +} + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep1.java new file mode 100644 index 0000000000..b88188748b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep1.java @@ -0,0 +1,31 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class DependentClassWithGenericField_deep1 { + + @DependentImmutableFieldAnnotation(value = "", genericString = "T1") + @ImmutableReferenceAnnotation("") + private T1 t1; + + @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") + @ImmutableReferenceAnnotation("eff imm ref") + private Generic_class1 gc; + + public DependentClassWithGenericField_deep1(T1 t1) { + this.t1 = t1; + gc = new Generic_class1 + (t1, new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); + } + + +} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep2.java new file mode 100644 index 0000000000..b12357c698 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep2.java @@ -0,0 +1,25 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class DependentClassWithGenericField_deep2 { + + @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") + @ImmutableReferenceAnnotation("eff imm ref") + private Generic_class1 gc; + + public DependentClassWithGenericField_deep2(T1 t1) { + gc = new Generic_class1 + (t1, new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); + } + +} + + + diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 79b8f1f129..fbd8cf1f3f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -1,9 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.br.fpcf.analyses +import org.opalj.br.ClassTypeSignature import org.opalj.br.Field import org.opalj.br.FieldType import org.opalj.br.ObjectType +import org.opalj.br.ProperTypeArgument +import org.opalj.br.SimpleClassTypeSignature import org.opalj.br.TypeVariableSignature import org.opalj.br.analyses.FieldAccessInformationKey import org.opalj.br.analyses.SomeProject @@ -30,6 +33,7 @@ import org.opalj.br.fpcf.properties.TypeImmutability_new 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.ProperPropertyComputationResult @@ -41,8 +45,9 @@ import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS import org.opalj.tac.fpcf.properties.TACAI -case class State() { - var typeImmutability: Option[Boolean] = None +case class State(f: Field) { + var field: Field = f + var typeImmutability: Option[Boolean] = Some(true) var referenceImmutability: Option[Boolean] = None var depencenciesTypes: scala.collection.mutable.Set[ObjectType] = scala.collection.mutable.Set[ObjectType]() @@ -78,42 +83,87 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) field: Field ): ProperPropertyComputationResult = { - println("Field Attributes: " + field.asField.attributes.toList.collectFirst({ - case TypeVariableSignature(t) => t - })) - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + def determineGenericFieldImmutability(state: State): Option[Boolean] = { + println( + "determine generic field imm-------------------------------------------------------------------" + ) + var genericFields: Set[String] = Set.empty + state.field.fieldTypeSignature.head match { + case ClassTypeSignature(_, SimpleClassTypeSignature(name, typeArguments), _) => { + typeArguments.foreach( + ta => { + ta match { + case ProperTypeArgument( + varianceIndicator, + ClassTypeSignature( + packageIdentifier1, + SimpleClassTypeSignature( + packageIdentifier2, + typeArguments2 + ), + _ + ) + ) => { + genericFields += packageIdentifier2 + } + case _ => + } + + } + ) + } + case _ => + } + genericFields.foreach(f => println("generic Field: " + f)) + //state.typeImmutability = Some(true) + + genericFields.toList.foreach(s => { + val objectType = ObjectType(s) + val result = propertyStore(objectType, TypeImmutability_new.key) + println("Result generic field with objtype: " + objectType + " result: " + result) + result match { + case FinalP(DeepImmutableType) => + case FinalP(ShallowImmutableType | MutableType_new) => { + return Some(false); //state.typeImmutability = Some(false) + } + case ep @ _ => { + dependencies += ep + } + } + }) + if (dependencies.size > 0) None; + else Some(true); + } def handleTypeImmutability(objectType: FieldType)(state: State): Option[Boolean] = { if (objectType.isArrayType) return Some(true); //TODO if (objectType.isBaseType) return Some(true); val result = propertyStore(objectType, TypeImmutability_new.key) - println("result: " + result) + println("Result: " + result) result match { - case FinalEP(e, DeepImmutableType) => { //ImmutableType || t == ImmutableContainerType) ⇒ { - println("has deep immutable type") + case FinalEP(e, DeepImmutableType) => { return Some(true); } - case FinalEP(e, DependentImmutableType) => { - println("has dependent immutable type") + case FinalEP(f, DependentImmutableType) => { + println(f + " has dependent imm type") //TODO under construction - state.dependentTypeImmutability = Some(true) - return Some(false); + //--------------------------------------------------------------------------------------- + state.typeImmutability = determineGenericFieldImmutability(state) + //--------------------------------------------------------------------------------------- + //state.dependentTypeImmutability = Some(true) + return state.typeImmutability; } case FinalEP(e, ShallowImmutableType) => { - println("has shallow immutable type") return Some(false); //TODO mindstorm if this approch is appropriate } case FinalEP(e, t) if (t == MutableType_new) => { //MutableType) ⇒ { - println("has mutable type") return Some(false); } case x @ _ => { dependencies += x - println(x) - println("immutability of type couldn't be determined") - return None; + return None; //TODO check!!!!! None; } } } @@ -145,46 +195,64 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) propertyStore(field, ReferenceImmutability.key) match { case FinalEP(_, MutableReference) => { - println("has mutable reference") Some(false) } case FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO - println("has immutable Reference") Some(true) } case x @ _ => { dependencies += x - println(x) - println("immutability of reference couldn't be determined") None } } } - val state: State = new State() + val state: State = new State(field) def createResult(state: State): ProperPropertyComputationResult = { - println("reference Immutability: " + state.referenceImmutability) - println("type immutabiliy: " + state.typeImmutability) - println("dependent immutability: " + state.dependentTypeImmutability) - state.referenceImmutability match { case Some(false) => Result(field, MutableField) case Some(true) => { //If the field type is object. It is a generic field //Test Dependent... //TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //if (field.fieldType == ObjectType("java/lang/Object")) { + println("field attributes: " + field.asField.attributes) val genericString = field.asField.attributes.toList.collectFirst({ case TypeVariableSignature(t) => t + case ClassTypeSignature( + packageIdentifier, + SimpleClassTypeSignature(simpleName, typeArguments), + classTypeSignatureSuffix + ) => { + val tA = typeArguments + .find(typeArgument => { + typeArgument match { + case ProperTypeArgument(variance, signature) => { + signature match { + case TypeVariableSignature(identifier) => true + case _ => false + } + } + case _ => false + } + + }) + if (tA.size > 0) + tA.head match { + case ProperTypeArgument(variance, TypeVariableSignature(identifier)) => identifier + case _ => "" + } else "" + + } }) - if (!field.attributes.isEmpty && genericString != None) { + if (!field.attributes.isEmpty && genericString != None && genericString != Some("")) { //if(genericString!=None) // println("Generic String: "+genericString) // println( // "test: "+DependentImmutableField(genericString).genericString // } - Result(field, DependentImmutableField(genericString)) //genericString)) + Result(field, DependentImmutableField(genericString)) } else state.typeImmutability match { @@ -209,7 +277,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case None if (dependencies.isEmpty) => Result(field, MutableField) case None => { - println(dependencies) InterimResult( field, MutableField, @@ -222,48 +289,38 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - println("c function called") dependencies = dependencies.filter(_.e ne eps.e) (eps: @unchecked) match { case x: InterimEP[_, _] => { - println("interim in continuation function") - println(x) dependencies += eps InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) } case x @ FinalEP(_, t) if (t == DeepImmutableType) => { // || t == ShallowImmutableType) ⇒ { // ImmutableContainerType || t == ImmutableType) ⇒ { - println(x) - println("has immutable type. Determined by continuation function.") state.typeImmutability = Some(true) } case x @ FinalEP(_, MutableType_new | ShallowImmutableType) => { //MutableType) ⇒ { - println(x) - println("has mutable type. Determined by continuation function.") state.typeImmutability = Some(false) } - case x @ FinalEP(_, DependentImmutableType) => { - println(x) - println("has dependent immutable type. Determined by continuation function.") - state.typeImmutability = Some(false) - state.dependentTypeImmutability = Some(true) + case x @ FinalEP(f, DependentImmutableType) => { + //------------------------------------------------------------------------------------- + state.typeImmutability = determineGenericFieldImmutability(state) + //------------------------------------------------------------------------------------- + //state.typeImmutability = Some(false) + //state.dependentTypeImmutability = Some(true) } case x @ FinalEP(_, MutableReference) => { - println(x) - println("has mutable reference. Determined by continuation function.") state.referenceImmutability = Some(false) } case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO - println(x) - println("has immutable reference. Determined by continuation function.") state.referenceImmutability = Some(true) } - case x @ _ => println("default value: " + x) + case x @ _ => dependencies = dependencies + x } createResult(state) } @@ -272,7 +329,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.depencenciesTypes = scala.collection.mutable.Set[ObjectType]() state.referenceImmutability = hasImmutableReference(field) state.typeImmutability = hasImmutableType(field)(state); - println("after type immutability determination") createResult(state) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 395e0da63e..264e39b48b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -151,9 +151,9 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal }) }) - genericTypeBounds.toList.foreach({ x => - println("Bound: " + x) - }) + //genericTypeBounds.toList.foreach({ x ⇒ + // println("Bound: "+x) + //}) genericTypeBounds } @@ -162,7 +162,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case t: ObjectType => //this is safe val a = classHierarchy.superclassType(t) - println("AAA::::" + a) a match { case None => Result(t, MutableClass); //MutableObjectDueToUnknownSupertypes) case Some(superClassType) => @@ -216,12 +215,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal )( cf: ClassFile ): ProperPropertyComputationResult = { - // assert(superClassMutability.isMutable.isNoOrUnknown) - - //---generic classes handling - //printf(cf.asVirtualClass.asClassFile.classSignature.get.toString()) - //cf.asClassFile.classSignature.get.formalTypeParameters. - //--------------------------------------------- val t = cf.thisType @@ -238,11 +231,13 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal !f.isStatic } var hasShallowImmutableFields = false - var dependentImmutableFields: Set[Option[String]] = Set.empty + val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) + + var dependentImmutableFields: Set[String] = Set.empty + fieldsPropertyStoreInformation.foreach( f => { - println("f:::" + f) f match { case FinalP(MutableField) => { if (lazyComputation) @@ -251,11 +246,13 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal return createResultForAllSubtypes(t, MutableClass); } case FinalP(ShallowImmutableField) => hasShallowImmutableFields = true - case FinalEP(f, DependentImmutableField(x)) => { - //println("FieldType: "+f.asField.fieldType) - //println("FieldTypeSignature: "+f.asField.fieldTypeSignature.get.toJVMSignature) - dependentImmutableFields = dependentImmutableFields + x + case FinalP(DependentImmutableField(x)) => { + x match { + case Some(v) => dependentImmutableFields = dependentImmutableFields + v + case _ => + } } + case FinalP(DeepImmutableField) => case ep @ InterimE(e) => hasFieldsWithUnknownMutability = true @@ -273,44 +270,25 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } ) - /** - * dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { - * case FinalP(MutableField) => //NonFinalField) ⇒ - * // <=> The class is definitively mutable and therefore also all subclasses. - * if (lazyComputation) - * return Result(t, MutableClass); //MutableObjectByAnalysis); // - * else - * return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); - * case ep @ InterimE(e) => - * hasFieldsWithUnknownMutability = true - * (e, ep) - * case epk @ EPK(e: Entity, _) => - * // <=> The mutability information is not yet available. - * hasFieldsWithUnknownMutability = true - * (e, epk) - * - * // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields - * }).toMap * - */ var minLocalImmutability: ClassImmutability_new = if (!superClassMutabilityIsFinal) { - println("OnE!") MutableClass //MutableObjectByAnalysis } else { - println("Two!!") ShallowImmutableClass //ImmutableContainer } // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability_new = superClassInformation match { case UBP(ShallowImmutableClass) => ShallowImmutableClass //ImmutableContainer - case _ => DeepImmutableClass // ImmutableObject + case _ => DeepImmutableClass // ImmutableObject } if (!dependentImmutableFields.isEmpty) { - maxLocalImmutability = DependentImmutableClass(determineGenericTypeBounds(cf)) //TODO possibly here - } else if (hasShallowImmutableFields) { + maxLocalImmutability = DependentImmutableClass() + } + + if (hasShallowImmutableFields) { maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } @@ -321,65 +299,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } - //--var fieldTypes: Set[ObjectType] = Set.empty - //--if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { - //-- fieldTypes = // IMPROVE Use the precise type of the field (if available)! - // cf.fields.collect { - // case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType - // }.toSet - //} - - // For each dependent class file we have to determine the mutability - // of instances of the respective type to determine the immutability - // of instances of this class. - // Basically, we have to distinguish the following cases: - // - A field's type is mutable or conditionally immutable=> - // This class is conditionally immutable. - // - A field's type is immutable => - // The field is ignored. - // (This class is as immutable as its superclass.) - // - A field's type is at least conditionally mutable or - // The immutability of the field's type is not yet known => - // This type is AtLeastConditionallyImmutable - // We have to declare a dependency on the respective type's immutability. - // - // Additional handling is required w.r.t. the supertype: - // If the supertype is Immutable => - // Nothing special to do. - // If the supertype is AtLeastConditionallyImmutable => - // We have to declare a dependency on the supertype. - // If the supertype is ConditionallyImmutable => - // This type is also at most conditionally immutable. - // - // We furthermore have to take the mutability of the fields into consideration: - // If a field is not effectively final => - // This type is mutable. - // If a field is effectively final => - // Nothing special to do. - - //val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) - //val hasMutableOrConditionallyImmutableField = - // IMPROVE Use the precise type of the field (if available)! - //-- fieldTypesImmutability.exists { eOptP ⇒ - //-- eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) - //-- } - - if (hasShallowImmutableFields) { - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - println("shallow immutable fields.......!!!!!!!!!!!!!!!!!!") - } //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 - * } - * fieldTypesWithUndecidedMutability.foreach { eOptP => - * dependees += (eOptP.e -> eOptP) - * }* - */ - //} - if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { // <=> the super classes' immutability is final // (i.e., ImmutableObject or ImmutableContainer) @@ -400,7 +319,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal def c(someEPS: SomeEPS): ProperPropertyComputationResult = { //[DEBUG] val oldDependees = dependees dependees = dependees.filter(_._1 ne someEPS.e) - println("someEPS: " + someEPS) someEPS match { // Superclass related dependencies: // @@ -422,26 +340,16 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case LUBP(MutableClass, DeepImmutableClass) => //_: MutableObject, ImmutableObject) ⇒ // No information about superclass - // Properties related to the type of the class' fields. - // - /** - * case UBP(ShallowImmutableType | MutableType_new) ⇒ //ImmutableContainerType | MutableType) ⇒ - * maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - * dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) // TypeImmutability.key) - * - * case ELBP(e, DeepImmutableType) ⇒ // ImmutableType) ⇒ // Immutable field type, no influence on mutability - * dependees -= e - * - * case UBP(DeepImmutableType) ⇒ //ImmutableType) ⇒ // No information about field type - * - * case FinalP(MutableType_new) ⇒ //MutableType) - * Result(t, MutableClass) //TODO check - */ - case FinalP(DependentImmutableField(x)) => { - println( - ".........................................kkk......................................: " + x.get - ) - maxLocalImmutability = DependentImmutableClass() // DependentImmutableClass //TODO possibly here? + case FinalEP(f, DependentImmutableField(x)) => { + //determineGenericFieldImmutability(f.asInstanceOf[Field]) + if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass + } else { + maxLocalImmutability = DependentImmutableClass() + } + + //} else + // maxLocalImmutability = DependentImmutableClass // DependentImmutableClass //TODO possibly here? } // Field Mutability related dependencies: @@ -483,8 +391,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) minLocalImmutability = ShallowImmutableClass //ImmutableContainer - println("minLocalImmutability: " + minLocalImmutability) - println("maxLocalImmutability: " + maxLocalImmutability) if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { /*[DEBUG] assert( @@ -583,7 +489,7 @@ trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { .map(ot => (ot, project.classFile(ot))) .foreach { case (_, Some(cf)) => cfs ::= cf - case (t, None) => + 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 diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index 0529ed6b85..ec5d976859 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -69,7 +69,7 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC ): ProperPropertyComputationResult = { typeExtensibility(t) match { case Yes | Unknown => - println("Yes Unknown"); Result(t, MutableType_new) // MutableType) + Result(t, MutableType_new) // MutableType) case No => step2(t) } } @@ -81,9 +81,7 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC if (cf.exists(_.isFinal) || directSubtypes.isEmpty /*... the type is not extensible*/ ) { val c = new ProperOnUpdateContinuation { c => - println("c function called") def apply(eps: SomeEPS): ProperPropertyComputationResult = { - println("EPS: " + eps) eps match { case ELUBP(_, lb: ClassImmutability_new, ub: ClassImmutability_new) => val thisLB = lb.correspondingTypeImmutability @@ -95,11 +93,10 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC } } } + val resultToMatch = ps(t, ClassImmutability_new.key) - println("Result1: " + resultToMatch) resultToMatch match { case x @ FinalP(p) => { - println("x::: " + x.p.correspondingTypeImmutability) Result(t, p.correspondingTypeImmutability); } @@ -108,7 +105,6 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC val thisLB = lb.correspondingTypeImmutability InterimResult(t, thisLB, thisUB, Seq(eps), c) case epk => { - println("here ") InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) } //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) @@ -120,7 +116,6 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType val resultToMatch2 = ps(t, ClassImmutability_new.key) - println("Result2: " + resultToMatch2) resultToMatch2 match { case FinalP(DeepImmutableClass) => //ImmutableObject) => case FinalP(MutableClass) => //(_: MutableObject) => From 8a911cedbb2db0eab1e1a20a7e3a7c0addc2c3df Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 24 Dec 2019 16:47:34 +0100 Subject: [PATCH 024/327] handling generic types of fields with recognition of the package name of the inserted types. --- .../L0FieldImmutabilityAnalysis.scala | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index fbd8cf1f3f..82fe3670d2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -89,7 +89,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) println( "determine generic field imm-------------------------------------------------------------------" ) - var genericFields: Set[String] = Set.empty + var genericFields: Set[ObjectType] = Set.empty state.field.fieldTypeSignature.head match { case ClassTypeSignature(_, SimpleClassTypeSignature(name, typeArguments), _) => { typeArguments.foreach( @@ -106,7 +106,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) _ ) ) => { - genericFields += packageIdentifier2 + packageIdentifier1 match { + case Some(pid1) => genericFields += ObjectType(pid1 + packageIdentifier2) + case _ => genericFields += ObjectType(packageIdentifier2) + } + } case _ => } @@ -119,10 +123,8 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) genericFields.foreach(f => println("generic Field: " + f)) //state.typeImmutability = Some(true) - genericFields.toList.foreach(s => { - val objectType = ObjectType(s) + genericFields.toList.foreach(objectType => { val result = propertyStore(objectType, TypeImmutability_new.key) - println("Result generic field with objtype: " + objectType + " result: " + result) result match { case FinalP(DeepImmutableType) => case FinalP(ShallowImmutableType | MutableType_new) => { @@ -146,7 +148,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) return Some(true); } case FinalEP(f, DependentImmutableType) => { - println(f + " has dependent imm type") //TODO under construction //--------------------------------------------------------------------------------------- state.typeImmutability = determineGenericFieldImmutability(state) @@ -230,17 +231,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case ProperTypeArgument(variance, signature) => { signature match { case TypeVariableSignature(identifier) => true - case _ => false + case _ => false } } case _ => false } - }) if (tA.size > 0) tA.head match { case ProperTypeArgument(variance, TypeVariableSignature(identifier)) => identifier - case _ => "" + case _ => "" } else "" } @@ -260,7 +260,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(false) => { state.dependentTypeImmutability match { case Some(true) => Result(field, DependentImmutableField()) - case _ => Result(field, ShallowImmutableField) + case _ => Result(field, ShallowImmutableField) } } case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) From e916246658d5b9d5a71e23eda6a26ac230782e88 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 24 Dec 2019 16:50:57 +0100 Subject: [PATCH 025/327] New test cases for handling the immutability of generic types. Even of nested ones --- .../ClassImmutabilityAnalysisDemo.scala | 3 +- .../TypeImmutabilityAnalysisDemo.scala | 10 ------ ...DependentClassWithGenericField_deep01.java | 27 ++++++++++++++ ...DependentClassWithGenericField_deep11.java | 30 ++++++++++++++++ .../FinalEmptyClass.java | 10 ++++++ .../Generic_class1.java | 34 ++++++++++++++++++ .../Generic_class2.java | 35 +++++++++++++++++++ .../Generic_class3.java | 25 +++++++++++++ .../Generic_class4_deep.java | 21 +++++++++++ .../Generic_class4_shallow.java | 19 ++++++++++ .../TrivialMutableClass.java | 19 ++++++++++ .../analyses/ClassImmutabilityAnalysis.scala | 4 +-- .../LxTypeImmutabilityAnalysis_new.scala | 2 +- 13 files changed, 224 insertions(+), 15 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep01.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep11.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/FinalEmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class1.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class2.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class3.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/TrivialMutableClass.java 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 index 447d52c55d..a5c34b7557 100644 --- 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 @@ -10,7 +10,6 @@ import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ClassImmutability_new import org.opalj.br.fpcf.properties.DeepImmutableClass @@ -49,7 +48,7 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { analysesManager.project.get(RTACallGraphKey) val (propertyStore, _) = analysesManager.runAll( - LazyTypeImmutabilityAnalysis, + //LazyTypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, LazyL2FieldMutabilityAnalysis, 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 index 5793b66312..1739638a81 100644 --- 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 @@ -49,16 +49,6 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { analysesManager.project.get(RTACallGraphKey) val (propertyStore, _) = analysesManager.runAll( - /** - * LazyTypeImmutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL0ReferenceImmutabilityAnalysis, - * LazyL0PurityAnalysis, - * LazyL2FieldMutabilityAnalysis, - * LazyL0FieldImmutabilityAnalysis, - * EagerLxClassImmutabilityAnalysis_new, - * EagerLxTypeImmutabilityAnalysis_new* - */ //LazyTypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep01.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep01.java new file mode 100644 index 0000000000..b018a847e9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep01.java @@ -0,0 +1,27 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public final class DependentClassWithGenericField_deep01 { + + @DeepImmutableFieldAnnotation(value = "") + private FinalEmptyClass fec; + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("eff imm ref") + private Generic_class1 gc; + + public DependentClassWithGenericField_deep01(FinalEmptyClass fec) { + this.fec = fec; + gc = new Generic_class1 + (fec, new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); + } +} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep11.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep11.java new file mode 100644 index 0000000000..b85faef3ce --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep11.java @@ -0,0 +1,30 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class DependentClassWithGenericField_deep11 { + + @DependentImmutableFieldAnnotation(value = "", genericString = "T1") + @ImmutableReferenceAnnotation("") + private T1 t1; + + @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") + @ImmutableReferenceAnnotation("eff imm ref") + private DependentClassWithGenericField_deep1 gc; + + public DependentClassWithGenericField_deep11(T1 t1) { + this.t1 = t1; + gc = new DependentClassWithGenericField_deep1(t1); + } + + +} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/FinalEmptyClass.java new file mode 100644 index 0000000000..e347c4e5ea --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/FinalEmptyClass.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public final class FinalEmptyClass { + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class1.java new file mode 100644 index 0000000000..d539ee2c7a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class1.java @@ -0,0 +1,34 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class Generic_class1 { + + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") + private T1 t1; + + @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") + private T2 t2; + + @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") + private T3 t3; + + @DependentImmutableFieldAnnotation(value = "T4", genericString = "T4") + private T4 t4; + + @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") + private T5 t5; + + public Generic_class1(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + } + +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class2.java new file mode 100644 index 0000000000..d61328c0b9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class2.java @@ -0,0 +1,35 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class Generic_class2 { + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value="T1", genericString = "T1") + private T1 t1; + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value="T2", genericString = "T2") + private T2 t2; + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value="T3", genericString = "T3") + private T3 t3; + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value = "", genericString = "") + private Generic_class1 gc; + + public Generic_class2(T1 t1, T2 t2, T3 t3, FinalEmptyClass fec1, FinalEmptyClass fec2){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + gc = new Generic_class1(fec1, fec2, t1,t2,t3); + } + +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class3.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class3.java new file mode 100644 index 0000000000..5afd485a96 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class3.java @@ -0,0 +1,25 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.FinalE; +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class Generic_class3 { + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") + private T1 t1; + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value = "", genericString = "") + private Generic_class2 gc; + + public Generic_class3(T1 t1, FinalEmptyClass fec1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4){ + this.t1 = t1; + gc = new Generic_class2(t1, fec1, fec2, fec3, fec4); + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_deep.java new file mode 100644 index 0000000000..4795b818cc --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_deep.java @@ -0,0 +1,21 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.FinalE; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public final class Generic_class4_deep { + + @ImmutableReferenceAnnotation("") + @DeepImmutableFieldAnnotation("") + private Generic_class3 gc; + + public Generic_class4_deep(FinalEmptyClass fec1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4, FinalEmptyClass fec5){ + gc = new Generic_class3(fec1, fec2, fec3, fec4, fec5); + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_shallow.java new file mode 100644 index 0000000000..92a17f3951 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_shallow.java @@ -0,0 +1,19 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; + +@ShallowImmutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +public final class Generic_class4_shallow { + + @ImmutableReferenceAnnotation("") + @ShallowImmutableFieldAnnotation("") + private Generic_class3 gc; + + public Generic_class4_shallow(TrivialMutableClass tmc1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4, FinalEmptyClass fec5){ + gc = new Generic_class3(tmc1, fec2, fec3, fec4, fec5); + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/TrivialMutableClass.java new file mode 100644 index 0000000000..2997ac5e50 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/TrivialMutableClass.java @@ -0,0 +1,19 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +public class TrivialMutableClass { + + @MutableFieldAnnotation("") + @MutableReferenceAnnotation("") + public int n = 0; + + @MutableFieldAnnotation("") + @MutableReferenceAnnotation("") + public String name = "name"; +} 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/ClassImmutabilityAnalysis.scala index aba6712bc7..07c2d07b91 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/ClassImmutabilityAnalysis.scala @@ -219,7 +219,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability = superClassInformation match { case UBP(ImmutableContainer) => ImmutableContainer - case _ => ImmutableObject + case _ => ImmutableObject } if (cf.fields.exists(f => !f.isStatic && f.fieldType.isArrayType)) { @@ -463,7 +463,7 @@ trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { .map(ot => (ot, project.classFile(ot))) .foreach { case (_, Some(cf)) => cfs ::= cf - case (t, None) => + 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 diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index ec5d976859..cc122f9762 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -56,7 +56,7 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC e: Entity ): ProperPropertyComputationResult = e match { case t: ObjectType => step1(typeExtensibility)(t) - case _ => throw new IllegalArgumentException(s"$e is not an ObjectType") + case _ => throw new IllegalArgumentException(s"$e is not an ObjectType") } /** From 8a322e924767d929b1912adec114a227c5343df3 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Mon, 6 Jan 2020 13:23:35 +0100 Subject: [PATCH 026/327] Formatting --- .../pointsto/AllocationSitePointsToSetScala.scala | 10 +++++----- .../src/main/scala/org/opalj/fpcf/PropertyStore.scala | 9 ++++----- .../scala/org/opalj/fpcf/par/PKECPropertyStore.scala | 4 ++-- .../cg/reflection/TamiFlexCallGraphAnalysis.scala | 1 + 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/pointsto/AllocationSitePointsToSetScala.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/pointsto/AllocationSitePointsToSetScala.scala index ed191f6f60..22271c0735 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/pointsto/AllocationSitePointsToSetScala.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/pointsto/AllocationSitePointsToSetScala.scala @@ -26,12 +26,12 @@ sealed trait AllocationSitePointsToSetScalaPropertyMetaInformation extends Prope } case class AllocationSitePointsToSetScala( - override val elements: Set[AllocationSite], - override val types: Set[ReferenceType], // TODO: Use normal Set here - val orderedTypes: List[ReferenceType] + override val elements: Set[AllocationSite], + override val types: Set[ReferenceType], // TODO: Use normal Set here + val orderedTypes: List[ReferenceType] ) extends PointsToSetLike[AllocationSite, Set[AllocationSite], AllocationSitePointsToSetScala] - with OrderedProperty - with AllocationSitePointsToSetScalaPropertyMetaInformation { + with OrderedProperty + with AllocationSitePointsToSetScalaPropertyMetaInformation { final def key: PropertyKey[AllocationSitePointsToSetScala] = AllocationSitePointsToSetScala.key 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/PKECPropertyStore.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/PKECPropertyStore.scala index c740999717..539b905499 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 @@ -428,14 +428,14 @@ final class PKECPropertyStore( private[this] def handleFinalResult( finalEP: SomeFinalEP, potentiallyIdemPotentUpdate: Boolean = true // TODO: THIS IS A HORRIBLE WORKAROUND - ): Unit = { + ): 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 (oldEPKState.eOptionP == finalEP && potentiallyIdemPotentUpdate) { if (tracer.isDefined) tracer.get.idempotentUpdate(properties(pkId).get(e)) return ; // IDEMPOTENT UPDATE 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, From f65fe706507292cb660ab5e4871d986989dca10c Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Mon, 6 Jan 2020 13:25:02 +0100 Subject: [PATCH 027/327] Fixed wrong phase setup --- .../si/src/test/scala/org/opalj/fpcf/PropertyStoreTest.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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..170a75fbde 100644 --- a/OPAL/si/src/test/scala/org/opalj/fpcf/PropertyStoreTest.scala +++ b/OPAL/si/src/test/scala/org/opalj/fpcf/PropertyStoreTest.scala @@ -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( @@ -907,7 +907,8 @@ sealed abstract class PropertyStoreTest[PS <: PropertyStore] } else { None } - case _ ⇒ ??? + case _ ⇒ + ??? } ) } From 02daee7720926e263b1b999b26055dc542a7ece8 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Mon, 6 Jan 2020 13:26:03 +0100 Subject: [PATCH 028/327] Make it TACAIBased - needs TACAI anyway --- ...dThreadRelatedCallsAnalysisScheduler.scala | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) 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 f3b67ff5c1..1c953a9721 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,9 +39,10 @@ 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 trait PointsToBasedThreadStartAnalysis - extends APIBasedAnalysis + extends TACAIBasedAPIBasedAnalysis with AbstractPointsToBasedAnalysis { def threadStartMethod: DeclaredMethod @@ -49,16 +51,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)).asVirtualMethodCall.receiver + val receiver = tac.stmts(tac.pcToIndex(pc)).asVirtualMethodCall.receiver handleStart(caller, receiver, pc, indirectCalls) } else indirectCalls.addIncompleteCallSite(pc) From 7abfc64fa2d9ddf39d0ad3fd68b819a2c150b2ca Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Mon, 6 Jan 2020 13:26:31 +0100 Subject: [PATCH 029/327] Two new WIP parallel property store implementations --- .../org/opalj/support/info/CallGraph.scala | 24 +- .../scala/org/opalj/support/info/Purity.scala | 14 +- OPAL/si/src/main/resources/reference.conf | 2 +- .../org/opalj/fpcf/par/DHTPropertyStore.scala | 1112 +++++++++++++++++ .../main/scala/org/opalj/fpcf/par/YAPPS.scala | 867 +++++++++++++ .../opalj/fpcf/par/DHTPropertyStoreTest.scala | 42 + .../scala/org/opalj/fpcf/par/YAPPSTest.scala | 41 + 7 files changed, 2085 insertions(+), 17 deletions(-) create mode 100644 OPAL/si/src/main/scala/org/opalj/fpcf/par/DHTPropertyStore.scala create mode 100644 OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala create mode 100644 OPAL/si/src/test/scala/org/opalj/fpcf/par/DHTPropertyStoreTest.scala create mode 100644 OPAL/si/src/test/scala/org/opalj/fpcf/par/YAPPSTest.scala 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 ef2cae19fa..d5dd178b59 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.AllocationSiteBasedPointsToScalaCallGraphKey import org.opalj.tac.cg.CallGraphSerializer @@ -70,7 +69,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" @@ -137,20 +136,25 @@ object CallGraph extends ProjectAnalysisApplication { cgFile: Option[String], outputFile: Option[String], pointsToFile: Option[String], - numThreads: Option[Int], + numThreads: Option[Int], projectTime: Seconds ): BasicReport = { project.getOrCreateProjectInformationKeyInitializationData( PropertyStoreKey, (context: List[PropertyStoreContext[AnyRef]]) ⇒ { implicit val lg: LogContext = project.logContext - val threads = numThreads.getOrElse(0) // We chose the sequential store as default + /*val threads = numThreads.getOrElse(0) // We chose the sequential store as default if (threads == 0) { org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) } else { org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = threads org.opalj.fpcf.par.PKECPropertyStore(context: _*) - } + }*/ + //org.opalj.fpcf.par.YAPPS(context: _*) + org.opalj.fpcf.par.DHTPropertyStore(context: _*) + //org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) + //org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = 4 + //org.opalj.fpcf.par.PKECPropertyStore(context: _*) } ) @@ -188,7 +192,7 @@ object CallGraph extends ProjectAnalysisApplication { if (cgAlgorithm == "PointsTo") { val ptss = ps.entities(AllocationSitePointsToSet.key).toList - import scala.collection.JavaConverters._ + /*import scala.collection.JavaConverters._ val statistic = ptss.groupBy(p ⇒ p.ub.elements.size).mapValues { spts ⇒ (spts.size, { val unique = new java.util.IdentityHashMap[AllocationSitePointsToSet, AllocationSitePointsToSet]() @@ -197,7 +201,7 @@ object CallGraph extends ProjectAnalysisApplication { }) }.map { case (size, (count, uniqueCount)) ⇒ (size, count, uniqueCount) }.toArray.sorted println("size, count, unique count") - println(statistic.mkString("\n")) + println(statistic.mkString("\n"))*/ println(s"PTSs ${ptss.size}") println(s"PTS entries ${ptss.map(p ⇒ p.ub.elements.size).sum}") @@ -524,4 +528,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/Purity.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Purity.scala index f18a62705c..09cd0776a0 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 @@ -60,10 +60,7 @@ 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 @@ -90,7 +87,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"+ @@ -197,12 +194,17 @@ object Purity { PropertyStoreKey, (context: List[PropertyStoreContext[AnyRef]]) ⇒ { implicit val lg: LogContext = project.logContext - if (numThreads == 0) { + /*if (numThreads == 0) { org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) } else { org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = numThreads org.opalj.fpcf.par.PKECPropertyStore(context: _*) - } + }*/ + //org.opalj.fpcf.par.YAPPS(context: _*) + org.opalj.fpcf.par.DHTPropertyStore(context: _*) + //org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) + //org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = 4 + //org.opalj.fpcf.par.PKECPropertyStore(context: _*) } ) diff --git a/OPAL/si/src/main/resources/reference.conf b/OPAL/si/src/main/resources/reference.conf index c450b5d446..2950324ef1 100644 --- a/OPAL/si/src/main/resources/reference.conf +++ b/OPAL/si/src/main/resources/reference.conf @@ -4,7 +4,7 @@ org.opalj { // to support the debugging of analyses developed using the property store. // I.e., debug performs a wide range of additionaly checks to identify errors as // early as possible. - fpcf.PropertyStore.Debug = false + fpcf.PropertyStore.Debug = true fpcf.PropertyStore.TraceFallbacks = false fpcf.PropertyStore.TraceSuppressedNotifications = false diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/DHTPropertyStore.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/DHTPropertyStore.scala new file mode 100644 index 0000000000..d329d466f6 --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/DHTPropertyStore.scala @@ -0,0 +1,1112 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org +package opalj +package fpcf +package par + +import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.ConcurrentLinkedQueue + +import scala.collection.mutable.AnyRefMap +import scala.collection.mutable.{Set ⇒ MutableSet} +import scala.collection.mutable.ArrayBuffer +import scala.collection.mutable.PriorityQueue +import scala.collection.mutable.Queue +import scala.util.control.ControlThrowable + +import org.opalj.log.LogContext +import org.opalj.fpcf.PropertyKey.fallbackPropertyBasedOnPKId + +/** + * Yet another parallel property store. + * Based on the ideas of a distributed hash table, computations for an entity are bound to a + * specific thread. + * + * @author Dominik Helm + */ +class DHTPropertyStore( + final val ctx: Map[Class[_], AnyRef] +)( + implicit + val logContext: LogContext +) extends ParallelPropertyStore { + propertyStore ⇒ + + val THREAD_COUNT = 1 + + private[this] val ps: Array[Array[AnyRefMap[Entity, DhtEpkState]]] = + Array.fill(THREAD_COUNT) { + Array.fill(PropertyKind.SupportedPropertyKinds) { + AnyRefMap.empty + } + } + + private[this] val dependers: Array[AnyRefMap[SomeEPK, MutableSet[SomeEPK]]] = + Array.fill(THREAD_COUNT) { AnyRefMap.empty } + + private[this] val tasks: Array[Array[ConcurrentLinkedQueue[DhtTask]]] = + Array.fill(THREAD_COUNT) { + Array.fill(THREAD_COUNT) { + new ConcurrentLinkedQueue + } + } + + //TODO Need Priority Queue here + private[this] val localTasks: Array[PriorityQueue[DhtContinuationTask]] = + Array.fill(THREAD_COUNT) { + new PriorityQueue()({ + (x: DhtContinuationTask, y: DhtContinuationTask) ⇒ + x.priority - y.priority + + }) + } + + private[this] val fastQueues: Array[Queue[DhtTask]] = + Array.fill(THREAD_COUNT) { new Queue() } + + private[this] val activeThreads: AtomicInteger = new AtomicInteger(THREAD_COUNT) + + private[this] val threads: Array[Thread] = new Array(THREAD_COUNT) + + private[this] val triggeredComputations: Array[Array[SomePropertyComputation]] = + new Array(PropertyKind.SupportedPropertyKinds) + + private[this] var setAndPreinitializedValues: List[SomeEPK] = List.empty + + override def MaxEvaluationDepth: Int = 0 //TODO + override def shutdown(): Unit = {} + var idle: Boolean = true + override def isIdle: Boolean = idle + + // -------------------------------------------------------------------------------------------- + // + // STATISTICS + // + // -------------------------------------------------------------------------------------------- + + private[this] var quiescenceCounter = 0 + override def quiescenceCount: Int = quiescenceCounter + + private[this] val scheduledTasks: Array[Int] = Array.fill(THREAD_COUNT) { 0 } + override def scheduledTasksCount: Int = scheduledTasks.sum + + private[this] val scheduledOnUpdateComputations: Array[Int] = Array.fill(THREAD_COUNT) { 0 } + override def scheduledOnUpdateComputationsCount: Int = scheduledOnUpdateComputations.sum + + override def fallbacksUsedForComputedPropertiesCount: Int = 0 //TODO + + override private[fpcf] def incrementFallbacksUsedForComputedPropertiesCounter(): Unit = { + // ??? + } + + // -------------------------------------------------------------------------------------------- + // + // BASIC QUERY METHODS (ONLY TO BE CALLED WHEN THE STORE IS QUIESCENT) + // + // -------------------------------------------------------------------------------------------- + + override def toString(printProperties: Boolean): String = + if (printProperties) { + var pkId = 0 + while (pkId <= PropertyKey.maxId) { + ps.iterator.flatMap(_(pkId).valuesIterator.map { + _.eOptP.toString.replace("\n", "\n\t") + }).toList.sorted.mkString(s"Entities for property key $pkId:\n\t", "\n\t", "\n") + pkId += 1 + } + ps.mkString("PropertyStore(\n\t", "\n\t", "\n)") + } else { + s"PropertyStore(properties=${ps.iterator.flatMap(_.map(_.size)).sum})" + } + + override def entities(propertyFilter: SomeEPS ⇒ Boolean): Iterator[Entity] = { + ps.iterator.flatMap { propertiesPerThread ⇒ + propertiesPerThread.iterator.flatMap { propertiesPerKind ⇒ + propertiesPerKind.valuesIterator.filter { epkState ⇒ + propertyFilter(epkState.eOptP.asEPS) + } + } + }.map(_.eOptP.e) + } + + override def entities[P <: Property](pk: PropertyKey[P]): Iterator[EPS[Entity, P]] = { + ps.iterator.flatMap { propertiesPerThread ⇒ + propertiesPerThread(pk.id).valuesIterator.map(_.eOptP.asInstanceOf[EPS[Entity, P]]) + } + } + + override def entities[P <: Property](lb: P, ub: P): Iterator[Entity] = { + entities { eps ⇒ eps.lb == lb && eps.ub == ub } + } + + override def entitiesWithLB[P <: Property](lb: P): Iterator[Entity] = { + entities { eps ⇒ eps.lb == lb } + } + + override def entitiesWithUB[P <: Property](ub: P): Iterator[Entity] = { + entities { eps ⇒ eps.ub == ub } + } + + override def properties[E <: Entity](e: E): Iterator[EPS[E, Property]] = { + ps(getResponsibleTId(e)).iterator.flatMap { propertiesPerKind ⇒ + val ePKState = propertiesPerKind.get(e) + if (ePKState.exists(_.eOptP.isEPS)) + Iterator.single(ePKState.get.eOptP.asInstanceOf[EPS[E, Property]]) + else + Iterator.empty + } + } + + override def hasProperty(e: Entity, pk: PropertyKind): Boolean = { + val ePKState = ps(getResponsibleTId(e))(pk.id).get(e) + ePKState.exists { state ⇒ + state.eOptP.hasUBP || state.eOptP.hasLBP + } + } + + override def isKnown(e: Entity): Boolean = { + ps(getResponsibleTId(e)).exists { propertiesPerKind ⇒ + propertiesPerKind.contains(e) + } + } + + override def get[E <: Entity, P <: Property](e: E, pk: PropertyKey[P]): Option[EOptionP[E, P]] = { + ps(getResponsibleTId(e))(pk.id).get(e).map(_.eOptP.asInstanceOf[EOptionP[E, P]]) + } + + override def get[E <: Entity, P <: Property](epk: EPK[E, P]): Option[EOptionP[E, P]] = { + get(epk.e, epk.pk) + } + + // -------------------------------------------------------------------------------------------- + // + // CORE IMPLEMENTATION - NOT THREAD SAFE PART + // + // -------------------------------------------------------------------------------------------- + + override protected[this] def doScheduleEagerComputationForEntity[E <: Entity]( + e: E + )(pc: PropertyComputation[E]): Unit = { + schedulePropertyComputation(e, pc, 0) + } + + override protected[this] def doRegisterTriggeredComputation[E <: Entity, P <: Property]( + pk: PropertyKey[P], + pc: PropertyComputation[E] + ): Unit = { + + // Recall that the scheduler has to take care of registering a triggered computation + // before the first analysis derives a respective value! + // Hence, there is no need to immediately check that we have to trigger a computation. + + val pkId = pk.id + val oldComputations: Array[SomePropertyComputation] = triggeredComputations(pkId) + var newComputations: Array[SomePropertyComputation] = null + + if (oldComputations == null) { + newComputations = Array[SomePropertyComputation](pc) + } else { + newComputations = java.util.Arrays.copyOf(oldComputations, oldComputations.length + 1) + newComputations(oldComputations.length) = pc + } + triggeredComputations(pkId) = newComputations + } + + override protected[this] def doSet(e: Entity, p: Property): Unit = { + val epkState = DhtEpkState(FinalEP(e, p), null, null) + + val oldP = ps(getResponsibleTId(e))(p.id).put(e, epkState) + if (oldP.isDefined) { + throw new IllegalStateException(s"$e already had the property $oldP") + } + setAndPreinitializedValues ::= EPK(e, p.key) + } + + override protected[this] def doPreInitialize[E <: Entity, P <: Property]( + e: E, + pk: PropertyKey[P] + )(pc: EOptionP[E, P] ⇒ InterimEP[E, P]): Unit = { + val pkId = pk.id + val propertiesOfKind = ps(getResponsibleTId(e))(pkId) + val oldEPKState = propertiesOfKind.get(e) + val newInterimEP: SomeInterimEP = + oldEPKState match { + case None ⇒ + val epk = EPK(e, pk) + setAndPreinitializedValues ::= epk + pc(epk) + case Some(epkState) ⇒ + pc(epkState.eOptP.asInstanceOf[EOptionP[E, P]]) + } + assert(newInterimEP.isRefinable) + val newEPKState = DhtEpkState(newInterimEP, null, null) + propertiesOfKind.put(e, newEPKState) + } + + // -------------------------------------------------------------------------------------------- + // + // CORE IMPLEMENTATION - THREAD SAFE PART + // + // -------------------------------------------------------------------------------------------- + + override def force[E <: Entity, P <: Property](e: E, pk: PropertyKey[P]): Unit = { + doApply(EPK(e, pk), e, pk.id) + } + + override def execute(f: ⇒ Unit): Unit = { + submitTask(new DhtExecuteTask(f), 0 /* TODO: Use some fair distribution */ , currentTId()) + } + + private[this] def schedulePropertyComputation[E <: Entity]( + e: E, + pc: PropertyComputation[E], + tId: Int + ): Unit = { + submitTask(new DhtPropertyComputationTask(e, pc), getResponsibleTId(e), tId) + } + + private[this] def currentTId(): Int = { + Thread.currentThread() match { + case DhtThread(id) ⇒ id + case _ ⇒ 0 + } + } + + private[this] val fakeEntities: Array[Int] = Array.fill(THREAD_COUNT) { 0 } + + override def handleResult(r: PropertyComputationResult): Unit = { + doHandleResult(r) + } + + private[this] def doHandleResult(r: PropertyComputationResult, origin: DhtEpkState = null): 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 { doHandleResult(_, origin) } + + case IncrementalResult.id ⇒ + val IncrementalResult(ir, npcs) = r + doHandleResult(ir, origin) + npcs /*: Iterator[(PropertyComputation[e],e)]*/ foreach { npc ⇒ + val (pc, e) = npc + val tId = currentTId() + schedulePropertyComputation(e, pc, tId) + } + + // + // 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(r, u, e, pk) + + case PrecomputedPartialResult.id ⇒ + val PrecomputedPartialResult(expectedEOptionP, updatedInterimEP, u) = r + handlePartialResult( + r, + oldEOptP ⇒ if (oldEOptP eq expectedEOptionP) { + Some(updatedInterimEP) + } else { + u.asInstanceOf[SomeEOptionP ⇒ Option[SomeInterimEP]](oldEOptP) + }, + expectedEOptionP.e, + expectedEOptionP.pk + ) + + case InterimPartialResult.id ⇒ + val InterimPartialResult(prs, dependees, c) = r + + prs foreach { pr ⇒ + handlePartialResult( + pr, + pr.u.asInstanceOf[SomeEOptionP ⇒ Option[SomeInterimEP]], + pr.e, + pr.pk + ) + } + + val ownTId = currentTId() + + /*if((origin ne null) && origin.eOptP.e.isInstanceOf[DhtFakeEntity]){ + val epk = origin.eOptP.asEPK + val e = epk.e + + val epkState = DhtEpkState( + epk, + { dependee: SomeEPS ⇒ + val result = c(dependee) + + ps(ownTId)(AnalysisKeyId).remove(e) + + val ds = dependers(ownTId) + dependees.foreach { dependee ⇒ + ds(dependee.toEPK) -= epk + } + + result + }, + dependees + ) + + ps(ownTId)(AnalysisKeyId).put(epk, epkState) + + updateDependees(epk, dependees, Some(origin), ownTId) + } else {*/ + val e = new DhtFakeEntity(ownTId + THREAD_COUNT * fakeEntities(ownTId)) + fakeEntities(ownTId) += 1 + val epk = EPK(e, AnalysisKey) + + val epkState = DhtEpkState( + epk, + { dependee: SomeEPS ⇒ + val result = c(dependee) + + ps(ownTId)(AnalysisKeyId).remove(e) + + val ds = dependers(ownTId) + dependees.foreach { dependee ⇒ + ds(dependee.toEPK) -= epk + } + + result + }, + dependees + ) + + ps(ownTId)(AnalysisKeyId).put(e, epkState) + + updateDependees(epk, dependees, None, ownTId) + //} + } + } + + private[this] def notifyDependers( + dependee: SomeEPS, + ownTId: Int, + unnotifiedPKs: Set[PropertyKind] = Set.empty + ): Unit = { + var tId = 0 + while (tId < THREAD_COUNT) { + submitTask(new DhtNotifyDependersTask(dependee, unnotifiedPKs), tId, ownTId) + tId += 1 + } + } + + private[this] def triggerComputations(e: Entity, pkId: Int, tId: Int): Unit = { + val computations = triggeredComputations(pkId) + if (computations ne null) { + computations foreach { pc ⇒ + schedulePropertyComputation(e, pc.asInstanceOf[PropertyComputation[Entity]], tId) + } + } + } + + private[this] def handleFinalResult( + finalEP: FinalEP[Entity, Property], + unnotifiedPKs: Set[PropertyKind] = Set.empty + ): Unit = { + val epkState = DhtEpkState(finalEP, null, null) + + val FinalEP(e, p) = finalEP + val ownTId = currentTId() + val targetTId = getResponsibleTId(e) + + if (targetTId != ownTId) { + submitTask(new DhtSetTask(e, finalEP, p.id), targetTId, ownTId) + return ; + } + + val oldPO = ps(ownTId)(p.id).put(e, epkState) + + if (oldPO.isDefined) { + val oldP = oldPO.get + + if (debug) { + if (oldP.eOptP.isFinal) { + throw new IllegalStateException(s"$e already had the property $oldP") + } else { + oldP.eOptP.checkIsValidPropertiesUpdate(finalEP, Nil) + } + } + + val dependees = oldP.dependees + if (dependees ne null) { + val ds = dependers(ownTId) + dependees.foreach { dependee ⇒ + val theDependers = ds.get(dependee.toEPK) + if (theDependers.isDefined) theDependers.get -= finalEP.toEPK + } + } + + if (oldP.eOptP.isEPK) + triggerComputations(e, p.id, ownTId) + } else { + triggerComputations(e, p.id, ownTId) + } + + notifyDependers(finalEP, ownTId, unnotifiedPKs) + } + + private[this] def handleInterimResult( + interimEP: InterimEP[Entity, _ >: Null <: Property], + c: ProperOnUpdateContinuation, + dependees: Traversable[SomeEOptionP] + ): Unit = { + val epkState = DhtEpkState(interimEP, c, dependees) + + val SomeEPS(e, p) = interimEP + val ownTId = currentTId() + val targetTId = getResponsibleTId(e) + + if (targetTId != ownTId) { + submitTask( + new DhtPropertyComputationTask( + e, + { _: Entity ⇒ InterimResult(interimEP, dependees, c) } + ), + targetTId, + ownTId + ) + return ; + } + + val oldPO = ps(ownTId)(p.id).put(e, epkState) + + if (oldPO.isDefined) { + val oldP = oldPO.get + + if (debug) { + oldPO.get.eOptP.checkIsValidPropertiesUpdate(interimEP, dependees) + } + + if (oldP.eOptP.isEPK) + triggerComputations(e, p.id, ownTId) + } else { + triggerComputations(e, p.id, ownTId) + } + + updateDependees(interimEP.toEPK, dependees, oldPO, ownTId) + + if (oldPO.isEmpty || interimEP.isUpdatedComparedTo(oldPO.get.eOptP)) + notifyDependers(interimEP, ownTId) + } + + private[this] def updateDependees(depender: SomeEPK, dependees: Traversable[SomeEOptionP], oldPO: Option[DhtEpkState], ownTId: Int): Unit = { + val oldDependees: Set[SomeEOptionP] = if (oldPO.isDefined) oldPO.get.dependees.toSet else Set.empty + var newDependees: Set[SomeEOptionP] = Set.empty + + val ds = dependers(ownTId) + dependees.foreach { dependee ⇒ + if (oldDependees.contains(dependee)) { + //oldDependees -= dependee + } else { + ds.getOrElseUpdate(dependee.toEPK, MutableSet.empty) += depender + newDependees += dependee + } + } + + //println(s"${dependees.size} vs ${newDependees.size} vs ${oldDependees.size}") + + //TODO Drop old status as depender for removed dependees? + + val suppressedPKs = suppressInterimUpdates(depender.pk.id) + var updateTasks: List[DhtTask] = List.empty + val hasKnownUpdate = newDependees.exists { dependee ⇒ + val dependeeState = ps(ownTId)(dependee.pk.id).get(dependee.e) + dependeeState match { + case Some(DhtEpkState(eOptP, _, _)) if (eOptP ne dependee) && eOptP.isEPS && (eOptP.isFinal || !suppressedPKs(eOptP.pk.id)) ⇒ + updateTasks = List(new DhtContinuationTask(depender.e, depender.pk.id, dependees.size, eOptP.asEPS)) + true + case _ ⇒ + updateTasks ::= new DhtNotifyIfUpdatedTask(depender, dependees.size, dependee) + false + } + } + + if (hasKnownUpdate) { + submitTask(updateTasks.head, ownTId, ownTId) + } else { + updateTasks.foreach { task ⇒ + val targetTId = getResponsibleTId(task.asInstanceOf[DhtNotifyIfUpdatedTask].dependee.e) + if (targetTId != ownTId) + submitTask(task, targetTId, ownTId) + } + } + } + + private[this] def handlePartialResult( + pr: PropertyComputationResult, + getResult: (SomeEOptionP ⇒ Option[SomeInterimEP]), + e: Entity, + pk: SomePropertyKey + ): Unit = { + val ownTId = currentTId() + val targetTId = getResponsibleTId(e) + + if (targetTId != ownTId) { + submitTask( + new DhtPropertyComputationTask( + e, + { _: Entity ⇒ pr } + ), + targetTId, + ownTId + ) + return ; + } + + val oldPO = ps(ownTId)(pk.id).get(e) + + val oldEOptP = if (oldPO.isDefined) { + oldPO.get.eOptP + } else { + EPK(e, pk) + } + + if (oldEOptP.isFinal) { + throw new IllegalStateException(s"$e already had the property ${oldEOptP.asFinal.p}") + } + + val result = getResult(oldEOptP) + + if (result.isDefined) { + ps(ownTId)(pk.id).put(e, DhtEpkState(result.get, null, null)) + if (oldEOptP.isEPK) + triggerComputations(e, pk.id, ownTId) + + notifyDependers(result.get, ownTId) + } + } + + override protected[this] def doApply[E <: Entity, P <: Property]( + epk: EPK[E, P], + e: E, + pkId: Int + ): EOptionP[E, P] = { + //TODO always return current state? + val targetTId = getResponsibleTId(e) + val ownTId = currentTId() + if (targetTId == ownTId || isIdle) { + val ePKState = ps(targetTId)(pkId).get(e) + if (ePKState.isDefined) + ePKState.get.eOptP.asInstanceOf[EOptionP[E, P]] + else + handleUnknownEPK(epk, e, pkId, targetTId, ownTId) + } else { + handleUnknownEPK(epk, e, pkId, targetTId, ownTId) + } + } + + private[this] def handleUnknownEPK[E <: Entity, P <: Property]( + epk: EPK[E, P], + e: E, + pkId: Int, + targetTId: Int, + sourceTId: Int + ): EOptionP[E, P] = { + val lazyComputation = lazyComputations(pkId) + if (lazyComputation ne null) { + submitTask( + new DhtLazyComputationTask( + e, + pkId, + lazyComputation.asInstanceOf[E ⇒ PropertyComputationResult] + ), + targetTId, + sourceTId + ) + epk + } 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) + ps(targetTId)(pkId).put(e, DhtEpkState(result, null, null)) + triggerComputations(e, pkId, sourceTId) + result.asInstanceOf[FinalEP[E, P]] + } else { + ps(targetTId)(pkId).put(e, DhtEpkState(epk, d ⇒ new Result(transformer._2(e, d.asFinal.p)), Some(dependee))) + updateDependees(epk, Some(dependee), None, targetTId) + epk + } + } else { + submitTask(new DhtSetInitialTask(e, epk, pkId), targetTId, sourceTId) + epk + } + } else { + val finalEP = computeFallback[E, P](e, pkId) + submitTask(new DhtSetInitialTask(e, finalEP, pkId), targetTId, sourceTId) + finalEP + } + } + + private[this] def startThreads(thread: Int ⇒ DhtThread): Unit = { + var tId = 0 + while (tId < THREAD_COUNT) { + val t = thread(tId) + threads(tId) = t + tId += 1 + } + threads.foreach { _.start() } + threads.foreach { _.join } + if (doTerminate) { + if (exception ne null) throw exception + else throw new InterruptedException + } + } + + private[this] def hasActiveThreads: Boolean = { + var tId = 0 + while (tId < THREAD_COUNT) { + if (isActive(tId).get()) { + return true; + } + tId += 1 + } + false + } + + override def waitOnPhaseCompletion(): Unit = { + idle = false + + // If some values were explicitly set, we have to trigger corresponding triggered + // computations. + setAndPreinitializedValues.foreach { epk ⇒ triggerComputations(epk.e, epk.pk.id, 0) } + setAndPreinitializedValues = List.empty + + while (subPhaseId < subPhaseFinalizationOrder.length) { + var continueCycles = false + do { + var continueFallbacks = false + do { + activeThreads.set(THREAD_COUNT) + startThreads(new DhtWorkerThread(_)) + + quiescenceCounter += 1 + + startThreads(new DhtFallbackThread(_)) + + continueFallbacks = hasActiveThreads + } while (continueFallbacks) + + startThreads(new DhtCycleResolutionThread(_)) + + resolveCycles() + + continueCycles = hasActiveThreads + } while (continueCycles) + + startThreads(new DhtPartialPropertiesFinalizerThread(_)) + + subPhaseId += 1 + } + + idle = true + } + + private[this] def resolveCycles(): Unit = { + val theInterimStates = new ArrayBuffer[DhtEpkState](interimStates.iterator.map(_.size).sum) + var tId = 0 + while (tId < THREAD_COUNT) { + theInterimStates ++= interimStates(tId) + tId += 1 + } + + val theSuccessors = (interimEPKState: DhtEpkState) ⇒ { + successors(getResponsibleTId(interimEPKState.eOptP.e))(interimEPKState) + } + + val cSCCs = graphs.closedSCCs(theInterimStates, theSuccessors) + + for (cSCC ← cSCCs) { + for (interimEPKState ← cSCC) { + val dependees = interimEPKState.dependees + val epk = interimEPKState.eOptP.toEPK + val e = epk.e + val targetTId = getResponsibleTId(e) + dependees.foreach { dependee ⇒ + dependers(targetTId)(dependee.toEPK) -= epk + } + submitTask( + new DhtSetTask( + e, + interimEPKState.eOptP.toFinalEP, + epk.pk.id + ), + targetTId, + 0 + ) + } + } + } + + private[this] val isActive: Array[AtomicBoolean] = Array.fill(THREAD_COUNT)(new AtomicBoolean(true)) + + private[this] def getResponsibleTId(e: Entity): Int = { + Math.abs(e.hashCode() >> 5) % THREAD_COUNT + } + + private[this] val interimStates: Array[ArrayBuffer[DhtEpkState]] = + Array.fill(THREAD_COUNT)(null) + private[this] val successors: Array[DhtEpkState ⇒ Traversable[DhtEpkState]] = + Array.fill(THREAD_COUNT)(null) + + private[this] def submitTask(task: DhtTask, targetTId: Int, sourceTId: Int): Unit = { + if (targetTId == sourceTId) { + if (task.isInstanceOf[DhtContinuationTask]) { + val ct = task.asInstanceOf[DhtContinuationTask] + localTasks(sourceTId).enqueue(ct) + val lastUpdate = nextUpdate(sourceTId).put((ct.dependee.toEPK, ct.dependerE, ct.dependerPKId), ct) + lastUpdate.foreach(_.perform = false) + } else { + fastQueues(sourceTId).enqueue(task) + } + isActive(targetTId).set(true) + } else { + tasks(targetTId)(sourceTId).offer(task) + val targetThread = threads(targetTId) + val wasActive = isActive(targetTId).getAndSet(true) + if (!wasActive) { + targetThread synchronized { + targetThread.notifyAll() + } + } + } + } + + //TODO this is a slow hack to prevent reordered continuation task messing up the state + private[this] val nextUpdate: Array[AnyRefMap[(SomeEPK, Entity, Int), DhtContinuationTask]] = Array.fill(THREAD_COUNT)(AnyRefMap.empty) + + class DhtThread(val ownTId: Int, name: String) extends Thread(name) + + object DhtThread { + def unapply(t: DhtThread): Some[Int] = { + Some(t.ownTId) + } + } + + class DhtWorkerThread(ownTId: Int) extends DhtThread(ownTId, s"PropertyStoreThread-#$ownTId") { + + private[this] val localQueue: PriorityQueue[DhtContinuationTask] = localTasks(ownTId) + private[this] val localQueues: Array[ConcurrentLinkedQueue[DhtTask]] = tasks(ownTId) + private[this] val fastQueue = fastQueues(ownTId) + + override def run(): Unit = { + try { + while (!doTerminate) { + consolidateQueues() + if (localQueue.isEmpty && fastQueue.isEmpty) { + val active = activeThreads.decrementAndGet() + if (active == 0) { + var continue = false + var tId = 0 + while (tId < THREAD_COUNT) { + if (tId != ownTId && isActive(tId).get()) { + continue = true + } + tId += 1 + } + consolidateQueues() + if (!continue && localQueue.isEmpty && fastQueue.isEmpty) { + threads.foreach { t ⇒ + if (t ne this) + t.interrupt() + } + isActive(ownTId).set(false) + return ; + } + activeThreads.incrementAndGet() + } else { + val t = Thread.currentThread() + t synchronized { + isActive(ownTId).set(false) //TODO does this have to be later? + consolidateQueues() + if (localQueue.isEmpty && fastQueue.isEmpty) { + try { + t.wait() + } catch { + case _: InterruptedException ⇒ return ; + } + } else { + isActive(ownTId).set(true) + } + } + activeThreads.incrementAndGet() + } + } else { + while (fastQueue.nonEmpty && !doTerminate) + fastQueue.dequeue().apply() + if (localQueue.nonEmpty) + localQueue.dequeue().apply() + } + } + } 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() + } + } + } + + private[this] def consolidateQueues(): Unit = { + var tId = 0 + val ownNextUpdates = nextUpdate(ownTId) + while (tId < THREAD_COUNT) { + val queue = localQueues(tId) + var t: DhtTask = null + while ({ t = queue.poll(); t ne null }) { + if (t.isInstanceOf[DhtContinuationTask]) { + val ct = t.asInstanceOf[DhtContinuationTask] + localQueue += ct + val lastUpdate = ownNextUpdates.put((ct.dependee.toEPK, ct.dependerE, ct.dependerPKId), ct) + lastUpdate.foreach(_.perform = false) + } else { + fastQueue.enqueue(t) + } + } + + tId += 1 + } + } + } + + class DhtFallbackThread(ownTId: Int) extends DhtThread(ownTId, s"PropertyStoreFallbackThread-#$ownTId") { + + override def run(): Unit = { + val localStore = ps(ownTId) + + var pkId = 0 + while (pkId <= PropertyKey.maxId) { + if (propertyKindsComputedInThisPhase(pkId)) { + localStore(pkId).valuesIterator.foreach { oldState ⇒ + if (oldState.eOptP.isEPK && ((oldState.dependees eq null) || oldState.dependees.isEmpty)) { + val e = oldState.eOptP.e + val reason = PropertyIsNotDerivedByPreviouslyExecutedAnalysis + val p = fallbackPropertyBasedOnPKId(propertyStore, reason, e, pkId) + val finalEP = FinalEP(e, p) + incrementFallbacksUsedForComputedPropertiesCounter() + handleFinalResult(finalEP) + } + } + } + pkId += 1 + } + } + } + + class DhtCycleResolutionThread(ownTId: Int) extends DhtThread(ownTId, s"PropertyStoreCycleResolutionThread-#$ownTId") { + + override def run(): Unit = { + val localInterimStates = ArrayBuffer.empty[DhtEpkState] + var pkId = 0 + while (pkId <= PropertyKey.maxId) { + if (propertyKindsComputedInThisPhase(pkId)) { + ps(ownTId)(pkId).valuesIterator.foreach { epkState ⇒ + if (epkState.eOptP.isRefinable) localInterimStates += epkState + } + } + pkId += 1 + } + interimStates(ownTId) = localInterimStates + + successors(ownTId) = (interimEPKState: DhtEpkState) ⇒ { + val dependees = interimEPKState.dependees + if (dependees != null) { + interimEPKState.dependees.map { eOptionP ⇒ + val tId = getResponsibleTId(eOptionP.e) + ps(tId)(eOptionP.pk.id)(eOptionP.e) + } + } else { + Traversable.empty + } + } + } + } + + class DhtPartialPropertiesFinalizerThread(ownTId: Int) extends DhtThread(ownTId, s"PropertyStorePartialPropertiesFinalizerThread-#$ownTId") { + + override def run(): Unit = { + val pksToFinalize = subPhaseFinalizationOrder(subPhaseId).toSet + + pksToFinalize foreach { pk ⇒ + val pkId = pk.id + ps(ownTId)(pkId).valuesIterator.foreach { epkState ⇒ + val eOptP = epkState.eOptP + if (eOptP.isRefinable && !eOptP.isEPK) //TODO Won't be required once subPhaseFinalizationOrder is reliably only the partial properties + handleFinalResult(eOptP.toFinalEP, pksToFinalize) + } + } + } + } + + trait DhtTask extends (() ⇒ Unit) { + val priority = 1000000 + } + + class DhtExecuteTask(f: ⇒ Unit) extends DhtTask { + override def apply(): Unit = { + f + } + } + + class DhtSetTask[E <: Entity, P <: Property](e: E, finalEP: FinalEP[E, P], pkId: Int) extends DhtTask { + override def apply(): Unit = { + val ownTId = currentTId() + val oldStateO = ps(ownTId)(pkId).put(e, DhtEpkState(finalEP, null, null)) + notifyDependers(finalEP, ownTId) + if (oldStateO.isEmpty) triggerComputations(e, pkId, ownTId) + } + } + + class DhtSetInitialTask[E <: Entity, P <: Property](e: E, eOptP: EOptionP[E, P], pkId: Int) extends DhtTask { + override def apply(): Unit = { + val ownTId = currentTId() + val newState = DhtEpkState(eOptP, null, null) + val epkState = ps(ownTId)(pkId).getOrElseUpdate(e, newState) + if (!eOptP.isEPK && (epkState eq newState)) { + notifyDependers(eOptP.asEPS, ownTId) + triggerComputations(e, pkId, ownTId) + } + } + } + + class DhtPropertyComputationTask[E <: Entity](e: E, pc: PropertyComputation[E]) extends DhtTask { + scheduledTasks(currentTId()) += 1 + + override def apply(): Unit = { + handleResult(pc(e)) + } + } + + class DhtLazyComputationTask[E <: Entity](e: E, pkId: Int, pc: PropertyComputation[E]) extends DhtTask { + scheduledTasks(currentTId()) += 1 + + override def apply(): Unit = { + val tId = currentTId() + val epkState = ps(tId)(pkId).get(e) + if (epkState.isEmpty) + handleResult(pc(e)) + } + } + + class DhtContinuationTask(val dependerE: Entity, val dependerPKId: Int, val dependeeCount: Int, val dependee: SomeEPS) extends DhtTask { + override val priority = dependeeCount + + var perform = true + + scheduledOnUpdateComputations(currentTId()) += 1 + + override def apply(): Unit = { + if (perform) { + val ownTId = currentTId() + /*if (ownTId != getResponsibleTId(dependee.e)) + ps(ownTId)(dependee.pk.id)(dependee.e) = DhtEpkState(dependee, null, null)*/ + val eSO = ps(ownTId)(dependerPKId).get(dependerE) + if (eSO.isDefined) { + val epkState = eSO.get + val dependeeEPK = dependee.toEPK + if (epkState.dependees != null && epkState.dependees.exists(_.toEPK == dependeeEPK)) { + //val ds = dependers(ownTId) + //ds(dependeeEPK) -= epkState.eOptP.toEPK + // TODO maybe get current state of dependee here? + doHandleResult(epkState.c(dependee), epkState) + } + } + } + } + } + + class DhtNotifyIfUpdatedTask(depender: SomeEPK, val dependeeCount: Int, val dependee: SomeEOptionP) extends DhtTask { + override def apply(): Unit = { + val dependeeTId = currentTId() + val dependerTId = getResponsibleTId(depender.e) + val e = dependee.e + val pk = dependee.pk + val pkId = pk.id + val ePKState = ps(dependeeTId)(pkId).get(e) + if (ePKState.isEmpty) { + handleUnknownEPK(EPK(e, pk), e, pkId, dependeeTId, dependerTId) + } else { + val eOptP = ePKState.get.eOptP + if ((eOptP ne dependee) && eOptP.isEPS) { + val continuationTask = new DhtContinuationTask(depender.e, depender.pk.id, dependeeCount, eOptP.asEPS) + assert(dependeeTId != dependerTId) + submitTask( + continuationTask, + dependerTId, + dependeeTId + ) + } + } + } + } + + class DhtNotifyDependersTask(val dependee: SomeEPS, val unnotifiedPKs: Set[PropertyKind]) extends DhtTask { + override def apply(): Unit = { + val ownTId = currentTId() + val ds = dependers(ownTId) + val theDependers = ds.get(dependee.toEPK) + if (theDependers.isDefined) { + theDependers.get.foreach { depender ⇒ + val EOptionP(e, pk) = depender + if (!unnotifiedPKs.contains(pk) && (dependee.isFinal || !suppressInterimUpdates(depender.pk.id)(dependee.pk.id))) { + submitTask(new DhtContinuationTask(e, pk.id, 0, dependee), ownTId, ownTId) + } + } + } + } + } +} + +object DHTPropertyStore extends PropertyStoreFactory[DHTPropertyStore] { + + final val MaxEvaluationDepthKey = "org.opalj.fpcf.par.PKECPropertyStore.MaxEvaluationDepth" + + def apply( + context: PropertyStoreContext[_ <: AnyRef]* + )( + implicit + logContext: LogContext + ): DHTPropertyStore = { + val contextMap: Map[Class[_], AnyRef] = context.map(_.asTuple).toMap + + val ps = new DHTPropertyStore(contextMap) + ps + } +} + +case class DhtEpkState(eOptP: SomeEOptionP, c: OnUpdateContinuation, dependees: Traversable[SomeEOptionP]) + +class DhtFakeEntity(override val hashCode: Int) \ No newline at end of file diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala new file mode 100644 index 0000000000..860539affa --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala @@ -0,0 +1,867 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf +package par + +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.PriorityBlockingQueue +import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.locks.ReentrantLock +import java.util.concurrent.TimeUnit + +import scala.collection.mutable.ArrayBuffer +import scala.util.control.ControlThrowable + +import org.opalj.log.LogContext +import org.opalj.fpcf.PropertyKey.fallbackPropertyBasedOnPKId + +/** + * Yet another parallel property store. + * + * @author Dominik Helm + */ +class YAPPS( + final val ctx: Map[Class[_], AnyRef] +)( + implicit + val logContext: LogContext +) extends ParallelPropertyStore { + propertyStore ⇒ + + val THREAD_COUNT = 4 + + override def MaxEvaluationDepth: Int = 0 + + val ps: Array[ConcurrentHashMap[Entity, YappsEPKState]] = + Array.fill(PropertyKind.SupportedPropertyKinds) { new ConcurrentHashMap() } + + val tasks: PriorityBlockingQueue[YappsTask] = new PriorityBlockingQueue() + + private[this] val triggeredComputations: Array[Array[SomePropertyComputation]] = + new Array(PropertyKind.SupportedPropertyKinds) + + private[this] var setAndPreinitializedValues: List[SomeEPK] = List.empty + + override def shutdown(): Unit = {} + + var idle = true + override def isIdle: Boolean = idle + + // -------------------------------------------------------------------------------------------- + // + // STATISTICS + // + // -------------------------------------------------------------------------------------------- + + private[this] var quiescenceCounter = 0 + override def quiescenceCount: Int = quiescenceCounter + + private[this] val scheduledTasks: Array[Int] = Array.fill(THREAD_COUNT) { 0 } + override def scheduledTasksCount: Int = scheduledTasks.sum + + private[this] val scheduledOnUpdateComputations: Array[Int] = Array.fill(THREAD_COUNT) { 0 } + override def scheduledOnUpdateComputationsCount: Int = scheduledOnUpdateComputations.sum + + override def fallbacksUsedForComputedPropertiesCount: Int = 0 //TODO + + override private[fpcf] def incrementFallbacksUsedForComputedPropertiesCounter(): Unit = { + // ??? + } + + // -------------------------------------------------------------------------------------------- + // + // BASIC QUERY METHODS (ONLY TO BE CALLED WHEN THE STORE IS QUIESCENT) + // + // -------------------------------------------------------------------------------------------- + + override def toString(printProperties: Boolean): String = + if (printProperties) { + var pkId = 0 + while (pkId <= PropertyKey.maxId) { + var entities: List[String] = List.empty + ps(pkId).forEachValue(Long.MaxValue, { state: YappsEPKState ⇒ + entities ::= state.eOptP.toString.replace("\n", "\n\t") + }) + entities.sorted.mkString(s"Entities for property key $pkId:\n\t", "\n\t", "\n") + pkId += 1 + } + ps.mkString("PropertyStore(\n\t", "\n\t", "\n)") + } else { + s"PropertyStore(properties=${ps.iterator.map(_.size).sum})" + } + + override def entities(propertyFilter: SomeEPS ⇒ Boolean): Iterator[Entity] = { + ps.iterator.flatMap { propertiesPerKind ⇒ + var result: List[Entity] = List.empty + propertiesPerKind.forEachValue(Long.MaxValue, { state: YappsEPKState ⇒ if (propertyFilter(state.eOptP.asEPS)) result ::= state.eOptP.e }) + result + } + } + + override def entities[P <: Property](pk: PropertyKey[P]): Iterator[EPS[Entity, P]] = { + var result: List[EPS[Entity, P]] = List.empty + ps(pk.id).forEachValue(Long.MaxValue, { state: YappsEPKState ⇒ result ::= state.eOptP.asInstanceOf[EPS[Entity, P]] }) + result.iterator + } + + override def entities[P <: Property](lb: P, ub: P): Iterator[Entity] = { + entities { eps ⇒ eps.lb == lb && eps.ub == ub } + } + + override def entitiesWithLB[P <: Property](lb: P): Iterator[Entity] = { + entities { eps ⇒ eps.lb == lb } + } + + override def entitiesWithUB[P <: Property](ub: P): Iterator[Entity] = { + entities { eps ⇒ eps.ub == ub } + } + + override def properties[E <: Entity](e: E): Iterator[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 ePKState = ps(pk.id).get(e) + (ePKState ne null) && (ePKState.eOptP.hasUBP || ePKState.eOptP.hasLBP) + } + + override def isKnown(e: Entity): Boolean = { + ps.exists { propertiesPerKind ⇒ + propertiesPerKind.containsKey(e) + } + } + + override def get[E <: Entity, P <: Property](e: E, pk: PropertyKey[P]): Option[EOptionP[E, P]] = { + val ePKState = ps(pk.id).get(e) + if (ePKState eq null) + None + else + Some(ePKState.eOptP.asInstanceOf[EOptionP[E, P]]) + } + + override def get[E <: Entity, P <: Property](epk: EPK[E, P]): Option[EOptionP[E, P]] = { + get(epk.e, epk.pk) + } + + // -------------------------------------------------------------------------------------------- + // + // CORE IMPLEMENTATION - NOT THREAD SAFE PART + // + // -------------------------------------------------------------------------------------------- + + override protected[this] def doScheduleEagerComputationForEntity[E <: Entity]( + e: E + )(pc: PropertyComputation[E]): Unit = { + schedulePropertyComputation(e, pc) + } + + override protected[this] def doRegisterTriggeredComputation[E <: Entity, P <: Property]( + pk: PropertyKey[P], + pc: PropertyComputation[E] + ): Unit = { + + // Recall that the scheduler has to take care of registering a triggered computation + // before the first analysis derives a respective value! + // Hence, there is no need to immediately check that we have to trigger a computation. + + val pkId = pk.id + val oldComputations: Array[SomePropertyComputation] = triggeredComputations(pkId) + var newComputations: Array[SomePropertyComputation] = null + + if (oldComputations == null) { + newComputations = Array[SomePropertyComputation](pc) + } else { + newComputations = java.util.Arrays.copyOf(oldComputations, oldComputations.length + 1) + newComputations(oldComputations.length) = pc + } + triggeredComputations(pkId) = newComputations + } + + override protected[this] def doSet(e: Entity, p: Property): Unit = { + val epkState = YappsEPKState(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 protected[this] def doPreInitialize[E <: Entity, P <: Property]( + e: E, + pk: PropertyKey[P] + )(pc: EOptionP[E, P] ⇒ InterimEP[E, P]): Unit = { + val pkId = pk.id + val propertiesOfKind = ps(pkId) + val oldEPKState = propertiesOfKind.get(e) + val newInterimEP: SomeInterimEP = + oldEPKState match { + case null ⇒ + val epk = EPK(e, pk) + setAndPreinitializedValues ::= epk + pc(epk) + case epkState ⇒ + pc(epkState.eOptP.asInstanceOf[EOptionP[E, P]]) + } + assert(newInterimEP.isRefinable) + val newEPKState = YappsEPKState(newInterimEP, null, null) + propertiesOfKind.put(e, newEPKState) + } + + // -------------------------------------------------------------------------------------------- + // + // CORE IMPLEMENTATION - THREAD SAFE PART + // + // -------------------------------------------------------------------------------------------- + + private[this] def scheduleTask(task: YappsTask): Unit = { + activeTasks.incrementAndGet() + tasks.offer(task) + } + + private[this] def schedulePropertyComputation[E <: Entity]( + e: E, + pc: PropertyComputation[E] + ): Unit = { + scheduleTask(new YappsPropertyComputationTask(e, pc)) + } + + override def force[E <: Entity, P <: Property](e: E, pk: PropertyKey[P]): Unit = { + doApply(EPK(e, pk), e, pk.id) + } + + override def execute(f: ⇒ Unit): Unit = { + scheduleTask(new YappsExecuteTask(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(r, u, e, pk) + + case PrecomputedPartialResult.id ⇒ + val PrecomputedPartialResult(expectedEOptionP, updatedInterimEP, u) = r + handlePartialResult( + r, + oldEOptP ⇒ if (oldEOptP eq expectedEOptionP) { + Some(updatedInterimEP) + } else { + u.asInstanceOf[SomeEOptionP ⇒ Option[SomeInterimEP]](oldEOptP) + }, + expectedEOptionP.e, + expectedEOptionP.pk + ) + + case InterimPartialResult.id ⇒ + val InterimPartialResult(prs, dependees, c) = r + + prs foreach { pr ⇒ + handlePartialResult( + pr, + pr.u.asInstanceOf[SomeEOptionP ⇒ Option[SomeInterimEP]], + pr.e, + pr.pk + ) + } + + val e = new FakeEntity() + val epk = EPK(e, AnalysisKey) + + val epkState = YappsEPKState( + epk, + { dependee: SomeEPS ⇒ + val result = c(dependee) + + ps(AnalysisKeyId).remove(e) + + dependees.foreach { dependee ⇒ + ps(dependee.pk.id).get(dependee.e).removeDepender(epk) + } + + result + }, + dependees + ) + + ps(AnalysisKeyId).put(e, epkState) + + updateDependees(epkState, dependees) + } + } + + 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; YappsEPKState(finalEP, null, null) }) + if (isFresh) triggerComputations(e, pk.id) + else ePKState.setFinal(finalEP, unnotifiedPKs) + + //TODO remove depender status + } + + private[this] def triggerComputations(e: Entity, pkId: Int): Unit = { + val computations = triggeredComputations(pkId) + if (computations ne null) { + computations foreach { pc ⇒ + schedulePropertyComputation(e, pc.asInstanceOf[PropertyComputation[Entity]]) + } + } + } + + private[this] def handleInterimResult( + interimEP: InterimEP[Entity, _ >: Null <: Property], + c: ProperOnUpdateContinuation, + dependees: Traversable[SomeEOptionP] + ): Unit = { + val SomeEPS(e, pk) = interimEP + var isFresh = false + val ePKState = + ps(pk.id).computeIfAbsent(e, { _ ⇒ isFresh = true; YappsEPKState(interimEP, c, dependees) }) + if (isFresh) { + triggerComputations(e, pk.id) + updateDependees(ePKState, dependees) + } else ePKState.update(interimEP, c, dependees) + + //TODO update depender status + } + + private[this] def handlePartialResult( + pr: PropertyComputationResult, + update: UpdateComputation[Entity, Property], + e: Entity, + pk: PropertyKey[Property] + ): Unit = { + val ePKState = ps(pk.id).computeIfAbsent(e, _ ⇒ YappsEPKState(EPK(e, pk), null, null)) + ePKState.update(update) + } + + def updateDependees(depender: YappsEPKState, newDependees: Traversable[SomeEOptionP]): Unit = { + val dependerEpk = depender.eOptP.toEPK + val suppressedPKs = suppressInterimUpdates(dependerEpk.pk.id) + newDependees.exists { dependee ⇒ + val dependeeState = ps(dependee.pk.id).get(dependee.e) + val updatedDependee = dependeeState.addDepender(dependerEpk, dependee) + if ((updatedDependee ne null) && (updatedDependee.isFinal || !suppressedPKs(dependee.pk.id))) { + scheduleTask(new YappsExecuteTask(depender.applyContinuation(updatedDependee, dependee))) + true + } else { + false + } + } + } + + override protected[this] def doApply[E <: Entity, P <: Property]( + epk: EPK[E, P], + e: E, + pkId: Int + ): EOptionP[E, P] = { + val current = ps(pkId).get(e) + if (current eq null) { + val lazyComputation = lazyComputations(pkId) + if (lazyComputation ne null) { + val previous = ps(pkId).putIfAbsent(e, YappsEPKState(epk, null, null)) + if (previous eq null) { + scheduleTask( + new YappsLazyComputationTask( + e, + lazyComputation.asInstanceOf[E ⇒ PropertyComputationResult], + pkId + ) + ) + } + epk + } 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, YappsEPKState(result, null, null)) + if (previous eq null) { + triggerComputations(e, pkId) + result.asInstanceOf[FinalEP[E, P]] + } else { + previous.eOptP.asInstanceOf[EOptionP[E, P]] + } + } else { + val newState = YappsEPKState(epk, d ⇒ new Result(transformer._2(e, d.asFinal.p)), Some(dependee)) + val previous = ps(pkId).putIfAbsent(e, newState) + if (previous eq null) { + updateDependees(newState, Some(dependee)) + epk + } else { + previous.eOptP.asInstanceOf[EOptionP[E, P]] + } + } + } else { + val previous = ps(pkId).putIfAbsent(e, YappsEPKState(epk, null, null)) + if (previous eq null) { + epk + } else { + previous.eOptP.asInstanceOf[EOptionP[E, P]] + } + } + } else { + val finalEP = computeFallback[E, P](e, pkId) + val previous = ps(pkId).putIfAbsent(e, YappsEPKState(finalEP, null, null)) + if (previous eq null) { + triggerComputations(e, pkId) + finalEP + } else { + previous.eOptP.asInstanceOf[EOptionP[E, P]] + } + } + } else { + current.eOptP.asInstanceOf[EOptionP[E, P]] + } + } + + private[this] val activeTasks = new AtomicInteger(0) + private[this] val threads: Array[YappsThread] = Array.fill(THREAD_COUNT) { null } + + private[this] def startThreads(thread: (Int) ⇒ YappsThread): Unit = { + var tId = 0 + while (tId < THREAD_COUNT) { + val t = thread(tId) + threads(tId) = t + tId += 1 + } + threads.foreach { _.start() } + threads.foreach { _.join } + if (doTerminate) { + if (exception ne null) throw exception + else throw new InterruptedException + } + } + + override def waitOnPhaseCompletion(): Unit = { + idle = false + + // 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 + + while (subPhaseId < subPhaseFinalizationOrder.length) { + var continueCycles = false + do { + var continueFallbacks = false + do { + startThreads(new YappsWorkerThread(_)) + + quiescenceCounter += 1 + + startThreads(new YappsFallbackThread(_)) + + continueFallbacks = !tasks.isEmpty + } while (continueFallbacks) + + startThreads(new YappsCycleResolutionThread(_)) + + resolveCycles() + + continueCycles = !tasks.isEmpty + } while (continueCycles) + + startThreads(new YappsPartialPropertiesFinalizerThread(_)) + + subPhaseId += 1 + } + + idle = true + } + + private[this] val interimStates: Array[ArrayBuffer[YappsEPKState]] = + Array.fill(THREAD_COUNT)(null) + private[this] val successors: Array[YappsEPKState ⇒ Traversable[YappsEPKState]] = + Array.fill(THREAD_COUNT)(null) + + private[this] def resolveCycles(): Unit = { + val theInterimStates = new ArrayBuffer[YappsEPKState](interimStates.iterator.map(_.size).sum) + var tId = 0 + while (tId < THREAD_COUNT) { + theInterimStates ++= interimStates(tId) + tId += 1 + } + + val theSuccessors = (interimEPKState: YappsEPKState) ⇒ { + successors(getResponsibleTId(interimEPKState.eOptP.e))(interimEPKState) + } + + val cSCCs = graphs.closedSCCs(theInterimStates, theSuccessors) + + for (cSCC ← cSCCs) { + for (interimEPKState ← cSCC) { + val dependees = interimEPKState.dependees + val epk = interimEPKState.eOptP.toEPK + dependees.foreach { dependee ⇒ + ps(dependee.pk.id).get(dependee.e).dependers -= epk + } + scheduleTask(new YappsSetTask(interimEPKState.eOptP.toFinalEP)) + } + } + } + + class YappsThread(name: String) extends Thread(name) + + class YappsWorkerThread(ownTId: Int) extends YappsThread(s"PropertyStoreThread-#$ownTId") { + + override def run(): Unit = { + try { + while (!doTerminate) { + val curTask = tasks.poll() + if (curTask eq null) { + val active = activeTasks.get() + if (active == 0) { + threads.foreach { t ⇒ + if (t ne this) + t.interrupt() + } + return ; + } else { + val nextTask = tasks.take() + nextTask.apply() + activeTasks.decrementAndGet() + } + } else { + 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 YappsFallbackThread(ownTId: Int) extends YappsThread(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: YappsEPKState ⇒ + 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 YappsCycleResolutionThread(ownTId: Int) extends YappsThread(s"PropertyStoreCycleResolutionThread-#$ownTId") { + + override def run(): Unit = { + val localInterimStates = ArrayBuffer.empty[YappsEPKState] + var pkId = 0 + while (pkId <= PropertyKey.maxId) { + if (propertyKindsComputedInThisPhase(pkId)) { + ps(pkId).forEachValue(Long.MaxValue, { epkState: YappsEPKState ⇒ + val eOptP = epkState.eOptP + if (getResponsibleTId(eOptP.e) == ownTId && + epkState.eOptP.isRefinable) { + localInterimStates.append(epkState) + } + }) + } + pkId += 1 + } + interimStates(ownTId) = localInterimStates + + successors(ownTId) = (interimEPKState: YappsEPKState) ⇒ { + val dependees = interimEPKState.dependees + if (dependees != null) { + interimEPKState.dependees.map { eOptionP ⇒ + ps(eOptionP.pk.id).get(eOptionP.e) + } + } else { + Traversable.empty + } + } + } + } + + class YappsPartialPropertiesFinalizerThread(ownTId: Int) extends YappsThread(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: YappsEPKState ⇒ + 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) + }) + } + } + } + + trait YappsTask extends (() ⇒ Unit) with Comparable[YappsTask] { + val priority: Int + + override def compareTo(other: YappsTask): Int = this.priority - other.priority + } + + class YappsExecuteTask(f: ⇒ Unit) extends YappsTask { + val priority = 0 + + override def apply(): Unit = { + f + } + } + + class YappsSetTask[E <: Entity, P <: Property]( + finalEP: FinalEP[E, P] + ) extends YappsTask { + val priority = 0 + + override def apply(): Unit = { + handleFinalResult(finalEP) + } + } + + class YappsPropertyComputationTask[E <: Entity]( + e: E, + pc: PropertyComputation[E] + ) extends YappsTask { + val priority = 0 + + override def apply(): Unit = { + handleResult(pc(e)) + } + } + + class YappsLazyComputationTask[E <: Entity]( + e: E, + pc: PropertyComputation[E], + pkId: Int + ) extends YappsTask { + val priority = 0 + + override def apply(): Unit = { + val state = ps(pkId).get(e) + state.lock.lock() + if (state.eOptP.isEPK) + handleResult(pc(e)) + state.lock.unlock() + } + } + + class YappsContinuationTask(depender: SomeEPK, dependee: SomeEPS, oldDependee: SomeEOptionP) extends YappsTask { + val priority = 0 + + override def apply(): Unit = { + val epkState = ps(depender.pk.id).get(depender.e) + if (epkState ne null) + epkState.applyContinuation(dependee, oldDependee) + } + } + + case class YappsEPKState( + @volatile var eOptP: SomeEOptionP, + @volatile var c: OnUpdateContinuation, + @volatile var dependees: Traversable[SomeEOptionP], + @volatile var dependers: Set[SomeEPK] = Set.empty + ) { + val lock: MyRRLock = new MyRRLock() + val dependersLock = new MyRRLock() + + def setFinal(finalEP: FinalEP[Entity, Property], unnotifiedPKs: Set[PropertyKind]): Unit = { + lock.lockInterruptibly() + val theEOptP = eOptP + if (theEOptP.isFinal) { + throw new IllegalStateException(s"${theEOptP.e} already had the property $theEOptP") + } else { + if (debug) eOptP.checkIsValidPropertiesUpdate(finalEP, Nil) + eOptP = finalEP + } + dependees = null + lock.unlock() + if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) + dependersLock.lock() + val theDependers = dependers + dependers = null + dependersLock.unlock() + notifyDependers(finalEP, theEOptP, theDependers, unnotifiedPKs) + } + + def update( + interimEP: InterimEP[Entity, Property], + newC: OnUpdateContinuation, + newDependees: Traversable[SomeEOptionP] + ): Unit = { + lock.lockInterruptibly() + val theEOptP = eOptP + if (theEOptP.isFinal) { + throw new IllegalStateException(s"${theEOptP.e} already had the property $theEOptP") + } else { + if (debug) eOptP.checkIsValidPropertiesUpdate(interimEP, newDependees) + eOptP = interimEP + c = newC + dependees = newDependees + } + lock.unlock() + if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) + if (interimEP.isUpdatedComparedTo(theEOptP)) { + dependersLock.lock() + val theDependers = dependers + //FIXME clear dependers + dependersLock.unlock() + notifyDependers(interimEP, theEOptP, theDependers) + } + + updateDependees(this, newDependees) + } + + def addDepender(depender: SomeEPK, dependee: SomeEOptionP): SomeEPS = { + dependersLock.lockInterruptibly() + val result = if ((eOptP eq dependee) || eOptP.isEPK) { + dependers += depender + null + } else { + eOptP.asEPS + } + dependersLock.unlock() + result + } + + def removeDepender(epk: SomeEPK): Unit = { + dependersLock.lockInterruptibly() + if (dependers != null) dependers -= epk + dependersLock.unlock() + } + + def update(update: UpdateComputation[Entity, Property]): Unit = { + lock.lockInterruptibly() + val theEOptP = eOptP + val newEOptP = update(theEOptP) match { + case Some(interimEP) ⇒ + eOptP = interimEP + interimEP + case _ ⇒ + null + } + val theDependers = dependers + lock.unlock() + if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) + if (newEOptP ne null) + notifyDependers(newEOptP, theEOptP, theDependers) + } + + def notifyDependers(theEOptP: SomeEPS, oldEOptP: SomeEOptionP, theDependers: Set[SomeEPK], unnotifiedPKs: Set[PropertyKind] = Set.empty): Unit = { + if (theDependers ne null) { + theDependers.foreach { depender ⇒ + if (!unnotifiedPKs.contains(depender.pk) && (theEOptP.isFinal || !suppressInterimUpdates(depender.pk.id)(theEOptP.pk.id))) + scheduleTask(new YappsContinuationTask(depender, theEOptP, oldEOptP)) + } + } + } + + def applyContinuation(dependee: SomeEPS, oldDependee: SomeEOptionP /*TODO remove*/ ): Unit = { + // Just tryLock - if there is already an update performed, that will re-trigger updates if necessary + val locked = lock.tryLock(0, TimeUnit.SECONDS) // Can't use tryLock(), as we need interruptability + if (locked) { + val theDependees = dependees + if (theDependees != null && theDependees.exists { + _.toEPK == oldDependee.toEPK + }) + handleResult(c(ps(dependee.pk.id).get(dependee.e).eOptP.asEPS)) + lock.unlock() + } + } + + } + + private[this] def getResponsibleTId(e: Entity): Int = { + Math.abs(e.hashCode() >> 5) % THREAD_COUNT + } +} + +object YAPPS extends PropertyStoreFactory[YAPPS] { + + final val MaxEvaluationDepthKey = "org.opalj.fpcf.par.PKECPropertyStore.MaxEvaluationDepth" + + def apply( + context: PropertyStoreContext[_ <: AnyRef]* + )( + implicit + logContext: LogContext + ): YAPPS = { + val contextMap: Map[Class[_], AnyRef] = context.map(_.asTuple).toMap + + val ps = new YAPPS(contextMap) + ps + } +} + +class MyRRLock extends ReentrantLock { + override def getOwner: Thread = super.getOwner +} \ No newline at end of file diff --git a/OPAL/si/src/test/scala/org/opalj/fpcf/par/DHTPropertyStoreTest.scala b/OPAL/si/src/test/scala/org/opalj/fpcf/par/DHTPropertyStoreTest.scala new file mode 100644 index 0000000000..b8163f6599 --- /dev/null +++ b/OPAL/si/src/test/scala/org/opalj/fpcf/par/DHTPropertyStoreTest.scala @@ -0,0 +1,42 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org +package opalj +package fpcf +package par + +abstract class DHTPropertyStoreTestWithDebugging + extends PropertyStoreTestWithDebugging[DHTPropertyStore] { + + override def afterAll(ps: DHTPropertyStore): Unit = { + // TODO Basic smoke test? + } +} + +class DHTPropertyStoreTestWithDebuggingMaxEvalDepthDefault + extends DHTPropertyStoreTestWithDebugging { + + def createPropertyStore(): DHTPropertyStore = { + val ps = new DHTPropertyStore(Map.empty) + ps.suppressError = true + ps + } + +} + +// ************************************************************************************************* +// ************************************* NO DEBUGGING ********************************************** +// ************************************************************************************************* + +abstract class DHTPropertyStoreTestWithoutDebugging + extends PropertyStoreTestWithoutDebugging[DHTPropertyStore] + +class DHTPropertyStoreTestWithoutDebuggingMaxEvalDepth128AndSeqTaskManager + extends DHTPropertyStoreTestWithoutDebugging { + + def createPropertyStore(): DHTPropertyStore = { + val ps = new DHTPropertyStore(Map.empty) + ps.suppressError = true + ps + } + +} \ No newline at end of file diff --git a/OPAL/si/src/test/scala/org/opalj/fpcf/par/YAPPSTest.scala b/OPAL/si/src/test/scala/org/opalj/fpcf/par/YAPPSTest.scala new file mode 100644 index 0000000000..13ffe51022 --- /dev/null +++ b/OPAL/si/src/test/scala/org/opalj/fpcf/par/YAPPSTest.scala @@ -0,0 +1,41 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package fpcf +package par + +abstract class YAPPSTestWithDebugging + extends PropertyStoreTestWithDebugging[YAPPS] { + + override def afterAll(ps: YAPPS): Unit = { + // TODO Basic smoke test? + } +} + +class YAPPSTestWithDebuggingMaxEvalDepthDefault + extends YAPPSTestWithDebugging { + + def createPropertyStore(): YAPPS = { + val ps = new YAPPS(Map.empty) + ps.suppressError = true + ps + } + +} + +// ************************************************************************************************* +// ************************************* NO DEBUGGING ********************************************** +// ************************************************************************************************* + +abstract class YAPPSTestWithoutDebugging + extends PropertyStoreTestWithoutDebugging[YAPPS] + +class YAPPSTestWithoutDebuggingMaxEvalDepth128AndSeqTaskManager + extends YAPPSTestWithoutDebugging { + + def createPropertyStore(): YAPPS = { + val ps = new YAPPS(Map.empty) + ps.suppressError = true + ps + } + +} \ No newline at end of file From 39ecb4d08b1dec67403372ff314b6f2bf77800c1 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Tue, 7 Jan 2020 16:18:50 +0100 Subject: [PATCH 030/327] Fixing errors in purity analyses --- .../br/fpcf/analyses/ConfiguredPurity.scala | 2 -- .../org/opalj/br/fpcf/properties/Purity.scala | 6 +++--- OPAL/tac/src/main/resources/reference.conf | 20 +++++++++---------- .../analyses/purity/L2PurityAnalysis.scala | 16 ++++++++++++--- 4 files changed, 26 insertions(+), 18 deletions(-) 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/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/tac/src/main/resources/reference.conf b/OPAL/tac/src/main/resources/reference.conf index d20c06d982..ed841a0d07 100644 --- a/OPAL/tac/src/main/resources/reference.conf +++ b/OPAL/tac/src/main/resources/reference.conf @@ -137,15 +137,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 +179,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/fpcf/analyses/purity/L2PurityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis.scala index 84678b3dba..e5874fb7fd 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 @@ -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; @@ -729,6 +732,8 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst * - classes files for class types returned (for their mutability) */ def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { + if(state.definedMethod.definedMethod.toJava == "com.sun.jmx.mbeanserver.MBeanIntrospector{ static boolean isValidParameter(java.lang.reflect.Method,java.lang.Object,int) }") + println() val oldPurity = state.ubPurity eps.ub.key match { case Purity.key ⇒ @@ -851,9 +856,14 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst s += 1 } + if(state.definedMethod.definedMethod.toJava == "com.sun.jmx.mbeanserver.MBeanIntrospector{ static boolean isValidParameter(java.lang.reflect.Method,java.lang.Object,int) }") + println() + 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 { From 30d1c12cb3e1dff8cb31863a5f7bee23c11922fe Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Wed, 8 Jan 2020 13:29:23 +0100 Subject: [PATCH 031/327] Debug output --- .../main/scala/org/opalj/fpcf/par/YAPPS.scala | 19 ++++++++++++++----- .../analyses/purity/L2PurityAnalysis.scala | 5 ----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala index 860539affa..d23f1de76c 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala @@ -391,7 +391,7 @@ class YAPPS( val dependeeState = ps(dependee.pk.id).get(dependee.e) val updatedDependee = dependeeState.addDepender(dependerEpk, dependee) if ((updatedDependee ne null) && (updatedDependee.isFinal || !suppressedPKs(dependee.pk.id))) { - scheduleTask(new YappsExecuteTask(depender.applyContinuation(updatedDependee, dependee))) + scheduleTask(new YappsContinuationTask(dependerEpk, updatedDependee, dependee)) true } else { false @@ -569,12 +569,16 @@ class YAPPS( return ; } else { val nextTask = tasks.take() - nextTask.apply() - activeTasks.decrementAndGet() + if (!doTerminate) { + nextTask.apply() + activeTasks.decrementAndGet() + } } } else { - curTask.apply() - activeTasks.decrementAndGet() + if (!doTerminate) { + curTask.apply() + activeTasks.decrementAndGet() + } } } } catch { @@ -674,6 +678,7 @@ class YAPPS( val priority = 0 override def apply(): Unit = { + println(s"Running exec. task") f } } @@ -684,6 +689,7 @@ class YAPPS( val priority = 0 override def apply(): Unit = { + println(s"Running set task for $finalEP") handleFinalResult(finalEP) } } @@ -695,6 +701,7 @@ class YAPPS( val priority = 0 override def apply(): Unit = { + println(s"Running comp. task for $e") handleResult(pc(e)) } } @@ -707,6 +714,7 @@ class YAPPS( val priority = 0 override def apply(): Unit = { + println(s"Running lazy comp. task for $e, $pkId") val state = ps(pkId).get(e) state.lock.lock() if (state.eOptP.isEPK) @@ -719,6 +727,7 @@ class YAPPS( val priority = 0 override def apply(): Unit = { + println(s"Running continuation task for $dependee -> $depender") val epkState = ps(depender.pk.id).get(depender.e) if (epkState ne null) epkState.applyContinuation(dependee, oldDependee) 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 e5874fb7fd..8d97a35f9a 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 @@ -732,8 +732,6 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst * - classes files for class types returned (for their mutability) */ def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { - if(state.definedMethod.definedMethod.toJava == "com.sun.jmx.mbeanserver.MBeanIntrospector{ static boolean isValidParameter(java.lang.reflect.Method,java.lang.Object,int) }") - println() val oldPurity = state.ubPurity eps.ub.key match { case Purity.key ⇒ @@ -856,9 +854,6 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst s += 1 } - if(state.definedMethod.definedMethod.toJava == "com.sun.jmx.mbeanserver.MBeanIntrospector{ static boolean isValidParameter(java.lang.reflect.Method,java.lang.Object,int) }") - println() - val callees = propertyStore(state.definedMethod, Callees.key) if (!checkPurityOfCallees(callees)) { assert(state.ubPurity.isInstanceOf[ClassifiedImpure]) From 1c0a8ac20b4c6c8926a15ddc948c6e77daa66099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Thu, 9 Jan 2020 09:59:00 +0100 Subject: [PATCH 032/327] Fixed non-determinism problem --- .../main/scala/org/opalj/fpcf/par/YAPPS.scala | 63 +++++++++++-------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala index d23f1de76c..b3d29b6133 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala @@ -7,7 +7,6 @@ import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.PriorityBlockingQueue import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.locks.ReentrantLock -import java.util.concurrent.TimeUnit import scala.collection.mutable.ArrayBuffer import scala.util.control.ControlThrowable @@ -387,15 +386,9 @@ class YAPPS( def updateDependees(depender: YappsEPKState, newDependees: Traversable[SomeEOptionP]): Unit = { val dependerEpk = depender.eOptP.toEPK val suppressedPKs = suppressInterimUpdates(dependerEpk.pk.id) - newDependees.exists { dependee ⇒ + newDependees.forall { dependee ⇒ val dependeeState = ps(dependee.pk.id).get(dependee.e) - val updatedDependee = dependeeState.addDepender(dependerEpk, dependee) - if ((updatedDependee ne null) && (updatedDependee.isFinal || !suppressedPKs(dependee.pk.id))) { - scheduleTask(new YappsContinuationTask(dependerEpk, updatedDependee, dependee)) - true - } else { - false - } + dependeeState.addDependerOrScheduleContinuation(dependerEpk, dependee, suppressedPKs) } } @@ -790,16 +783,29 @@ class YAPPS( updateDependees(this, newDependees) } - def addDepender(depender: SomeEPK, dependee: SomeEOptionP): SomeEPS = { + def addDependerOrScheduleContinuation( + depender: SomeEPK, + dependee: SomeEOptionP, + suppressedPKs: Array[Boolean] + ): Boolean = { dependersLock.lockInterruptibly() - val result = if ((eOptP eq dependee) || eOptP.isEPK) { - dependers += depender - null - } else { - eOptP.asEPS + try { + 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). + if ((theEOptP ne dependee) && + // Note that the dependee might be a different reference of an equivalent EPK. + theEOptP.isEPS && + (theEOptP.isFinal || !suppressedPKs(dependee.pk.id))) { + scheduleTask(new YappsContinuationTask(depender, theEOptP.asEPS, dependee)) + false + } else { + dependers += depender + true + } + } finally { + dependersLock.unlock() } - dependersLock.unlock() - result } def removeDepender(epk: SomeEPK): Unit = { @@ -835,16 +841,21 @@ class YAPPS( } def applyContinuation(dependee: SomeEPS, oldDependee: SomeEOptionP /*TODO remove*/ ): Unit = { - // Just tryLock - if there is already an update performed, that will re-trigger updates if necessary - val locked = lock.tryLock(0, TimeUnit.SECONDS) // Can't use tryLock(), as we need interruptability - if (locked) { - val theDependees = dependees - if (theDependees != null && theDependees.exists { - _.toEPK == oldDependee.toEPK - }) - handleResult(c(ps(dependee.pk.id).get(dependee.e).eOptP.asEPS)) - lock.unlock() + // IMPROVE: Use tryLock() instead + lock.lockInterruptibly() + val theDependees = dependees + // We are still interessted in that dependee? + if (theDependees != null && theDependees.exists { + // IMPROVE: We should be able to avoid the toEPK. + _.toEPK == oldDependee.toEPK + }) { + // We always retrieve the most up-to-date state of the dependee. + val currentDependee = ps(dependee.pk.id).get(dependee.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". + handleResult(c(currentDependee)) } + lock.unlock() } } From 14a32bc21090720276cd31b16d38875b20fa387e Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Thu, 9 Jan 2020 10:05:39 +0100 Subject: [PATCH 033/327] Clear dependers that will be notified --- OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala index b3d29b6133..7d58afc4f3 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala @@ -773,9 +773,10 @@ class YAPPS( lock.unlock() if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) if (interimEP.isUpdatedComparedTo(theEOptP)) { - dependersLock.lock() + dependersLock.lockInterruptibly() val theDependers = dependers - //FIXME clear dependers + // Clear all dependers that will be notified, they will re-register if required + dependers = dependers.filter(d => suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) dependersLock.unlock() notifyDependers(interimEP, theEOptP, theDependers) } From f387c7f829e86f6d6c60603c59232434e99bc5da Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Thu, 9 Jan 2020 14:14:08 +0100 Subject: [PATCH 034/327] Removed debug output --- .../tools/src/main/scala/org/opalj/support/info/Purity.scala | 4 ++-- OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) 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 09cd0776a0..a32d0f97ce 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 @@ -200,8 +200,8 @@ object Purity { org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = numThreads org.opalj.fpcf.par.PKECPropertyStore(context: _*) }*/ - //org.opalj.fpcf.par.YAPPS(context: _*) - org.opalj.fpcf.par.DHTPropertyStore(context: _*) + org.opalj.fpcf.par.YAPPS(context: _*) + //org.opalj.fpcf.par.DHTPropertyStore(context: _*) //org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) //org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = 4 //org.opalj.fpcf.par.PKECPropertyStore(context: _*) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala index 7d58afc4f3..60d5514b6d 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala @@ -671,7 +671,6 @@ class YAPPS( val priority = 0 override def apply(): Unit = { - println(s"Running exec. task") f } } @@ -682,7 +681,6 @@ class YAPPS( val priority = 0 override def apply(): Unit = { - println(s"Running set task for $finalEP") handleFinalResult(finalEP) } } @@ -694,7 +692,6 @@ class YAPPS( val priority = 0 override def apply(): Unit = { - println(s"Running comp. task for $e") handleResult(pc(e)) } } @@ -707,7 +704,6 @@ class YAPPS( val priority = 0 override def apply(): Unit = { - println(s"Running lazy comp. task for $e, $pkId") val state = ps(pkId).get(e) state.lock.lock() if (state.eOptP.isEPK) @@ -720,7 +716,6 @@ class YAPPS( val priority = 0 override def apply(): Unit = { - println(s"Running continuation task for $dependee -> $depender") val epkState = ps(depender.pk.id).get(depender.e) if (epkState ne null) epkState.applyContinuation(dependee, oldDependee) From b2c13bf5470f0623d5729d66ae117bf263ceee62 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 16 Jan 2020 16:43:20 +0100 Subject: [PATCH 035/327] Extending Stmt with isxxstmts ... --- .../src/main/scala/org/opalj/tac/Stmt.scala | 1715 +++++++++-------- 1 file changed, 870 insertions(+), 845 deletions(-) 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..a5e375ac93 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala @@ -18,72 +18,76 @@ import org.opalj.value.ValueInformation */ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { - /** - * The program counter of the original '''underyling bytecode instruction'''. - * - * This `pc` is independent of the (implicit) `index` of the statement - * in the generated statements array! This pc is, e.g., useful for - * getting line number information. - */ - def pc: UShort - - def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean - - /** - * Called by the framework to enable each statement/expression to re-map the target - * `pc` of a(n unconditional) jump instruction to the index of the respective quadruple - * statement in the statements array. - * - * ==Example== - * The bytecode instruction: `5: goto 10` (where 5 is the original `pc` and `10` is - * the branchoffset) is re-mapped to a `goto pcToIndex(5+10)` quadruples statement. - */ - private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit - - override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] - - // TYPE CONVERSION METHODS - - def asIf: If[V] = throw new ClassCastException(); - def asGoto: Goto = throw new ClassCastException(); - def asRet: Ret = throw new ClassCastException(); - def asJSR: JSR = throw new ClassCastException(); - def asSwitch: Switch[V] = throw new ClassCastException(); - def asAssignmentLike: AssignmentLikeStmt[V] = throw new ClassCastException(); - def asAssignment: Assignment[V] = throw new ClassCastException(); - def asReturnValue: ReturnValue[V] = throw new ClassCastException(); - def asReturn: Return = throw new ClassCastException(); - def asNop: Nop = throw new ClassCastException(); - def asSynchronizationStmt: SynchronizationStmt[V] = throw new ClassCastException(); - def asMonitorEnter: MonitorEnter[V] = throw new ClassCastException(); - def asMonitorExit: MonitorExit[V] = throw new ClassCastException(); - def asArrayStore: ArrayStore[V] = throw new ClassCastException(); - def asThrow: Throw[V] = throw new ClassCastException(); - def asFieldWriteAccessStmt: FieldWriteAccessStmt[V] = throw new ClassCastException(); - def asPutStatic: PutStatic[V] = throw new ClassCastException(); - def asPutField: PutField[V] = throw new ClassCastException(); - /*inner type*/ def asMethodCall: MethodCall[V] = throw new ClassCastException(); - /*inner type*/ def asInstanceMethodCall: InstanceMethodCall[V] = throw new ClassCastException(); - def asNonVirtualMethodCall: NonVirtualMethodCall[V] = throw new ClassCastException(); - def asVirtualMethodCall: VirtualMethodCall[V] = throw new ClassCastException(); - def asStaticMethodCall: StaticMethodCall[V] = throw new ClassCastException(); - def asInvokedynamicMethodCall: InvokedynamicMethodCall[V] = throw new ClassCastException(); - def asExprStmt: ExprStmt[V] = throw new ClassCastException(); - def asCaughtException: CaughtException[V] = throw new ClassCastException(); - def asCheckcast: Checkcast[V] = throw new ClassCastException(); - - def isAssignment: Boolean = false - def isExprStmt: Boolean = false - def isNonVirtualMethodCall: Boolean = false - def isVirtualMethodCall: Boolean = false - def isStaticMethodCall: Boolean = false - + /** + * The program counter of the original '''underyling bytecode instruction'''. + * + * This `pc` is independent of the (implicit) `index` of the statement + * in the generated statements array! This pc is, e.g., useful for + * getting line number information. + */ + def pc: UShort + + def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean + + /** + * Called by the framework to enable each statement/expression to re-map the target + * `pc` of a(n unconditional) jump instruction to the index of the respective quadruple + * statement in the statements array. + * + * ==Example== + * The bytecode instruction: `5: goto 10` (where 5 is the original `pc` and `10` is + * the branchoffset) is re-mapped to a `goto pcToIndex(5+10)` quadruples statement. + */ + private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit + + override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] + + // TYPE CONVERSION METHODS + def asIf: If[V] = throw new ClassCastException(); + def asGoto: Goto = throw new ClassCastException(); + def asRet: Ret = throw new ClassCastException(); + def asJSR: JSR = throw new ClassCastException(); + def asSwitch: Switch[V] = throw new ClassCastException(); + def asAssignmentLike: AssignmentLikeStmt[V] = throw new ClassCastException(); + def asAssignment: Assignment[V] = throw new ClassCastException(); + def asReturnValue: ReturnValue[V] = throw new ClassCastException(); + def asReturn: Return = throw new ClassCastException(); + def asNop: Nop = throw new ClassCastException(); + def asSynchronizationStmt: SynchronizationStmt[V] = throw new ClassCastException(); + def asMonitorEnter: MonitorEnter[V] = throw new ClassCastException(); + def asMonitorExit: MonitorExit[V] = throw new ClassCastException(); + def asArrayStore: ArrayStore[V] = throw new ClassCastException(); + def asThrow: Throw[V] = throw new ClassCastException(); + def asFieldWriteAccessStmt: FieldWriteAccessStmt[V] = throw new ClassCastException(); + def asPutStatic: PutStatic[V] = throw new ClassCastException(); + def asPutField: PutField[V] = throw new ClassCastException(); + /*inner type*/ + def asMethodCall: MethodCall[V] = throw new ClassCastException(); + /*inner type*/ + def asInstanceMethodCall: InstanceMethodCall[V] = throw new ClassCastException(); + def asNonVirtualMethodCall: NonVirtualMethodCall[V] = throw new ClassCastException(); + def asVirtualMethodCall: VirtualMethodCall[V] = throw new ClassCastException(); + def asStaticMethodCall: StaticMethodCall[V] = throw new ClassCastException(); + def asInvokedynamicMethodCall: InvokedynamicMethodCall[V] = throw new ClassCastException(); + def asExprStmt: ExprStmt[V] = throw new ClassCastException(); + def asCaughtException: CaughtException[V] = throw new ClassCastException(); + def asCheckcast: Checkcast[V] = throw new ClassCastException(); + + def isAssignment: Boolean = false + def isExprStmt: Boolean = false + def isNonVirtualMethodCall: Boolean = false + def isVirtualMethodCall: Boolean = false + def isStaticMethodCall: Boolean = false + def isIfStmt: Boolean = false + def isMonitorEnter: Boolean = false + def isMonitorExit: Boolean = false + def isPutStatic: Boolean = false } /** @@ -101,62 +105,64 @@ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { * generating source code. */ case class If[+V <: Var[V]]( - pc: PC, - left: Expr[V], - condition: RelationalOperator, - right: Expr[V], - private[tac] var target: Int + pc: PC, + left: Expr[V], + condition: RelationalOperator, + right: Expr[V], + private[tac] var target: Int ) extends Stmt[V] { - final override def asIf: this.type = this - final override def astID: Int = If.ASTID - final def leftExpr: Expr[V] = left - final def rightExpr: Expr[V] = right - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(left) && p(right) - } - - /** - * The target statement that is executed if the condition evaluates to `true`. - */ - def targetStmt: Int = target - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - left.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - right.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - target = pcToIndex(target) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - If(pc, left.toCanonicalForm, condition, right.toCanonicalForm, target) - } - - final override def isSideEffectFree: Boolean = { - assert(left.isValueExpression && right.isValueExpression) - true - } - - override def toString: String = s"If(pc=$pc,$left,$condition,$right,target=$target)" + final override def asIf: this.type = this + + final override def isIfStmt: Boolean = true + final override def astID: Int = If.ASTID + final def leftExpr: Expr[V] = left + final def rightExpr: Expr[V] = right + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(left) && p(right) + } + + /** + * The target statement that is executed if the condition evaluates to `true`. + */ + def targetStmt: Int = target + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + left.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + right.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + target = pcToIndex(target) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + If(pc, left.toCanonicalForm, condition, right.toCanonicalForm, target) + } + + final override def isSideEffectFree: Boolean = { + assert(left.isValueExpression && right.isValueExpression) + true + } + + override def toString: String = s"If(pc=$pc,$left,$condition,$right,target=$target)" } object If { - final val ASTID = 0 + final val ASTID = 0 } trait VariableFreeStmt extends Stmt[Nothing] { - final override def toCanonicalForm( - implicit - ev: Nothing <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - this - } + final override def toCanonicalForm( + implicit + ev: Nothing <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + this + } } @@ -167,30 +173,31 @@ trait VariableFreeStmt extends Stmt[Nothing] { */ 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 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 - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - target = pcToIndex(target) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + target = pcToIndex(target) + } - final override def isSideEffectFree: Boolean = true + final override def isSideEffectFree: Boolean = true - /** - * @note Calling this method is only supported after the quadruples representation - * is created and the re-mapping of `pc`s to instruction indexes has happened! - */ - def targetStmt: Int = target + /** + * @note Calling this method is only supported after the quadruples representation + * is created and the re-mapping of `pc`s to instruction indexes has happened! + */ + def targetStmt: Int = target - override def toString: String = s"Goto(pc=$pc,target=$target)" + override def toString: String = s"Goto(pc=$pc,target=$target)" } object Goto { - final val ASTID = 1 + final val ASTID = 1 } /** @@ -205,26 +212,27 @@ object Goto { */ case class Ret(pc: PC, private var returnAddresses: PCs) extends VariableFreeStmt { - 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 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 - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - returnAddresses = returnAddresses map { pcToIndex } - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + returnAddresses = returnAddresses map { pcToIndex } + } - final override def isSideEffectFree: Boolean = true + final override def isSideEffectFree: Boolean = true - override def toString: String = { - s"Ret(pc=$pc,returnAddresses=${returnAddresses.mkString("(", ",", ")")})" - } + override def toString: String = { + s"Ret(pc=$pc,returnAddresses=${returnAddresses.mkString("(", ",", ")")})" + } } object Ret { - final val ASTID = 2 + final val ASTID = 2 } /** @@ -239,32 +247,33 @@ object Ret { */ 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 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 - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - target = pcToIndex(target) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + target = pcToIndex(target) + } - final override def isSideEffectFree: Boolean = true + final override def isSideEffectFree: Boolean = true - /** - * The first statement of the called subroutine. - * - * @note Calling this method is only supported after the quadruples representation - * is created and the re-mapping of `pc`s to instruction indexes has happened! - */ - def targetStmt: Int = target + /** + * The first statement of the called subroutine. + * + * @note Calling this method is only supported after the quadruples representation + * is created and the re-mapping of `pc`s to instruction indexes has happened! + */ + def targetStmt: Int = target - override def toString: String = s"JSR(pc=$pc,target=$target)" + override def toString: String = s"JSR(pc=$pc,target=$target)" } object JSR { - final val ASTID = 3 + final val ASTID = 3 } /** @@ -277,176 +286,177 @@ object JSR { * were determined to be dead. */ case class Switch[+V <: Var[V]]( - pc: PC, - private var defaultTarget: Int, - index: Expr[V], - private var npairs: RefArray[IntIntPair /*(Case Value, Jump Target)*/ ] + pc: PC, + private var defaultTarget: Int, + index: Expr[V], + private var npairs: RefArray[IntIntPair /*(Case Value, Jump Target)*/ ] ) extends Stmt[V] { - final override def asSwitch: this.type = this - final override def astID: Int = Switch.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(index) - } - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - npairs._UNSAFE_mapped { x ⇒ - val newIndex = pcToIndex(x._2) - // assert(newIndex >= 0) - x.copy(_2 = newIndex) - } - defaultTarget = pcToIndex(defaultTarget) - index.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - Switch(pc, defaultTarget, index.toCanonicalForm, npairs) - } - - final override def isSideEffectFree: Boolean = { - assert(index.isValueExpression) - true - } - - // Calling this method is only supported after the three-address code representation - // is created and the remapping of pcs to instruction indexes has happened! - def caseStmts: IntArray = npairs.map(x ⇒ x._2) - - // Calling this method is only supported after the quadruples representation - // is created and the remapping of pcs to instruction indexes has happened! - def defaultStmt: Int = defaultTarget - - override def toString: String = { - val npairs = this.npairs.mkString("(", ",", ")") - s"Switch(pc=$pc,defaultTarget=$defaultTarget,index=$index,npairs=$npairs" - } + final override def asSwitch: this.type = this + final override def astID: Int = Switch.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(index) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + npairs._UNSAFE_mapped { x => + val newIndex = pcToIndex(x._2) + // assert(newIndex >= 0) + x.copy(_2 = newIndex) + } + defaultTarget = pcToIndex(defaultTarget) + index.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + Switch(pc, defaultTarget, index.toCanonicalForm, npairs) + } + + final override def isSideEffectFree: Boolean = { + assert(index.isValueExpression) + true + } + + // Calling this method is only supported after the three-address code representation + // is created and the remapping of pcs to instruction indexes has happened! + def caseStmts: IntArray = npairs.map(x => x._2) + + // Calling this method is only supported after the quadruples representation + // is created and the remapping of pcs to instruction indexes has happened! + def defaultStmt: Int = defaultTarget + + override def toString: String = { + val npairs = this.npairs.mkString("(", ",", ")") + s"Switch(pc=$pc,defaultTarget=$defaultTarget,index=$index,npairs=$npairs" + } } object Switch { - final val ASTID = 4 + final val ASTID = 4 } sealed abstract class AssignmentLikeStmt[+V <: Var[V]] extends Stmt[V] { - def expr: Expr[V] - final override def asAssignmentLike: AssignmentLikeStmt[V] = this + def expr: Expr[V] + final override def asAssignmentLike: AssignmentLikeStmt[V] = this } object AssignmentLikeStmt { - def unapply[V <: Var[V]](stmt: Stmt[V]): Option[(PC, Expr[V])] = { - stmt match { - case s: AssignmentLikeStmt[V] ⇒ Some((s.pc, s.expr)) - case _ ⇒ None - } + def unapply[V <: Var[V]](stmt: Stmt[V]): Option[(PC, Expr[V])] = { + stmt match { + case s: AssignmentLikeStmt[V] => Some((s.pc, s.expr)) + case _ => None } + } } case class Assignment[+V <: Var[V]]( - pc: PC, - targetVar: V, - expr: Expr[V] + pc: PC, + targetVar: V, + expr: Expr[V] ) extends AssignmentLikeStmt[V] { - final override def asAssignment: this.type = this - final override def isAssignment: Boolean = true + final override def asAssignment: this.type = this + final override def isAssignment: Boolean = true - final override def astID: Int = Assignment.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(expr) - } + final override def astID: Int = Assignment.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(expr) + } - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - targetVar.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + targetVar.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - Assignment(pc, ev(targetVar).toCanonicalForm, expr.toCanonicalForm) - } + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + Assignment(pc, ev(targetVar).toCanonicalForm, expr.toCanonicalForm) + } - final override def isSideEffectFree: Boolean = expr.isSideEffectFree + final override def isSideEffectFree: Boolean = expr.isSideEffectFree - override def hashCode(): Opcode = (Assignment.ASTID * 1171 + pc) * 31 + expr.hashCode + override def hashCode(): Opcode = (Assignment.ASTID * 1171 + pc) * 31 + expr.hashCode - override def toString: String = s"Assignment(pc=$pc,$targetVar,$expr)" + override def toString: String = s"Assignment(pc=$pc,$targetVar,$expr)" } object Assignment { - final val ASTID = 5 + final val ASTID = 5 } case class ReturnValue[+V <: Var[V]](pc: Int, expr: Expr[V]) extends Stmt[V] { - final override def asReturnValue: this.type = this - 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( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - ReturnValue(pc, expr.toCanonicalForm) - } - - 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 - false - } - - override def toString: String = s"ReturnValue(pc=$pc,$expr)" + final override def asReturnValue: this.type = this + final override def astID: Int = ReturnValue.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(expr) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + ReturnValue(pc, expr.toCanonicalForm) + } + + 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 + false + } + + override def toString: String = s"ReturnValue(pc=$pc,$expr)" } object ReturnValue { - final val ASTID = 6 + final val ASTID = 6 } sealed abstract class SimpleStmt extends VariableFreeStmt { - /** - * Nothing to do. - */ - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = {} + /** + * Nothing to do. + */ + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = {} } 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 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 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 - false - } + 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 + false + } - override def toString: String = s"Return(pc=$pc)" + override def toString: String = s"Return(pc=$pc)" } object Return { - final val ASTID = 7 + final val ASTID = 7 } /** @@ -473,305 +483,314 @@ object Return { */ case class Nop(pc: Int) extends SimpleStmt { - final override def asNop: this.type = this - final override def astID: Int = Nop.ASTID - final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = true + final override def asNop: this.type = this + final override def astID: Int = Nop.ASTID + final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] => Boolean): Boolean = + true - final override def isSideEffectFree: Boolean = true + final override def isSideEffectFree: Boolean = true - override def toString: String = s"Nop(pc=$pc)" + override def toString: String = s"Nop(pc=$pc)" } object Nop { - final val ASTID = 8 + final val ASTID = 8 } sealed abstract class SynchronizationStmt[+V <: Var[V]] extends Stmt[V] { - final override def asSynchronizationStmt: this.type = this + final override def asSynchronizationStmt: this.type = this - def objRef: Expr[V] + def objRef: Expr[V] - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - objRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + objRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } } case class MonitorEnter[+V <: Var[V]](pc: PC, objRef: Expr[V]) extends SynchronizationStmt[V] { - final override def asMonitorEnter: this.type = this - final override def astID: Int = MonitorEnter.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(objRef) - } + final override def asMonitorEnter: this.type = this - final override def isSideEffectFree: Boolean = { - // IMPROVE Is the lock as such ever used (do we potentially have concurrency)? - false - } + final override def isMonitorEnter: Boolean = true - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - MonitorEnter(pc, objRef.toCanonicalForm) - } + final override def astID: Int = MonitorEnter.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(objRef) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE Is the lock as such ever used (do we potentially have concurrency)? + false + } - override def toString: String = s"MonitorEnter(pc=$pc,$objRef)" + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + MonitorEnter(pc, objRef.toCanonicalForm) + } + + override def toString: String = s"MonitorEnter(pc=$pc,$objRef)" } object MonitorEnter { - final val ASTID = 9 + final val ASTID = 9 } case class MonitorExit[+V <: Var[V]](pc: PC, objRef: Expr[V]) extends SynchronizationStmt[V] { - final override def asMonitorExit: this.type = this - final override def astID: Int = MonitorExit.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(objRef) - } + final override def asMonitorExit: this.type = this - final override def isSideEffectFree: Boolean = { - // IMPROVE Is the lock as such ever used (do we potentially have concurrency)? - false - } + 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) + } - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - MonitorExit(pc, objRef.toCanonicalForm) - } + final override def isSideEffectFree: Boolean = { + // IMPROVE Is the lock as such ever used (do we potentially have concurrency)? + false + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + MonitorExit(pc, objRef.toCanonicalForm) + } - override def toString: String = s"MonitorExit(pc=$pc,$objRef)" + override def toString: String = s"MonitorExit(pc=$pc,$objRef)" } object MonitorExit { - final val ASTID = 10 + final val ASTID = 10 } case class ArrayStore[+V <: Var[V]]( - pc: PC, - arrayRef: Expr[V], - index: Expr[V], - value: Expr[V] + pc: PC, + arrayRef: Expr[V], + index: Expr[V], + value: Expr[V] ) extends Stmt[V] { - final override def asArrayStore: this.type = this - 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) - } - - final override def isSideEffectFree: Boolean = { - // IMPROVE Is it a redundant write? - false - } - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - arrayRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - index.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - ArrayStore(pc, arrayRef.toCanonicalForm, index.toCanonicalForm, value.toCanonicalForm) - } - - override def toString: String = s"ArrayStore(pc=$pc,$arrayRef,$index,$value)" + final override def asArrayStore: this.type = this + 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) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE Is it a redundant write? + false + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + arrayRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + index.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + ArrayStore(pc, arrayRef.toCanonicalForm, index.toCanonicalForm, value.toCanonicalForm) + } + + override def toString: String = s"ArrayStore(pc=$pc,$arrayRef,$index,$value)" } object ArrayStore { - final val ASTID = 11 + final val ASTID = 11 } case class Throw[+V <: Var[V]](pc: PC, exception: Expr[V]) extends Stmt[V] { - final override def asThrow: this.type = this - final override def astID: Int = Throw.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(exception) - } + final override def asThrow: this.type = this + final override def astID: Int = Throw.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(exception) + } - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - exception.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + exception.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - Throw(pc, exception.toCanonicalForm) - } + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + Throw(pc, exception.toCanonicalForm) + } - final override def isSideEffectFree: Boolean = false + final override def isSideEffectFree: Boolean = false - override def hashCode(): Opcode = (Throw.ASTID * 1171 + pc) * 31 + exception.hashCode + override def hashCode(): Opcode = (Throw.ASTID * 1171 + pc) * 31 + exception.hashCode - override def toString: String = s"Throw(pc=$pc,$exception)" + override def toString: String = s"Throw(pc=$pc,$exception)" } object Throw { - final val ASTID = 12 + final val ASTID = 12 } sealed abstract class FieldWriteAccessStmt[+V <: Var[V]] extends Stmt[V] { - def declaringClass: ObjectType - def name: String - def declaredFieldType: FieldType - def value: Expr[V] - - final override def asFieldWriteAccessStmt: this.type = this - - /** - * Identifies the field if it can be found. - */ - def resolveField(implicit p: ProjectLike): Option[Field] = { - p.resolveFieldReference(declaringClass, name, declaredFieldType) - } + def declaringClass: ObjectType + def name: String + def declaredFieldType: FieldType + def value: Expr[V] + + final override def asFieldWriteAccessStmt: this.type = this + + /** + * Identifies the field if it can be found. + */ + def resolveField(implicit p: ProjectLike): Option[Field] = { + p.resolveFieldReference(declaringClass, name, declaredFieldType) + } } case class PutStatic[+V <: Var[V]]( - pc: PC, - declaringClass: ObjectType, - name: String, - declaredFieldType: FieldType, - value: Expr[V] + pc: PC, + declaringClass: ObjectType, + name: String, + declaredFieldType: FieldType, + value: Expr[V] ) extends FieldWriteAccessStmt[V] { - final override def asPutStatic: this.type = this - 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( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - PutStatic(pc, declaringClass, name, declaredFieldType, value.toCanonicalForm) - } - - final override def isSideEffectFree: Boolean = { - // IMPROVE Is it a redundant write? - false - } - - override def hashCode(): Opcode = { - ((PutStatic.ASTID * 1171 + pc) * 31 + declaringClass.hashCode) * 31 + name.hashCode - } - - override def toString: String = { - s"PutStatic(pc=$pc,${declaringClass.toJava},$name,${declaredFieldType.toJava},$value)" - } + 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) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + PutStatic(pc, declaringClass, name, declaredFieldType, value.toCanonicalForm) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE Is it a redundant write? + false + } + + override def hashCode(): Opcode = { + ((PutStatic.ASTID * 1171 + pc) * 31 + declaringClass.hashCode) * 31 + name.hashCode + } + + override def toString: String = { + s"PutStatic(pc=$pc,${declaringClass.toJava},$name,${declaredFieldType.toJava},$value)" + } } object PutStatic { - final val ASTID = 13 + final val ASTID = 13 } case class PutField[+V <: Var[V]]( - pc: Int, - declaringClass: ObjectType, - name: String, - declaredFieldType: FieldType, - objRef: Expr[V], - value: Expr[V] + pc: Int, + declaringClass: ObjectType, + name: String, + declaredFieldType: FieldType, + objRef: Expr[V], + value: Expr[V] ) extends FieldWriteAccessStmt[V] { - 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( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - objRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - val newObjRef = objRef.toCanonicalForm - val newValue = value.toCanonicalForm - PutField(pc, declaringClass, name, declaredFieldType, newObjRef, newValue) - } - - final override def isSideEffectFree: Boolean = { - // IMPROVE Is it a redundant write? - false - } - - override def hashCode(): Opcode = { - ((PutField.ASTID * 1171 + pc) * 31 + declaringClass.hashCode) * 31 + name.hashCode - } - - override def toString: String = { - s"PutField(pc=$pc,${declaringClass.toJava},$name,${declaredFieldType.toJava},$objRef,$value)" - } + 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) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + objRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + val newObjRef = objRef.toCanonicalForm + val newValue = value.toCanonicalForm + PutField(pc, declaringClass, name, declaredFieldType, newObjRef, newValue) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE Is it a redundant write? + false + } + + override def hashCode(): Opcode = { + ((PutField.ASTID * 1171 + pc) * 31 + declaringClass.hashCode) * 31 + name.hashCode + } + + override def toString: String = { + s"PutField(pc=$pc,${declaringClass.toJava},$name,${declaredFieldType.toJava},$objRef,$value)" + } } object PutField { - final val ASTID = 14 + final val ASTID = 14 } sealed abstract class MethodCall[+V <: Var[V]] extends Stmt[V] with Call[V] { - final override def isSideEffectFree: Boolean = false // IMPROVE Check if a call has no side-effect + final override def isSideEffectFree: Boolean = false // IMPROVE Check if a call has no side-effect - final override def asMethodCall: this.type = this + final override def asMethodCall: this.type = this } sealed abstract class InstanceMethodCall[+V <: Var[V]] extends MethodCall[V] { - final override def allParams: Seq[Expr[V]] = receiver +: params + final override def allParams: Seq[Expr[V]] = receiver +: params - def receiver: Expr[V] - final override def receiverOption: Some[Expr[V]] = Some(receiver) - final override def asInstanceMethodCall: this.type = this - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(receiver) && params.forall(param ⇒ p(param)) - } + def receiver: Expr[V] + final override def receiverOption: Some[Expr[V]] = Some(receiver) + final override def asInstanceMethodCall: this.type = this + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(receiver) && params.forall(param => p(param)) + } - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - receiver.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - params foreach { p ⇒ p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + receiver.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + params foreach { p => + p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) } + } } object InstanceMethodCall { - def unapply[V <: Var[V]]( - call: InstanceMethodCall[V] - ): Some[(Int, ReferenceType, Boolean, String, MethodDescriptor, Expr[V], Seq[Expr[V]])] = { - import call._ - Some((pc, declaringClass, isInterface, name, descriptor, receiver, params)) - } + def unapply[V <: Var[V]]( + call: InstanceMethodCall[V] + ): Some[(Int, ReferenceType, Boolean, String, MethodDescriptor, Expr[V], Seq[Expr[V]])] = { + import call._ + Some((pc, declaringClass, isInterface, name, descriptor, receiver, params)) + } } /** @@ -779,175 +798,177 @@ object InstanceMethodCall { * I.e., it is either a super-call, a private instance method call or a constructor call. */ case class NonVirtualMethodCall[+V <: Var[V]]( - pc: Int, - declaringClass: ObjectType, - isInterface: Boolean, - name: String, - descriptor: MethodDescriptor, - receiver: Expr[V], - params: Seq[Expr[V]] + pc: Int, + declaringClass: ObjectType, + isInterface: Boolean, + name: String, + descriptor: MethodDescriptor, + receiver: Expr[V], + params: Seq[Expr[V]] ) extends InstanceMethodCall[V] { - final override def asNonVirtualMethodCall: this.type = this - final override def isNonVirtualMethodCall: Boolean = true - final override def astID: Int = NonVirtualMethodCall.ASTID - - /** - * Identifies the potential call target if it can be found. - * - * @see [ProjectLike#specialCall] for further details. - */ - def resolveCallTarget(callerClassType: ObjectType)(implicit p: ProjectLike): Result[Method] = { - p.specialCall(callerClassType, declaringClass, isInterface, name, descriptor) - } - - // convenience method to enable Call to define a single method to handle all kinds of calls - def resolveCallTargets( - callingContext: ObjectType - )( - implicit - p: ProjectLike, - ev: V <:< DUVar[ValueInformation] - ): Set[Method] = { - resolveCallTarget(callingContext)(p).toSet - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - NonVirtualMethodCall( - pc, - declaringClass, - isInterface, - name, - descriptor, - receiver.toCanonicalForm, - params.map(_.toCanonicalForm) - ) - } - - override def toString: String = { - val sig = descriptor.toJava(name) - val declClass = declaringClass.toJava - val params = this.params.mkString("(", ",", ")") - s"NonVirtualMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$receiver,$params)" - } + final override def asNonVirtualMethodCall: this.type = this + final override def isNonVirtualMethodCall: Boolean = true + final override def astID: Int = NonVirtualMethodCall.ASTID + + /** + * Identifies the potential call target if it can be found. + * + * @see [ProjectLike#specialCall] for further details. + */ + def resolveCallTarget(callerClassType: ObjectType)(implicit p: ProjectLike): Result[Method] = { + p.specialCall(callerClassType, declaringClass, isInterface, name, descriptor) + } + + // convenience method to enable Call to define a single method to handle all kinds of calls + def resolveCallTargets( + callingContext: ObjectType + )( + implicit + p: ProjectLike, + ev: V <:< DUVar[ValueInformation] + ): Set[Method] = { + resolveCallTarget(callingContext)(p).toSet + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + NonVirtualMethodCall( + pc, + declaringClass, + isInterface, + name, + descriptor, + receiver.toCanonicalForm, + params.map(_.toCanonicalForm) + ) + } + + override def toString: String = { + val sig = descriptor.toJava(name) + val declClass = declaringClass.toJava + val params = this.params.mkString("(", ",", ")") + s"NonVirtualMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$receiver,$params)" + } } object NonVirtualMethodCall { - final val ASTID = 15 + final val ASTID = 15 } case class VirtualMethodCall[+V <: Var[V]]( - pc: Int, - declaringClass: ReferenceType, - isInterface: Boolean, - name: String, - descriptor: MethodDescriptor, - receiver: Expr[V], - params: Seq[Expr[V]] + pc: Int, + declaringClass: ReferenceType, + isInterface: Boolean, + name: String, + descriptor: MethodDescriptor, + receiver: Expr[V], + params: Seq[Expr[V]] ) extends InstanceMethodCall[V] with VirtualCall[V] { - final override def asVirtualMethodCall: this.type = this - final override def isVirtualMethodCall: Boolean = true - final override def astID: Int = VirtualMethodCall.ASTID - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - VirtualMethodCall( - pc, - declaringClass, - isInterface, - name, - descriptor, - receiver.toCanonicalForm, - params.map(_.toCanonicalForm) - ) - } - - override def toString: String = { - val sig = descriptor.toJava(name) - val declClass = declaringClass.toJava - val params = this.params.mkString("(", ",", ")") - s"VirtualMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$receiver,$params)" - } + final override def asVirtualMethodCall: this.type = this + final override def isVirtualMethodCall: Boolean = true + final override def astID: Int = VirtualMethodCall.ASTID + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + VirtualMethodCall( + pc, + declaringClass, + isInterface, + name, + descriptor, + receiver.toCanonicalForm, + params.map(_.toCanonicalForm) + ) + } + + override def toString: String = { + val sig = descriptor.toJava(name) + val declClass = declaringClass.toJava + val params = this.params.mkString("(", ",", ")") + s"VirtualMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$receiver,$params)" + } } object VirtualMethodCall { - final val ASTID = 16 + final val ASTID = 16 } case class StaticMethodCall[+V <: Var[V]]( - pc: Int, - declaringClass: ObjectType, - isInterface: Boolean, - name: String, - descriptor: MethodDescriptor, - params: Seq[Expr[V]] + pc: Int, + declaringClass: ObjectType, + isInterface: Boolean, + name: String, + descriptor: MethodDescriptor, + params: Seq[Expr[V]] ) extends MethodCall[V] { - final override def allParams: Seq[Expr[V]] = params - - final override def asStaticMethodCall: this.type = this - final override def isStaticMethodCall: Boolean = true - final override def astID: Int = StaticMethodCall.ASTID - final override def receiverOption: Option[Expr[V]] = None - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - params.forall(param ⇒ p(param)) - } - - /** - * Identifies the potential call target if it can be found. - * - * @see [ProjectLike#staticCall] for further details. - */ - def resolveCallTarget(implicit p: ProjectLike): Result[Method] = { - p.staticCall(declaringClass, isInterface, name, descriptor) - } - - // convenience method to enable Call to define a single method to handle all kinds of calls - def resolveCallTargets( - callingContext: ObjectType - )( - implicit - p: ProjectLike, - ev: V <:< DUVar[ValueInformation] - ): Set[Method] = { - resolveCallTarget(p).toSet - } - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - params.foreach { p ⇒ p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) } - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - StaticMethodCall( - pc, - declaringClass, - isInterface, - name, - descriptor, - params.map(_.toCanonicalForm) - ) - } - - override def toString: String = { - val sig = descriptor.toJava(name) - val declClass = declaringClass.toJava - val params = this.params.mkString("(", ",", ")") - s"StaticMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$params)" - } + final override def allParams: Seq[Expr[V]] = params + + final override def asStaticMethodCall: this.type = this + final override def isStaticMethodCall: Boolean = true + final override def astID: Int = StaticMethodCall.ASTID + final override def receiverOption: Option[Expr[V]] = None + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + params.forall(param => p(param)) + } + + /** + * Identifies the potential call target if it can be found. + * + * @see [ProjectLike#staticCall] for further details. + */ + def resolveCallTarget(implicit p: ProjectLike): Result[Method] = { + p.staticCall(declaringClass, isInterface, name, descriptor) + } + + // convenience method to enable Call to define a single method to handle all kinds of calls + def resolveCallTargets( + callingContext: ObjectType + )( + implicit + p: ProjectLike, + ev: V <:< DUVar[ValueInformation] + ): Set[Method] = { + resolveCallTarget(p).toSet + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + params.foreach { p => + p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + StaticMethodCall( + pc, + declaringClass, + isInterface, + name, + descriptor, + params.map(_.toCanonicalForm) + ) + } + + override def toString: String = { + val sig = descriptor.toJava(name) + val declClass = declaringClass.toJava + val params = this.params.mkString("(", ",", ")") + s"StaticMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$params)" + } } object StaticMethodCall { - final val ASTID = 17 + final val ASTID = 17 } /** @@ -957,96 +978,98 @@ object StaticMethodCall { * @tparam V The type of the [[Var]]s. */ case class InvokedynamicMethodCall[+V <: Var[V]]( - pc: PC, - bootstrapMethod: BootstrapMethod, - name: String, - descriptor: MethodDescriptor, - params: Seq[Expr[V]] + pc: PC, + bootstrapMethod: BootstrapMethod, + name: String, + descriptor: MethodDescriptor, + params: Seq[Expr[V]] ) extends Stmt[V] { - final override def astID: Int = InvokedynamicMethodCall.ASTID - final override def asInvokedynamicMethodCall: this.type = this - // IMPROVE [FUTURE] Use some analysis to determine if a method call is side effect free - final override def isSideEffectFree: Boolean = false - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - params.forall(param ⇒ p(param)) - } - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - params.foreach { p ⇒ p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) } - } - - override def hashCode(): Int = { - (((InvokedynamicMethodCall.ASTID * 1171 + - pc) * 31 + - bootstrapMethod.hashCode) * 31 + - name.hashCode) * 31 + - descriptor.hashCode - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - InvokedynamicMethodCall( - pc, - bootstrapMethod, - name, - descriptor, - params.map(_.toCanonicalForm) - ) - } - - override def toString: String = { - val sig = descriptor.toJava(name) - val params = this.params.mkString("(", ",", ")") - s"InvokedynamicMethodCall(pc=$pc,$bootstrapMethod,$sig,$params)" - } + final override def astID: Int = InvokedynamicMethodCall.ASTID + final override def asInvokedynamicMethodCall: this.type = this + // IMPROVE [FUTURE] Use some analysis to determine if a method call is side effect free + final override def isSideEffectFree: Boolean = false + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + params.forall(param => p(param)) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + params.foreach { p => + p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + } + + override def hashCode(): Int = { + (((InvokedynamicMethodCall.ASTID * 1171 + + pc) * 31 + + bootstrapMethod.hashCode) * 31 + + name.hashCode) * 31 + + descriptor.hashCode + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + InvokedynamicMethodCall( + pc, + bootstrapMethod, + name, + descriptor, + params.map(_.toCanonicalForm) + ) + } + + override def toString: String = { + val sig = descriptor.toJava(name) + val params = this.params.mkString("(", ",", ")") + s"InvokedynamicMethodCall(pc=$pc,$bootstrapMethod,$sig,$params)" + } } object InvokedynamicMethodCall { final val ASTID = 18 } /** An expression where the value is not further used. */ case class ExprStmt[+V <: Var[V]](pc: Int, expr: Expr[V]) extends AssignmentLikeStmt[V] { - final override def asExprStmt: this.type = this - final override def isExprStmt: Boolean = true - final override def astID: Int = ExprStmt.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(expr) - } - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def isSideEffectFree: Boolean = { - assert( - !expr.isSideEffectFree, - "useless ExprStmt - the referenced expression is side-effect free" - ) - false - } - - override def hashCode(): Opcode = (ExprStmt.ASTID * 1171 + pc) * 31 + expr.hashCode - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - ExprStmt(pc, expr.toCanonicalForm) - } - - override def toString: String = s"ExprStmt(pc=$pc,$expr)" + final override def asExprStmt: this.type = this + final override def isExprStmt: Boolean = true + final override def astID: Int = ExprStmt.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(expr) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def isSideEffectFree: Boolean = { + assert( + !expr.isSideEffectFree, + "useless ExprStmt - the referenced expression is side-effect free" + ) + false + } + + override def hashCode(): Opcode = (ExprStmt.ASTID * 1171 + pc) * 31 + expr.hashCode + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + ExprStmt(pc, expr.toCanonicalForm) + } + + override def toString: String = s"ExprStmt(pc=$pc,$expr)" } object ExprStmt { - final val ASTID = 19 + final val ASTID = 19 } /** @@ -1055,13 +1078,13 @@ object ExprStmt { */ object VirtualFunctionCallStatement { - def unapply[V <: Var[V]](stmt: Stmt[V]): Option[VirtualFunctionCall[V]] = { - stmt match { - case ExprStmt(_, vfc: VirtualFunctionCall[V]) ⇒ Some(vfc) - case Assignment(_, _, vfc: VirtualFunctionCall[V]) ⇒ Some(vfc) - case _ ⇒ None - } + def unapply[V <: Var[V]](stmt: Stmt[V]): Option[VirtualFunctionCall[V]] = { + stmt match { + case ExprStmt(_, vfc: VirtualFunctionCall[V]) => Some(vfc) + case Assignment(_, _, vfc: VirtualFunctionCall[V]) => Some(vfc) + case _ => None } + } } /** @@ -1070,13 +1093,13 @@ object VirtualFunctionCallStatement { */ object NonVirtualFunctionCallStatement { - def unapply[V <: Var[V]](stmt: Stmt[V]): Option[NonVirtualFunctionCall[V]] = { - stmt match { - case ExprStmt(_, vfc: NonVirtualFunctionCall[V]) ⇒ Some(vfc) - case Assignment(_, _, vfc: NonVirtualFunctionCall[V]) ⇒ Some(vfc) - case _ ⇒ None - } + def unapply[V <: Var[V]](stmt: Stmt[V]): Option[NonVirtualFunctionCall[V]] = { + stmt match { + case ExprStmt(_, vfc: NonVirtualFunctionCall[V]) => Some(vfc) + case Assignment(_, _, vfc: NonVirtualFunctionCall[V]) => Some(vfc) + case _ => None } + } } /** @@ -1085,13 +1108,13 @@ object NonVirtualFunctionCallStatement { */ object StaticFunctionCallStatement { - def unapply[V <: Var[V]](stmt: Stmt[V]): Option[StaticFunctionCall[V]] = { - stmt match { - case ExprStmt(_, vfc: StaticFunctionCall[V]) ⇒ Some(vfc) - case Assignment(_, _, vfc: StaticFunctionCall[V]) ⇒ Some(vfc) - case _ ⇒ None - } + def unapply[V <: Var[V]](stmt: Stmt[V]): Option[StaticFunctionCall[V]] = { + stmt match { + case ExprStmt(_, vfc: StaticFunctionCall[V]) => Some(vfc) + case Assignment(_, _, vfc: StaticFunctionCall[V]) => Some(vfc) + case _ => None } + } } /** @@ -1101,76 +1124,78 @@ object StaticFunctionCallStatement { * @note `CaughtException` expression are only created by [[TACAI]]! */ case class CaughtException[+V <: Var[V]]( - pc: PC, - exceptionType: Option[ObjectType], - private var throwingStmts: IntTrieSet + pc: PC, + exceptionType: Option[ObjectType], + private var throwingStmts: IntTrieSet ) extends Stmt[V] { - final override def asCaughtException: CaughtException[V] = this - final override def astID: Int = CaughtException.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = true - - final override def isSideEffectFree: Boolean = false - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - throwingStmts = throwingStmts map { pc ⇒ ai.remapPC(pcToIndex)(pc) } - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - this.asInstanceOf[Stmt[DUVar[ValueInformation]]] - } - - /** - * The origin(s) of the caught exception(s). An origin identifies the instruction - * that ex- or implicitly created the exception: - * - If the exception is created locally (`new XXXException`) and also caught within the - * same method, then the origin identifies a normal variable definition site. - * - If the exception is a parameter the parameter's origin (-1,... -n) is returned. - * - If the exception was raised due to a sideeffect of evaluating an expression, then the - * origin is smaller or equal to [[org.opalj.ai.ImmediateVMExceptionsOriginOffset]] and can be - * tranformed to the index of the responsible instruction using - * [[org.opalj.ai#pcOfImmediateVMException]]. - */ - def origins: IntTrieSet = throwingStmts - - /** - * Textual description of the sources of the caught exceptions. If the exception was - * thrown by the JVM due to the evaluation of an expression (e.g., NullPointerException, - * DivisionByZero,..) then the string will be `exception@` where index identifies - * the failing expression. In case an exception is caught that was thrown using `ATHROW` - * the local variable/parameter which stores the local variable is returned. - */ - final def exceptionLocations: Iterator[String] = { - throwingStmts.iterator.map { defSite ⇒ - if (defSite < 0) { - if (ai.isImmediateVMException(defSite)) - "exception[VM]@"+ai.pcOfImmediateVMException(defSite) - else if (ai.isMethodExternalExceptionOrigin(defSite)) - "exception@"+ai.pcOfMethodExternalException(defSite) - else - "param"+(-defSite - 1).toHexString - } else { - "lv"+defSite.toHexString - } - } - } - - override def toString: String = { - val exceptionType = this.exceptionType.map(_.toJava).getOrElse("") - val exceptionLocations = this.exceptionLocations.mkString("{", ",", "}") - s"CaughtException(pc=$pc,$exceptionType,caused by=$exceptionLocations)" - } + final override def asCaughtException: CaughtException[V] = this + final override def astID: Int = CaughtException.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = true + + final override def isSideEffectFree: Boolean = false + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + throwingStmts = throwingStmts map { pc => + ai.remapPC(pcToIndex)(pc) + } + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + this.asInstanceOf[Stmt[DUVar[ValueInformation]]] + } + + /** + * The origin(s) of the caught exception(s). An origin identifies the instruction + * that ex- or implicitly created the exception: + * - If the exception is created locally (`new XXXException`) and also caught within the + * same method, then the origin identifies a normal variable definition site. + * - If the exception is a parameter the parameter's origin (-1,... -n) is returned. + * - If the exception was raised due to a sideeffect of evaluating an expression, then the + * origin is smaller or equal to [[org.opalj.ai.ImmediateVMExceptionsOriginOffset]] and can be + * tranformed to the index of the responsible instruction using + * [[org.opalj.ai#pcOfImmediateVMException]]. + */ + def origins: IntTrieSet = throwingStmts + + /** + * Textual description of the sources of the caught exceptions. If the exception was + * thrown by the JVM due to the evaluation of an expression (e.g., NullPointerException, + * DivisionByZero,..) then the string will be `exception@` where index identifies + * the failing expression. In case an exception is caught that was thrown using `ATHROW` + * the local variable/parameter which stores the local variable is returned. + */ + final def exceptionLocations: Iterator[String] = { + throwingStmts.iterator.map { defSite => + if (defSite < 0) { + if (ai.isImmediateVMException(defSite)) + "exception[VM]@" + ai.pcOfImmediateVMException(defSite) + else if (ai.isMethodExternalExceptionOrigin(defSite)) + "exception@" + ai.pcOfMethodExternalException(defSite) + else + "param" + (-defSite - 1).toHexString + } else { + "lv" + defSite.toHexString + } + } + } + + override def toString: String = { + val exceptionType = this.exceptionType.map(_.toJava).getOrElse("") + val exceptionLocations = this.exceptionLocations.mkString("{", ",", "}") + s"CaughtException(pc=$pc,$exceptionType,caused by=$exceptionLocations)" + } } object CaughtException { - final val ASTID = 20 + final val ASTID = 20 } @@ -1179,38 +1204,38 @@ object CaughtException { */ case class Checkcast[+V <: Var[V]](pc: PC, value: Expr[V], cmpTpe: ReferenceType) extends Stmt[V] { - final override def asCheckcast: this.type = this - final override def astID: Int = Checkcast.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(value) - } - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - Checkcast(pc, value.toCanonicalForm, cmpTpe) - } - - final override def isSideEffectFree: Boolean = { - // IMPROVE identify (from the JVM verifiers point-of-view) truly useless checkcasts - // A useless checkcast is one where the static intra-procedural type information which - // is available in the bytecode is sufficient to determine that the type is a subtype - // of the tested type (i.e., only those check casts are truly usefull that would not - // lead to a failing validation of the bytecode by the JVM!) - false - } - - override def toString: String = s"Checkcast(pc=$pc,$value,${cmpTpe.toJava})" + final override def asCheckcast: this.type = this + final override def astID: Int = Checkcast.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(value) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + Checkcast(pc, value.toCanonicalForm, cmpTpe) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE identify (from the JVM verifiers point-of-view) truly useless checkcasts + // A useless checkcast is one where the static intra-procedural type information which + // is available in the bytecode is sufficient to determine that the type is a subtype + // of the tested type (i.e., only those check casts are truly usefull that would not + // lead to a failing validation of the bytecode by the JVM!) + false + } + + override def toString: String = s"Checkcast(pc=$pc,$value,${cmpTpe.toJava})" } object Checkcast { - final val ASTID = 21 + final val ASTID = 21 } From d336013035a5c3b62269ce8a9ea85a93d77d0e54 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 16 Jan 2020 17:01:07 +0100 Subject: [PATCH 036/327] First approach for recognizing double checked locking pattern. Not yet works with all tests. --- ...bilityLazyInitializationAnalysisDemo.scala | 65 + .../DoubleCheckedLockingClass1.java | 22 + .../SimpleLazyInstantiation.java | 14 + .../SimpleLazyIntInstantiation.java | 13 + .../SimpleLazyObjectsInstantiation.java | 14 + .../LazyInitializationAnnotation.java | 31 + .../NoLazyInitializationAnnotation.java | 31 + ...hreadSafeLazyInitializationAnnotation.java | 38 + ...eImmutabilityLazyInitializationTests.scala | 85 ++ ...mmutabilityLazyInitializationMatcher.scala | 62 + ...erenceImmutabilityLazyInitialization.scala | 50 + ...mutabilityLazyInitializationAnalysis.scala | 1328 +++++++++++++++++ 12 files changed, 1753 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutabilityLazyInitialization.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala new file mode 100644 index 0000000000..a9b3b9b7ac --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala @@ -0,0 +1,65 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.br.fpcf.properties.LazyInitialization +import org.opalj.br.fpcf.properties.NoLazyInitialization +import org.opalj.br.fpcf.properties.NotThreadSafeLazyInitialization +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis + +/** + * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object ReferenceImmutabilityLazyInitializationAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + val (propertyStore, _) = analysesManager.runAll( + EagerL0ReferenceImmutabilityLazyInitializationAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis + ); + + "Not lazy initialized References: " + propertyStore + .finalEntities(NoLazyInitialization) + .toList + .toString() + "\n" + + "Not thread safe lazy initialization: " + propertyStore + .finalEntities(NotThreadSafeLazyInitialization) + .toList + .toString() + "\n" + + "Lazy Initialization: " + propertyStore + .finalEntities(LazyInitialization) + .toList + .toString() + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java new file mode 100644 index 0000000000..dd566fb057 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java @@ -0,0 +1,22 @@ +package org.opalj.fpcf.fixtures.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.LazyInitializationAnnotation; + +public class DoubleCheckedLockingClass1{ + + @LazyInitializationAnnotation("") + private static DoubleCheckedLockingClass1 instance; + public static DoubleCheckedLockingClass1 getInstance() { + if(instance==null){ + synchronized(DoubleCheckedLockingClass1.class) { + if(instance==null){ + instance = new DoubleCheckedLockingClass1(); + } + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java new file mode 100644 index 0000000000..f92be000bb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java @@ -0,0 +1,14 @@ +package org.opalj.fpcf.fixtures.reference_immutability_lazy_initialization; + +import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.NotThreadSafeLazyInitializationAnnotation; + +public class SimpleLazyInstantiation{ + @NotThreadSafeLazyInitializationAnnotation("") + private SimpleLazyInstantiation instance; + + public SimpleLazyInstantiation init() { + SimpleLazyInstantiation result; + result = instance == null ? new SimpleLazyInstantiation() : instance; + return result; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java new file mode 100644 index 0000000000..b5c65fb42f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java @@ -0,0 +1,13 @@ +package org.opalj.fpcf.fixtures.reference_immutability_lazy_initialization; + +import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.LazyInitializationAnnotation; + +public class SimpleLazyIntInstantiation{ + @LazyInitializationAnnotation("") + 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/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java new file mode 100644 index 0000000000..f8eafab1b3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java @@ -0,0 +1,14 @@ +package org.opalj.fpcf.fixtures.reference_immutability_lazy_initialization; + +import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.NotThreadSafeLazyInitializationAnnotation; + +public class SimpleLazyObjectsInstantiation{ + @NotThreadSafeLazyInitializationAnnotation("") + private SimpleLazyObjectsInstantiation instance; + public SimpleLazyObjectsInstantiation getInstance() { + if(instance==null) + instance = new SimpleLazyObjectsInstantiation(); + return instance; + } +} + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java new file mode 100644 index 0000000000..bcd4e1dcaf --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability_lazy_initialization; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceMatcher; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is lazy initialized + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = LazyInitializedReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface LazyInitializationAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityLazyInitializationAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java new file mode 100644 index 0000000000..3a7eb903b5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability_lazy_initialization; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceMatcher; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated reference is immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = NoLazyInitializationMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface NoLazyInitializationAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityLazyInitializationAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java new file mode 100644 index 0000000000..d26cfd01e9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java @@ -0,0 +1,38 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability_lazy_initialization; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceMatcher; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated reference is mutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = NotThreadSafeInitializationMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface NotThreadSafeLazyInitializationAnnotation { + + + /** + * True if the field is non-final because it is read prematurely. + * Tests may ignore @NonFinal annotations if the FieldPrematurelyRead property for the field + * did not identify the premature read. + */ + boolean prematurelyRead() default false; + /** + * A short reasoning of this property. + */ + String value() default "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityLazyInitializationAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala new file mode 100644 index 0000000000..53723d03b7 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala @@ -0,0 +1,85 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis + +/** + * @author Tobias Peter Roth + */ +class ReferenceImmutabilityLazyInitializationTests extends PropertiesTest { + + 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("ReferenceImmutabilityLazyInitialization") + ) + } + + describe("the org.opalj.fpcf.analyses.ReferenceImmutabilityLazyInitialization is executed") { + val as = executeAnalyses( + Set( + EagerL0ReferenceImmutabilityLazyInitializationAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties( + as, + fieldsWithAnnotations(as.project), + Set("ReferenceImmutabilityLazyInitialization") + ) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala new file mode 100644 index 0000000000..78eb7a2a70 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala @@ -0,0 +1,62 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability_lazy_initialization + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.LazyInitialization +import org.opalj.br.fpcf.properties.NoLazyInitialization +import org.opalj.br.fpcf.properties.NotThreadSafeLazyInitialization +import org.opalj.br.fpcf.properties.ReferenceImmutabilityLazyInitialization +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher + +/** + * @author Tobias Peter Roth + */ +class ReferenceImmutabilityLazyInitializationMatcher( + val property: ReferenceImmutabilityLazyInitialization +) 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 NoLazyInitializationMatcher + extends ReferenceImmutabilityLazyInitializationMatcher(NoLazyInitialization) + +class NotThreadSafeInitializationMatcher + extends ReferenceImmutabilityLazyInitializationMatcher(NotThreadSafeLazyInitialization) + +class LazyInitializationMatcher + extends ReferenceImmutabilityLazyInitializationMatcher(LazyInitialization) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutabilityLazyInitialization.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutabilityLazyInitialization.scala new file mode 100644 index 0000000000..1f0a796ec7 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutabilityLazyInitialization.scala @@ -0,0 +1,50 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.properties + +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +sealed trait ReferenceImmutabilityLazyInstantiationPropertyMetaInformation + extends PropertyMetaInformation { + + type Self = ReferenceImmutabilityLazyInitialization + +} + +/** + * Describes if the reference of a org.opalj.br.Field was lazyliy initialized. + * + * [[NoLazyInitialization]] The reference is not lazily initialized + * + * [[NotThreadSafeLazyInitialization]] The reference is lazily initialized, but not threadsafe + * + * [[LazyInitialization]] The reference is lazy initialized + * + * @author Tobias Peter Roth + */ +sealed trait ReferenceImmutabilityLazyInitialization + extends Property + with ReferenceImmutabilityLazyInstantiationPropertyMetaInformation { + final def key: PropertyKey[ReferenceImmutabilityLazyInitialization] = + ReferenceImmutabilityLazyInitialization.key +} + +object ReferenceImmutabilityLazyInitialization + extends ReferenceImmutabilityLazyInstantiationPropertyMetaInformation { + + final val PropertyKeyName = "opalj.ReferenceImmutabilityLazyInitialization" + + final val key: PropertyKey[ReferenceImmutabilityLazyInitialization] = { + PropertyKey.create( + PropertyKeyName, + NoLazyInitialization + ) + } +} + +case object NoLazyInitialization extends ReferenceImmutabilityLazyInitialization + +case object NotThreadSafeLazyInitialization extends ReferenceImmutabilityLazyInitialization + +case object LazyInitialization extends ReferenceImmutabilityLazyInitialization diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala new file mode 100644 index 0000000000..11b5112836 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala @@ -0,0 +1,1328 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses + +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.BasicFPCFEagerAnalysisScheduler +import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.FieldAccessInformationKey +import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.cg.ClosedPackagesKey +import org.opalj.br.analyses.cg.TypeExtensibilityKey +import org.opalj.br.cfg.BasicBlock +import org.opalj.br.cfg.CFG +import org.opalj.br.cfg.CFGNode +import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.fpcf.properties.EscapeProperty +import org.opalj.br.fpcf.properties.FieldPrematurelyRead +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.ai.isImmediateVMException +import org.opalj.ai.pcOfImmediateVMException +import org.opalj.ai.pcOfMethodExternalException +import org.opalj.br.ComputationalTypeFloat +import org.opalj.br.ComputationalTypeInt +import org.opalj.br.DeclaredMethod +import org.opalj.br.Field +import org.opalj.br.FloatType +import org.opalj.br.Method +import org.opalj.br.ObjectType +import org.opalj.br.PC +import org.opalj.br.PCs +import org.opalj.br.fpcf.properties.AtMost +import org.opalj.br.fpcf.properties.EscapeInCallee +import org.opalj.br.fpcf.properties.EscapeViaReturn +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitialization +import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.br.fpcf.properties.NoEscape +import org.opalj.br.fpcf.properties.NoLazyInitialization +import org.opalj.br.fpcf.properties.NotPrematurelyReadField +import org.opalj.br.fpcf.properties.NotThreadSafeLazyInitialization +import org.opalj.br.fpcf.properties.PrematurelyReadField +import org.opalj.br.fpcf.properties.ReferenceImmutability +import org.opalj.br.fpcf.properties.ReferenceImmutabilityLazyInitialization +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.LBP +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyComputationResult +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.SomeEPS +import org.opalj.fpcf.UBP +import org.opalj.fpcf.Result +import org.opalj.tac.Assignment +import org.opalj.tac.CaughtException +import org.opalj.tac.DUVar +import org.opalj.tac.Expr +import org.opalj.tac.FieldWriteAccessStmt +import org.opalj.tac.GetField +import org.opalj.tac.GetStatic +import org.opalj.tac.If +import org.opalj.tac.NonVirtualFunctionCall +import org.opalj.tac.PutField +import org.opalj.tac.PutStatic +import org.opalj.tac.ReturnValue +import org.opalj.tac.StaticFunctionCall +import org.opalj.tac.Stmt +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACStmts +import org.opalj.tac.TACode +import org.opalj.tac.VirtualFunctionCall +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.tac.SelfReferenceParameter + +import scala.collection.mutable + +/** + * + * Implementation is used from the old L2FieldMutability implementation + * but the lattice is mapped to the new reference immutability lattice. + * + * @note Requires that the 3-address code's expressions are not deeply nested. + * + * @author Dominik Helm + * @author Florian Kübler + * @author Michael Eichberg + * @author Tobias Peter Roth + */ +class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val project: SomeProject) + extends FPCFAnalysis { + + case class State( + field: Field, + var referenceImmutability: ReferenceImmutabilityLazyInitialization = NoLazyInitialization, + 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, + var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty, + var isThreadSafeType: Boolean = true + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty + } + + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + } + } + + 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) + + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field ⇒ determineReferenceImmutabilityLazyInitialization(field) + case _ ⇒ + val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + + /** + * Analyzes the mutability of private non-final fields. + * + * 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 determineReferenceImmutabilityLazyInitialization( + field: Field + ): ProperPropertyComputationResult = { + implicit val state: State = State(field) + // Fields are not final if they are read prematurely! + //if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) + // return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); + //if (field.isFinal) + // return createResult(); + + //state.referenceImmutability = ImmutableReference //EffectivelyFinalField + + //val thisType = field.classFile.thisType + + //if (field.isPublic) + // return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) + + // 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) { + * if (!closedPackages.isClosed(thisType.packageName)) { + * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + * } + * project.classesPerPackage(thisType.packageName) + * } else { + * Set(field.classFile) + * } + * + * val classesHavingAccess: Iterator[ClassFile] = + * if (field.isProtected) { + * if (typeExtensibility(thisType).isYesOrUnknown) { + * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + * } + * 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))) + /// return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode ← getTACAI(method, pcs) + } { + //if ( + checkMethod(method, taCode, pcs) //) { + // return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); + //} + + } + + ///if (state.lazyInitInvocation.isDefined) { + /// val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + /// handleCalls(calleesEOP) + ///} + + createResult() + } + + /** + * def handleCalls( + * calleesEOP: EOptionP[DeclaredMethod, Callees] + * )( + * implicit + * state: State + * ): Boolean = { + * calleesEOP match { + * case FinalP(callees) => + * state.calleesDependee = None + * handleCallees(callees) + * case InterimUBP(callees) => + * state.calleesDependee = Some(calleesEOP) + * handleCallees(callees) + * case _ => + * state.calleesDependee = Some(calleesEOP) + * false + * } + * }* + */ + /** + * + * def handleCallees(callees: Callees)(implicit state: State): Boolean = { + * val pc = state.lazyInitInvocation.get._2 + * if (callees.isIncompleteCallSite(pc)) { + * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + * true + * } else { + * val targets = callees.callees(pc).toTraversable + * if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { + * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + * true + * } else false + * } + * }* + */ + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + * The code below can be used to identify such value, but is not yet adapted to using the + * TACAI property */ + /* + var constantVal: Option[Any] = None + var allInitializeConstant = true + + val field = state.field + var constructors: Set[Method] = + if(field.isStatic) Set.empty else field.classFile.constructors.toSet + + val writesIterator = fieldAccessInformation.writeAccesses(field).iterator + while (writesIterator.hasNext && allInitializeConstant) { + val (method, pc) = writesIterator.next() + constructors -= method + val code = tacai(method).stmts + + val index = pcToIndex(pc) + val stmt = code(index) + if (stmt.astID == PutStatic.ASTID || + stmt.asPutField.objRef.asVar.definedBy == SelfReferenceParameter) { + val write = stmt.asFieldWriteAccessStmt + if (write.resolveField(p).contains(state.field)) { + val defs = write.value.asVar.definedBy + if (defs.size == 1 && defs.head >= 0) { + val defSite = code(defs.head).asAssignment.expr + val const = if (defSite.isIntConst) + Some(defSite.asIntConst.value) + else if (defSite.isFloatConst) + Some(defSite.asFloatConst.value) + else None + if (const.isDefined) { + if (constantVal.isDefined) { + if (constantVal != const) { + allInitializeConstant = false + constantVal = None + } + } else constantVal = const + } else { + allInitializeConstant = false + constantVal = None + } + } + } + } + } + + for (constructor ← constructors) { + // TODO iterate all statements + val NonVirtualMethodCall(_, declClass, _, name, _, rcvr, _) = stmt + // Consider calls to other constructors as initializations as either + // the called constructor will initialize the field or delegate to yet + // another constructor + if (declClass != state.field.classFile.thisType || name != "" || + rcvr.asVar.definedBy != SelfReferenceParameter) { + if (constantVal.isDefined) allInitializeConstant = false + else constantVal = Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + } + } + + constantVal */ + } + + /** + * 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 NoLazyInitialization)) //NonFinalFieldByAnalysis)) + InterimResult( + state.field, + NoLazyInitialization, //MutableReference, //NonFinalFieldByAnalysis, + state.referenceImmutability, + state.dependees, + c + ) + else { + if (state.referenceImmutability == LazyInitialization && !state.isThreadSafeType) + Result(state.field, NotThreadSafeLazyInitialization) + 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 = { + val isNotFinal: Boolean = eps.pk match { + case EscapeProperty.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + checkMethod(method, newEP.ub.tac.get, pcs) + //case Callees.key => + // handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key ⇒ + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + isNonDeterministic(newEP) + case ReferenceImmutabilityLazyInitialization.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + !isImmutableReference(newEP) + } + + if (isNotFinal) { + Result(state.field, NoLazyInitialization) //Result(state.field, NonFinalFieldByAnalysis) + } else + createResult() + } + + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int], + pcs: PCs + )(implicit state: State): Boolean = { + println("PS: "+propertyStore(declaredMethods(method), Purity.key)) + val write = code(writeIndex).asFieldWriteAccessStmt + println("1") + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + ////return false; + state.isThreadSafeType = false + } + println("2") + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method + } + println("3") + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + case None ⇒ return false; + } + println("4") + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) //return false; + // possibly double checked locking + { + //TODO + if (isDoubleCheckedLocking(method, pcs)) { + state.isThreadSafeType = true; + return true; + } else + return false; + } + println("5") + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + println("6") + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + println("7") + true + } + + 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + def isDoubleCheckedLocking(method: Method, pcs: PCs)(implicit state: State): Boolean = { + val fieldAccessInformation = project.get(FieldAccessInformationKey) + var guards: List[(Int, Int)] = Nil + var assignments: List[Int] = Nil + var monitorEnter: Option[(Int, Option[ObjectType])] = None + var monitorExit: Option[(Int, Option[ObjectType])] = None + var result = true + val accessingPcs = fieldAccessInformation.allWriteAccesses + .filter(p ⇒ p._1 == state.field) + .head + ._2 + .filter(p ⇒ p._1 == method) + .head + ._2 + + if (state.field.fieldType.isObjectType && method.asMethod.isStatic) { + val tacCode = getTACAI(method, pcs) + tacCode match { + case Some(tac) ⇒ { + val hm = new mutable.HashMap[Int, Assignment[V]]() + //index for tac-code + var i: Int = -1 + tac.instructions.foreach(instr ⇒ { + i = i + 1 + if (instr.isIfStmt) { + val currentFieldsClassType = state.field.fieldType.asObjectType // method.classFile.thisType + val ifLeftType = instr.asIf.left.asVar.value.asReferenceValue.asReferenceType + if ( //is non null check + instr.asIf.condition == NE && + // has same type as field + ifLeftType.equals(currentFieldsClassType) //guards field? + ) { + // => guards the value + guards = (i, instr.asIf.target) :: guards + } + } + if (instr.isAssignment) { + hm += (i -> instr.asAssignment) + if (accessingPcs.contains(instr.pc)) + assignments = instr.pc.toInt :: assignments + } + + if (instr.isPutStatic && accessingPcs.contains(instr.pc)) { + assignments = i :: assignments + } + + if (instr.isMonitorEnter) { + val defB = instr.asMonitorEnter.objRef.asVar.definedBy.head + var objTypeOfMonitorEnter: Option[ObjectType] = None + try { + objTypeOfMonitorEnter = + Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) + } catch { + case _: Throwable ⇒ + } + monitorEnter = Some((i, objTypeOfMonitorEnter)) + hm.foreach(x ⇒ println(x)) + } + + if (instr.isMonitorExit) { + val defB = instr.asMonitorExit.objRef.asVar.definedBy.head + var objTypeOfMonitorExit: Option[ObjectType] = None + try { + objTypeOfMonitorExit = + Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) + } catch { + case _: Throwable ⇒ + } + monitorExit = Some((i, objTypeOfMonitorExit)) + } + }) + } + case _ ⇒ result = false + } + assignments.foreach(a ⇒ guards.foreach(g ⇒ result = result && a > g._1 && a < g._2)) + result = result && assignments.size >= 1 + result = result && monitorEnter != None && monitorExit != None + monitorEnter match { + case Some((n, Some(ot: ObjectType))) ⇒ + ot == state.field.fieldType.asObjectType && + //outer guard(s) + guards.filter(x ⇒ n > x._1).size >= 1 && + //inner guard(s) + guards.filter(x ⇒ n < x._1).size >= 1 + case None ⇒ result = false + case _ ⇒ result = false + } + monitorExit match { + case Some((n, Some(ot: ObjectType))) if (ot == state.field.fieldType.asObjectType) ⇒ + case None ⇒ result = false + case _ ⇒ result = false + } + result + } else + false + } + + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + println("51") + val definitions = write.value.asVar.definedBy + println("52") + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + println("53") + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) + println("54") + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + println("55") + // The field write must be guarded correctly + val r1 = isDeterministic + val r2 = checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + println("isDeterministic: "+r1) + println("checkWriteIsGuarded: "+r2) + //r1 && r2 //TODO check + if (!(r1 && r2) && !state.isThreadSafeType) { + //state.isThreadSafeType = false + true + } else + r1 && r2 + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID ⇒ + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID ⇒ + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ ⇒ return false; // neither a field read nor a return + } + index += 1 + } + false + } + + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + )) + result + } + + /** + * Analyzes field writes for a single method, returning false if the field may still be + * effectively final and true otherwise. + */ + def checkMethod( + method: Method, + taCode: TACode[TACMethodParameter, V], + pcs: PCs + )(implicit state: State): Boolean = { + val field = state.field + val stmts = taCode.stmts + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + + if (field.isStatic) { + if (method.isConstructor) + return true; + } else { + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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.referenceImmutability == NotThreadSafeLazyInitialization || state.referenceImmutability == LazyInitialization) //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; + + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + // A field written outside an initializer must be lazily + // initialized or it is non-final + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex, + pcs + )) + return true; + state.referenceImmutability = LazyInitialization //LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead + } + } + } + false + } + + /** + * 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 eps: InterimEP[Method, TACAI] ⇒ + * state.tacDependees += method → ((eps, pcs)) + * eps.ub.tac + * case epk ⇒ + * state.tacDependees += method → ((epk, pcs)) + * None + * } + * } + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) + } + } + } + } + + /** + * 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 = 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 + } + + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt ⇒ + stmt.astID == CaughtException.ASTID + } flatMap { exception ⇒ + exception.asCaughtException.origins.map { origin: Int ⇒ + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator + } + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement + + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + true + } + + /** + * 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]] + )(implicit state: State): Boolean = { + println("begin checkwriteisdeterministic") + def isConstant(uvar: Expr[V]): Boolean = { + val defSites = uvar.asVar.definedBy + + def isConstantDef(index: Int) = { + println("index: "+index) + if (index < 0) false + else if (code(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + println("expr: "+expr) + println("resolveField(p): "+expr.asFieldRead.resolveField(p)) + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + false + }) + } + } + + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) + } + + val value = origin.expr + + println("value.astID: "+value.astID) + + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID ⇒ + println("a") + value.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + println("b") + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + println("c") + false + } + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + println("d") + // 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.exists(!isConstant(_))) { + println("e") + false + } else { + println("f") + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true + } + case _ ⇒ + println("g") + // 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, code) + } + + value.isConst || isNonConstDeterministic + } + + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) + } + } + + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + true + } + + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode ⇒ + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) + } + result.toList + } + + /** + * 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. + */ + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID ⇒ + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + } + + None + } + + /** + * 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])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID ⇒ + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) + case _ ⇒ None + } + field.contains(state.field) + } + + /** + * 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]] + )(implicit state: State): Boolean = { + + /** + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + */ + def isDefaultConst(expr: Expr[V]): Boolean = { + println("Expression: "+expr) + 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } + + /** + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. + */ + def isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index ⇒ + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } + } + + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else //TODO check + //false + { + + //TODO if expression = Nullexpr + println("----------------------------------------<<<<") + state.isThreadSafeType = false + true //TODO check + } + } + + /** + * Checks if 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) ⇒ true + case eps ⇒ + state.prematurelyReadDependee = Some(eps) + false + } + + /** + * Checkes 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. + */ + 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 + } + + /** + * Checkes if 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, ReferenceImmutability] + )(implicit state: State): Boolean = eop match { + case FinalEP(e, ImmutableReference) ⇒ true + case FinalEP(e, MutableReference) ⇒ false + // + case LBP(ImmutableReference) ⇒ true + case UBP(MutableReference) ⇒ false + + /** + * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ + * true + * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * + */ + case _ ⇒ + state.referenceImmutabilityDependees += eop + true + } +} + +trait L0ReferenceImmutabilityLazyInitializationAnalysisScheduler extends FPCFAnalysisScheduler { + + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(ReferenceImmutabilityLazyInitialization), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.ub(EscapeProperty) + ) + + final def derivedProperty: PropertyBounds = + PropertyBounds.lub(ReferenceImmutabilityLazyInitialization) + +} + +/** + * Executor for the field mutability analysis. + */ +object EagerL0ReferenceImmutabilityLazyInitializationAnalysis + extends L0ReferenceImmutabilityLazyInitializationAnalysisScheduler + with BasicFPCFEagerAnalysisScheduler { + + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)( + analysis.determineReferenceImmutabilityLazyInitialization + ) + analysis + } + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty +} + +/** + * Executor for the lazy field mutability analysis. + */ +object LazyL0ReferenceImmutabilityLazyInitializationAnalysis + extends L0ReferenceImmutabilityLazyInitializationAnalysisScheduler + with BasicFPCFLazyAnalysisScheduler { + + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) + ps.registerLazyPropertyComputation( + ReferenceImmutabilityLazyInitialization.key, + analysis.determineReferenceImmutabilityLazyInitialization + ) + analysis + } + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) +} From e9851d90a2fd122dd2dfe210969b96319d95fc7d Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 16 Jan 2020 20:02:36 +0100 Subject: [PATCH 037/327] double checked locking tests improvements. Now all should run --- .../SimpleLazyInstantiation.java | 10 +++++----- .../SimpleLazyObjectsInstantiation.java | 4 ++-- .../LazyInitializationAnnotation.java | 4 +--- .../ReferenceImmutabilityLazyInitializationTests.scala | 2 -- .../fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala | 2 +- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java index f92be000bb..aabf03c0bb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java @@ -4,11 +4,11 @@ public class SimpleLazyInstantiation{ @NotThreadSafeLazyInitializationAnnotation("") - private SimpleLazyInstantiation instance; + private static SimpleLazyInstantiation instance; - public SimpleLazyInstantiation init() { - SimpleLazyInstantiation result; - result = instance == null ? new SimpleLazyInstantiation() : instance; - return result; + public static SimpleLazyInstantiation init() { + if(instance==null) + instance = new SimpleLazyInstantiation(); + return instance; } } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java index f8eafab1b3..a53170923b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java @@ -4,8 +4,8 @@ public class SimpleLazyObjectsInstantiation{ @NotThreadSafeLazyInitializationAnnotation("") - private SimpleLazyObjectsInstantiation instance; - public SimpleLazyObjectsInstantiation getInstance() { + private static SimpleLazyObjectsInstantiation instance; + public static SimpleLazyObjectsInstantiation getInstance() { if(instance==null) instance = new SimpleLazyObjectsInstantiation(); return instance; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java index bcd4e1dcaf..cd5dbd96cb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java @@ -3,8 +3,6 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceMatcher; -import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; import java.lang.annotation.Documented; @@ -16,7 +14,7 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = LazyInitializedReferenceMatcher.class) +@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = LazyInitializationMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface LazyInitializationAnnotation { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala index 53723d03b7..4d23a7d223 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala @@ -9,7 +9,6 @@ import org.opalj.br.analyses.Project import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis @@ -40,7 +39,6 @@ class ReferenceImmutabilityLazyInitializationTests extends PropertiesTest { val as = executeAnalyses( Set( EagerL0ReferenceImmutabilityLazyInitializationAnalysis, - LazyL0ReferenceImmutabilityAnalysis, LazyL2FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index cc122f9762..ec5d976859 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -56,7 +56,7 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC e: Entity ): ProperPropertyComputationResult = e match { case t: ObjectType => step1(typeExtensibility)(t) - case _ => throw new IllegalArgumentException(s"$e is not an ObjectType") + case _ => throw new IllegalArgumentException(s"$e is not an ObjectType") } /** From 3f434b61f7e417f5b28c65b6e624db117b403317 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Mon, 20 Jan 2020 10:37:18 +0100 Subject: [PATCH 038/327] state before merging with current dev branch. Including improvements for running on jdk. --- .../analyses/ClassImmutabilityAnalysis.scala | 4 +- .../src/main/scala/org/opalj/tac/Stmt.scala | 14 ++-- .../L0FieldImmutabilityAnalysis.scala | 64 +++++++++++-------- .../L0ReferenceImmutabilityAnalysis.scala | 22 ++++++- .../LxTypeImmutabilityAnalysis_new.scala | 3 +- 5 files changed, 67 insertions(+), 40 deletions(-) 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/ClassImmutabilityAnalysis.scala index 07c2d07b91..aba6712bc7 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/ClassImmutabilityAnalysis.scala @@ -219,7 +219,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability = superClassInformation match { case UBP(ImmutableContainer) => ImmutableContainer - case _ => ImmutableObject + case _ => ImmutableObject } if (cf.fields.exists(f => !f.isStatic && f.fieldType.isArrayType)) { @@ -463,7 +463,7 @@ trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { .map(ot => (ot, project.classFile(ot))) .foreach { case (_, Some(cf)) => cfs ::= cf - case (t, None) => + 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 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 a5e375ac93..f2d0421a8a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala @@ -350,7 +350,7 @@ object AssignmentLikeStmt { def unapply[V <: Var[V]](stmt: Stmt[V]): Option[(PC, Expr[V])] = { stmt match { case s: AssignmentLikeStmt[V] => Some((s.pc, s.expr)) - case _ => None + case _ => None } } @@ -1080,9 +1080,9 @@ object VirtualFunctionCallStatement { def unapply[V <: Var[V]](stmt: Stmt[V]): Option[VirtualFunctionCall[V]] = { stmt match { - case ExprStmt(_, vfc: VirtualFunctionCall[V]) => Some(vfc) + case ExprStmt(_, vfc: VirtualFunctionCall[V]) => Some(vfc) case Assignment(_, _, vfc: VirtualFunctionCall[V]) => Some(vfc) - case _ => None + case _ => None } } } @@ -1095,9 +1095,9 @@ object NonVirtualFunctionCallStatement { def unapply[V <: Var[V]](stmt: Stmt[V]): Option[NonVirtualFunctionCall[V]] = { stmt match { - case ExprStmt(_, vfc: NonVirtualFunctionCall[V]) => Some(vfc) + case ExprStmt(_, vfc: NonVirtualFunctionCall[V]) => Some(vfc) case Assignment(_, _, vfc: NonVirtualFunctionCall[V]) => Some(vfc) - case _ => None + case _ => None } } } @@ -1110,9 +1110,9 @@ object StaticFunctionCallStatement { def unapply[V <: Var[V]](stmt: Stmt[V]): Option[StaticFunctionCall[V]] = { stmt match { - case ExprStmt(_, vfc: StaticFunctionCall[V]) => Some(vfc) + case ExprStmt(_, vfc: StaticFunctionCall[V]) => Some(vfc) case Assignment(_, _, vfc: StaticFunctionCall[V]) => Some(vfc) - case _ => None + case _ => None } } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 82fe3670d2..1eff352c8a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -90,34 +90,39 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) "determine generic field imm-------------------------------------------------------------------" ) var genericFields: Set[ObjectType] = Set.empty - state.field.fieldTypeSignature.head match { - case ClassTypeSignature(_, SimpleClassTypeSignature(name, typeArguments), _) => { - typeArguments.foreach( - ta => { - ta match { - case ProperTypeArgument( - varianceIndicator, - ClassTypeSignature( - packageIdentifier1, - SimpleClassTypeSignature( - packageIdentifier2, - typeArguments2 - ), - _ - ) - ) => { - packageIdentifier1 match { - case Some(pid1) => genericFields += ObjectType(pid1 + packageIdentifier2) - case _ => genericFields += ObjectType(packageIdentifier2) - } + state.field.fieldTypeSignature match { + case Some(fts) => + //} + //state.field.fieldTypeSignature.head + fts match { + case ClassTypeSignature(_, SimpleClassTypeSignature(name, typeArguments), _) => { + typeArguments.foreach( + ta => { + ta match { + case ProperTypeArgument( + varianceIndicator, + ClassTypeSignature( + packageIdentifier1, + SimpleClassTypeSignature( + packageIdentifier2, + typeArguments2 + ), + _ + ) + ) => { + packageIdentifier1 match { + case Some(pid1) => genericFields += ObjectType(pid1 + packageIdentifier2) + case _ => genericFields += ObjectType(packageIdentifier2) + } + } + case _ => + } } - case _ => - } - + ) } - ) - } + case _ => + } case _ => } genericFields.foreach(f => println("generic Field: " + f)) @@ -152,7 +157,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) //--------------------------------------------------------------------------------------- state.typeImmutability = determineGenericFieldImmutability(state) //--------------------------------------------------------------------------------------- - //state.dependentTypeImmutability = Some(true) + //statfielde.dependentTypeImmutability = Some(true) return state.typeImmutability; } @@ -163,6 +168,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) return Some(false); } case x @ _ => { + println("x: " + x) dependencies += x return None; //TODO check!!!!! None; } @@ -277,6 +283,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case None if (dependencies.isEmpty) => Result(field, MutableField) case None => { + println("last step: ") + println("field: " + field) + println("MutableField: " + MutableField) + println("DeepImmutableField: " + DeepImmutableField) + println("dependencies: " + dependencies) InterimResult( field, MutableField, @@ -285,7 +296,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) c(state) ) } - } } def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index 95da9cdc5d..f6cf200319 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -42,6 +42,7 @@ import org.opalj.br.fpcf.properties.EscapeInCallee import org.opalj.br.fpcf.properties.EscapeViaReturn import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.LazyInitializedField import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.NoEscape import org.opalj.br.fpcf.properties.NotPrematurelyReadField @@ -386,9 +387,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec !isImmutableReference(newEP) } - if (isNotFinal) + if (isNotFinal) { Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) - else + } else createResult() } @@ -411,6 +412,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec cfg: CFG[Stmt[V], TACStmts[V]], pcToIndex: Array[Int] )(implicit state: State): Boolean = { + println("PS: " + propertyStore(declaredMethods(method), Purity.key)) val write = code(writeIndex).asFieldWriteAccessStmt if (state.field.fieldType.computationalType != ComputationalTypeInt && @@ -574,8 +576,21 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (defaultValue.isEmpty) return true; + //TODO Lazy Initialization here + /** + * val liResult = propertyStore(field, ReferenceImmutabilityLazyInitialization.key) + * + * liResult match { + * case FinalP(NoLazyInitialization) => return true; + * case FinalP(NotThreadSafeLazyInitialization) => return true; + * case FinalP(LazyInitialization) => + * state.referenceImmutability = LazyInitializedReference + * case _ => return true; + * } + */ // A field written outside an initializer must be lazily // initialized or it is non-final + if (!isLazyInitialization( index, defaultValue.get, @@ -586,7 +601,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec )) return true; - state.referenceImmutability = LazyInitializedReference //LazyInitializedField + state.referenceImmutability = LazyInitializedReference + LazyInitializedField } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { // note that here we assume real three address code (flat hierarchy) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index ec5d976859..09fef956d2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -226,7 +226,8 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC maxImmutability = ShallowImmutableType //ImmutableContainerType dependencies = dependencies - e nextResult() - case FinalEP(e, x) if (x == DependentImmutableClass || x == DependentImmutableType) => { + case FinalEP(e, DependentImmutableClass(_) | DependentImmutableType) => { + //if (x == DependentImmutableClass() || x == DependentImmutableType) => { maxImmutability = DependentImmutableType dependencies = dependencies - e nextResult() From ebb296fdfbe39be5b6be7f0e044be029142b2cf8 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Mon, 20 Jan 2020 13:20:30 +0100 Subject: [PATCH 039/327] Fixed toString --- OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala index 60d5514b6d..0752dd1cd4 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala @@ -75,16 +75,14 @@ class YAPPS( override def toString(printProperties: Boolean): String = if (printProperties) { - var pkId = 0 - while (pkId <= PropertyKey.maxId) { + val properties = for (pkId ← 0 to PropertyKey.maxId) yield { var entities: List[String] = List.empty ps(pkId).forEachValue(Long.MaxValue, { state: YappsEPKState ⇒ entities ::= state.eOptP.toString.replace("\n", "\n\t") }) entities.sorted.mkString(s"Entities for property key $pkId:\n\t", "\n\t", "\n") - pkId += 1 } - ps.mkString("PropertyStore(\n\t", "\n\t", "\n)") + properties.mkString("PropertyStore(\n\t", "\n\t", "\n)") } else { s"PropertyStore(properties=${ps.iterator.map(_.size).sum})" } @@ -771,7 +769,7 @@ class YAPPS( dependersLock.lockInterruptibly() val theDependers = dependers // Clear all dependers that will be notified, they will re-register if required - dependers = dependers.filter(d => suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) + dependers = dependers.filter(d ⇒ suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) dependersLock.unlock() notifyDependers(interimEP, theEOptP, theDependers) } From af68e2a0262e8fc61a781098ce3719ce4c00fb14 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Mon, 20 Jan 2020 15:34:45 +0100 Subject: [PATCH 040/327] ref imm analyis improvements --- .../L0ReferenceImmutabilityAnalysis.scala | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index 95da9cdc5d..f6cf200319 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -42,6 +42,7 @@ import org.opalj.br.fpcf.properties.EscapeInCallee import org.opalj.br.fpcf.properties.EscapeViaReturn import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.LazyInitializedField import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.NoEscape import org.opalj.br.fpcf.properties.NotPrematurelyReadField @@ -386,9 +387,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec !isImmutableReference(newEP) } - if (isNotFinal) + if (isNotFinal) { Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) - else + } else createResult() } @@ -411,6 +412,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec cfg: CFG[Stmt[V], TACStmts[V]], pcToIndex: Array[Int] )(implicit state: State): Boolean = { + println("PS: " + propertyStore(declaredMethods(method), Purity.key)) val write = code(writeIndex).asFieldWriteAccessStmt if (state.field.fieldType.computationalType != ComputationalTypeInt && @@ -574,8 +576,21 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (defaultValue.isEmpty) return true; + //TODO Lazy Initialization here + /** + * val liResult = propertyStore(field, ReferenceImmutabilityLazyInitialization.key) + * + * liResult match { + * case FinalP(NoLazyInitialization) => return true; + * case FinalP(NotThreadSafeLazyInitialization) => return true; + * case FinalP(LazyInitialization) => + * state.referenceImmutability = LazyInitializedReference + * case _ => return true; + * } + */ // A field written outside an initializer must be lazily // initialized or it is non-final + if (!isLazyInitialization( index, defaultValue.get, @@ -586,7 +601,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec )) return true; - state.referenceImmutability = LazyInitializedReference //LazyInitializedField + state.referenceImmutability = LazyInitializedReference + LazyInitializedField } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { // note that here we assume real three address code (flat hierarchy) From 77f133194c5f031c64c2e74fb025362d6ddf055e Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 28 Jan 2020 16:26:22 +0100 Subject: [PATCH 041/327] Improving methods guaranteeing the ordering in the type immutability lattice --- .../properties/TypeImmutability_new.scala | 153 +++++++++--------- 1 file changed, 75 insertions(+), 78 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala index 6b43e42c51..68d4a303b6 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -8,7 +8,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait TypeImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = TypeImmutability_new + final type Self = TypeImmutability_new } /** @@ -29,19 +29,19 @@ sealed trait TypeImmutability_new extends OrderedProperty with TypeImmutabilityPropertyMetaInformation_new { - /** - * Returns the key used by all `TypeImmutability_new` properties. - */ - final def key = TypeImmutability_new.key + /** + * Returns the key used by all `TypeImmutability_new` properties. + */ + final def key = TypeImmutability_new.key - def isDeepImmutable: Boolean - def isShallowImmutable: Boolean - def isDependentImmutable: Boolean + def isDeepImmutable: Boolean + def isShallowImmutable: Boolean + def isDependentImmutable: Boolean - /** `true` if the immutability is unknown or if the type is mutable.*/ - def isMutable: Boolean + /** `true` if the immutability is unknown or if the type is mutable.*/ + def isMutable: Boolean - def meet(other: TypeImmutability_new): TypeImmutability_new + def meet(other: TypeImmutability_new): TypeImmutability_new } /** @@ -49,13 +49,13 @@ sealed trait TypeImmutability_new */ object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[TypeImmutability_new]] property. - */ - final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( - "org.opalj.TypeImmutability_new", - MutableType_new - ) + /** + * The key associated with every [[TypeImmutability_new]] property. + */ + final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( + "org.opalj.TypeImmutability_new", + MutableType_new + ) } /** @@ -64,79 +64,76 @@ object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new */ case object DeepImmutableType extends TypeImmutability_new { - override def isDeepImmutable: Boolean = true - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = false - override def isDependentImmutable: 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 = {} + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (this == that) - this - else - that + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (this == that) + this + else + that } -case object ShallowImmutableType extends TypeImmutability_new { +case object DependentImmutableType extends TypeImmutability_new { + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = true + + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (that == MutableType_new || 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"); + } + } - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = true - override def isMutable: Boolean = false - override def isDependentImmutable: Boolean = false - - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (that == MutableType_new) - 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 DependentImmutableType extends TypeImmutability_new { - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = false - override def isDependentImmutable: Boolean = true - - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (that == MutableType_new) - that - else - this - - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - /** - * if (other == DependentImmutableType || other == ShallowImmutableType) { - * throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - * } - * }* - */ +case object ShallowImmutableType extends TypeImmutability_new { + + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = true + override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = false + + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (that == MutableType_new) + that + else + this + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + + if (other == DeepImmutableType || other == DependentImmutableType) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } + } } case object MutableType_new extends TypeImmutability_new { - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = true - override def isDependentImmutable: 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_new): this.type = this - def meet(other: TypeImmutability_new): this.type = this + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - /** - * if (other != MutableType_new) { - * throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - * } - * }* - */ + if (other != MutableType_new) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } + } } From d8c28f4c102aa62e9a929cf7ae44b5e49e2de04a Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 28 Jan 2020 17:00:12 +0100 Subject: [PATCH 042/327] prepare analysis for running on jdk --- .../L0FieldImmutabilityAnalysis.scala | 58 +----- .../L0ReferenceImmutabilityAnalysis.scala | 1 - ...mutabilityLazyInitializationAnalysis.scala | 168 ++++++++++-------- .../LxClassImmutabilityAnalysis_new.scala | 18 +- 4 files changed, 107 insertions(+), 138 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 1eff352c8a..d115616740 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -43,7 +43,6 @@ import org.opalj.fpcf.PropertyComputationResult import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS -import org.opalj.tac.fpcf.properties.TACAI case class State(f: Field) { var field: Field = f @@ -86,14 +85,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var dependencies: Set[EOptionP[Entity, Property]] = Set.empty def determineGenericFieldImmutability(state: State): Option[Boolean] = { - println( - "determine generic field imm-------------------------------------------------------------------" - ) var genericFields: Set[ObjectType] = Set.empty state.field.fieldTypeSignature match { case Some(fts) => - //} - //state.field.fieldTypeSignature.head fts match { case ClassTypeSignature(_, SimpleClassTypeSignature(name, typeArguments), _) => { typeArguments.foreach( @@ -112,7 +106,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) => { packageIdentifier1 match { case Some(pid1) => genericFields += ObjectType(pid1 + packageIdentifier2) - case _ => genericFields += ObjectType(packageIdentifier2) + case _ => genericFields += ObjectType(packageIdentifier2) } } @@ -125,9 +119,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case _ => } - genericFields.foreach(f => println("generic Field: " + f)) - //state.typeImmutability = Some(true) - genericFields.toList.foreach(objectType => { val result = propertyStore(objectType, TypeImmutability_new.key) result match { @@ -147,7 +138,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (objectType.isArrayType) return Some(true); //TODO if (objectType.isBaseType) return Some(true); val result = propertyStore(objectType, TypeImmutability_new.key) - println("Result: " + result) result match { case FinalEP(e, DeepImmutableType) => { return Some(true); @@ -168,7 +158,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) return Some(false); } case x @ _ => { - println("x: " + x) dependencies += x return None; //TODO check!!!!! None; } @@ -176,27 +165,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } def hasImmutableType(field: Field)(state: State): Option[Boolean] = { - //val hasFieldImmutableType = handleTypeImmutability(field.fieldType.asFieldType)(state) - - /** - * hasFieldImmutableType match { - * case Some(false) => return Some(false); - * case _ => { - * state.depencenciesTypes.foreach( - * t => { - * val isImmutable = handleTypeImmutability(t)(state) - * isImmutable match { - * case Some(false) => return Some(false); - * case _ => - * } - * } - * ) - * Some(true) - * } - * } * - */ - } def hasImmutableReference(field: Field): Option[Boolean] = { @@ -222,8 +191,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(true) => { //If the field type is object. It is a generic field //Test Dependent... //TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //var genericString: Option[String] = None //if (field.fieldType == ObjectType("java/lang/Object")) { - println("field attributes: " + field.asField.attributes) + //++ println("field attributes: "+field.asField.attributes) val genericString = field.asField.attributes.toList.collectFirst({ case TypeVariableSignature(t) => t case ClassTypeSignature( @@ -237,7 +207,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case ProperTypeArgument(variance, signature) => { signature match { case TypeVariableSignature(identifier) => true - case _ => false + case _ => false } } case _ => false @@ -245,28 +215,21 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) }) if (tA.size > 0) tA.head match { - case ProperTypeArgument(variance, TypeVariableSignature(identifier)) => identifier + case ProperTypeArgument(variance, TypeVariableSignature(identifier)) => + identifier case _ => "" } else "" - } }) if (!field.attributes.isEmpty && genericString != None && genericString != Some("")) { - - //if(genericString!=None) - // println("Generic String: "+genericString) - // println( - // "test: "+DependentImmutableField(genericString).genericString - // } Result(field, DependentImmutableField(genericString)) - } else state.typeImmutability match { case Some(true) => Result(field, DeepImmutableField) case Some(false) => { state.dependentTypeImmutability match { case Some(true) => Result(field, DependentImmutableField()) - case _ => Result(field, ShallowImmutableField) + case _ => Result(field, ShallowImmutableField) } } case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) @@ -283,11 +246,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case None if (dependencies.isEmpty) => Result(field, MutableField) case None => { - println("last step: ") - println("field: " + field) - println("MutableField: " + MutableField) - println("DeepImmutableField: " + DeepImmutableField) - println("dependencies: " + dependencies) InterimResult( field, MutableField, @@ -335,7 +293,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) createResult(state) } - //-- state.depencenciesTypes = scala.collection.mutable.Set[ObjectType]() state.referenceImmutability = hasImmutableReference(field) state.typeImmutability = hasImmutableType(field)(state); @@ -347,7 +304,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.ub(TACAI), PropertyBounds.lub(ReferenceImmutability), PropertyBounds.lub(TypeImmutability_new), PropertyBounds.lub(FieldImmutability) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index f6cf200319..03bfaecc4b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -412,7 +412,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec cfg: CFG[Stmt[V], TACStmts[V]], pcToIndex: Array[Int] )(implicit state: State): Boolean = { - println("PS: " + propertyStore(declaredMethods(method), Purity.key)) val write = code(writeIndex).asFieldWriteAccessStmt if (state.field.fieldType.computationalType != ComputationalTypeInt && diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala index 11b5112836..3e33cbeb0f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala @@ -34,6 +34,7 @@ import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.PC import org.opalj.br.PCs +import org.opalj.br.ReferenceType import org.opalj.br.fpcf.properties.AtMost import org.opalj.br.fpcf.properties.EscapeInCallee import org.opalj.br.fpcf.properties.EscapeViaReturn @@ -443,9 +444,9 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p pcToIndex: Array[Int], pcs: PCs )(implicit state: State): Boolean = { - println("PS: "+propertyStore(declaredMethods(method), Purity.key)) + //xx println("PS: "+propertyStore(declaredMethods(method), Purity.key)) val write = code(writeIndex).asFieldWriteAccessStmt - println("1") + //xx println("1") if (state.field.fieldType.computationalType != ComputationalTypeInt && state.field.fieldType.computationalType != ComputationalTypeFloat) { // Only handle lazy initialization of ints and floats as they are guaranteed to be @@ -453,12 +454,12 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p ////return false; state.isThreadSafeType = false } - println("2") + //xx println("2") val reads = fieldAccessInformation.readAccesses(state.field) if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { return false; // Reads outside the (single) lazy initialization method } - println("3") + //xx println("3") // There must be a guarding if-Statement // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the // first statement that is executed after the if-Statement if the field's value was not the @@ -468,7 +469,7 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p case Some((guard, guarded, read)) ⇒ (guard, guarded, read) case None ⇒ return false; } - println("4") + //xx println("4") // Detect only simple patterns where the lazily initialized value is returned immediately if (!checkImmediateReturn(write, writeIndex, readIndex, code)) //return false; // possibly double checked locking @@ -480,16 +481,16 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p } else return false; } - println("5") + //xx println("5") // The value written must be computed deterministically and the writes guarded correctly if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) return false; - println("6") + //xx println("6") // Field reads (except for the guard) may only be executed if the field's value is not the // default value if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) return false; - println("7") + //xx println("7") true } @@ -531,55 +532,67 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p val hm = new mutable.HashMap[Int, Assignment[V]]() //index for tac-code var i: Int = -1 - tac.instructions.foreach(instr ⇒ { - i = i + 1 - if (instr.isIfStmt) { - val currentFieldsClassType = state.field.fieldType.asObjectType // method.classFile.thisType - val ifLeftType = instr.asIf.left.asVar.value.asReferenceValue.asReferenceType - if ( //is non null check - instr.asIf.condition == NE && - // has same type as field - ifLeftType.equals(currentFieldsClassType) //guards field? - ) { - // => guards the value - guards = (i, instr.asIf.target) :: guards + if (tac != null && tac.instructions != null) + tac.instructions.foreach(instr ⇒ { + i = i + 1 + if (instr.isIfStmt) { + var currentFieldsClassType: ObjectType = null + if (state.field.fieldType.isObjectType) + currentFieldsClassType = state.field.fieldType.asObjectType // method.classFile.thisType + var ifLeftType: ReferenceType = null + if (instr.asIf.left.isVar && instr.asIf.left.asVar.value.isReferenceValue) // && instr.asIf.left.asVar.value.asReferenceValue) + try { + ifLeftType = instr.asIf.left.asVar.value.asReferenceValue.asReferenceType + } catch { + case _: Throwable ⇒ + } + + if ( //is non null check + instr.asIf.condition == NE && + // has same type as field + currentFieldsClassType != null && + ifLeftType != null && + ifLeftType.equals(currentFieldsClassType) //guards field? + ) { + // => guards the value + guards = (i, instr.asIf.target) :: guards + } + } + if (instr.isAssignment) { + hm += (i -> instr.asAssignment) + if (accessingPcs.contains(instr.pc)) + assignments = instr.pc.toInt :: assignments } - } - if (instr.isAssignment) { - hm += (i -> instr.asAssignment) - if (accessingPcs.contains(instr.pc)) - assignments = instr.pc.toInt :: assignments - } - if (instr.isPutStatic && accessingPcs.contains(instr.pc)) { - assignments = i :: assignments - } + if (instr.isPutStatic && accessingPcs.contains(instr.pc)) { + assignments = i :: assignments + } - if (instr.isMonitorEnter) { - val defB = instr.asMonitorEnter.objRef.asVar.definedBy.head - var objTypeOfMonitorEnter: Option[ObjectType] = None - try { - objTypeOfMonitorEnter = - Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) - } catch { - case _: Throwable ⇒ + if (instr.isMonitorEnter) { + val defB = instr.asMonitorEnter.objRef.asVar.definedBy.head + var objTypeOfMonitorEnter: Option[ObjectType] = None + try { + objTypeOfMonitorEnter = + Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) + } catch { + case _: Throwable ⇒ + } + monitorEnter = Some((i, objTypeOfMonitorEnter)) + //hm.foreach(x ⇒ println(x)) } - monitorEnter = Some((i, objTypeOfMonitorEnter)) - hm.foreach(x ⇒ println(x)) - } - if (instr.isMonitorExit) { - val defB = instr.asMonitorExit.objRef.asVar.definedBy.head - var objTypeOfMonitorExit: Option[ObjectType] = None - try { - objTypeOfMonitorExit = - Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) - } catch { - case _: Throwable ⇒ + if (instr.isMonitorExit) { + val defB = instr.asMonitorExit.objRef.asVar.definedBy.head + var objTypeOfMonitorExit: Option[ObjectType] = None + try { + objTypeOfMonitorExit = + Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) + } catch { + case _: Throwable ⇒ + } + monitorExit = Some((i, objTypeOfMonitorExit)) } - monitorExit = Some((i, objTypeOfMonitorExit)) - } - }) + }) } case _ ⇒ result = false } @@ -588,7 +601,8 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p result = result && monitorEnter != None && monitorExit != None monitorEnter match { case Some((n, Some(ot: ObjectType))) ⇒ - ot == state.field.fieldType.asObjectType && + result = result && + ot == state.field.fieldType.asObjectType && //outer guard(s) guards.filter(x ⇒ n > x._1).size >= 1 && //inner guard(s) @@ -615,28 +629,28 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]] )(implicit state: State): Boolean = { - println("51") + //xx println("51") val definitions = write.value.asVar.definedBy - println("52") + //xx println("52") val isDeterministic = if (definitions.size == 1) { // The value written must be computed deterministically - println("53") + //xx println("53") checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) } else { // More than one definition site for the value might lead to differences between // invocations, but not if this method has no parameters and is deterministic // (in this case, the definition reaching the write will always be the same) - println("54") + //xx println("54") method.descriptor.parametersCount == 0 && !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) } - println("55") + //xx println("55") // The field write must be guarded correctly val r1 = isDeterministic val r2 = checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - println("isDeterministic: "+r1) - println("checkWriteIsGuarded: "+r2) + //xx println("isDeterministic: "+r1) + //xx println("checkWriteIsGuarded: "+r2) //r1 && r2 //TODO check if (!(r1 && r2) && !state.isThreadSafeType) { //state.isThreadSafeType = false @@ -924,18 +938,18 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p method: Method, code: Array[Stmt[V]] )(implicit state: State): Boolean = { - println("begin checkwriteisdeterministic") + //xx println("begin checkwriteisdeterministic") def isConstant(uvar: Expr[V]): Boolean = { val defSites = uvar.asVar.definedBy def isConstantDef(index: Int) = { - println("index: "+index) + //xx println("index: "+index) if (index < 0) false else if (code(defSites.head).asAssignment.expr.isConst) true else { val expr = code(index).asAssignment.expr - println("expr: "+expr) - println("resolveField(p): "+expr.asFieldRead.resolveField(p)) + //xx println("expr: "+expr) + //println("resolveField(p): " + expr.asFieldRead.resolveField(p)) expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { case Some(field) ⇒ isImmutableReference(propertyStore(field, ReferenceImmutability.key)) @@ -951,35 +965,35 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p val value = origin.expr - println("value.astID: "+value.astID) + //xx println("value.astID: "+value.astID) val isNonConstDeterministic = value.astID match { case GetStatic.ASTID | GetField.ASTID ⇒ - println("a") + //xx println("a") value.asFieldRead.resolveField(p) match { case Some(field) ⇒ - println("b") + //xx println("b") isImmutableReference(propertyStore(field, ReferenceImmutability.key)) case _ ⇒ // Unknown field - println("c") + //xx println("c") false } case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ - println("d") + //xx println("d") // 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.exists(!isConstant(_))) { - println("e") + //xx println("e") false } else { - println("f") + //xx println("f") state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) true } case _ ⇒ - println("g") + //xx println("g") // 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. @@ -1133,7 +1147,9 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p val ifStmt = code(result.get._1).asIf val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + var fieldReadUses: IntTrieSet = IntTrieSet.empty + if (definitions.head >= 0 && code(definitions.head).isAssignment) + fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ use == result.get._1 || use == result.get._2 } @@ -1174,8 +1190,10 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p * Checks if an expression is an IntConst or FloatConst with the corresponding default value. */ def isDefaultConst(expr: Expr[V]): Boolean = { - println("Expression: "+expr) - if (expr.isVar) { + //xx println("Expression: "+expr) + if (expr.isNullExpr) + true //-- + else if (expr.isVar) { val defSites = expr.asVar.definedBy val head = defSites.head defSites.size == 1 && head >= 0 && isDefaultConst(code(head).asAssignment.expr) @@ -1205,7 +1223,7 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p { //TODO if expression = Nullexpr - println("----------------------------------------<<<<") + //xx println("----------------------------------------<<<<") state.isThreadSafeType = false true //TODO check } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 264e39b48b..715084a6e6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -46,7 +46,6 @@ 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.br.fpcf.properties.TypeImmutability_new /** * @@ -151,9 +150,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal }) }) - //genericTypeBounds.toList.foreach({ x ⇒ - // println("Bound: "+x) - //}) genericTypeBounds } @@ -284,7 +280,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case _ => DeepImmutableClass // ImmutableObject } - if (!dependentImmutableFields.isEmpty) { + if (!dependentImmutableFields.isEmpty && maxLocalImmutability != ShallowImmutableClass) { maxLocalImmutability = DependentImmutableClass() } @@ -331,7 +327,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case UBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container if (someEPS.isFinal) dependees -= SuperClassKey maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) //TypeImmutability.key) + //dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) //TypeImmutability.key) case LBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && @@ -365,8 +361,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case ELBP(e, ShallowImmutableField | DeepImmutableField) => //FinalField) => dependees -= e - if (minLocalImmutability != ShallowImmutableClass && // ImmutableContainer && - !dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) + if (minLocalImmutability != ShallowImmutableClass) // && // ImmutableContainer && + //!dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible case UBP(ShallowImmutableField | DeepImmutableField) => //_: FinalField) ⇒ // no information about field mutability @@ -388,8 +384,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal */ // Lift lower bound once no dependencies other than field type mutabilities are left - if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && - dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) + if (minLocalImmutability != ShallowImmutableClass) // && //ImmutableContainer && + //dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) minLocalImmutability = ShallowImmutableClass //ImmutableContainer if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { /*[DEBUG] @@ -441,7 +437,7 @@ trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new, FieldImmutability) //TypeImmutability, FieldImmutability) + PropertyBounds.lubs(ClassImmutability_new, FieldImmutability) //TypeImmutability, FieldImmutability) override type InitializationData = TraversableOnce[ClassFile] From c4f2668182e10167577cda5b161c14f704b16d97 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 28 Jan 2020 17:05:48 +0100 Subject: [PATCH 043/327] introducing performance measurements for evaluation purposes --- .../ClassImmutabilityAnalysisDemo.scala | 103 ++++++--- ...AnalysisDemo_performanceMeasurements.scala | 103 +++++++++ .../FieldImmutabilityAnalysisDemo.scala | 92 ++++++--- ...AnalysisDemo_performanceMeasurements.scala | 100 +++++++++ .../analyses/ImmutabilityAnalysisDemo.scala | 66 +++--- .../ImmutabilityAnalysisDemo_new.scala | 195 ++++++++++++++++++ .../ReferenceImmutabilityAnalysisDemo.scala | 110 ++++++---- ...AnalysisDemo_performanceMeasurements.scala | 97 +++++++++ ...bilityLazyInitializationAnalysisDemo.scala | 136 +++++++++--- ...AnalysisDemo_performanceMeasurements.scala | 117 +++++++++++ .../TypeImmutabilityAnalysisDemo.scala | 117 +++++++---- ...yAnalysisDemo_performanceMeasurement.scala | 105 ++++++++++ 12 files changed, 1147 insertions(+), 194 deletions(-) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala 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 index a5c34b7557..2918e919df 100644 --- 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 @@ -1,7 +1,11 @@ /* 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.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new import org.opalj.br.analyses.BasicReport @@ -10,16 +14,26 @@ import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ClassImmutability_new 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.fpcf.FinalEP +import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis; +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -47,33 +61,68 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { analysesManager.project.get(RTACallGraphKey) - val (propertyStore, _) = analysesManager.runAll( - //LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyStaticDataUsageAnalysis, + LazySimpleEscapeAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds + } + + val sb = new StringBuilder + sb.append("Mutable Class: \n") + sb.append( + propertyStore + .finalEntities(MutableClass) + .toList + .map(x => x.toString + "\n") + ) + sb.append("\nDependent Immutable Class: \n") + sb.append( + propertyStore + .entities(ClassImmutability_new.key) + .toList + .collect({ case x @ FinalEP(_, DependentImmutableClass(_)) => x }) + .map(x => x.toString + "\n") + ) + sb.append("\nShallow Immutable Class: \n") + sb.append( + propertyStore + .finalEntities(ShallowImmutableClass) + .toList + .map(x => x.toString + "\n") ) + sb.append("\nDeep Immutable Class: \n") + sb.append( + propertyStore + .finalEntities(DeepImmutableClass) + .toList + .map(x => x.toString + "\n") + ) + + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/classImm" + dateString + ".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() - "Mutable Class: " + propertyStore - .finalEntities(MutableClass) - .toList - .toString() + "\n" + - "Dependent Immutable Class: " + propertyStore - .entities(ClassImmutability_new.key) - .toList - .toString() + "\n" + - "Shallow Immutable Class: " + propertyStore - .finalEntities(ShallowImmutableClass) - .toList - .toString() + "\n" + - "Deep Immutable Class: " + propertyStore - .finalEntities(DeepImmutableClass) - .toList - .toString() + "\n" + " took : " + analysisTime + " seconds" } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala new file mode 100644 index 0000000000..39924dbd93 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -0,0 +1,103 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds; + +/** + * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result + * + * @author Tobias Peter Roth + */ +object ClassImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { + + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i <- 0 until 19) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyStaticDataUsageAnalysis, + LazySimpleEscapeAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + + /** + * "Mutable Class: "+propertyStore + * .finalEntities(MutableClass) + * .toList + * .toString()+"\n"+ + * "Dependent Immutable Class: "+propertyStore + * .entities(ClassImmutability_new.key) + * .toList + * .toString()+"\n"+ + * "Shallow Immutable Class: "+propertyStore + * .finalEntities(ShallowImmutableClass) + * .toList + * .toString()+"\n"+ + * "Deep Immutable Class: "+propertyStore + * .finalEntities(DeepImmutableClass) + * .toList + * .toString()+"\n" + */ + times.foreach(s => println(s + " seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size + f"took: $aver seconds on average" + } +} 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 index 1efd2e07a6..7ca9c569cc 100644 --- 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 @@ -1,7 +1,11 @@ /* 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.analyses.BasicReport import org.opalj.br.analyses.Project @@ -16,12 +20,15 @@ import org.opalj.br.fpcf.properties.DependentImmutableField import org.opalj.br.fpcf.properties.FieldImmutability import org.opalj.br.fpcf.properties.MutableField import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds /** * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. @@ -49,37 +56,68 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { analysesManager.project.get(RTACallGraphKey) - val (propertyStore, _) = analysesManager.runAll( - LazyLxClassImmutabilityAnalysis_new, - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new - ); + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { - "Mutable Fields: " + propertyStore - .finalEntities(MutableField) - .toList - .toString() + "\n" + - "Shallow Immutable Fields: " + propertyStore - .finalEntities(ShallowImmutableField) - .toList - .toString() + "\n" + - "Dependet Immutable Fields:" + propertyStore - .finalEntities(DependentImmutableField(None)) - .toList - .toString() + "\n" + - "Deep Immutable Fields: " + propertyStore - .finalEntities(DeepImmutableField) - .toList - .toString() + "\n" + + propertyStore = analysesManager + .runAll( + LazyLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds + } + val sb: StringBuilder = new StringBuilder + sb.append("Mutable Fields: \n") + sb.append( + propertyStore + .finalEntities(MutableField) + .toList + .map(x => x.toString + "\n") + .toString() + ) + sb.append("\nShallow Immutable Fields: \n") + sb.append( + propertyStore + .finalEntities(ShallowImmutableField) + .toList + .map(x => x.toString + "\n") + .toString() + ) + sb.append("\nDependet Immutable Fields: \n") + sb.append( propertyStore .entities(FieldImmutability.key) .toList + .collect({ case x: DependentImmutableField => x }) + .map(x => x.toString + "\n") + .toString() + ) + sb.append("Deep Immutable Fields: ") + sb.append( + propertyStore + .finalEntities(DeepImmutableField) + .toList + .map(x => x.toString + "\n") .toString + ) + + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/refImm" + dateString + ".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + + " took : " + analysisTime + " seconds" } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala new file mode 100644 index 0000000000..be2e23b644 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -0,0 +1,100 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds + +/** + * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object FieldImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0FieldImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0FieldImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i <- 0 until 19) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + + /** + * "Mutable Fields: "+propertyStore + * .finalEntities(MutableField) + * .toList + * .toString()+"\n"+ + * "Shallow Immutable Fields: "+propertyStore + * .finalEntities(ShallowImmutableField) + * .toList + * .toString()+"\n"+ + * "Dependet Immutable Fields:"+propertyStore + * .finalEntities(DependentImmutableField(None)) + * .toList + * .toString()+"\n"+ + * "Deep Immutable Fields: "+propertyStore + * .finalEntities(DeepImmutableField) + * .toList + * .toString()+"\n"+ + * propertyStore + * .entities(FieldImmutability.key) + * .toList + * .toString* + */ + times.foreach(s => println(s + " seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size + f"took: $aver seconds on average" + } +} 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..067d5b6ed5 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 @@ -61,10 +61,12 @@ object ImmutabilityAnalysisDemo extends ProjectAnalysisApplication { r = time[() ⇒ String](10, 50, 15, analyze(project, parallelismLevel))(handleResults) println( s"Results with $parallelismLevel threads:\n"+ - performanceData.values. - map(v ⇒ v.map(_.toSeconds.toString(false))). - map(v ⇒ List("setup\t", "analysis\t").zip(v).map(e ⇒ e._1 + e._2).mkString("", "\n", "\n")). - mkString("\n") + performanceData.values + .map(v ⇒ v.map(_.toSeconds.toString(false))) + .map( + v ⇒ List("setup\t", "analysis\t").zip(v).map(e ⇒ e._1 + e._2).mkString("", "\n", "\n") + ) + .mkString("\n") ) gc() @@ -82,24 +84,32 @@ object ImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val propertyStore = project.get(PropertyStoreKey) time { - propertyStore.setupPhase(Set[PropertyKind]( - FieldMutability.key, ClassImmutability.key, TypeImmutability.key - )) + propertyStore.setupPhase( + Set[PropertyKind]( + FieldMutability.key, + ClassImmutability.key, + TypeImmutability.key + ) + ) LazyL0FieldMutabilityAnalysis.register(project, propertyStore, null) EagerClassImmutabilityAnalysis.start(project, propertyStore, null) EagerTypeImmutabilityAnalysis.start(project, propertyStore, null) propertyStore.waitOnPhaseCompletion() - } { r ⇒ analysisTime = r } + } { r ⇒ + analysisTime = r + } result += s"\t- analysis time: ${analysisTime.toSeconds}\n" () ⇒ { val immutableClasses = - propertyStore.entities(ClassImmutability.key). - filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration).toBuffer. - groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub). - map { kv ⇒ + propertyStore + .entities(ClassImmutability.key) + .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) + .toBuffer + .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) + .map { kv ⇒ ( kv._1, kv._2.toList.sortWith { (a, b) ⇒ @@ -111,27 +121,29 @@ object ImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } val immutableClassesPerCategory = - immutableClasses. - map(kv ⇒ "\t\t"+kv._1+": "+kv._2.size). - toBuffer.sorted. - mkString("\n") + immutableClasses.map(kv ⇒ "\t\t"+kv._1+": "+kv._2.size).toBuffer.sorted.mkString("\n") val immutableTypes = - propertyStore.entities(TypeImmutability.key). - filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration).toBuffer. - groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub). - map(kv ⇒ (kv._1, kv._2.size)) + propertyStore + .entities(TypeImmutability.key) + .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) + .toBuffer + .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) + .map(kv ⇒ (kv._1, kv._2.size)) val immutableTypesPerCategory = immutableTypes.map(kv ⇒ "\t\t"+kv._1+": "+kv._2).toBuffer.sorted.mkString("\n") val immutableClassesInfo = - immutableClasses.values.flatten.filter { ep ⇒ - !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration - }.map { eps ⇒ - eps.e.asInstanceOf[ClassFile].thisType.toJava+ - " => "+eps.ub+ - " => "+propertyStore(eps.e, TypeImmutability.key).ub - }.mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") + immutableClasses.values.flatten + .filter { ep ⇒ + !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration + } + .map { eps ⇒ + eps.e.asInstanceOf[ClassFile].thisType.toJava+ + " => "+eps.ub+ + " => "+propertyStore(eps.e, TypeImmutability.key).ub + } + .mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") "\t- details:\n"+ immutableClassesInfo+ diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala new file mode 100644 index 0000000000..36c8c4aab5 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala @@ -0,0 +1,195 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.analyses + +import java.net.URL + +import org.opalj.br.ClassFile +import org.opalj.br.analyses.BasicReport +import org.opalj.br.analyses.Project +import org.opalj.br.analyses.ProjectAnalysisApplication +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.ClassImmutability +import org.opalj.br.fpcf.properties.ClassImmutability_new +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.FieldMutability +import org.opalj.br.fpcf.properties.FieldPrematurelyRead +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.properties.ReferenceImmutability +import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.br.fpcf.properties.TypeImmutability_new +import org.opalj.fpcf.EPS +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyKind +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.Nanoseconds +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.gc + +/** + * Determines the immutability of the classes of a project. + * + * @author Michael Eichberg + * @author Tobias Peter Roth + * + */ +object ImmutabilityAnalysisDemo_new extends ProjectAnalysisApplication { + + override def title: String = "determines the immutability of objects and types" + + override def description: String = "determines the immutability of objects and types" + + private[this] var setupTime = Nanoseconds.None + private[this] var analysisTime = Nanoseconds.None + private[this] var performanceData: Map[Nanoseconds, List[Nanoseconds]] = Map.empty + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + var r: () ⇒ String = null + + def handleResults(t: Nanoseconds, ts: Seq[Nanoseconds]) = { + performanceData += ((t, List(setupTime, analysisTime))) + performanceData = performanceData.filter((t_ts) ⇒ ts.contains(t_ts._1)) + } + + List(1).foreach { parallelismLevel ⇒ + performanceData = Map.empty + gc() + + println(s"\nRunning analysis with $parallelismLevel thread(s):") + r = time[() ⇒ String](10, 50, 15, analyze(project, parallelismLevel))(handleResults) + println( + s"Results with $parallelismLevel threads:\n"+ + performanceData.values + .map(v ⇒ v.map(_.toSeconds.toString(false))) + .map( + v ⇒ List("setup\t", "analysis\t").zip(v).map(e ⇒ e._1 + e._2).mkString("", "\n", "\n") + ) + .mkString("\n") + ) + + gc() + } + BasicReport(r()) + } + + def analyze(theProject: Project[URL], parallelismLevel: Int): () ⇒ String = { + var result = "Results:\n" + val project = Project.recreate(theProject) // We need an empty project(!) + project.get(RTACallGraphKey) + + // The following measurements (t) are done such that the results are comparable with the + // reactive async approach developed by P. Haller and Simon Gries. + PropertyStoreKey.parallelismLevel = parallelismLevel + //PropertyStoreKey + val propertyStore = project.get(PropertyStoreKey) + + time { + propertyStore.setupPhase( + Set[PropertyKind]( + FieldMutability.key, + ClassImmutability.key, + TypeImmutability.key, + FieldPrematurelyRead.key, + Purity.key, + FieldImmutability.key, + ReferenceImmutability.key, + ClassImmutability_new.key, + TypeImmutability_new.key + ) + ) + //LazyL0FieldMutabilityAnalysis.register(project, propertyStore, null) // (project, propertyStore) + //EagerClassImmutabilityAnalysis.start(project, propertyStore, project.allClassFiles) + //EagerTypeImmutabilityAnalysis.start(project, propertyStore, null) //project.allClassFiles) + + LazyClassImmutabilityAnalysis.register(project, propertyStore, project.allClassFiles) + LazyL2FieldMutabilityAnalysis.register(project, propertyStore, null) + LazyTypeImmutabilityAnalysis.register(project, propertyStore, null) + LazyUnsoundPrematurelyReadFieldsAnalysis.register(project, propertyStore, null) + LazyL2PurityAnalysis.register(project, propertyStore, null) + + EagerL0ReferenceImmutabilityAnalysis.start(project, propertyStore, null) + EagerL0FieldImmutabilityAnalysis.start(project, propertyStore, null) + EagerLxClassImmutabilityAnalysis_new.start(project, propertyStore, project.allClassFiles) + EagerLxTypeImmutabilityAnalysis_new.start(project, propertyStore, null) + //propertyStore.suppressError = true + //propertyStore.waitOnPhaseCompletion() + } { r ⇒ + analysisTime = r + } + + result += s"\t- analysis time: ${analysisTime.toSeconds}\n" + + () ⇒ { + val immutableReferences = + propertyStore.entities(ReferenceImmutability.key) + val immutableClasses = + propertyStore + .entities(ClassImmutability_new.key) + .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) + .toBuffer + .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) + .map { kv ⇒ + ( + kv._1, + kv._2.toList.sortWith { (a, b) ⇒ + val cfA = a.e.asInstanceOf[ClassFile] + val cfB = b.e.asInstanceOf[ClassFile] + cfA.thisType.toJava < cfB.thisType.toJava + } + ) + } + + val immutableClassesPerCategory = + immutableClasses + .map(kv ⇒ "\t\t"+kv._1+": "+kv._2.size) + .toBuffer + .sorted + .mkString("\n") + + val immutableTypes = + propertyStore + .entities(TypeImmutability_new.key) + .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) + .toBuffer + .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) + .map(kv ⇒ (kv._1, kv._2.size)) + val immutableTypesPerCategory = + immutableTypes.map(kv ⇒ "\t\t"+kv._1+": "+kv._2).toBuffer.sorted.mkString("\n") + + val immutableClassesInfo = + immutableClasses.values.flatten + .filter { ep ⇒ + !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration + } + .map { eps ⇒ + eps.e.asInstanceOf[ClassFile].thisType.toJava+ + " => "+eps.ub+ + " => "+propertyStore(eps.e, TypeImmutability_new.key).ub + } + .mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") + + "immutable References: "+immutableReferences.size+"\n" + "\t- details:\n"+ + immutableClassesInfo+ + "\nSummary (w.r.t classes):\n"+ + "\tObject Immutability:\n"+ + immutableClassesPerCategory+"\n"+ + "\tType Immutability:\n"+ + immutableTypesPerCategory+"\n"+ + "\n"+propertyStore.toString(false) + } + } +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index 25dcf825c8..c9f7275191 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -11,11 +11,22 @@ import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds +import java.io._ +import java.util.Calendar + +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis /** * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. @@ -24,46 +35,73 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyTypeImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds } + var sb: StringBuilder = new StringBuilder() + sb = sb.append("Mutable References: \n") + sb = sb.append( + propertyStore.finalEntities(MutableReference).toList.map(x => x.toString + "\n").toString() + ) - def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - val (propertyStore, _) = analysesManager.runAll( - //EagerUnsoundPrematurelyReadFieldsAnalysis, - //EagerL2PurityAnalysis, - //EagerL2FieldMutabilityAnalysis, - EagerL0ReferenceImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis - ); + sb = sb.append("\n Lazy Initialized Reference: \n") + sb = sb.append( + propertyStore + .finalEntities(LazyInitializedReference) + .toList + .map(x => x.toString + "\n") + .toString() + ) - "Mutable References: "+propertyStore - .finalEntities(MutableReference) - .toList - .toString()+"\n"+ - "Lazy Initialized Reference: "+propertyStore - .finalEntities(LazyInitializedReference) - .toList - .toString()+"\n"+ - "Immutable References: "+propertyStore - .finalEntities(ImmutableReference) - .toList - .toString() - } + /** + * .toList + * .toString() + "\n" +* + */ + sb = sb.append("\nImmutable References: \n") + sb = sb.append( + propertyStore.finalEntities(ImmutableReference).toList.map(x => x.toString + "\n").toString() + ) + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/refLazyImm" + dateString + ".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + " took : " + analysisTime + " seconds" + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala new file mode 100644 index 0000000000..24ff667072 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -0,0 +1,97 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +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.util.PerformanceEvaluation.time +import org.opalj.util.Seconds + +/** + * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object ReferenceImmutabilityAnalysisDemo_performanceMeasurements + extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i <- 0 until 19) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyTypeImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + + /** + * "Mutable References: "+ + * propertyStore + * .finalEntities(MutableReference) + * .toList + * .toString()+"\n"+ + * "Lazy Initialized Reference: "+propertyStore + * .finalEntities(LazyInitializedReference) + * .toList + * .toString()+"\n"+ + * "Immutable References: "+propertyStore + * .finalEntities(ImmutableReference) + * .toList + * .toString()+"\n"+* + */ + times.foreach(s => println(s + " seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size + f"took: $aver seconds on average" + } + +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala index a9b3b9b7ac..4daa6d1b84 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala @@ -1,21 +1,34 @@ /* 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.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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.LazyInitialization import org.opalj.br.fpcf.properties.NoLazyInitialization import org.opalj.br.fpcf.properties.NotThreadSafeLazyInitialization +import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis 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.util.PerformanceEvaluation.time +import org.opalj.util.Seconds /** * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. @@ -24,42 +37,97 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ object ReferenceImmutabilityLazyInitializationAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - val (propertyStore, _) = analysesManager.runAll( - EagerL0ReferenceImmutabilityLazyInitializationAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis - ); + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityLazyInitializationAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyTypeImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyFieldLocalityAnalysis, + LazyReturnValueFreshnessAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + /*def printResult(string: String, property: Property)( + implicit + propertyStore: PropertyStore + ): Unit = {} **/ + val sb: StringBuilder = new StringBuilder() - "Not lazy initialized References: " + propertyStore - .finalEntities(NoLazyInitialization) - .toList - .toString() + "\n" + - "Not thread safe lazy initialization: " + propertyStore - .finalEntities(NotThreadSafeLazyInitialization) - .toList - .toString() + "\n" + - "Lazy Initialization: " + propertyStore - .finalEntities(LazyInitialization) - .toList - .toString() - } + sb.append("Not initialized References: \n") + sb.append( + propertyStore + .finalEntities(NoLazyInitialization) + .toList + .map(x ⇒ x.toString+"\n") + .toString() + ) + sb.append("\nNot threadsafe Lazy Initialization: \n") + sb.append( + propertyStore + .finalEntities(NotThreadSafeLazyInitialization) + .toList + .map(x ⇒ x.toString+"\n") + .toString() + ) + sb.append("\nLazy Initialization: \n") + sb.append( + propertyStore + .finalEntities(LazyInitialization) + .toList + .map(x ⇒ x.toString+"\n") + .toString() + ) + + /** + * "Not lazy initialized References: " + propertyStore + * .finalEntities(NoLazyInitialization) + * .toList + * .toString() + "\n" + + * "Not thread safe lazy initialization: " + propertyStore + * .finalEntities(NotThreadSafeLazyInitialization) + * .toList + * .toString() + "\n" + + * "Lazy Initialization: " + propertyStore + * .finalEntities(LazyInitialization) + * .toList + * .toString()* + */ + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/refDCLImm"+dateString+".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + + " took : "+analysisTime+" seconds" + s"took $analysisTime seconds " + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala new file mode 100644 index 0000000000..4c6f2c79d9 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala @@ -0,0 +1,117 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +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.util.PerformanceEvaluation.time +import org.opalj.util.Seconds + +/** + * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements + extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i ← 0 until 19) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityLazyInitializationAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyTypeImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyFieldLocalityAnalysis, + LazyReturnValueFreshnessAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + /*def printResult(string: String, property: Property)( + implicit + propertyStore: PropertyStore + ): Unit = {} **/ + /** + * val notInitializedReferencesString = propertyStore + * .finalEntities(NoLazyInitialization) + * .toList + * .toString() + * val notThreadSafeLazyInitializationString = propertyStore + * .finalEntities(NotThreadSafeLazyInitialization) + * .toList + * .toString() + * val lazyInitializationString = propertyStore + * .finalEntities(LazyInitialization) + * .toList + * .toString() + * println(s"Not initialized References $notInitializedReferencesString") + * println(s"Not threadsafe Lazy Initialization: $notThreadSafeLazyInitializationString") + * println(s"Lazy Initialization String: $lazyInitializationString") + */ + /** + * "Not lazy initialized References: " + propertyStore + * .finalEntities(NoLazyInitialization) + * .toList + * .toString() + "\n" + + * "Not thread safe lazy initialization: " + propertyStore + * .finalEntities(NotThreadSafeLazyInitialization) + * .toList + * .toString() + "\n" + + * "Lazy Initialization: " + propertyStore + * .finalEntities(LazyInitialization) + * .toList + * .toString()* + */ + times.foreach(s ⇒ println(s+" seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size + f"took: $aver seconds on average" + } +} 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 index 1739638a81..dcbcb5df3b 100644 --- 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 @@ -1,7 +1,11 @@ /* 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.analyses.BasicReport import org.opalj.br.analyses.Project @@ -9,18 +13,25 @@ import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis 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_new import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis -//import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -29,51 +40,71 @@ import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis */ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" + override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" - override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" + override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(project: Project[URL]): String = { - - val analysesManager = project.get(FPCFAnalysesManagerKey) + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - analysesManager.project.get(RTACallGraphKey) - - val (propertyStore, _) = analysesManager.runAll( - //LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - EagerLxTypeImmutabilityAnalysis_new, - LazyLxClassImmutabilityAnalysis_new + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyFieldLocalityAnalysis, + LazySimpleEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis ) - "Mutable Type: "+propertyStore - .finalEntities(MutableType_new) - .toList - .toString()+"\n"+ - "Shallow Immutable Type: "+propertyStore - .finalEntities(ShallowImmutableType) - .toList - .toString()+"\n"+ - "Dependent Immutable Type: "+propertyStore - .finalEntities(DependentImmutableType) - .toList - .toString()+"\n"+ - "Deep Immutable Type: "+propertyStore - .finalEntities(DeepImmutableType) - .toList - .toString()+"\n" + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds } + val sb: StringBuilder = new StringBuilder + sb.append("\nMutableTypes: \n") + sb.append( + propertyStore + .finalEntities(MutableType_new) + .toList + .map(x => x.toString + "\n") + .toString + ) + sb.append("\nShallow Immutable Types:\n") + sb.append(propertyStore.finalEntities(ShallowImmutableType).toList.map(x => x.toString + "\n")) + sb.append("\nDependent Immutable Types: \n") + sb.append( + propertyStore.finalEntities(DependentImmutableType).toList.map(x => x.toString + "\n") + ) + sb.append("\nDeep Immutable Types:\n") + sb.append(propertyStore.finalEntities(DeepImmutableType).toList.map(x => x.toString + "\n")) + sb.append(s"\nType immutability analysis took: $analysisTime on average") + + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/typeImm" + dateString + ".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + + " took : " + analysisTime + " seconds" + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala new file mode 100644 index 0000000000..996a182686 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala @@ -0,0 +1,105 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis +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.ShallowImmutableType +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis + +/** + * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result + * + * @author Tobias Peter Roth + */ +object TypeImmutabilityAnalysisDemo_performanceMeasurement extends ProjectAnalysisApplication { + + override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" + + override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(theProject: Project[URL]): String = { + + var times: List[Seconds] = Nil: List[Seconds] + for (i ← 0 until 19) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyFieldLocalityAnalysis, + LazySimpleEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + + /** + * "Mutable Type: "+propertyStore + * .finalEntities(MutableType_new) + * .toList + * .toString()+"\n"+ + * "Shallow Immutable Type: "+propertyStore + * .finalEntities(ShallowImmutableType) + * .toList + * .toString()+"\n"+ + * "Dependent Immutable Type: "+propertyStore + * .finalEntities(DependentImmutableType) + * .toList + * .toString()+"\n"+ + * "Deep Immutable Type: "+propertyStore + * .finalEntities(DeepImmutableType) + * .toList + * .toString()+"\n"* + */ + times.foreach(s ⇒ println(s+" seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size + f"took: $aver seconds on average" + } +} From 436715af126e91d76ce79c52c1e149d07a2566bb Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 23 Oct 2019 22:36:25 +0200 Subject: [PATCH 044/327] First proposal of the lattice for field immutability --- .../fpcf/properties/FieldMutability_new.scala | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala new file mode 100644 index 0000000000..b0f5c3e421 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala @@ -0,0 +1,37 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.properties + +import org.opalj.fpcf._ + +sealed trait FieldMutabilityPropertyMetaInformation_new extends PropertyMetaInformation { + + type Self = FieldMutability_new + +} +/** + * @author Tobias Peter Roth + */ +sealed trait FieldMutability_new extends Property with FieldMutabilityPropertyMetaInformation_new { + + final def key: PropertyKey[FieldMutability_new] = FieldMutability_new.key +} + +object FieldMutability_new extends FieldMutabilityPropertyMetaInformation_new { + + final val PropertyKeyName = "opalj.FieldMutability_new" + + final val key: PropertyKey[FieldMutability_new] = { + PropertyKey.create( + PropertyKeyName, + MutableField + ) + } +} + +sealed trait ImmutableField extends FieldMutability_new + +case object ShallowImmutableField extends ImmutableField + +case object DeepImmutableField extends ImmutableField + +case object MutableField extends FieldMutability_new \ No newline at end of file From c9f9b4c69cd40a198c20f40c77db4cfaa0336630 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 23 Oct 2019 22:39:54 +0200 Subject: [PATCH 045/327] Start of the new analysis of field immutability using the new lattice --- .../L3FieldMutabilityAnalysis_new.scala | 339 ++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala new file mode 100644 index 0000000000..48b7af0a5e --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala @@ -0,0 +1,339 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.analyses + +import org.opalj.br.{Field, Method, PCs} +import org.opalj.br.analyses.{FieldAccessInformationKey, SomeProject} +import org.opalj.br.analyses.cg.{ClosedPackagesKey, TypeExtensibilityKey} +import org.opalj.br.fpcf.{BasicFPCFEagerAnalysisScheduler, BasicFPCFLazyAnalysisScheduler, FPCFAnalysis, FPCFAnalysisScheduler} +import org.opalj.br.fpcf.properties._ +import org.opalj.fpcf._ +import org.opalj.tac.fpcf.analyses.escape.EagerInterProceduralEscapeAnalysis.V +import org.opalj.tac._ +import org.opalj.tac.fpcf.properties.TACAI + +/** + * New implementation of the fieldimmutability analysis + * @author Tobias Peter Roth + */ +class L3FieldMutabilityAnalysis_new private[analyses] (val project: SomeProject) extends FPCFAnalysis { + + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) + + case class State() { + var hasImmutableType: Boolean = false + var isFinal = false + var effectivelyFinal = true + var influencedByMethod = false + var escapesViaMethod = false + } + + def doDetermineFieldMutability_new(entity: Entity): PropertyComputationResult = { + entity match { + case field: Field ⇒ determineFieldMutability_new(field) + case _ ⇒ + val m = entity.getClass.getName+"is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + } + + + private[analyses] def determineFieldMutability_new(field: Field): PropertyComputationResult = { + val state: State = new State() + + //Reference Immutablity + if (field.isProtected || field.isPackagePrivate || field.isPublic) { + return Result(field, MutableField) + } + state.isFinal = field.isFinal + + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + dependencies += EPK(field.fieldType, TypeImmutability.key) + + /** + * def isSetByConstructor(): Boolean = { + * fieldAccessInformation.writeAccesses(field). + * filter(_._1.isConstructor). + * foreach(x ⇒ + * ( + * x._1.asMethod.descriptor.parameterTypes.foreach(x ⇒ + * if (x == field.fieldType) { + * return true + * }) + * )) + * false + * }* + */ + + // if constructor sets the field it could escape if it is not immutable + def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { + //dependencies = dependencies.filter(_.e ne eps.e) + (eps: @unchecked) match { + //case _: InterimEP[_, _] ⇒ + // dependencies += eps + // InterimResult(InterimEP(field, MutableField), dependencies, c(state)) + case FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) ⇒ + if (!state.influencedByMethod & (state.isFinal || state.effectivelyFinal)) { + Result(field, DeepImmutableField) + } else { + + return InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + } + case FinalEP(e, MutableType) ⇒ { + if ((state.isFinal || state.effectivelyFinal) && !state.influencedByMethod) { + return Result(field, ShallowImmutableField) + } + return Result(field, MutableField) + } + } + } + + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode = getTACAI(method, pcs) + } { + if (methodInfluencesField(method, taCode.get, pcs, field)) { + state.influencedByMethod = true + } + if (escapesViaMethod(method, taCode.get, pcs, field)) { + state.escapesViaMethod = true + } + } + + //determine field Type immutability + val result = propertyStore(field.fieldType, TypeImmutability.key) + println("Result "+result) + + result match { + case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) ⇒ { + println("has immutable type") + state.hasImmutableType = true + } + case x: InterimEP[_, _] ⇒ return InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + case _ ⇒ {} + } + println("Escapes Via Method: "+state.escapesViaMethod) + println("Effectively Final: "+state.effectivelyFinal) + println("is final: "+state.isFinal) + println("Has ImmutableType : "+state.hasImmutableType) + println("influenced by method: "+state.influencedByMethod) + println("State"+state.toString) + + if (field.isPublic || field.isPackagePrivate) { + return Result(field, MutableField) + } + + if (((state.isFinal) || state.effectivelyFinal) && state.escapesViaMethod && !state.hasImmutableType) { + return Result(field, MutableField) + } + if (((state.isFinal) || state.effectivelyFinal) && state.escapesViaMethod && state.hasImmutableType) { + return Result(field, DeepImmutableField) + } + if (state.influencedByMethod) { + return Result(field, MutableField) + } + if (((state.isFinal) || state.effectivelyFinal) && !state.hasImmutableType) { + return Result(field, ShallowImmutableField) + } + if (((state.isFinal) || state.effectivelyFinal) && state.hasImmutableType) { + return Result(field, DeepImmutableField) + } + if (((state.isFinal) || state.effectivelyFinal) && !state.influencedByMethod) { + if (state.hasImmutableType) { + return Result(field, DeepImmutableField) + } else { + return Result(field, ShallowImmutableField) + } + } + if (((state.isFinal) || state.effectivelyFinal) && state.escapesViaMethod && state.hasImmutableType) { + + return Result(field, DeepImmutableField) + } + + // not final or eff final reference and/or not influenced by a method + return Result(field, MutableField) + } + /** + * Analyzes field writes for a single method, returning false if the field may still be + * effectively final and true otherwise. + */ + + def escapesViaMethod( + method: Method, + taCode: TACode[TACMethodParameter, V], + pcs: PCs, + field: Field + ): Boolean = + { + if (method.isConstructor) { + method.asMethod.descriptor.parameterTypes.foreach(x ⇒ if (x == field.fieldType) return true) + } + println("method: "+method.toJava.toString) + val stmts = taCode.stmts + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + println(stmt.astID) + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + println("field.isStatic "+field.isStatic) + if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + return true + } + if (field.isStatic) { + } + } else { + + if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + + return true; + } + return false + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead + } + } + } + false + } + + def methodInfluencesField( + method: Method, + taCode: TACode[TACMethodParameter, V], + pcs: PCs, + field: Field + ): Boolean = { + if (method.isConstructor) { + method.asMethod.descriptor.parameterTypes.foreach(x ⇒ if (x == field.fieldType) return true) + } + println("method: "+method.toJava.toString) + val stmts = taCode.stmts + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + println(stmt.astID) + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + println("field.isStatic "+field.isStatic) + if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + return false + } + if (field.isStatic) { + } + } else { + + if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + return false; + } + return true + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead + } + } + } + false + } + + def referenceHasEscaped( + reference: V, + stmts: Array[Stmt[V]], + method: Method + ): Boolean = { + reference.definedBy.forall { defSite ⇒ + { + //TODO + false + } + } + } + + /** + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + + def getTACAI( + method: Method, + pcs: PCs + ): Option[TACode[TACMethodParameter, V]] = { + propertyStore(method, TACAI.key) match { + case finalEP: FinalEP[Method, TACAI] ⇒ + finalEP.ub.tac + case eps: InterimEP[Method, TACAI] ⇒ + eps.ub.tac + case epk ⇒ + None + } + } + +} + +trait L3FieldMutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { + + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(FieldMutability), + PropertyBounds.lub(TypeImmutability), + PropertyBounds.lub(FieldMutability_new), + PropertyBounds.ub(EscapeProperty) + ) + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability_new) + +} + +/** + * Executor for the new field mutability analysis. + */ +object EagerL3FieldMutabilityAnalysis_new + extends L3FieldMutabilityAnalysisScheduler_new + with BasicFPCFEagerAnalysisScheduler { + + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L3FieldMutabilityAnalysis_new(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldMutability_new) + analysis + } + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty +} + +/** + * Executor for the new lazy field mutability analysis. + */ +object LazyL3FieldMutabilityAnalysis_new + extends L3FieldMutabilityAnalysisScheduler_new + with BasicFPCFLazyAnalysisScheduler { + + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L3FieldMutabilityAnalysis_new(p) + //TODO + //ps.registerLazyPropertyComputation( + //FieldMutability_new.key, analysis.determineFieldMutability_new + //) + analysis + } + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + +} From 9a83db2059e42344f7a1df1c2c7d56cd0c4be62f Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 23 Oct 2019 22:44:44 +0200 Subject: [PATCH 046/327] Runner for the new field immutable analysis implementation --- .../RunFieldMutabilityAnalysisNew.scala | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala new file mode 100644 index 0000000000..ae28589d93 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala @@ -0,0 +1,45 @@ +/* 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.ProjectAnalysisApplication +import org.opalj.br.analyses.Project +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.br.fpcf.analyses.{EagerClassImmutabilityAnalysis, EagerL3FieldMutabilityAnalysis_new, EagerTypeImmutabilityAnalysis} +import org.opalj.br.fpcf.properties.{DeepImmutableField, MutableField, ShallowImmutableField} +import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis + +/** + * Runs the new field mutability implementation + * + * @author Tobias Peter Roth + */ +object RunFieldMutabilityAnalysisNew extends ProjectAnalysisApplication { + + override def title: String = "run new field immutability analysis" + + override def description: String = "run new field immutability analysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + //project. + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll(EagerTypeImmutabilityAnalysis, EagerL3FieldMutabilityAnalysis_new, EagerL1FieldMutabilityAnalysis, EagerClassImmutabilityAnalysis); + return "Mutable Fields: "+propertyStore.finalEntities(MutableField).toList.toString()+"\n"+ + "Shallow Immutable Fields: "+propertyStore.finalEntities(ShallowImmutableField).toList.toString()+"\n"+ + "Deep Immutable Fields: "+propertyStore.finalEntities(DeepImmutableField).toList.toString() + } +} From 9b1055f4b64b3f8d73767a9710674d3c3dd8c811 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 6 Nov 2019 16:19:21 +0100 Subject: [PATCH 047/327] Introduced Reference Immutability. A whole new lattice, the analysis which is partly taken from the field mutability analysis and mapped to the new lattice and the demo-class for running the reference immutability analysis. --- .../ReferenceImmutabilityAnalysisDemo.scala | 56 + .../properties/ReferenceImmutability.scala | 52 + .../L0ReferenceImmutabilityAnalysis.scala | 1109 +++++++++++++++++ 3 files changed, 1217 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala new file mode 100644 index 0000000000..1b8292afa4 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -0,0 +1,56 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.EagerL0PurityAnalysis +import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis + +/** + * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll( + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerL0ReferenceImmutabilityAnalysis + ); + + "Mutable References: "+propertyStore + .finalEntities(MutableReference) + .toList + .toString()+"\n"+ + "Immutable References: "+propertyStore + .finalEntities(ImmutableReference) + .toList + .toString() + } +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala new file mode 100644 index 0000000000..54e86d106d --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -0,0 +1,52 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.properties + +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaInformation { + + type Self = ReferenceImmutability + +} + +/** + * Describes the reference immutability of org.opalj.br.Field. + * The analysis is used from the old implementation of the L2FieldMutabilityAnalysis. + * + * [[MutableReference]] A reference through which the object it points to could be changed. + * + * [[LazyInitializedReference]] A reference through which the object it points to is changed in a initial like phase + * and afterwards never changed through this reference + * + * [[ImmutableReference]] A reference through which the object it leads to is set in the constructor and afterwards + * never changed. And there is no possibility to change it afterwards through this reference.. + * + * @author Tobias Peter Roth + */ +sealed trait ReferenceImmutability + extends Property + with ReferenceImmutabilityPropertyMetaInformation { + final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key +} + +object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { + + final val PropertyKeyName = "opalj.ReferenceImmutability" + + final val key: PropertyKey[ReferenceImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableReference + ) + } +} + +sealed trait RefImm extends ReferenceImmutability + +case object MutableReference extends RefImm + +case object LazyInitializedReference extends RefImm + +case object ImmutableReference extends RefImm diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala new file mode 100644 index 0000000000..4d2590fb19 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -0,0 +1,1109 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package tac +package fpcf +package analyses + +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._ +import org.opalj.fpcf._ +import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler +import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler +import org.opalj.br._ +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.FieldAccessInformationKey +import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.cg.ClosedPackagesKey +import org.opalj.br.analyses.cg.TypeExtensibilityKey +import org.opalj.br.cfg.BasicBlock +import org.opalj.br.cfg.CFG +import org.opalj.br.cfg.CFGNode +import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.fpcf.properties.EscapeProperty +import org.opalj.br.fpcf.properties.FieldPrematurelyRead +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.ai.isImmediateVMException +import org.opalj.ai.pcOfImmediateVMException +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.value.ValueInformation + +/** + * + * Implementation is used from the old L2FieldMutability implementation + * but the lattice is mapped to the new reference immutability lattice. + * + * @note Requires that the 3-address code's expressions are not deeply nested. + * + * @author Dominik Helm + * @author Florian Kübler + * @author Michael Eichberg + * @author Tobias Peter Roth + */ +class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProject) + extends FPCFAnalysis { + + case class State( + field: Field, + var referenceImmutability: ReferenceImmutability = ImmutableReference, + 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, + var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty + } + + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + } + } + + 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) + + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field ⇒ determineReferenceImmutability(field) + case _ ⇒ + val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + + /** + * Analyzes the mutability of private non-final fields. + * + * 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 determineReferenceImmutability( + field: Field + ): ProperPropertyComputationResult = { + implicit val state: State = State(field) + println( + "field.isPrematurelyRead "+isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key)) + ) + // Fields are not final if they are read prematurely! + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) + return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); + println("field.isFinal "+field.isFinal) + if (field.isFinal) + return createResult(); + + state.referenceImmutability = ImmutableReference //EffectivelyFinalField + + val thisType = field.classFile.thisType + + if (field.isPublic) + return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) + + // 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) { + if (!closedPackages.isClosed(thisType.packageName)) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } + + val classesHavingAccess: Iterator[ClassFile] = + if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + 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))) + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode ← getTACAI(method, pcs) + } { + if (methodUpdatesField(method, taCode, pcs)) { + println("method updates field: true") + return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); + } else + println("method updates field: false") + + } + + if (state.lazyInitInvocation.isDefined) { + val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + handleCalls(calleesEOP) + } + + createResult() + } + + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: State + ): Boolean = { + calleesEOP match { + case FinalP(callees) ⇒ + state.calleesDependee = None + handleCallees(callees) + case InterimUBP(callees) ⇒ + state.calleesDependee = Some(calleesEOP) + handleCallees(callees) + case _ ⇒ + state.calleesDependee = Some(calleesEOP) + false + } + } + + def handleCallees(callees: Callees)(implicit state: State): Boolean = { + val pc = state.lazyInitInvocation.get._2 + if (callees.isIncompleteCallSite(pc)) { + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else { + val targets = callees.callees(pc).toTraversable + if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else false + } + } + + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + * The code below can be used to identify such value, but is not yet adapted to using the + * TACAI property */ + /* + var constantVal: Option[Any] = None + var allInitializeConstant = true + + val field = state.field + var constructors: Set[Method] = + if(field.isStatic) Set.empty else field.classFile.constructors.toSet + + val writesIterator = fieldAccessInformation.writeAccesses(field).iterator + while (writesIterator.hasNext && allInitializeConstant) { + val (method, pc) = writesIterator.next() + constructors -= method + val code = tacai(method).stmts + + val index = pcToIndex(pc) + val stmt = code(index) + if (stmt.astID == PutStatic.ASTID || + stmt.asPutField.objRef.asVar.definedBy == SelfReferenceParameter) { + val write = stmt.asFieldWriteAccessStmt + if (write.resolveField(p).contains(state.field)) { + val defs = write.value.asVar.definedBy + if (defs.size == 1 && defs.head >= 0) { + val defSite = code(defs.head).asAssignment.expr + val const = if (defSite.isIntConst) + Some(defSite.asIntConst.value) + else if (defSite.isFloatConst) + Some(defSite.asFloatConst.value) + else None + if (const.isDefined) { + if (constantVal.isDefined) { + if (constantVal != const) { + allInitializeConstant = false + constantVal = None + } + } else constantVal = const + } else { + allInitializeConstant = false + constantVal = None + } + } + } + } + } + + for (constructor ← constructors) { + // TODO iterate all statements + val NonVirtualMethodCall(_, declClass, _, name, _, rcvr, _) = stmt + // Consider calls to other constructors as initializations as either + // the called constructor will initialize the field or delegate to yet + // another constructor + if (declClass != state.field.classFile.thisType || name != "" || + rcvr.asVar.definedBy != SelfReferenceParameter) { + if (constantVal.isDefined) allInitializeConstant = false + else constantVal = Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + } + } + + constantVal */ + } + + /** + * 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 NonFinalFieldByAnalysis)) + InterimResult( + state.field, + MutableReference, //NonFinalFieldByAnalysis, + 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 = { + val isNotFinal = eps.pk match { + case EscapeProperty.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + methodUpdatesField(method, newEP.ub.tac.get, pcs) + case Callees.key ⇒ + handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key ⇒ + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + isNonDeterministic(newEP) + case ReferenceImmutability.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + !isImmutableReference(newEP) + } + + if (isNotFinal) + Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) + else + createResult() + } + + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + )(implicit state: State): Boolean = { + val write = code(writeIndex).asFieldWriteAccessStmt + + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + return false; + } + + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method + } + + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + case None ⇒ return false; + } + + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) + return false; + + println( + "Check writes: "+checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg) + ) + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + + true + } + + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val definitions = write.value.asVar.definedBy + + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + // The field write must be guarded correctly + isDeterministic && + checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID ⇒ + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID ⇒ + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ ⇒ return false; // neither a field read nor a return + } + index += 1 + } + false + } + + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + /** + * 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 = { + println("xxxxxx") + val field = state.field + val stmts = taCode.stmts + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + if (field.isStatic) { + if (method.isConstructor) + return true; + } else { + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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.referenceImmutability == LazyInitializedReference) //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; + + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + // A field written outside an initializer must be lazily + // initialized or it is non-final + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )) + return true; + + state.referenceImmutability = LazyInitializedReference //LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead + } + } + } + false + } + + /** + * 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 eps: InterimEP[Method, TACAI] ⇒ + * state.tacDependees += method → ((eps, pcs)) + * eps.ub.tac + * case epk ⇒ + * state.tacDependees += method → ((epk, pcs)) + * None + * } + * } + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) + } + } + } + } + + /** + * 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 = 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 + } + + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt ⇒ + stmt.astID == CaughtException.ASTID + } flatMap { exception ⇒ + exception.asCaughtException.origins.map { origin: Int ⇒ + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator + } + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement + + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + true + } + + /** + * 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]] + )(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(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + false + }) + } + } + + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) + } + + val value = origin.expr + + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID ⇒ + value.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + false + } + 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.exists(!isConstant(_))) { + false + } else { + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true + } + 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, code) + } + + value.isConst || isNonConstDeterministic + } + + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) + } + } + + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + true + } + + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode ⇒ + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) + } + result.toList + } + + /** + * 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. + */ + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID ⇒ + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + } + + None + } + + /** + * 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])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID ⇒ + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) + case _ ⇒ None + } + field.contains(state.field) + } + + /** + * 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]] + )(implicit state: State): Boolean = { + + /** + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + */ + 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } + + /** + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. + */ + def isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index ⇒ + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } + } + + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else false + } + + /** + * Checks if 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) ⇒ true + case eps ⇒ + state.prematurelyReadDependee = Some(eps) + false + } + + /** + * Checkes 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. + */ + 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 + } + + /** + * Checkes if 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, ReferenceImmutability] + )(implicit state: State): Boolean = eop match { + case FinalEP(e, ImmutableReference) ⇒ true + case FinalEP(e, MutableReference) ⇒ false + + /** + * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ + * true + * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * + */ + case _ ⇒ + state.referenceImmutabilityDependees += eop + true + } +} + +trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { + + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.ub(EscapeProperty), + PropertyBounds.lub(ReferenceImmutability) + ) + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) + +} + +/** + * Executor for the field mutability analysis. + */ +object EagerL0ReferenceImmutabilityAnalysis + extends L0ReferenceImmutabilityAnalysisScheduler + with BasicFPCFEagerAnalysisScheduler { + + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) + analysis + } + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty +} + +/** + * Executor for the lazy field mutability analysis. + */ +object LazyL0ReferenceImmutabilityAnalysis + extends L0ReferenceImmutabilityAnalysisScheduler + with BasicFPCFLazyAnalysisScheduler { + + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + ReferenceImmutability.key, + analysis.determineReferenceImmutability + ) + analysis + } + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) +} From 3c21d3d6d539efddd72d95a1afe041a147151403 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 6 Nov 2019 16:54:38 +0100 Subject: [PATCH 048/327] clean up --- .../RunFieldMutabilityAnalysisNew.scala | 45 --- .../fpcf/properties/FieldMutability_new.scala | 37 -- .../L3FieldMutabilityAnalysis_new.scala | 339 ------------------ 3 files changed, 421 deletions(-) delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala delete mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala deleted file mode 100644 index ae28589d93..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/RunFieldMutabilityAnalysisNew.scala +++ /dev/null @@ -1,45 +0,0 @@ -/* 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.ProjectAnalysisApplication -import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.analyses.{EagerClassImmutabilityAnalysis, EagerL3FieldMutabilityAnalysis_new, EagerTypeImmutabilityAnalysis} -import org.opalj.br.fpcf.properties.{DeepImmutableField, MutableField, ShallowImmutableField} -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis - -/** - * Runs the new field mutability implementation - * - * @author Tobias Peter Roth - */ -object RunFieldMutabilityAnalysisNew extends ProjectAnalysisApplication { - - override def title: String = "run new field immutability analysis" - - override def description: String = "run new field immutability analysis" - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(project: Project[URL]): String = { - //project. - val analysesManager = project.get(FPCFAnalysesManagerKey) - - val (propertyStore, _) = analysesManager.runAll(EagerTypeImmutabilityAnalysis, EagerL3FieldMutabilityAnalysis_new, EagerL1FieldMutabilityAnalysis, EagerClassImmutabilityAnalysis); - return "Mutable Fields: "+propertyStore.finalEntities(MutableField).toList.toString()+"\n"+ - "Shallow Immutable Fields: "+propertyStore.finalEntities(ShallowImmutableField).toList.toString()+"\n"+ - "Deep Immutable Fields: "+propertyStore.finalEntities(DeepImmutableField).toList.toString() - } -} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala deleted file mode 100644 index b0f5c3e421..0000000000 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldMutability_new.scala +++ /dev/null @@ -1,37 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.br.fpcf.properties - -import org.opalj.fpcf._ - -sealed trait FieldMutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - - type Self = FieldMutability_new - -} -/** - * @author Tobias Peter Roth - */ -sealed trait FieldMutability_new extends Property with FieldMutabilityPropertyMetaInformation_new { - - final def key: PropertyKey[FieldMutability_new] = FieldMutability_new.key -} - -object FieldMutability_new extends FieldMutabilityPropertyMetaInformation_new { - - final val PropertyKeyName = "opalj.FieldMutability_new" - - final val key: PropertyKey[FieldMutability_new] = { - PropertyKey.create( - PropertyKeyName, - MutableField - ) - } -} - -sealed trait ImmutableField extends FieldMutability_new - -case object ShallowImmutableField extends ImmutableField - -case object DeepImmutableField extends ImmutableField - -case object MutableField extends FieldMutability_new \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala deleted file mode 100644 index 48b7af0a5e..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L3FieldMutabilityAnalysis_new.scala +++ /dev/null @@ -1,339 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.br.fpcf.analyses - -import org.opalj.br.{Field, Method, PCs} -import org.opalj.br.analyses.{FieldAccessInformationKey, SomeProject} -import org.opalj.br.analyses.cg.{ClosedPackagesKey, TypeExtensibilityKey} -import org.opalj.br.fpcf.{BasicFPCFEagerAnalysisScheduler, BasicFPCFLazyAnalysisScheduler, FPCFAnalysis, FPCFAnalysisScheduler} -import org.opalj.br.fpcf.properties._ -import org.opalj.fpcf._ -import org.opalj.tac.fpcf.analyses.escape.EagerInterProceduralEscapeAnalysis.V -import org.opalj.tac._ -import org.opalj.tac.fpcf.properties.TACAI - -/** - * New implementation of the fieldimmutability analysis - * @author Tobias Peter Roth - */ -class L3FieldMutabilityAnalysis_new private[analyses] (val project: SomeProject) extends FPCFAnalysis { - - final val typeExtensibility = project.get(TypeExtensibilityKey) - final val closedPackages = project.get(ClosedPackagesKey) - final val fieldAccessInformation = project.get(FieldAccessInformationKey) - - case class State() { - var hasImmutableType: Boolean = false - var isFinal = false - var effectivelyFinal = true - var influencedByMethod = false - var escapesViaMethod = false - } - - def doDetermineFieldMutability_new(entity: Entity): PropertyComputationResult = { - entity match { - case field: Field ⇒ determineFieldMutability_new(field) - case _ ⇒ - val m = entity.getClass.getName+"is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } - } - - - private[analyses] def determineFieldMutability_new(field: Field): PropertyComputationResult = { - val state: State = new State() - - //Reference Immutablity - if (field.isProtected || field.isPackagePrivate || field.isPublic) { - return Result(field, MutableField) - } - state.isFinal = field.isFinal - - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - dependencies += EPK(field.fieldType, TypeImmutability.key) - - /** - * def isSetByConstructor(): Boolean = { - * fieldAccessInformation.writeAccesses(field). - * filter(_._1.isConstructor). - * foreach(x ⇒ - * ( - * x._1.asMethod.descriptor.parameterTypes.foreach(x ⇒ - * if (x == field.fieldType) { - * return true - * }) - * )) - * false - * }* - */ - - // if constructor sets the field it could escape if it is not immutable - def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - //dependencies = dependencies.filter(_.e ne eps.e) - (eps: @unchecked) match { - //case _: InterimEP[_, _] ⇒ - // dependencies += eps - // InterimResult(InterimEP(field, MutableField), dependencies, c(state)) - case FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) ⇒ - if (!state.influencedByMethod & (state.isFinal || state.effectivelyFinal)) { - Result(field, DeepImmutableField) - } else { - - return InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) - } - case FinalEP(e, MutableType) ⇒ { - if ((state.isFinal || state.effectivelyFinal) && !state.influencedByMethod) { - return Result(field, ShallowImmutableField) - } - return Result(field, MutableField) - } - } - } - - for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode = getTACAI(method, pcs) - } { - if (methodInfluencesField(method, taCode.get, pcs, field)) { - state.influencedByMethod = true - } - if (escapesViaMethod(method, taCode.get, pcs, field)) { - state.escapesViaMethod = true - } - } - - //determine field Type immutability - val result = propertyStore(field.fieldType, TypeImmutability.key) - println("Result "+result) - - result match { - case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) ⇒ { - println("has immutable type") - state.hasImmutableType = true - } - case x: InterimEP[_, _] ⇒ return InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) - case _ ⇒ {} - } - println("Escapes Via Method: "+state.escapesViaMethod) - println("Effectively Final: "+state.effectivelyFinal) - println("is final: "+state.isFinal) - println("Has ImmutableType : "+state.hasImmutableType) - println("influenced by method: "+state.influencedByMethod) - println("State"+state.toString) - - if (field.isPublic || field.isPackagePrivate) { - return Result(field, MutableField) - } - - if (((state.isFinal) || state.effectivelyFinal) && state.escapesViaMethod && !state.hasImmutableType) { - return Result(field, MutableField) - } - if (((state.isFinal) || state.effectivelyFinal) && state.escapesViaMethod && state.hasImmutableType) { - return Result(field, DeepImmutableField) - } - if (state.influencedByMethod) { - return Result(field, MutableField) - } - if (((state.isFinal) || state.effectivelyFinal) && !state.hasImmutableType) { - return Result(field, ShallowImmutableField) - } - if (((state.isFinal) || state.effectivelyFinal) && state.hasImmutableType) { - return Result(field, DeepImmutableField) - } - if (((state.isFinal) || state.effectivelyFinal) && !state.influencedByMethod) { - if (state.hasImmutableType) { - return Result(field, DeepImmutableField) - } else { - return Result(field, ShallowImmutableField) - } - } - if (((state.isFinal) || state.effectivelyFinal) && state.escapesViaMethod && state.hasImmutableType) { - - return Result(field, DeepImmutableField) - } - - // not final or eff final reference and/or not influenced by a method - return Result(field, MutableField) - } - /** - * Analyzes field writes for a single method, returning false if the field may still be - * effectively final and true otherwise. - */ - - def escapesViaMethod( - method: Method, - taCode: TACode[TACMethodParameter, V], - pcs: PCs, - field: Field - ): Boolean = - { - if (method.isConstructor) { - method.asMethod.descriptor.parameterTypes.foreach(x ⇒ if (x == field.fieldType) return true) - } - println("method: "+method.toJava.toString) - val stmts = taCode.stmts - for (pc ← pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - println(stmt.astID) - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID ⇒ - if (method.isInitializer) { - println("field.isStatic "+field.isStatic) - if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - return true - } - if (field.isStatic) { - } - } else { - - if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - - return true; - } - return false - } - case _ ⇒ throw new RuntimeException("unexpected field access"); - } - } else { - // nothing to do as the put field is dead - } - } - } - false - } - - def methodInfluencesField( - method: Method, - taCode: TACode[TACMethodParameter, V], - pcs: PCs, - field: Field - ): Boolean = { - if (method.isConstructor) { - method.asMethod.descriptor.parameterTypes.foreach(x ⇒ if (x == field.fieldType) return true) - } - println("method: "+method.toJava.toString) - val stmts = taCode.stmts - for (pc ← pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - println(stmt.astID) - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID ⇒ - if (method.isInitializer) { - println("field.isStatic "+field.isStatic) - if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - return false - } - if (field.isStatic) { - } - } else { - - if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - return false; - } - return true - } - case _ ⇒ throw new RuntimeException("unexpected field access"); - } - } else { - // nothing to do as the put field is dead - } - } - } - false - } - - def referenceHasEscaped( - reference: V, - stmts: Array[Stmt[V]], - method: Method - ): Boolean = { - reference.definedBy.forall { defSite ⇒ - { - //TODO - false - } - } - } - - /** - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - - def getTACAI( - method: Method, - pcs: PCs - ): Option[TACode[TACMethodParameter, V]] = { - propertyStore(method, TACAI.key) match { - case finalEP: FinalEP[Method, TACAI] ⇒ - finalEP.ub.tac - case eps: InterimEP[Method, TACAI] ⇒ - eps.ub.tac - case epk ⇒ - None - } - } - -} - -trait L3FieldMutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { - - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(FieldMutability), - PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(FieldMutability_new), - PropertyBounds.ub(EscapeProperty) - ) - - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability_new) - -} - -/** - * Executor for the new field mutability analysis. - */ -object EagerL3FieldMutabilityAnalysis_new - extends L3FieldMutabilityAnalysisScheduler_new - with BasicFPCFEagerAnalysisScheduler { - - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L3FieldMutabilityAnalysis_new(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldMutability_new) - analysis - } - - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty -} - -/** - * Executor for the new lazy field mutability analysis. - */ -object LazyL3FieldMutabilityAnalysis_new - extends L3FieldMutabilityAnalysisScheduler_new - with BasicFPCFLazyAnalysisScheduler { - - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L3FieldMutabilityAnalysis_new(p) - //TODO - //ps.registerLazyPropertyComputation( - //FieldMutability_new.key, analysis.determineFieldMutability_new - //) - analysis - } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - -} From 5f5c1860679e84ee0da5b13b45701d32947547a1 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 6 Nov 2019 17:47:54 +0100 Subject: [PATCH 049/327] Introduces the field immutability analysis that is based on the reference immutability analysis together with the type immutability analysis together with a new lattice. --- .../FieldImmutabilityAnalysisDemo.scala | 72 +++++ .../fpcf/properties/FieldImmutability.scala | 47 ++++ .../L0FieldImmutabilityAnalysis.scala | 259 ++++++++++++++++++ 3 files changed, 378 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo.scala create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldImmutability.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala 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..e8d71a009f --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo.scala @@ -0,0 +1,72 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.EagerClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis +import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.DeepImmutableField +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.TACAITransformer +import org.opalj.tac.fpcf.analyses.escape.EagerSimpleEscapeAnalysis + +/** + * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0FieldImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0FieldImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll( + EagerTypeImmutabilityAnalysis, + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerClassImmutabilityAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerSimpleEscapeAnalysis, + TACAITransformer, + EagerL0FieldImmutabilityAnalysis + ); + + "Mutable Fields: " + propertyStore + .finalEntities(MutableField) + .toList + .toString() + "\n" + + "Shallow Immutable Fields: " + propertyStore + .finalEntities(ShallowImmutableField) + .toList + .toString() + "\n" + + "Deep Immutable Fields: " + propertyStore + .finalEntities(DeepImmutableField) + .toList + .toString() + } +} 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..4b7dc9cf75 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldImmutability.scala @@ -0,0 +1,47 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.properties + +import org.opalj.fpcf.Property +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 an immutable reference + * + * [[ShallowImmutableField]] A field with an immutable reference and a shallow immutable or mutable data type + * + * [[DeepImmutableField]] A field with an immutable reference and a deep immutable field type + * + * @author Tobias Peter Roth + */ +sealed trait FieldImmutability extends Property 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 MutableField extends FieldImmutability + +sealed trait ImmutableField extends FieldImmutability + +case object ShallowImmutableField extends ImmutableField + +case object DeepImmutableField extends ImmutableField diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala new file mode 100644 index 0000000000..213460beef --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -0,0 +1,259 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.analyses + +import org.opalj.br.Field +import org.opalj.br.analyses.FieldAccessInformationKey +import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.cg.ClosedPackagesKey +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.DeepImmutableField +import org.opalj.br.fpcf.properties.EscapeProperty +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.FieldMutability +import org.opalj.br.fpcf.properties.FieldPrematurelyRead +import org.opalj.br.fpcf.properties.ImmutableContainerType +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.ImmutableType +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.br.fpcf.properties.MutableType +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.properties.ReferenceImmutability +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.Entity +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.InterimEP +import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Property +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.fpcf.properties.TACAI + +case class State() { + var typeImmutability: Option[Boolean] = None + var referenceImmutability: Option[Boolean] = None +} + +/** + * Analyses that determines the mutability of org.opalj.br.Field + * Because it depends on the Field Immutability Lattice it combines the immutability of the fields reference and + * it's type. Thus needs the information of the reference of the field from the [[L0ReferenceImmutabilityAnalysis]] + * and the information of the type immutability determined by the type immutability analysis. + * Till now it uses the old type immutability analysis. + * + * @author Tobias Peter Roth + */ +class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) + extends FPCFAnalysis { + + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) + + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { + entity match { + case field: Field ⇒ determineFieldImmutability(field) + case _ ⇒ + val m = entity.getClass.getName+"is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + } + private[analyses] def determineFieldImmutability( + field: Field + ): ProperPropertyComputationResult = { + + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + + def hasImmutableType(field: Field): Option[Boolean] = { + val result = propertyStore(field.fieldType, TypeImmutability.key) + result match { + case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) ⇒ { + println("has immutable type") + Some(true) + } + case FinalEP(e, t) if (t == MutableType) ⇒ { + println("has mutable type") + Some(false) + } + case x @ _ ⇒ { + dependencies += x + println(x) + println("immutability of type couldn't be determined") + None + } + } + } + def hasImmutableReference(field: Field): Option[Boolean] = { + + propertyStore(field, ReferenceImmutability.key) match { + case FinalEP(_, MutableReference) ⇒ { + println("has mutable reference") + Some(false) + } + case FinalEP(_, ImmutableReference) ⇒ { + println("has immutable Reference") + Some(true) + } + case x @ _ ⇒ { + dependencies += x + println(x) + println("immutability of reference couldn't be determined") + None + } + } + + } + + val state: State = new State() + + def createResult(state: State): ProperPropertyComputationResult = { + state.referenceImmutability match { + case Some(false) ⇒ Result(field, MutableField) + case Some(true) ⇒ { + state.typeImmutability match { + case Some(false) ⇒ Result(field, ShallowImmutableField) + case Some(true) ⇒ Result(field, DeepImmutableField) + case None ⇒ { + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } + } + } + case None ⇒ { + + println("first interim") + println(dependencies) + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } + + } + } + def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { + println("c function called") + dependencies = dependencies.filter(_.e ne eps.e) + (eps: @unchecked) match { + case x: InterimEP[_, _] ⇒ { + println("interim in continuation function") + println(x) + dependencies += eps + InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + } + + case x @ FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) ⇒ { + println(x) + println("has immutable type. Determined by continuation function.") + state.typeImmutability = Some(true) + } + + case x @ FinalEP(_, MutableType) ⇒ { + println(x) + println("has mutable type. Determined by continuation function.") + state.typeImmutability = Some(false) + } + + case x @ FinalEP(_, MutableReference) ⇒ { + println(x) + println("has mutable reference. Determined by continuation function.") + state.referenceImmutability = Some(false) + } + + case x @ FinalEP(_, ImmutableReference) ⇒ { + println(x) + println("has immutable reference. Determined by continuation function.") + state.referenceImmutability = Some(true) + } + + case _ ⇒ {} + } + createResult(state) + } + + //-- + state.referenceImmutability = hasImmutableReference(field) + state.typeImmutability = hasImmutableType(field); + println("after type immutability determination") + createResult(state) + } + +} + +trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { + + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(FieldMutability), + PropertyBounds.lub(EscapeProperty), + PropertyBounds.lub(TypeImmutability), + PropertyBounds.lub(FieldImmutability), + PropertyBounds.ub(EscapeProperty) + ) + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) + +} + +/** + * Executor for the field immutability analysis. + */ +object EagerL0FieldImmutabilityAnalysis + extends L0FieldImmutabilityAnalysisScheduler + with BasicFPCFEagerAnalysisScheduler { + + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) + analysis + } + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty +} + +/** + * Executor for the lazy field immutability analysis. + */ +object LazyL0FieldImmutabilityAnalysis + extends L0FieldImmutabilityAnalysisScheduler + with BasicFPCFLazyAnalysisScheduler { + + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldImmutability.key, + analysis.determineFieldImmutability + ) + analysis + } + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + +} From 9d554dbd3f4d2616573d7817ed7b1d1c17855711 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 6 Nov 2019 18:04:27 +0100 Subject: [PATCH 050/327] Introduces a new lattice for class immutability analysis. Takes to given class immutability analysis and adapts it to the new lattice as well as the new field immutability analysis. --- .../ClassImmutabilityAnalysisDemo.scala | 74 +++ .../properties/ClassImmutability_new.scala | 50 ++ .../LxClassImmutabilityAnalysis_new.scala | 556 ++++++++++++++++++ 3 files changed, 680 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo.scala create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala 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..f8fe01cf43 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo.scala @@ -0,0 +1,74 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.analyses + +import java.net.URL + +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new +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.EagerClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis +import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +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.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis + +/** + * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result + * + * @author Tobias Peter Roth + */ +object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll( + EagerClassImmutabilityAnalysis, + EagerTypeImmutabilityAnalysis, + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + EagerLxClassImmutabilityAnalysis_new + ) + "Mutable Class: "+propertyStore + .finalEntities(MutableClass) + .toList + .toString()+"\n"+ + "Dependent Immutable Class: "+propertyStore + .finalEntities(DependentImmutableClass) + .toList + .toString()+"\n"+ + "Shallow Immutable Class: "+propertyStore + .finalEntities(ShallowImmutableClass) + .toList + .toString()+"\n"+ + "Deep Immutable Class: "+propertyStore + .finalEntities(DeepImmutableClass) + .toList + .toString()+"\n" + } +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala new file mode 100644 index 0000000000..7a4602aedc --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -0,0 +1,50 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.properties + +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { + final type Self = ClassImmutability_new +} + +/** + * Describes the class immutability of org.opalj.br.ClassFile + * + * [[MutableClass]] A class with minimum 1 mutable field + * + * [[DependentImmutableClass]] A class with no mutable field but with at least one generic field where the + * immutability depends on the generic type of the field + * + * [[ShallowImmutableClass]] A class with no mutable field, no field with generic type but with at least one + * shallow immutable field + * + * [[DeepImmutableClass]] A class with only deep immutable fields + * + * @author Tobias Peter Roth + */ +sealed trait ClassImmutability_new + extends Property + with ClassImmutabilityPropertyMetaInformation_new { + final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key +} + +object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { + + /** + * The key associated with every [[ClassImmutability_new]] property. + */ + final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( + "opalj.ClassImmutability_new", + MutableClass + ) +} + +case object MutableClass extends ClassImmutability_new + +case object DependentImmutableClass extends ClassImmutability_new + +case object ShallowImmutableClass extends ClassImmutability_new + +case object DeepImmutableClass extends ClassImmutability_new diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala new file mode 100644 index 0000000000..81a2bba0bb --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -0,0 +1,556 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses + +import org.opalj.br.ClassFile +import org.opalj.br.ObjectType +import org.opalj.log.LogContext +import org.opalj.log.OPALLogger +import org.opalj.fpcf.ELBP +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.Entity +import org.opalj.fpcf.EPK +import org.opalj.fpcf.EPS +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.Result +import org.opalj.fpcf.Results +import org.opalj.fpcf.IncrementalResult +import org.opalj.fpcf.InterimE +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.SomeEPS +import org.opalj.fpcf.UBP +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_new +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.FieldImmutability +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.ImmutableType +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.TypeImmutability + +/** + * This code mainly taken from the old ClassImmutabilityAnalysis adapted to the + * new class immutability analysis lattice and uses the new field immutability analysis. + * + * 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 + * those fields which could be final, but which are not declared as final. + * + * 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 Peter Roth + * + */ +class LxClassImmutabilityAnalysis_new(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_new //MutableObject + ): 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 ::= ( + ( + determineClassImmutability_new(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, MutableClass) //MutableObjectDueToUnknownSupertypes) + } + } + IncrementalResult(Results(results), nextComputations.iterator) + } + + def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { + e match { + case t: ObjectType => + //this is safe + classHierarchy.superclassType(t) match { + case None => Result(t, MutableClass) //MutableObjectDueToUnknownSupertypes) + case Some(superClassType) => + val cf = project.classFile(t) match { + case None => + return Result(t, MutableClass) // MutableObjectByAnalysis) //TODO consider other lattice element + case Some(cf) => cf + } + + propertyStore(superClassType, ClassImmutability_new.key) match { + case UBP(MutableClass) => //MutableObject) ⇒ + Result(t, MutableClass) + case eps: EPS[ObjectType, ClassImmutability_new] => + determineClassImmutability_new( + superClassType, + eps, + eps.isFinal, + lazyComputation = true + )(cf) + case epk => + determineClassImmutability_new( + 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 determineClassImmutability_new( + superClassType: ObjectType, + superClassInformation: EOptionP[Entity, Property], + superClassMutabilityIsFinal: Boolean, + lazyComputation: Boolean + )( + cf: ClassFile + ): ProperPropertyComputationResult = { + // assert(superClassMutability.isMutable.isNoOrUnknown) + 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.filter { f => + !f.isStatic + } + dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { + case FinalP(MutableField) => //NonFinalField) ⇒ + // <=> The class is definitively mutable and therefore also all subclasses. + if (lazyComputation) + return Result(t, MutableClass); //MutableObjectByAnalysis); // + else + return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); + case ep @ InterimE(e) => + hasFieldsWithUnknownMutability = true + (e, ep) + case epk @ EPK(e: Entity, _) => + // <=> The mutability information is not yet available. + hasFieldsWithUnknownMutability = true + (e, epk) + + // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields + }).toMap + + var minLocalImmutability: ClassImmutability_new = + if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) + MutableClass //MutableObjectByAnalysis + else + ShallowImmutableClass //ImmutableContainer + + // NOTE: maxLocalImmutability does not take the super classes' mutability into account! + var maxLocalImmutability: ClassImmutability_new = superClassInformation match { + case UBP(ImmutableContainer) => ShallowImmutableClass //ImmutableContainer + case _ => DeepImmutableClass // ImmutableObject + } + + 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 //ImmutableContainer + } + + var fieldTypes: Set[ObjectType] = Set.empty + if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { + fieldTypes = // IMPROVE Use the precise type of the field (if available)! + cf.fields.collect { + case f if !f.isStatic && f.fieldType.isObjectType => f.fieldType.asObjectType + }.toSet + } + + // For each dependent class file we have to determine the mutability + // of instances of the respective type to determine the immutability + // of instances of this class. + // Basically, we have to distinguish the following cases: + // - A field's type is mutable or conditionally immutable=> + // This class is conditionally immutable. + // - A field's type is immutable => + // The field is ignored. + // (This class is as immutable as its superclass.) + // - A field's type is at least conditionally mutable or + // The immutability of the field's type is not yet known => + // This type is AtLeastConditionallyImmutable + // We have to declare a dependency on the respective type's immutability. + // + // Additional handling is required w.r.t. the supertype: + // If the supertype is Immutable => + // Nothing special to do. + // If the supertype is AtLeastConditionallyImmutable => + // We have to declare a dependency on the supertype. + // If the supertype is ConditionallyImmutable => + // This type is also at most conditionally immutable. + // + // We furthermore have to take the mutability of the fields into consideration: + // If a field is not effectively final => + // This type is mutable. + // If a field is effectively final => + // Nothing special to do. + + val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) + val hasMutableOrConditionallyImmutableField = + // IMPROVE Use the precise type of the field (if available)! + fieldTypesImmutability.exists { eOptP => + eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) + } + + if (hasMutableOrConditionallyImmutableField) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + } 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 + } + fieldTypesWithUndecidedMutability.foreach { eOptP => + dependees += (eOptP.e -> eOptP) + } + } + + 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 + someEPS match { + // Superclass related dependencies: + // + case UBP(MutableClass) => // MutableObject) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); + + case LBP(DeepImmutableClass) => //_:ImmutableObject) ⇒ // the super class + dependees -= SuperClassKey + + case UBP(DeepImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container + if (someEPS.isFinal) dependees -= SuperClassKey + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + + case LBP(DeepImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container + if (minLocalImmutability != DeepImmutableClass && //ImmutableContainer && + !dependees.valuesIterator.exists(_.pk == FieldMutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + + case LUBP(MutableClass, DeepImmutableClass) => //_: MutableObject, ImmutableObject) ⇒ // No information about superclass + + // Properties related to the type of the class' fields. + // + case UBP(ShallowImmutableClass | MutableClass) => //ImmutableContainerType | MutableType) ⇒ + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + + case ELBP(e, ImmutableType) => // Immutable field type, no influence on mutability + dependees -= e + + case UBP(ImmutableType) => // No information about field type + + // Field Mutability related dependencies: + // + case UBP(MutableField) => //_: NonFinalField) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); + + case ELBP(e, _: FinalField) => + dependees -= e + if (minLocalImmutability != ImmutableContainer && + !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + + case UBP(_: FinalField) => // no information about field mutability + + } + + if (someEPS.isRefinable) { + val entity = if (someEPS.pk == ClassImmutability_new.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" + ) + */ + + // Lift lower bound once no dependencies other than field type mutabilities are left + if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && + dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer + + 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, c) + } + } + + //[DEBUG] assert(initialImmutability.isRefinable) + val result = + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + if (lazyComputation) + result + else { + val isFinal = dependees.isEmpty + createIncrementalResult( + t, + EPS(t, minLocalImmutability, maxLocalImmutability), + isFinal, + result + ) + } + } +} + +trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability, FieldImmutability) + + 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 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.map(cf => set(cf.thisType, DeepImmutableClass)) //ImmutableObject)) + + // 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 immutability analysis eagerly. + * @author Tobias Peter Roth + * @author Michael Eichberg + */ +object EagerLxClassImmutabilityAnalysis_new + extends ClassImmutabilityAnalysisScheduler_new + 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 LxClassImmutabilityAnalysis_new(p) + ps.scheduleEagerComputationsForEntities(cfs)( + analysis.determineClassImmutability_new( + superClassType = null, + FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), + superClassMutabilityIsFinal = true, + lazyComputation = false + ) + ) + analysis + } +} + +/** + * Scheduler to run the immutability analysis lazily. + * @author Tobias Peter Roth + * @author Michael Eichberg + */ +object LazyLxClassImmutabilityAnalysis_new + extends ClassImmutabilityAnalysisScheduler_new + with FPCFLazyAnalysisScheduler { + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + override def register( + p: SomeProject, + ps: PropertyStore, + unused: InitializationData + ): FPCFAnalysis = { + val analysis = new LxClassImmutabilityAnalysis_new(p) + ps.registerLazyPropertyComputation( + ClassImmutability_new.key, + analysis.doDetermineClassImmutability_new + ) + analysis + } +} From 71dae6b8b1eb4044b242681f93e978b3efcc92cd Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 6 Nov 2019 19:25:27 +0100 Subject: [PATCH 051/327] introducing tests - not final, still errors --- .../class_immutability/FinalEmptyClass.java | 7 ++ .../TrivialMutableClass.java | 16 ++++ .../field_immutability/FinalEmptyClass.java | 7 ++ .../fixtures/field_immutability/Test.java | 5 ++ .../TrivialMutableClass.java | 6 ++ .../privateFieldNotBlank_deep.java | 6 ++ .../privateFieldNotBlank_shallow.java | 6 ++ ...FinalFieldBlank_costructorEscape_deep.java | 10 +++ ...alFieldBlank_costructorEscape_shallow.java | 10 +++ .../privateFinalFieldNotBlank_deep.java | 5 ++ .../privateFinalFieldNotBlank_shallow.java | 5 ++ .../private_getterEscape_deep.java | 18 ++++ .../private_getterEscape_shallow.java | 10 +++ .../private_setter_deep.java | 14 ++++ .../private_setter_shallow.java | 14 ++++ .../privatefinal_getterEscape_deep.java | 10 +++ .../privatefinal_getterEscape_shallow.java | 9 ++ .../protectedClass_deep.java | 8 ++ .../protectedClass_shallow.java | 8 ++ .../reference_immutability/Singleton.java | 37 +++++++++ .../DeepImmutableClassAnnotation.java | 24 ++++++ .../DependentImmutableClassAnnotation.java | 25 ++++++ .../MutableClassAnnotation.java | 25 ++++++ .../ShallowImmutableClassAnnotation.java | 25 ++++++ .../DeepImmutableFieldAnnotation.java | 30 +++++++ .../MutableFieldAnnotation.java | 29 +++++++ .../ShallowImmutableFieldAnnotation.java | 29 +++++++ .../ImmutableReferenceAnnotation.java | 28 +++++++ .../LazyInitializedReferenceAnnotation.java | 29 +++++++ .../MutableReferenceAnnotation.java | 29 +++++++ .../opalj/fpcf/ClassImmutabilityTests.scala | 81 ++++++++++++++++++ .../opalj/fpcf/FieldImmutabilityTests.scala | 83 +++++++++++++++++++ .../fpcf/ReferenceImmutabilityTests.scala | 74 +++++++++++++++++ .../ClassImmutabilityMatcher.scala | 56 +++++++++++++ .../FieldImmutabilityMatcher.scala | 53 ++++++++++++ .../ReferenceImmutabilityMatcher.scala | 59 +++++++++++++ 36 files changed, 890 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java new file mode 100644 index 0000000000..09f556d812 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java @@ -0,0 +1,7 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; + +@DeepImmutableClassAnnotation("trivial empty") +public final class FinalEmptyClass { +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java new file mode 100644 index 0000000000..4127e70979 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java @@ -0,0 +1,16 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +@MutableClassAnnotation("because of public fields") +public class TrivialMutableClass { + @MutableReferenceAnnotation("public") + @MutableFieldAnnotation("mutable reference") + public int n = 0; + + @MutableReferenceAnnotation("public") + @MutableFieldAnnotation("mutable reference") + public String name = "name"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java new file mode 100644 index 0000000000..eefbc17fa1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java @@ -0,0 +1,7 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; + +@DeepImmutableClassAnnotation("because it is empty") +public final class FinalEmptyClass { +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java new file mode 100644 index 0000000000..08edf8f91e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java @@ -0,0 +1,5 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class Test { + private String name = "name"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java new file mode 100644 index 0000000000..ea9dbaabb8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java @@ -0,0 +1,6 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class TrivialMutableClass { + public int n = 0; + public String name = "name"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java new file mode 100644 index 0000000000..f57aea697a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java @@ -0,0 +1,6 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privateFieldNotBlank_deep { + + private FinalEmptyClass name = new FinalEmptyClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java new file mode 100644 index 0000000000..ef894f276b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java @@ -0,0 +1,6 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privateFieldNotBlank_shallow { + + private TrivialMutableClass tmc = new TrivialMutableClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java new file mode 100644 index 0000000000..7bd19b235f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privateFinalFieldBlank_costructorEscape_deep { + + + private final FinalEmptyClass fec; + public privateFinalFieldBlank_costructorEscape_deep(FinalEmptyClass fec) { + this.fec = fec; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java new file mode 100644 index 0000000000..7e81db2a26 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privateFinalFieldBlank_costructorEscape_shallow { + + private final TrivialMutableClass tmc; + + public privateFinalFieldBlank_costructorEscape_shallow(TrivialMutableClass tmc) { + this.tmc = tmc; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java new file mode 100644 index 0000000000..28cde37ff6 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java @@ -0,0 +1,5 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privateFinalFieldNotBlank_deep { + private final FinalEmptyClass fec = new FinalEmptyClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java new file mode 100644 index 0000000000..350f8366dd --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java @@ -0,0 +1,5 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privateFinalFieldNotBlank_shallow { + private final TrivialMutableClass tmc = new TrivialMutableClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java new file mode 100644 index 0000000000..a6f6d75b7a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java @@ -0,0 +1,18 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("") +public class private_getterEscape_deep { + + public FinalEmptyClass getFec() { + return fec; + } + + @ImmutableReferenceAnnotation("") + @MutableFieldAnnotation("") + private FinalEmptyClass fec = new FinalEmptyClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java new file mode 100644 index 0000000000..64f799ccda --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class private_getterEscape_shallow { + public TrivialMutableClass getTmc() { + return tmc; + } + + private TrivialMutableClass tmc = new TrivialMutableClass(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java new file mode 100644 index 0000000000..a6b38fdeaa --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java @@ -0,0 +1,14 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class private_setter_deep { + + public void setFec(FinalEmptyClass fec) { + this.fec = fec; + } + + private FinalEmptyClass fec = new FinalEmptyClass(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java new file mode 100644 index 0000000000..54a592857a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java @@ -0,0 +1,14 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class private_setter_shallow { + + public void setTmc(TrivialMutableClass tmc) { + this.tmc = tmc; + } + + private TrivialMutableClass tmc = new TrivialMutableClass(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java new file mode 100644 index 0000000000..738c10c689 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privatefinal_getterEscape_deep { + public FinalEmptyClass getFec() { + return fec; + } + + private final FinalEmptyClass fec = new FinalEmptyClass(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java new file mode 100644 index 0000000000..3cb21f4715 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java @@ -0,0 +1,9 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +public class privatefinal_getterEscape_shallow { + public TrivialMutableClass getTmc() { + return tmc; + } + + private final TrivialMutableClass tmc = new TrivialMutableClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java new file mode 100644 index 0000000000..94f3ab5884 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java @@ -0,0 +1,8 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; + +public class protectedClass_deep { + protected FinalEmptyClass fec = new FinalEmptyClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java new file mode 100644 index 0000000000..177fbd12f1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java @@ -0,0 +1,8 @@ +package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; + +public class protectedClass_shallow { + protected TrivialMutableClass tmc = new TrivialMutableClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java new file mode 100644 index 0000000000..4824dea4c4 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java @@ -0,0 +1,37 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.reference_immutability; +/* + * Test case from the old L2 Field Mutability tests + */ + +public class Singleton { + + private String name; + + + private Object mutex = new Object(); + + private Singleton() { + this.name = ""; + } + + public String getName() { + synchronized (mutex) { + return name; + } + } + + // STATIC FUNCTIONALITY + + 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/properties/class_immutability/DeepImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java new file mode 100644 index 0000000000..ad27d7d1c6 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.class_immutability; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated class is deep immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ClassImmutability_new",validator = DeepImmutableClassMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DeepImmutableClassAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java new file mode 100644 index 0000000000..c85be61a9c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.class_immutability; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated class is dependent immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ClassImmutability_new",validator = DependentImmutableClassMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DependentImmutableClassAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java new file mode 100644 index 0000000000..648a8431aa --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.class_immutability; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.class_mutability.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 Tobias Peter Roth + */ +@PropertyValidator(key = "ClassImmutability_new",validator = MutableClassMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface MutableClassAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java new file mode 100644 index 0000000000..dd5152b9ff --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.class_immutability; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated class is shallow immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ClassImmutability_new",validator = ShallowImmutableClassMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ShallowImmutableClassAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java new file mode 100644 index 0000000000..05c2833d38 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java @@ -0,0 +1,30 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.field_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is deep immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key="FieldImmutability",validator=DeepImmutableFieldMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DeepImmutableFieldAnnotation { + + /** + * A short reasoning of this property. + */ + String value() ; // default = "N/A"; + + Class[] analyses() default { + L0FieldImmutabilityAnalysis.class + }; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java new file mode 100644 index 0000000000..b62adbe54f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.field_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is mutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key="FieldImmutability",validator=MutableFieldMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface MutableFieldAnnotation { + + /** + * A short reasoning of this property. + */ + String value() ; // default = "N/A"; + + Class[] analyses() default { + L0FieldImmutabilityAnalysis.class + }; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java new file mode 100644 index 0000000000..c522fc6f5d --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.field_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is shallow immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key="FieldImmutability",validator=ShallowImmutableFieldMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ShallowImmutableFieldAnnotation { + + /** + * A short reasoning of this property. + */ + String value() ; // default = "N/A"; + + Class[] analyses() default { + L0FieldImmutabilityAnalysis.class + }; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java new file mode 100644 index 0000000000..c4b12d7c0a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated reference is immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutability",validator = ImmutableReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ImmutableReferenceAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java new file mode 100644 index 0000000000..45b58b85ab --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is lazy initialized + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface LazyInitializedReferenceAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java new file mode 100644 index 0000000000..822be18a0a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated reference is mutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutability",validator = MutableReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface MutableReferenceAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala new file mode 100644 index 0000000000..e9c5e67fc9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -0,0 +1,81 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.EagerClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis +import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new + +/** + * @author Tobias Peter Roth + */ +class ClassImmutabilityTests extends PropertiesTest { + + 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("ReferenceImmutability")) + } + + describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + val as = executeAnalyses( + Set( + EagerClassImmutabilityAnalysis, + EagerTypeImmutabilityAnalysis, + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + EagerLxClassImmutabilityAnalysis_new + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ + +} 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..64be6de388 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -0,0 +1,83 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.EagerClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis +import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.TACAITransformer +import org.opalj.tac.fpcf.analyses.escape.EagerSimpleEscapeAnalysis + +/** + * @author Tobias Peter Roth + */ +class FieldImmutabilityTests extends PropertiesTest { + + 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("ReferenceImmutability")) + } + + describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + val as = executeAnalyses( + Set( + EagerTypeImmutabilityAnalysis, + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerClassImmutabilityAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerSimpleEscapeAnalysis, + TACAITransformer, + EagerL0FieldImmutabilityAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala new file mode 100644 index 0000000000..8cfd18c1a9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -0,0 +1,74 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.{EagerL0PurityAnalysis, EagerUnsoundPrematurelyReadFieldsAnalysis} +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.{ + EagerL0ReferenceImmutabilityAnalysis, + EagerL1FieldMutabilityAnalysis +} + +/** + * @author Tobias Peter Roth + */ +class ReferenceImmutabilityTests extends PropertiesTest { + + 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("ReferenceImmutability")) + } + + describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { + val as = executeAnalyses( + Set( + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala new file mode 100644 index 0000000000..5b210c2997 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala @@ -0,0 +1,56 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.class_immutability + +import org.opalj.br.{AnnotationLike, ObjectType} +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties._ +import org.opalj.fpcf.{Entity, Property} +import org.opalj.fpcf.properties.AbstractPropertyMatcher + +/** + * @author Tobias Peter Roth + */ +class ClassImmutabilityMatcher(val property: ClassImmutability_new) + 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(MutableClass) + +class DependentImmutableClassMatcher extends ClassImmutabilityMatcher(DependentImmutableClass) + +class ShallowImmutableClassMatcher extends ClassImmutabilityMatcher(ShallowImmutableClass) + +class DeepImmutableClassMatcher extends ClassImmutabilityMatcher(DeepImmutableClass) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala new file mode 100644 index 0000000000..03298c1cc8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala @@ -0,0 +1,53 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.field_immutability + +import org.opalj.br.{AnnotationLike, ObjectType} +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties._ +import org.opalj.fpcf.{Entity, Property} +import org.opalj.fpcf.properties.AbstractPropertyMatcher + +/** + * @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(MutableField) + +class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(ShallowImmutableField) + +class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(DeepImmutableField) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala new file mode 100644 index 0000000000..de82d1030b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala @@ -0,0 +1,59 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.br.fpcf.properties.ReferenceImmutability +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher + +/** + * @author Tobias Peter Roth + */ +class ReferenceImmutabilityMatcher(val property: ReferenceImmutability) + 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 MutableReferenceMatcher extends ReferenceImmutabilityMatcher(MutableReference) + +class LazyInitializedReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedReference) + +class ImmutableReferenceMatcher extends ReferenceImmutabilityMatcher(ImmutableReference) From 1e537db53d463e6b983148e5140d2a0c3ad0f825 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 7 Nov 2019 11:34:09 +0100 Subject: [PATCH 052/327] adjusted the behavior of the createResult function in case of empty dependencies --- .../L0FieldImmutabilityAnalysis.scala | 310 +++++++++--------- 1 file changed, 155 insertions(+), 155 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 213460beef..3b03a228c7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -40,8 +40,8 @@ import org.opalj.fpcf.SomeEPS import org.opalj.tac.fpcf.properties.TACAI case class State() { - var typeImmutability: Option[Boolean] = None - var referenceImmutability: Option[Boolean] = None + var typeImmutability: Option[Boolean] = None + var referenceImmutability: Option[Boolean] = None } /** @@ -56,162 +56,162 @@ case class State() { class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - final val typeExtensibility = project.get(TypeExtensibilityKey) - final val closedPackages = project.get(ClosedPackagesKey) - final val fieldAccessInformation = project.get(FieldAccessInformationKey) + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) - def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { - entity match { - case field: Field ⇒ determineFieldImmutability(field) - case _ ⇒ - val m = entity.getClass.getName+"is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { + entity match { + case field: Field => determineFieldImmutability(field) + case _ => + val m = entity.getClass.getName + "is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + } + private[analyses] def determineFieldImmutability( + field: Field + ): ProperPropertyComputationResult = { + + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + + def hasImmutableType(field: Field): Option[Boolean] = { + val result = propertyStore(field.fieldType, TypeImmutability.key) + result match { + case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) => { + println("has immutable type") + Some(true) + } + case FinalEP(e, t) if (t == MutableType) => { + println("has mutable type") + Some(false) + } + case x @ _ => { + dependencies += x + println(x) + println("immutability of type couldn't be determined") + None } + } } - private[analyses] def determineFieldImmutability( - field: Field - ): ProperPropertyComputationResult = { + def hasImmutableReference(field: Field): Option[Boolean] = { - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - - def hasImmutableType(field: Field): Option[Boolean] = { - val result = propertyStore(field.fieldType, TypeImmutability.key) - result match { - case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) ⇒ { - println("has immutable type") - Some(true) - } - case FinalEP(e, t) if (t == MutableType) ⇒ { - println("has mutable type") - Some(false) - } - case x @ _ ⇒ { - dependencies += x - println(x) - println("immutability of type couldn't be determined") - None - } - } + propertyStore(field, ReferenceImmutability.key) match { + case FinalEP(_, MutableReference) => { + println("has mutable reference") + Some(false) } - def hasImmutableReference(field: Field): Option[Boolean] = { - - propertyStore(field, ReferenceImmutability.key) match { - case FinalEP(_, MutableReference) ⇒ { - println("has mutable reference") - Some(false) - } - case FinalEP(_, ImmutableReference) ⇒ { - println("has immutable Reference") - Some(true) - } - case x @ _ ⇒ { - dependencies += x - println(x) - println("immutability of reference couldn't be determined") - None - } - } - + case FinalEP(_, ImmutableReference) => { + println("has immutable Reference") + Some(true) } + case x @ _ => { + dependencies += x + println(x) + println("immutability of reference couldn't be determined") + None + } + } - val state: State = new State() - - def createResult(state: State): ProperPropertyComputationResult = { - state.referenceImmutability match { - case Some(false) ⇒ Result(field, MutableField) - case Some(true) ⇒ { - state.typeImmutability match { - case Some(false) ⇒ Result(field, ShallowImmutableField) - case Some(true) ⇒ Result(field, DeepImmutableField) - case None ⇒ { - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } - } - } - case None ⇒ { - - println("first interim") - println(dependencies) - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } + } + val state: State = new State() + + def createResult(state: State): ProperPropertyComputationResult = { + state.referenceImmutability match { + case Some(false) => Result(field, MutableField) + case Some(true) => { + state.typeImmutability match { + case Some(true) => Result(field, DeepImmutableField) + case Some(false) => Result(field, ShallowImmutableField) + case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) + case None => { + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) } + } + } + case None => { + println("first interim") + println(dependencies) + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) } - def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - println("c function called") - dependencies = dependencies.filter(_.e ne eps.e) - (eps: @unchecked) match { - case x: InterimEP[_, _] ⇒ { - println("interim in continuation function") - println(x) - dependencies += eps - InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) - } - case x @ FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) ⇒ { - println(x) - println("has immutable type. Determined by continuation function.") - state.typeImmutability = Some(true) - } + } + } + def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { + println("c function called") + dependencies = dependencies.filter(_.e ne eps.e) + (eps: @unchecked) match { + case x: InterimEP[_, _] => { + println("interim in continuation function") + println(x) + dependencies += eps + InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + } - case x @ FinalEP(_, MutableType) ⇒ { - println(x) - println("has mutable type. Determined by continuation function.") - state.typeImmutability = Some(false) - } + case x @ FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) => { + println(x) + println("has immutable type. Determined by continuation function.") + state.typeImmutability = Some(true) + } - case x @ FinalEP(_, MutableReference) ⇒ { - println(x) - println("has mutable reference. Determined by continuation function.") - state.referenceImmutability = Some(false) - } + case x @ FinalEP(_, MutableType) => { + println(x) + println("has mutable type. Determined by continuation function.") + state.typeImmutability = Some(false) + } - case x @ FinalEP(_, ImmutableReference) ⇒ { - println(x) - println("has immutable reference. Determined by continuation function.") - state.referenceImmutability = Some(true) - } + case x @ FinalEP(_, MutableReference) => { + println(x) + println("has mutable reference. Determined by continuation function.") + state.referenceImmutability = Some(false) + } - case _ ⇒ {} - } - createResult(state) + case x @ FinalEP(_, ImmutableReference) => { + println(x) + println("has immutable reference. Determined by continuation function.") + state.referenceImmutability = Some(true) } - //-- - state.referenceImmutability = hasImmutableReference(field) - state.typeImmutability = hasImmutableType(field); - println("after type immutability determination") - createResult(state) + case x @ _ => println("default value: " + x) + } + createResult(state) } + //-- + state.referenceImmutability = hasImmutableReference(field) + state.typeImmutability = hasImmutableType(field); + println("after type immutability determination") + createResult(state) + } + } trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(FieldMutability), - PropertyBounds.lub(EscapeProperty), - PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(FieldImmutability), - PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(FieldMutability), + PropertyBounds.lub(EscapeProperty), + PropertyBounds.lub(TypeImmutability), + PropertyBounds.lub(FieldImmutability), + PropertyBounds.ub(EscapeProperty) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) } @@ -222,16 +222,16 @@ object EagerL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -241,19 +241,19 @@ object LazyL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - FieldImmutability.key, - analysis.determineFieldImmutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldImmutability.key, + analysis.determineFieldImmutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } From 9dd8c8a14d1fda9ebfd84a085877acfd90b09648 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 12 Nov 2019 15:20:29 +0100 Subject: [PATCH 053/327] New Lattice Element Dependent Immutable Field and the according handling of the Field Immutability Analysis and the new Class Immutability Analysis. --- .../ClassImmutabilityAnalysisDemo.scala | 78 ++++---- .../FieldImmutabilityAnalysisDemo.scala | 5 + .../properties/ClassImmutability_new.scala | 18 +- .../fpcf/properties/FieldImmutability.scala | 4 + .../L0FieldImmutabilityAnalysis.scala | 66 ++++--- .../LxClassImmutabilityAnalysis_new.scala | 167 ++++++++++++------ 6 files changed, 208 insertions(+), 130 deletions(-) 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 index f8fe01cf43..4fa1ac4f0c 100644 --- 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 @@ -8,7 +8,6 @@ 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.EagerClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis @@ -27,48 +26,47 @@ import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis */ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" - override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(project: Project[URL]): String = { + def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) + val analysesManager = project.get(FPCFAnalysesManagerKey) - val (propertyStore, _) = analysesManager.runAll( - EagerClassImmutabilityAnalysis, - EagerTypeImmutabilityAnalysis, - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerL0ReferenceImmutabilityAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - EagerLxClassImmutabilityAnalysis_new - ) - "Mutable Class: "+propertyStore - .finalEntities(MutableClass) - .toList - .toString()+"\n"+ - "Dependent Immutable Class: "+propertyStore - .finalEntities(DependentImmutableClass) - .toList - .toString()+"\n"+ - "Shallow Immutable Class: "+propertyStore - .finalEntities(ShallowImmutableClass) - .toList - .toString()+"\n"+ - "Deep Immutable Class: "+propertyStore - .finalEntities(DeepImmutableClass) - .toList - .toString()+"\n" - } + val (propertyStore, _) = analysesManager.runAll( + EagerTypeImmutabilityAnalysis, + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + EagerLxClassImmutabilityAnalysis_new + ) + "Mutable Class: " + propertyStore + .finalEntities(MutableClass) + .toList + .toString() + "\n" + + "Dependent Immutable Class: " + propertyStore + .finalEntities(DependentImmutableClass) + .toList + .toString() + "\n" + + "Shallow Immutable Class: " + propertyStore + .finalEntities(ShallowImmutableClass) + .toList + .toString() + "\n" + + "Deep Immutable Class: " + propertyStore + .finalEntities(DeepImmutableClass) + .toList + .toString() + "\n" + } } 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 index e8d71a009f..8a1fd29738 100644 --- 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 @@ -13,6 +13,7 @@ import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis 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.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis @@ -64,6 +65,10 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .finalEntities(ShallowImmutableField) .toList .toString() + "\n" + + "Dependet Immutable Fields:" + propertyStore + .finalEntities(DependentImmutableField) + .toList + .toString() + "\n" + "Deep Immutable Fields: " + propertyStore .finalEntities(DeepImmutableField) .toList diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index 7a4602aedc..8059b5d74e 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -6,7 +6,7 @@ import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = ClassImmutability_new + final type Self = ClassImmutability_new } /** @@ -27,18 +27,18 @@ sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaIn sealed trait ClassImmutability_new extends Property with ClassImmutabilityPropertyMetaInformation_new { - final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key + final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key } object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[ClassImmutability_new]] property. - */ - final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( - "opalj.ClassImmutability_new", - MutableClass - ) + /** + * The key associated with every [[ClassImmutability_new]] property. + */ + final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( + "opalj.ClassImmutability_new", + MutableClass + ) } case object MutableClass extends ClassImmutability_new 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 index 4b7dc9cf75..7c6fb4c654 100644 --- 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 @@ -17,6 +17,8 @@ sealed trait FieldImmutabilityPropertyMetaInformation extends PropertyMetaInform * * [[ShallowImmutableField]] A field with an immutable reference and a shallow immutable or mutable data type * + * [[DependentImmutableField]] A field which immutability depends on its type. + * * [[DeepImmutableField]] A field with an immutable reference and a deep immutable field type * * @author Tobias Peter Roth @@ -44,4 +46,6 @@ sealed trait ImmutableField extends FieldImmutability case object ShallowImmutableField extends ImmutableField +case object DependentImmutableField extends ImmutableField + case object DeepImmutableField extends ImmutableField diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 3b03a228c7..c0762cdd2c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -2,6 +2,7 @@ package org.opalj.br.fpcf.analyses import org.opalj.br.Field +import org.opalj.br.ObjectType import org.opalj.br.analyses.FieldAccessInformationKey import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.ClosedPackagesKey @@ -11,17 +12,15 @@ import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.DeepImmutableField -import org.opalj.br.fpcf.properties.EscapeProperty +import org.opalj.br.fpcf.properties.DependentImmutableField import org.opalj.br.fpcf.properties.FieldImmutability -import org.opalj.br.fpcf.properties.FieldMutability -import org.opalj.br.fpcf.properties.FieldPrematurelyRead import org.opalj.br.fpcf.properties.ImmutableContainerType import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.ImmutableType +import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableField import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.MutableType -import org.opalj.br.fpcf.properties.Purity import org.opalj.br.fpcf.properties.ReferenceImmutability import org.opalj.br.fpcf.properties.ShallowImmutableField import org.opalj.br.fpcf.properties.TypeImmutability @@ -75,7 +74,13 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var dependencies: Set[EOptionP[Entity, Property]] = Set.empty def hasImmutableType(field: Field): Option[Boolean] = { + println("Field:: " + field) + println(field.fieldType) + println(field.fieldType.isBaseType) + if (field.fieldType.isArrayType) return Some(true); //TODO + if (field.fieldType.isBaseType) return Some(true); val result = propertyStore(field.fieldType, TypeImmutability.key) + println("result: " + result) result match { case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) => { println("has immutable type") @@ -100,7 +105,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) println("has mutable reference") Some(false) } - case FinalEP(_, ImmutableReference) => { + case FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO println("has immutable Reference") Some(true) } @@ -111,32 +116,38 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) None } } - } val state: State = new State() def createResult(state: State): ProperPropertyComputationResult = { + println("reference Immutability: " + state.referenceImmutability) + println("type immutabiliy: " + state.typeImmutability) + state.referenceImmutability match { case Some(false) => Result(field, MutableField) case Some(true) => { - state.typeImmutability match { - case Some(true) => Result(field, DeepImmutableField) - case Some(false) => Result(field, ShallowImmutableField) - case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) - case None => { - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) + //If the field type is object. It is a generic field + if (field.fieldType == ObjectType("java/lang/Object")) + Result(field, DependentImmutableField) + else + state.typeImmutability match { + case Some(true) => Result(field, DeepImmutableField) + case Some(false) => Result(field, ShallowImmutableField) + case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) + case None => { + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } } - } } + case None if (dependencies.isEmpty) => Result(field, MutableField) case None => { - println("first interim") println(dependencies) InterimResult( field, @@ -178,7 +189,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.referenceImmutability = Some(false) } - case x @ FinalEP(_, ImmutableReference) => { + case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO println(x) println("has immutable reference. Determined by continuation function.") state.referenceImmutability = Some(true) @@ -201,14 +212,15 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), + //PropertyBounds.lub(Purity), + //PropertyBounds.lub(FieldPrematurelyRead), PropertyBounds.ub(TACAI), - PropertyBounds.lub(FieldMutability), - PropertyBounds.lub(EscapeProperty), + //PropertyBounds.lub(FieldMutability), + //PropertyBounds.lub(EscapeProperty), + PropertyBounds.lub(ReferenceImmutability), PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(FieldImmutability), - PropertyBounds.ub(EscapeProperty) + PropertyBounds.lub(FieldImmutability) + //PropertyBounds.ub(EscapeProperty) ) final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 81a2bba0bb..9e1c4c9995 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -34,19 +34,19 @@ import org.opalj.br.fpcf.FPCFEagerAnalysisScheduler import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler import org.opalj.br.fpcf.properties.ClassImmutability_new 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.FieldMutability -import org.opalj.br.fpcf.properties.FinalField -import org.opalj.br.fpcf.properties.ImmutableContainer import org.opalj.br.fpcf.properties.ImmutableType import org.opalj.br.fpcf.properties.MutableClass import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.MutableType import org.opalj.br.fpcf.properties.ShallowImmutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableField import org.opalj.br.fpcf.properties.TypeImmutability /** - * This code mainly taken from the old ClassImmutabilityAnalysis adapted to the - * new class immutability analysis lattice and uses the new field immutability analysis. * * 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 @@ -184,6 +184,12 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal cf: ClassFile ): ProperPropertyComputationResult = { // assert(superClassMutability.isMutable.isNoOrUnknown) + + //---generic classes handling + //printf(cf.asVirtualClass.asClassFile.classSignature.get.toString()) + //cf.asClassFile.classSignature.get.formalTypeParameters. + //--------------------------------------------- + val t = cf.thisType var dependees = Map.empty[Entity, EOptionP[Entity, Property]] @@ -198,24 +204,54 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal val instanceFields = cf.fields.filter { f => !f.isStatic } - dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { - case FinalP(MutableField) => //NonFinalField) ⇒ - // <=> The class is definitively mutable and therefore also all subclasses. - if (lazyComputation) - return Result(t, MutableClass); //MutableObjectByAnalysis); // - else - return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); - case ep @ InterimE(e) => - hasFieldsWithUnknownMutability = true - (e, ep) - case epk @ EPK(e: Entity, _) => - // <=> The mutability information is not yet available. - hasFieldsWithUnknownMutability = true - (e, epk) - - // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields - }).toMap + var hasShallowImmutableFields = false + var hasDependentImmutableFields = false + val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) + fieldsPropertyStoreInformation.foreach( + f => + f match { + 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 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); + } + ) + /** + * dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { + * case FinalP(MutableField) => //NonFinalField) ⇒ + * // <=> The class is definitively mutable and therefore also all subclasses. + * if (lazyComputation) + * return Result(t, MutableClass); //MutableObjectByAnalysis); // + * else + * return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); + * case ep @ InterimE(e) => + * hasFieldsWithUnknownMutability = true + * (e, ep) + * case epk @ EPK(e: Entity, _) => + * // <=> The mutability information is not yet available. + * hasFieldsWithUnknownMutability = true + * (e, epk) + * + * // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields + * }).toMap * + */ var minLocalImmutability: ClassImmutability_new = if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) MutableClass //MutableObjectByAnalysis @@ -224,8 +260,14 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability_new = superClassInformation match { - case UBP(ImmutableContainer) => ShallowImmutableClass //ImmutableContainer - case _ => DeepImmutableClass // ImmutableObject + case UBP(ShallowImmutableClass) => ShallowImmutableClass //ImmutableContainer + case _ => DeepImmutableClass // ImmutableObject + } + + if (hasDependentImmutableFields) { + maxLocalImmutability = DependentImmutableClass + } else if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } if (cf.fields.exists(f => !f.isStatic && f.fieldType.isArrayType)) { @@ -235,13 +277,13 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } - var fieldTypes: Set[ObjectType] = Set.empty - if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { - fieldTypes = // IMPROVE Use the precise type of the field (if available)! - cf.fields.collect { - case f if !f.isStatic && f.fieldType.isObjectType => f.fieldType.asObjectType - }.toSet - } + //--var fieldTypes: Set[ObjectType] = Set.empty + //--if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { + //-- fieldTypes = // IMPROVE Use the precise type of the field (if available)! + // cf.fields.collect { + // case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType + // }.toSet + //} // For each dependent class file we have to determine the mutability // of instances of the respective type to determine the immutability @@ -271,25 +313,27 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal // If a field is effectively final => // Nothing special to do. - val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) - val hasMutableOrConditionallyImmutableField = - // IMPROVE Use the precise type of the field (if available)! - fieldTypesImmutability.exists { eOptP => - eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) - } + //val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) + //val hasMutableOrConditionallyImmutableField = + // IMPROVE Use the precise type of the field (if available)! + //-- fieldTypesImmutability.exists { eOptP ⇒ + //-- eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) + //-- } - if (hasMutableOrConditionallyImmutableField) { + if (hasShallowImmutableFields) { maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - } 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 - } - fieldTypesWithUndecidedMutability.foreach { eOptP => - dependees += (eOptP.e -> eOptP) - } - } + } //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 + * } + * fieldTypesWithUndecidedMutability.foreach { eOptP => + * dependees += (eOptP.e -> eOptP) + * }* + */ + //} if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { // <=> the super classes' immutability is final @@ -310,6 +354,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal def c(someEPS: SomeEPS): ProperPropertyComputationResult = { //[DEBUG] val oldDependees = dependees + dependees = dependees.filter(_._1 ne someEPS.e) + println("someEPS: " + someEPS) someEPS match { // Superclass related dependencies: // @@ -319,14 +365,14 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case LBP(DeepImmutableClass) => //_:ImmutableObject) ⇒ // the super class dependees -= SuperClassKey - case UBP(DeepImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container + case UBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container if (someEPS.isFinal) dependees -= SuperClassKey maxLocalImmutability = ShallowImmutableClass //ImmutableContainer dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - case LBP(DeepImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container + case LBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container if (minLocalImmutability != DeepImmutableClass && //ImmutableContainer && - !dependees.valuesIterator.exists(_.pk == FieldMutability.key)) + !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible case LUBP(MutableClass, DeepImmutableClass) => //_: MutableObject, ImmutableObject) ⇒ // No information about superclass @@ -342,18 +388,29 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case UBP(ImmutableType) => // No information about field type + case FinalP(MutableType) => Result(t, MutableClass) //TODO check + + case FinalP(DependentImmutableField) => maxLocalImmutability = DependentImmutableClass // Field Mutability related dependencies: // + + case FinalP(ShallowImmutableField) => { + maxLocalImmutability = ShallowImmutableClass + } + case FinalP(MutableField) => return Result(t, MutableClass); + case UBP(MutableField) => //_: NonFinalField) ⇒ return Result(t, MutableClass); //MutableObjectByAnalysis); - case ELBP(e, _: FinalField) => + case ELBP(e, ShallowImmutableField | DeepImmutableField) => //FinalField) => dependees -= e - if (minLocalImmutability != ImmutableContainer && + if (minLocalImmutability != ShallowImmutableClass && // ImmutableContainer && !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - case UBP(_: FinalField) => // no information about field mutability + case UBP(ShallowImmutableField | DeepImmutableField) => //_: FinalField) ⇒ // no information about field mutability + + case _ => Result(t, MutableClass) //TODO check } @@ -402,6 +459,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } //[DEBUG] assert(initialImmutability.isRefinable) + println("minLocalImmutability: " + minLocalImmutability) + println("maxLocalImmutability: " + maxLocalImmutability) val result = InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) if (lazyComputation) From 91b7ba334965510cf533b8a39eed0d08b44ce991 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 12 Nov 2019 15:39:07 +0100 Subject: [PATCH 054/327] Improved test environment: ReferenceImmutability, FieldImmutability, and ClassImmutability_new tests should no work --- .../ReferenceImmutabilityAnalysisDemo.scala | 73 +- .../class_immutability/FinalEmptyClass.java | 3 +- .../class_immutability/Generic_class1.java | 33 + .../TrivialMutableClass.java | 2 +- .../field_immutability/FinalEmptyClass.java | 2 +- .../fixtures/field_immutability/Test.java | 8 + .../TrivialMutableClass.java | 12 + .../privateFieldNotBlank_deep.java | 8 +- .../privateFieldNotBlank_shallow.java | 7 + ...FinalFieldBlank_costructorEscape_deep.java | 8 +- ...alFieldBlank_costructorEscape_shallow.java | 7 + .../privateFinalFieldNotBlank_deep.java | 8 + .../privateFinalFieldNotBlank_shallow.java | 8 + .../private_getterEscape_deep.java | 15 +- .../private_getterEscape_shallow.java | 7 + .../private_setter_deep.java | 4 + .../private_setter_shallow.java | 4 + .../privatefinal_getterEscape_deep.java | 7 + .../privatefinal_getterEscape_shallow.java | 7 + .../protectedClass_deep.java | 4 + .../protectedClass_shallow.java | 4 + .../DeclaredFinalFields.java | 91 ++ .../LazyInitialization.java | 394 ++++++++ .../PrivateFieldUpdater.java | 39 + .../reference_immutability/Singleton.java | 15 +- .../DeepImmutableClassAnnotation.java | 5 + .../DependentImmutableClassAnnotation.java | 4 + .../MutableClassAnnotation.java | 4 + .../ShallowImmutableClassAnnotation.java | 4 + .../DependentImmutableFieldAnnotation.java | 31 + .../MutableReferenceAnnotation.java | 9 +- .../opalj/fpcf/ClassImmutabilityTests.scala | 49 +- .../opalj/fpcf/FieldImmutabilityTests.scala | 36 +- .../fpcf/ReferenceImmutabilityTests.scala | 17 +- .../ClassImmutabilityMatcher.scala | 20 +- .../FieldImmutabilityMatcher.scala | 12 +- .../FieldMutabilityMatcher.scala | 2 +- .../field_mutability/NonFinalMatcher.scala | 5 +- .../MutableReferenceMatcher.scala | 70 ++ .../ReferenceImmutabilityMatcher.scala | 4 - .../properties/ClassImmutability_new.scala | 18 +- .../fpcf/properties/FieldImmutability.scala | 18 +- .../L0FieldImmutabilityAnalysis.scala | 330 +++---- .../L0ReferenceImmutabilityAnalysis.scala | 64 +- .../LxClassImmutabilityAnalysis_new.scala | 882 +++++++++--------- 45 files changed, 1606 insertions(+), 748 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/DeclaredFinalFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/LazyInitialization.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/PrivateFieldUpdater.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/MutableReferenceMatcher.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index 1b8292afa4..eb2d7a4b80 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -2,6 +2,7 @@ package org.opalj.fpcf.analyses import java.net.URL + import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication @@ -9,6 +10,7 @@ import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis @@ -20,37 +22,42 @@ import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis */ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - - val (propertyStore, _) = analysesManager.runAll( - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerL0ReferenceImmutabilityAnalysis - ); - - "Mutable References: "+propertyStore - .finalEntities(MutableReference) - .toList - .toString()+"\n"+ - "Immutable References: "+propertyStore - .finalEntities(ImmutableReference) - .toList - .toString() - } + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll( + EagerUnsoundPrematurelyReadFieldsAnalysis, + EagerL0PurityAnalysis, + EagerL1FieldMutabilityAnalysis, + EagerL0ReferenceImmutabilityAnalysis + ); + + "Mutable References: " + propertyStore + .finalEntities(MutableReference) + .toList + .toString() + "\n" + + "Lazy Initialized Reference: " + propertyStore + .finalEntities(LazyInitializedReference) + .toList + .toString() + "\n" + + "Immutable References: " + propertyStore + .finalEntities(ImmutableReference) + .toList + .toString() + } + } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java index 09f556d812..5152cb6190 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java @@ -2,6 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -@DeepImmutableClassAnnotation("trivial empty") +@DeepImmutableClassAnnotation("It has no fields and is final") public final class FinalEmptyClass { + } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java new file mode 100644 index 0000000000..5ec7f3f1c8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java @@ -0,0 +1,33 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@DependentImmutableClassAnnotation("Dependent Immutability of T1,...,T5") +public class Generic_class1 { + @DependentImmutableFieldAnnotation("T1") + @ImmutableReferenceAnnotation("effectively") + private T1 t1; + @DependentImmutableFieldAnnotation("T2") + @ImmutableReferenceAnnotation("effectively") + private T2 t2; + @DependentImmutableFieldAnnotation("T3") + @ImmutableReferenceAnnotation("effectively") + private T3 t3; + @DependentImmutableFieldAnnotation("T4") + @ImmutableReferenceAnnotation("effectively") + private T4 t4; + @DependentImmutableFieldAnnotation("T5") + @ImmutableReferenceAnnotation("effectively") + private T5 t5; + + public Generic_class1(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + } + +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java index 4127e70979..e79dc0e79f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java @@ -4,7 +4,7 @@ import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -@MutableClassAnnotation("because of public fields") +@MutableClassAnnotation("It has Mutable Fields") public class TrivialMutableClass { @MutableReferenceAnnotation("public") @MutableFieldAnnotation("mutable reference") diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java index eefbc17fa1..d59e446c4a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java @@ -2,6 +2,6 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -@DeepImmutableClassAnnotation("because it is empty") +@DeepImmutableClassAnnotation("Because of Emptiness") public final class FinalEmptyClass { } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java index 08edf8f91e..faebb9737e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java @@ -1,5 +1,13 @@ package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") public class Test { + @ShallowImmutableFieldAnnotation("Because of Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("Effectively immutable") private String name = "name"; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java index ea9dbaabb8..a29aefed9d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java @@ -1,6 +1,18 @@ package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +@MutableClassAnnotation("It has Mutable Fields") public class TrivialMutableClass { + + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Because it is public") public int n = 0; + + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Because it is public") public String name = "name"; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java index f57aea697a..e18e45d65c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java @@ -1,6 +1,12 @@ package org.opalj.fpcf.fixtures.field_immutability; -public class privateFieldNotBlank_deep { +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +@DeepImmutableClassAnnotation("Because it has only Deep Immutable Field Types") +public class privateFieldNotBlank_deep { + @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("Effectively Immutable Reference") private FinalEmptyClass name = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java index ef894f276b..cd937aafe1 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java @@ -1,6 +1,13 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("Because it has only Shallow Immutable Fields") public class privateFieldNotBlank_shallow { + @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") + @ImmutableReferenceAnnotation("Effectively Immutable Reference") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java index 7bd19b235f..6a4dd9da24 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java @@ -1,8 +1,14 @@ package org.opalj.fpcf.fixtures.field_immutability; -public class privateFinalFieldBlank_costructorEscape_deep { +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +@DeepImmutableClassAnnotation("It has only Deep Immutable Fields") +public class privateFinalFieldBlank_costructorEscape_deep { + @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("Declared final Reference") private final FinalEmptyClass fec; public privateFinalFieldBlank_costructorEscape_deep(FinalEmptyClass fec) { this.fec = fec; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java index 7e81db2a26..58207793b2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java @@ -1,7 +1,14 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") public class privateFinalFieldBlank_costructorEscape_shallow { + @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") + @ImmutableReferenceAnnotation("Declared final Field") private final TrivialMutableClass tmc; public privateFinalFieldBlank_costructorEscape_shallow(TrivialMutableClass tmc) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java index 28cde37ff6..81ce97c380 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java @@ -1,5 +1,13 @@ package org.opalj.fpcf.fixtures.field_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@DeepImmutableClassAnnotation("It has only Deep Immutable Fields") public class privateFinalFieldNotBlank_deep { + @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("Declared final Field") private final FinalEmptyClass fec = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java index 350f8366dd..c980ed2895 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java @@ -1,5 +1,13 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") public class privateFinalFieldNotBlank_shallow { + + @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") + @ImmutableReferenceAnnotation("Declared final Field") private final TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java index a6f6d75b7a..b3bf7e004f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java @@ -1,18 +1,15 @@ package org.opalj.fpcf.fixtures.field_immutability; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -@ShallowImmutableClassAnnotation("") +@DeepImmutableClassAnnotation("Only Deep Immutable Fields") public class private_getterEscape_deep { - public FinalEmptyClass getFec() { return fec; } - - @ImmutableReferenceAnnotation("") - @MutableFieldAnnotation("") + @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("It is effectively immutable") private FinalEmptyClass fec = new FinalEmptyClass(); -} +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java index 64f799ccda..801cc1b0cc 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java @@ -1,10 +1,17 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("Becaus it has only Shallow Immutable Fields") public class private_getterEscape_shallow { public TrivialMutableClass getTmc() { return tmc; } + @ShallowImmutableFieldAnnotation("Because of Immutable Reference and Mutable Field Type") + @ImmutableReferenceAnnotation("effectively immutable") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java index a6b38fdeaa..07cb969109 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java @@ -1,14 +1,18 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +@MutableClassAnnotation("Because it has Mutable Fields") public class private_setter_deep { public void setFec(FinalEmptyClass fec) { this.fec = fec; } + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("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/field_immutability/private_setter_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java index 54a592857a..7edcb57d35 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java @@ -1,14 +1,18 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +@MutableClassAnnotation("Because it has Mutable Fields") public class private_setter_shallow { public void setTmc(TrivialMutableClass tmc) { this.tmc = tmc; } + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Not final field could be set via setter") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java index 738c10c689..1c79a391c0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java @@ -1,10 +1,17 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@DeepImmutableClassAnnotation("It has only Deep Immutable Fields") public class privatefinal_getterEscape_deep { public FinalEmptyClass getFec() { return fec; } + @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("Declared final Field") private final FinalEmptyClass fec = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java index 3cb21f4715..922e39bf2f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java @@ -1,9 +1,16 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +@ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") public class privatefinal_getterEscape_shallow { public TrivialMutableClass getTmc() { return tmc; } + @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") + @ImmutableReferenceAnnotation("Declared final Reference") private final TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java index 94f3ab5884..21e1621fc6 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java @@ -1,8 +1,12 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +@MutableClassAnnotation("It has Mutable Fields") public class protectedClass_deep { + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Because it is declared as protected") protected FinalEmptyClass fec = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java index 177fbd12f1..a9d738b700 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java @@ -1,8 +1,12 @@ package org.opalj.fpcf.fixtures.field_immutability; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +@MutableClassAnnotation("It has Mutable Fields") public class protectedClass_shallow { + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Because it is declared as protected") protected TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/DeclaredFinalFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/DeclaredFinalFields.java new file mode 100644 index 0000000000..f32e29b2a5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/DeclaredFinalFields.java @@ -0,0 +1,91 @@ +/* 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.fixtures.reference_immutability; + +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +/** + * 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 fields that are declared final. Some of them are not strictly final because they can + * be observed uninitialized. + * + * @author Dominik Helm + */ +public class DeclaredFinalFields extends Super { + + @ImmutableReferenceAnnotation("Initialized directly") + private final int a = 1; + + @ImmutableReferenceAnnotation("Initialized through instance initializer") + private final int b; + + @ImmutableReferenceAnnotation("Initialized through constructor") + private final int c; + + @MutableReferenceAnnotation(value = "Prematurely read through super constructor", prematurelyRead = true) + private final int d; + + @MutableReferenceAnnotation(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/reference_immutability/LazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/LazyInitialization.java new file mode 100644 index 0000000000..2547759f2e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/LazyInitialization.java @@ -0,0 +1,394 @@ +/* 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.fixtures.reference_immutability; + +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.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; + +/** + * Test classes for simple lazy initialization patterns and anti-patterns. + * + * @author Dominik Helm + */ + +class Simple { + + @LazyInitializedReferenceAnnotation("Simple lazy initialization") + private int x; + + public int init() { + if (x == 0) { + x = 5; + } + return x; + } +} + +class Local { + + @LazyInitializedReferenceAnnotation("Lazy initialization with local") + private int x; + + public int init() { + int y = this.x; + if (y == 0) { + x = y = 5; + } + return y; + } +} + +class LocalWrong { + + @MutableReferenceAnnotation("Incorrect lazy initialization with local") + private int x; + + public int init() { + int y = this.x; + if (y == 0) { + x = 5; + } + return y; + } +} + +class LocalReversed { + + @LazyInitializedReferenceAnnotation("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 { + + @LazyInitializedReferenceAnnotation("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 { + + @LazyInitializedReferenceAnnotation("Simple lazy initialization (reversed)") + private int x; + + public int init() { + if (x != 0) + return x; + x = 5; + return x; + } +} + +class SimpleWithDifferentDefault { + + @LazyInitialized(value = "Simple lazy initialization, but different default value", + analyses = {}) + @MutableReferenceAnnotation(value = "Analysis doesn't recognize lazy initialization with different default") + //TODO + private int x; + + public SimpleWithDifferentDefault() { + x = -1; + } + + public SimpleWithDifferentDefault(int a) { + this(); + } + + public int init() { + if (x == -1) { + x = 5; + } + return x; + } +} + +class WrongDefault { + + @MutableReferenceAnnotation("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 { + + @LazyInitializedReferenceAnnotation("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 { + + @MutableReferenceAnnotation("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 { + + @LazyInitializedReferenceAnnotation("Lazy initialization with call to deterministic method on final field") + private int x; + + @ImmutableReferenceAnnotation("Declared final field") + private final Inner inner; + + public DeterministicCallOnFinalField(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 DeterministicCallOnNonFinalField { + + @MutableReferenceAnnotation("Wrong lazy initialization with call to non-deterministic method on final field") + private int x; + + @MutableReferenceAnnotation("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 { + + @MutableReferenceAnnotation("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 { + + @LazyInitializedReferenceAnnotation("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 { + + @MutableReferenceAnnotation("Field can be observed partially updated") + private int x; + + public int init() { + if (x == 0) { + x = 5; + x ^= -1; + } + return x; + } +} + +class VisibleInitialization { + + @MutableReferenceAnnotation("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. + */ + @ImmutableReferenceAnnotation(value = "Field is never initialized, so it stays on its default value") + 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 PossibleExceptionInInitialization { + + @MutableReferenceAnnotation("Incorrect because lazy initialization is may not happen due to exception") + private int x; + + public int init(int i) { + int y = this.x; + if (y == 0) { + int z = 10 / i; + y = x = 5; + } + return y; + } +} + +class CaughtExceptionInInitialization { + + @MutableReferenceAnnotation("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/reference_immutability/PrivateFieldUpdater.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/PrivateFieldUpdater.java new file mode 100644 index 0000000000..01b9c43677 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/PrivateFieldUpdater.java @@ -0,0 +1,39 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.reference_immutability; + +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.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; + +/** + * Simple demo class which updates the private field of another instance of this class. + */ +public class PrivateFieldUpdater { + + @ImmutableReferenceAnnotation("only initialized by the constructor") + private String name; + + @MutableReferenceAnnotation("incremented whenever `this` object is passed to another `NonFinal` object") + 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/reference_immutability/Singleton.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java index 4824dea4c4..47c79df0d4 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java @@ -1,14 +1,20 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.reference_immutability; -/* - * Test case from the old L2 Field Mutability tests - */ + +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.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; public class Singleton { + @MutableReferenceAnnotation("written by static initializer after the field becomes (indirectly) readable") private String name; - + @ImmutableReferenceAnnotation("only initialized once by the constructor") private Object mutex = new Object(); private Singleton() { @@ -23,6 +29,7 @@ public String getName() { // STATIC FUNCTIONALITY + @ImmutableReferenceAnnotation("only set in the static initializer") private static Singleton theInstance; static { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java index ad27d7d1c6..36557c4675 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java @@ -1,7 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability; +import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -21,4 +23,7 @@ * A short reasoning of this property. */ String value();// default = "N/A"; + + Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; + } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java index c85be61a9c..e1925a8bc5 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java @@ -1,8 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability; +import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; +import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -22,4 +24,6 @@ * A short reasoning of this property. */ String value();// default = "N/A"; + + Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java index 648a8431aa..73e6289a9d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java @@ -1,8 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability; +import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; +import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -22,4 +24,6 @@ * A short reasoning of this property. */ String value();// default = "N/A"; + + Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java index dd5152b9ff..92812b409f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java @@ -1,8 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability; +import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; +import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -22,4 +24,6 @@ * A short reasoning of this property. */ String value();// default = "N/A"; + + Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java new file mode 100644 index 0000000000..eb76bef20c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.field_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is shallow immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key="FieldImmutability",validator=DependentImmutableFieldMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DependentImmutableFieldAnnotation { + + /** + * A short reasoning of this property. + */ + String value() ; // default = "N/A"; + + Class[] analyses() default { + L0FieldImmutabilityAnalysis.class + }; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java index 822be18a0a..1adf72d4b7 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java @@ -19,10 +19,17 @@ @Retention(RetentionPolicy.CLASS) public @interface MutableReferenceAnnotation { + + /** + * True if the field is non-final because it is read prematurely. + * Tests may ignore @NonFinal annotations if the FieldPrematurelyRead property for the field + * did not identify the premature read. + */ + boolean prematurelyRead() default false; /** * A short reasoning of this property. */ - String value();// default = "N/A"; + String value() default "N/A"; Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala index e9c5e67fc9..a16da84d99 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -6,15 +6,14 @@ 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.EagerClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** * @author Tobias Peter Roth @@ -28,27 +27,32 @@ class ClassImmutabilityTests extends PropertiesTest { p.get(RTACallGraphKey) } - describe("no analysis is scheduled") { - val as = executeAnalyses(Set.empty) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) - } - - describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + /** + * describe("no analysis is scheduled") { + * val as = executeAnalyses(Set.empty) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) + * }* + */ + describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { + println(1) val as = executeAnalyses( Set( - EagerClassImmutabilityAnalysis, - EagerTypeImmutabilityAnalysis, - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerL0ReferenceImmutabilityAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, +/****/ + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, EagerLxClassImmutabilityAnalysis_new ) ) + println(2) as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) + println(3) + validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) //Set("ClassImmutability_new")) + println(4) } /** * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { @@ -77,5 +81,4 @@ class ClassImmutabilityTests extends PropertiesTest { * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) * } * */ - } 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 index 64be6de388..442fda7ab6 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -6,16 +6,15 @@ 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.EagerClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.TACAITransformer -import org.opalj.tac.fpcf.analyses.escape.EagerSimpleEscapeAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** * @author Tobias Peter Roth @@ -32,21 +31,22 @@ class FieldImmutabilityTests extends PropertiesTest { describe("no analysis is scheduled") { val as = executeAnalyses(Set.empty) as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) } describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { val as = executeAnalyses( Set( - EagerTypeImmutabilityAnalysis, - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerClassImmutabilityAnalysis, - EagerL0ReferenceImmutabilityAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerSimpleEscapeAnalysis, - TACAITransformer, - EagerL0FieldImmutabilityAnalysis + LazyTypeImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyClassImmutabilityAnalysis + // LazySimpleEscapeAnalysis, + // TACAITransformer ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index 8cfd18c1a9..3e5b2ea958 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -6,12 +6,12 @@ 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.{EagerL0PurityAnalysis, EagerUnsoundPrematurelyReadFieldsAnalysis} +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.{ - EagerL0ReferenceImmutabilityAnalysis, - EagerL1FieldMutabilityAnalysis -} +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** * @author Tobias Peter Roth @@ -34,10 +34,11 @@ class ReferenceImmutabilityTests extends PropertiesTest { describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { val as = executeAnalyses( Set( - EagerUnsoundPrematurelyReadFieldsAnalysis, EagerL0ReferenceImmutabilityAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala index 5b210c2997..188a95ec8d 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala @@ -1,10 +1,16 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability -import org.opalj.br.{AnnotationLike, ObjectType} +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties._ -import org.opalj.fpcf.{Entity, Property} +import org.opalj.br.fpcf.properties.ClassImmutability_new +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.fpcf.Entity +import org.opalj.fpcf.Property import org.opalj.fpcf.properties.AbstractPropertyMatcher /** @@ -28,6 +34,7 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) analyses.exists(as.contains) + } def validateProperty( @@ -37,10 +44,15 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) a: AnnotationLike, properties: Traversable[Property] ): Option[String] = { + println(11) if (!properties.exists(p ⇒ p == property)) { // ... when we reach this point the expected property was not found. - Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) + println(22) + val r = Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) + println(33) + r } else { + println(44) None } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala index 03298c1cc8..64e406fb58 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala @@ -3,8 +3,13 @@ package org.opalj.fpcf.properties.field_immutability import org.opalj.br.{AnnotationLike, ObjectType} import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties._ -import org.opalj.fpcf.{Entity, Property} +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.MutableField +import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property import org.opalj.fpcf.properties.AbstractPropertyMatcher /** @@ -27,6 +32,7 @@ class FieldImmutabilityMatcher(val property: FieldImmutability) extends Abstract val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) analyses.exists(as.contains) + } def validateProperty( @@ -50,4 +56,6 @@ class MutableFieldMatcher extends FieldImmutabilityMatcher(MutableField) class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(ShallowImmutableField) +class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(DependentImmutableField) + class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(DeepImmutableField) 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/field_mutability/FieldMutabilityMatcher.scala index 2f04704357..d29dbffb92 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/field_mutability/FieldMutabilityMatcher.scala @@ -21,7 +21,7 @@ import org.opalj.br.fpcf.properties.LazyInitializedField */ class FieldMutabilityMatcher(val property: FieldMutability) extends AbstractPropertyMatcher { - private final val PropertyReasonID = 0 + final private val PropertyReasonID = 0 override def isRelevant( p: SomeProject, 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/field_mutability/NonFinalMatcher.scala index 08526a42b0..a53af488ff 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/field_mutability/NonFinalMatcher.scala @@ -39,7 +39,9 @@ class NonFinalMatcher extends AbstractPropertyMatcher { if (!analyses.exists(as.contains)) return false; - val prematurelyRead = getValue(p, annotationType, a.elementValuePairs, "prematurelyRead").asInstanceOf[BooleanValue].value + val prematurelyRead = getValue(p, annotationType, a.elementValuePairs, "prematurelyRead") + .asInstanceOf[BooleanValue] + .value if (prematurelyRead) { val propertyStore = p.get(PropertyStoreKey) @@ -65,5 +67,4 @@ class NonFinalMatcher extends AbstractPropertyMatcher { Some(a.elementValuePairs.head.value.toString) } } - } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/MutableReferenceMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/MutableReferenceMatcher.scala new file mode 100644 index 0000000000..18ccdeb426 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/MutableReferenceMatcher.scala @@ -0,0 +1,70 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability + +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.MutableReference +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 + +/** + * @author Tobias Peter Roth + */ +class MutableReferenceMatcher extends AbstractPropertyMatcher { + + val property = MutableReference + + 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/reference_immutability/ReferenceImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala index de82d1030b..fa412de678 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala @@ -6,7 +6,6 @@ import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference -import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.ReferenceImmutability import org.opalj.fpcf.Entity import org.opalj.fpcf.Property @@ -49,11 +48,8 @@ class ReferenceImmutabilityMatcher(val property: ReferenceImmutability) None } } - } -class MutableReferenceMatcher extends ReferenceImmutabilityMatcher(MutableReference) - class LazyInitializedReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedReference) class ImmutableReferenceMatcher extends ReferenceImmutabilityMatcher(ImmutableReference) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index 8059b5d74e..7a4602aedc 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -6,7 +6,7 @@ import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = ClassImmutability_new + final type Self = ClassImmutability_new } /** @@ -27,18 +27,18 @@ sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaIn sealed trait ClassImmutability_new extends Property with ClassImmutabilityPropertyMetaInformation_new { - final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key + final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key } object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[ClassImmutability_new]] property. - */ - final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( - "opalj.ClassImmutability_new", - MutableClass - ) + /** + * The key associated with every [[ClassImmutability_new]] property. + */ + final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( + "opalj.ClassImmutability_new", + MutableClass + ) } case object MutableClass extends ClassImmutability_new 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 index 7c6fb4c654..f6bbffb40b 100644 --- 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 @@ -7,7 +7,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait FieldImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - type Self = FieldImmutability + type Self = FieldImmutability } @@ -25,19 +25,19 @@ sealed trait FieldImmutabilityPropertyMetaInformation extends PropertyMetaInform */ sealed trait FieldImmutability extends Property with FieldImmutabilityPropertyMetaInformation { - final def key: PropertyKey[FieldImmutability] = FieldImmutability.key + final def key: PropertyKey[FieldImmutability] = FieldImmutability.key } object FieldImmutability extends FieldImmutabilityPropertyMetaInformation { - final val PropertyKeyName = "opalj.FieldImmutability" + final val PropertyKeyName = "opalj.FieldImmutability" - final val key: PropertyKey[FieldImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableField - ) - } + final val key: PropertyKey[FieldImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableField + ) + } } case object MutableField extends FieldImmutability diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index c0762cdd2c..1836727cfe 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -39,8 +39,8 @@ import org.opalj.fpcf.SomeEPS import org.opalj.tac.fpcf.properties.TACAI case class State() { - var typeImmutability: Option[Boolean] = None - var referenceImmutability: Option[Boolean] = None + var typeImmutability: Option[Boolean] = None + var referenceImmutability: Option[Boolean] = None } /** @@ -55,175 +55,175 @@ case class State() { class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - final val typeExtensibility = project.get(TypeExtensibilityKey) - final val closedPackages = project.get(ClosedPackagesKey) - final val fieldAccessInformation = project.get(FieldAccessInformationKey) + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) - def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { - entity match { - case field: Field => determineFieldImmutability(field) - case _ => - val m = entity.getClass.getName + "is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { + entity match { + case field: Field ⇒ determineFieldImmutability(field) + case _ ⇒ + val m = entity.getClass.getName+"is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } } - } - private[analyses] def determineFieldImmutability( - field: Field - ): ProperPropertyComputationResult = { + private[analyses] def determineFieldImmutability( + field: Field + ): ProperPropertyComputationResult = { - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - def hasImmutableType(field: Field): Option[Boolean] = { - println("Field:: " + field) - println(field.fieldType) - println(field.fieldType.isBaseType) - if (field.fieldType.isArrayType) return Some(true); //TODO - if (field.fieldType.isBaseType) return Some(true); - val result = propertyStore(field.fieldType, TypeImmutability.key) - println("result: " + result) - result match { - case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) => { - println("has immutable type") - Some(true) - } - case FinalEP(e, t) if (t == MutableType) => { - println("has mutable type") - Some(false) - } - case x @ _ => { - dependencies += x - println(x) - println("immutability of type couldn't be determined") - None + def hasImmutableType(field: Field): Option[Boolean] = { + println("Field:: "+field) + println(field.fieldType) + println(field.fieldType.isBaseType) + if (field.fieldType.isArrayType) return Some(true); //TODO + if (field.fieldType.isBaseType) return Some(true); + val result = propertyStore(field.fieldType, TypeImmutability.key) + println("result: "+result) + result match { + case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) ⇒ { + println("has immutable type") + Some(true) + } + case FinalEP(e, t) if (t == MutableType) ⇒ { + println("has mutable type") + Some(false) + } + case x @ _ ⇒ { + dependencies += x + println(x) + println("immutability of type couldn't be determined") + None + } + } } - } - } - def hasImmutableReference(field: Field): Option[Boolean] = { + def hasImmutableReference(field: Field): Option[Boolean] = { - propertyStore(field, ReferenceImmutability.key) match { - case FinalEP(_, MutableReference) => { - println("has mutable reference") - Some(false) - } - case FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO - println("has immutable Reference") - Some(true) - } - case x @ _ => { - dependencies += x - println(x) - println("immutability of reference couldn't be determined") - None + propertyStore(field, ReferenceImmutability.key) match { + case FinalEP(_, MutableReference) ⇒ { + println("has mutable reference") + Some(false) + } + case FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ { //TODO + println("has immutable Reference") + Some(true) + } + case x @ _ ⇒ { + dependencies += x + println(x) + println("immutability of reference couldn't be determined") + None + } + } } - } - } - val state: State = new State() + val state: State = new State() - def createResult(state: State): ProperPropertyComputationResult = { - println("reference Immutability: " + state.referenceImmutability) - println("type immutabiliy: " + state.typeImmutability) + def createResult(state: State): ProperPropertyComputationResult = { + println("reference Immutability: "+state.referenceImmutability) + println("type immutabiliy: "+state.typeImmutability) + + state.referenceImmutability match { + case Some(false) ⇒ Result(field, MutableField) + case Some(true) ⇒ { + //If the field type is object. It is a generic field + if (field.fieldType == ObjectType("java/lang/Object")) + Result(field, DependentImmutableField) + else + state.typeImmutability match { + case Some(true) ⇒ Result(field, DeepImmutableField) + case Some(false) ⇒ Result(field, ShallowImmutableField) + case None if (dependencies.isEmpty) ⇒ Result(field, ShallowImmutableField) + case None ⇒ { + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } + } + } + case None if (dependencies.isEmpty) ⇒ Result(field, MutableField) + case None ⇒ { + println(dependencies) + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } - state.referenceImmutability match { - case Some(false) => Result(field, MutableField) - case Some(true) => { - //If the field type is object. It is a generic field - if (field.fieldType == ObjectType("java/lang/Object")) - Result(field, DependentImmutableField) - else - state.typeImmutability match { - case Some(true) => Result(field, DeepImmutableField) - case Some(false) => Result(field, ShallowImmutableField) - case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) - case None => { - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } } } - case None if (dependencies.isEmpty) => Result(field, MutableField) - case None => { - println(dependencies) - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } + def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { + println("c function called") + dependencies = dependencies.filter(_.e ne eps.e) + (eps: @unchecked) match { + case x: InterimEP[_, _] ⇒ { + println("interim in continuation function") + println(x) + dependencies += eps + InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + } - } - } - def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - println("c function called") - dependencies = dependencies.filter(_.e ne eps.e) - (eps: @unchecked) match { - case x: InterimEP[_, _] => { - println("interim in continuation function") - println(x) - dependencies += eps - InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) - } + case x @ FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) ⇒ { + println(x) + println("has immutable type. Determined by continuation function.") + state.typeImmutability = Some(true) + } - case x @ FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) => { - println(x) - println("has immutable type. Determined by continuation function.") - state.typeImmutability = Some(true) - } + case x @ FinalEP(_, MutableType) ⇒ { + println(x) + println("has mutable type. Determined by continuation function.") + state.typeImmutability = Some(false) + } - case x @ FinalEP(_, MutableType) => { - println(x) - println("has mutable type. Determined by continuation function.") - state.typeImmutability = Some(false) - } + case x @ FinalEP(_, MutableReference) ⇒ { + println(x) + println("has mutable reference. Determined by continuation function.") + state.referenceImmutability = Some(false) + } - case x @ FinalEP(_, MutableReference) => { - println(x) - println("has mutable reference. Determined by continuation function.") - state.referenceImmutability = Some(false) - } + case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ { //TODO + println(x) + println("has immutable reference. Determined by continuation function.") + state.referenceImmutability = Some(true) + } - case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO - println(x) - println("has immutable reference. Determined by continuation function.") - state.referenceImmutability = Some(true) + case x @ _ ⇒ println("default value: "+x) + } + createResult(state) } - case x @ _ => println("default value: " + x) - } - createResult(state) + //-- + state.referenceImmutability = hasImmutableReference(field) + state.typeImmutability = hasImmutableType(field); + println("after type immutability determination") + createResult(state) } - //-- - state.referenceImmutability = hasImmutableReference(field) - state.typeImmutability = hasImmutableType(field); - println("after type immutability determination") - createResult(state) - } - } trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - //PropertyBounds.lub(Purity), - //PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - //PropertyBounds.lub(FieldMutability), - //PropertyBounds.lub(EscapeProperty), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(FieldImmutability) + final override def uses: Set[PropertyBounds] = Set( + //PropertyBounds.lub(Purity), + //PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + //PropertyBounds.lub(FieldMutability), + //PropertyBounds.lub(EscapeProperty), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.lub(TypeImmutability), + PropertyBounds.lub(FieldImmutability) //PropertyBounds.ub(EscapeProperty) - ) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) } @@ -234,16 +234,16 @@ object EagerL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -253,19 +253,19 @@ object LazyL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - FieldImmutability.key, - analysis.determineFieldImmutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldImmutability.key, + analysis.determineFieldImmutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index 4d2590fb19..6fc3cc9170 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -8,11 +8,8 @@ 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._ -import org.opalj.fpcf._ import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler -import org.opalj.br._ import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.FieldAccessInformationKey @@ -31,6 +28,41 @@ import org.opalj.br.fpcf.properties.cg.Callees import org.opalj.ai.isImmediateVMException import org.opalj.ai.pcOfImmediateVMException import org.opalj.ai.pcOfMethodExternalException +import org.opalj.br.ClassFile +import org.opalj.br.ComputationalTypeFloat +import org.opalj.br.ComputationalTypeInt +import org.opalj.br.DeclaredMethod +import org.opalj.br.Field +import org.opalj.br.FloatType +import org.opalj.br.Method +import org.opalj.br.PC +import org.opalj.br.PCs +import org.opalj.br.fpcf.properties.AtMost +import org.opalj.br.fpcf.properties.EscapeInCallee +import org.opalj.br.fpcf.properties.EscapeViaReturn +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.br.fpcf.properties.NoEscape +import org.opalj.br.fpcf.properties.NotPrematurelyReadField +import org.opalj.br.fpcf.properties.PrematurelyReadField +import org.opalj.br.fpcf.properties.ReferenceImmutability +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.LBP +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyComputationResult +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.SomeEPS +import org.opalj.fpcf.UBP +import org.opalj.fpcf.Result import org.opalj.tac.common.DefinitionSite import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.properties.TACAI @@ -315,7 +347,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * dependees or as Result otherwise. */ def createResult()(implicit state: State): ProperPropertyComputationResult = { - if (state.hasDependees && (state.referenceImmutability ne NonFinalFieldByAnalysis)) + if (state.hasDependees && (state.referenceImmutability ne MutableReference)) //NonFinalFieldByAnalysis)) InterimResult( state.field, MutableReference, //NonFinalFieldByAnalysis, @@ -500,8 +532,18 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec method: Method, code: Array[Stmt[V]] )(implicit state: State): Boolean = { - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + )) + println("paramsCount: "+method.descriptor.parametersCount == 0) + println( + "isnondeterministic-result: "+isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + ).toString + ) + println(propertyStore(declaredMethods(method), Purity.key)) + println("lazyInitializerIsDeterministic: "+result) + result } /** @@ -1023,8 +1065,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec def isNonDeterministic( eop: EOptionP[DeclaredMethod, Purity] )(implicit state: State): Boolean = eop match { - case LBP(p: Purity) if p.isDeterministic ⇒ - false + case LBP(p: Purity) if p.isDeterministic ⇒ false case UBP(p: Purity) if !p.isDeterministic ⇒ true case _ ⇒ state.purityDependees += eop @@ -1040,6 +1081,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec )(implicit state: State): Boolean = eop match { case FinalEP(e, ImmutableReference) ⇒ true case FinalEP(e, MutableReference) ⇒ false + // + case LBP(ImmutableReference) ⇒ true + case UBP(MutableReference) ⇒ false /** * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ @@ -1058,8 +1102,8 @@ trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { PropertyBounds.lub(Purity), PropertyBounds.lub(FieldPrematurelyRead), PropertyBounds.ub(TACAI), - PropertyBounds.ub(EscapeProperty), - PropertyBounds.lub(ReferenceImmutability) + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.ub(EscapeProperty) ) final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 9e1c4c9995..9779090536 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -72,7 +72,7 @@ import org.opalj.br.fpcf.properties.TypeImmutability * */ class LxClassImmutabilityAnalysis_new(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. @@ -81,358 +81,358 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal * 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_new //MutableObject - ): 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 ::= ( - ( - determineClassImmutability_new(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, MutableClass) //MutableObjectDueToUnknownSupertypes) - } + /** + * 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_new //MutableObject + ): MultiResult = { + val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) + val r = allSubtypes.map { st ⇒ + new FinalEP(st, immutability) + }.toSeq + MultiResult(r) } - IncrementalResult(Results(results), nextComputations.iterator) - } - - def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { - e match { - case t: ObjectType => - //this is safe - classHierarchy.superclassType(t) match { - case None => Result(t, MutableClass) //MutableObjectDueToUnknownSupertypes) - case Some(superClassType) => - val cf = project.classFile(t) match { - case None => - return Result(t, MutableClass) // MutableObjectByAnalysis) //TODO consider other lattice element - case Some(cf) => cf - } - propertyStore(superClassType, ClassImmutability_new.key) match { - case UBP(MutableClass) => //MutableObject) ⇒ - Result(t, MutableClass) - case eps: EPS[ObjectType, ClassImmutability_new] => - determineClassImmutability_new( - superClassType, - eps, - eps.isFinal, - lazyComputation = true - )(cf) - case epk => - determineClassImmutability_new( - superClassType, - epk, - superClassMutabilityIsFinal = false, - lazyComputation = true - )(cf) + @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 ::= ( + ( + determineClassImmutability_new(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, MutableClass) //MutableObjectDueToUnknownSupertypes) } - } - case _ => - val m = e.getClass.getSimpleName + " is not an org.opalj.br.ObjectType" - throw new IllegalArgumentException(m) + IncrementalResult(Results(results), nextComputations.iterator) } - } - - 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 determineClassImmutability_new( - superClassType: ObjectType, - superClassInformation: EOptionP[Entity, Property], - superClassMutabilityIsFinal: Boolean, - lazyComputation: Boolean - )( - cf: ClassFile - ): ProperPropertyComputationResult = { - // assert(superClassMutability.isMutable.isNoOrUnknown) - - //---generic classes handling - //printf(cf.asVirtualClass.asClassFile.classSignature.get.toString()) - //cf.asClassFile.classSignature.get.formalTypeParameters. - //--------------------------------------------- - - val t = cf.thisType - - var dependees = Map.empty[Entity, EOptionP[Entity, Property]] - - if (!superClassMutabilityIsFinal) { - dependees += (SuperClassKey -> superClassInformation) + def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { + e match { + case t: ObjectType ⇒ + //this is safe + classHierarchy.superclassType(t) match { + case None ⇒ Result(t, MutableClass) //MutableObjectDueToUnknownSupertypes) + case Some(superClassType) ⇒ + val cf = project.classFile(t) match { + case None ⇒ + return Result(t, MutableClass) // MutableObjectByAnalysis) //TODO consider other lattice element + case Some(cf) ⇒ cf + } + + propertyStore(superClassType, ClassImmutability_new.key) match { + case UBP(MutableClass) ⇒ //MutableObject) ⇒ + Result(t, MutableClass) + case eps: EPS[ObjectType, ClassImmutability_new] ⇒ + determineClassImmutability_new( + superClassType, + eps, + eps.isFinal, + lazyComputation = true + )(cf) + case epk ⇒ + determineClassImmutability_new( + superClassType, + epk, + superClassMutabilityIsFinal = false, + lazyComputation = true + )(cf) + } + + } + case _ ⇒ + val m = e.getClass.getSimpleName+" is not an org.opalj.br.ObjectType" + throw new IllegalArgumentException(m) + } } - // Collect all fields for which we need to determine the effective mutability! - var hasFieldsWithUnknownMutability = false - - val instanceFields = cf.fields.filter { f => - !f.isStatic - } - var hasShallowImmutableFields = false - var hasDependentImmutableFields = false - val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) - fieldsPropertyStoreInformation.foreach( - f => - f match { - 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 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); - } - ) + private[this] object SuperClassKey /** - * dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { - * case FinalP(MutableField) => //NonFinalField) ⇒ - * // <=> The class is definitively mutable and therefore also all subclasses. - * if (lazyComputation) - * return Result(t, MutableClass); //MutableObjectByAnalysis); // - * else - * return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); - * case ep @ InterimE(e) => - * hasFieldsWithUnknownMutability = true - * (e, ep) - * case epk @ EPK(e: Entity, _) => - * // <=> The mutability information is not yet available. - * hasFieldsWithUnknownMutability = true - * (e, epk) + * Determines the immutability of instances of the given class type `t`. * - * // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields - * }).toMap * + * @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. */ - var minLocalImmutability: ClassImmutability_new = - if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) - MutableClass //MutableObjectByAnalysis - else - ShallowImmutableClass //ImmutableContainer - - // NOTE: maxLocalImmutability does not take the super classes' mutability into account! - var maxLocalImmutability: ClassImmutability_new = superClassInformation match { - case UBP(ShallowImmutableClass) => ShallowImmutableClass //ImmutableContainer - case _ => DeepImmutableClass // ImmutableObject - } + def determineClassImmutability_new( + superClassType: ObjectType, + superClassInformation: EOptionP[Entity, Property], + superClassMutabilityIsFinal: Boolean, + lazyComputation: Boolean + )( + cf: ClassFile + ): ProperPropertyComputationResult = { + // assert(superClassMutability.isMutable.isNoOrUnknown) + + //---generic classes handling + //printf(cf.asVirtualClass.asClassFile.classSignature.get.toString()) + //cf.asClassFile.classSignature.get.formalTypeParameters. + //--------------------------------------------- + + val t = cf.thisType + + var dependees = Map.empty[Entity, EOptionP[Entity, Property]] + + if (!superClassMutabilityIsFinal) { + dependees += (SuperClassKey -> superClassInformation) + } - if (hasDependentImmutableFields) { - maxLocalImmutability = DependentImmutableClass - } else if (hasShallowImmutableFields) { - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - } + // Collect all fields for which we need to determine the effective mutability! + var hasFieldsWithUnknownMutability = false - 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 //ImmutableContainer - } + val instanceFields = cf.fields.filter { f ⇒ + !f.isStatic + } + var hasShallowImmutableFields = false + var hasDependentImmutableFields = false + val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) + fieldsPropertyStoreInformation.foreach( + f ⇒ + f match { + 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 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); + } + ) + + /** + * dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { + * case FinalP(MutableField) => //NonFinalField) ⇒ + * // <=> The class is definitively mutable and therefore also all subclasses. + * if (lazyComputation) + * return Result(t, MutableClass); //MutableObjectByAnalysis); // + * else + * return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); + * case ep @ InterimE(e) => + * hasFieldsWithUnknownMutability = true + * (e, ep) + * case epk @ EPK(e: Entity, _) => + * // <=> The mutability information is not yet available. + * hasFieldsWithUnknownMutability = true + * (e, epk) + * + * // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields + * }).toMap * + */ + var minLocalImmutability: ClassImmutability_new = + if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) + MutableClass //MutableObjectByAnalysis + else + ShallowImmutableClass //ImmutableContainer - //--var fieldTypes: Set[ObjectType] = Set.empty - //--if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { - //-- fieldTypes = // IMPROVE Use the precise type of the field (if available)! - // cf.fields.collect { - // case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType - // }.toSet - //} - - // For each dependent class file we have to determine the mutability - // of instances of the respective type to determine the immutability - // of instances of this class. - // Basically, we have to distinguish the following cases: - // - A field's type is mutable or conditionally immutable=> - // This class is conditionally immutable. - // - A field's type is immutable => - // The field is ignored. - // (This class is as immutable as its superclass.) - // - A field's type is at least conditionally mutable or - // The immutability of the field's type is not yet known => - // This type is AtLeastConditionallyImmutable - // We have to declare a dependency on the respective type's immutability. - // - // Additional handling is required w.r.t. the supertype: - // If the supertype is Immutable => - // Nothing special to do. - // If the supertype is AtLeastConditionallyImmutable => - // We have to declare a dependency on the supertype. - // If the supertype is ConditionallyImmutable => - // This type is also at most conditionally immutable. - // - // We furthermore have to take the mutability of the fields into consideration: - // If a field is not effectively final => - // This type is mutable. - // If a field is effectively final => - // Nothing special to do. - - //val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) - //val hasMutableOrConditionallyImmutableField = - // IMPROVE Use the precise type of the field (if available)! - //-- fieldTypesImmutability.exists { eOptP ⇒ - //-- eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) - //-- } - - if (hasShallowImmutableFields) { - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - } //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 - * } - * fieldTypesWithUndecidedMutability.foreach { eOptP => - * dependees += (eOptP.e -> eOptP) - * }* - */ - //} - - 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) - ); - } + // NOTE: maxLocalImmutability does not take the super classes' mutability into account! + var maxLocalImmutability: ClassImmutability_new = superClassInformation match { + case UBP(ShallowImmutableClass) ⇒ ShallowImmutableClass //ImmutableContainer + case _ ⇒ DeepImmutableClass // ImmutableObject + } + + if (hasDependentImmutableFields) { + maxLocalImmutability = DependentImmutableClass + } else if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + } + + 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 //ImmutableContainer + } - def c(someEPS: SomeEPS): ProperPropertyComputationResult = { - //[DEBUG] val oldDependees = dependees - dependees = dependees.filter(_._1 ne someEPS.e) - println("someEPS: " + someEPS) - someEPS match { - // Superclass related dependencies: + //--var fieldTypes: Set[ObjectType] = Set.empty + //--if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { + //-- fieldTypes = // IMPROVE Use the precise type of the field (if available)! + // cf.fields.collect { + // case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType + // }.toSet + //} + + // For each dependent class file we have to determine the mutability + // of instances of the respective type to determine the immutability + // of instances of this class. + // Basically, we have to distinguish the following cases: + // - A field's type is mutable or conditionally immutable=> + // This class is conditionally immutable. + // - A field's type is immutable => + // The field is ignored. + // (This class is as immutable as its superclass.) + // - A field's type is at least conditionally mutable or + // The immutability of the field's type is not yet known => + // This type is AtLeastConditionallyImmutable + // We have to declare a dependency on the respective type's immutability. + // + // Additional handling is required w.r.t. the supertype: + // If the supertype is Immutable => + // Nothing special to do. + // If the supertype is AtLeastConditionallyImmutable => + // We have to declare a dependency on the supertype. + // If the supertype is ConditionallyImmutable => + // This type is also at most conditionally immutable. // - case UBP(MutableClass) => // MutableObject) ⇒ - return Result(t, MutableClass); //MutableObjectByAnalysis); + // We furthermore have to take the mutability of the fields into consideration: + // If a field is not effectively final => + // This type is mutable. + // If a field is effectively final => + // Nothing special to do. + + //val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) + //val hasMutableOrConditionallyImmutableField = + // IMPROVE Use the precise type of the field (if available)! + //-- fieldTypesImmutability.exists { eOptP ⇒ + //-- eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) + //-- } + + if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + } //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 + * } + * fieldTypesWithUndecidedMutability.foreach { eOptP => + * dependees += (eOptP.e -> eOptP) + * }* + */ + //} + + 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) + ); + } - case LBP(DeepImmutableClass) => //_:ImmutableObject) ⇒ // the super class - dependees -= SuperClassKey + def c(someEPS: SomeEPS): ProperPropertyComputationResult = { + //[DEBUG] val oldDependees = dependees + dependees = dependees.filter(_._1 ne someEPS.e) + println("someEPS: "+someEPS) + someEPS match { + // Superclass related dependencies: + // + case UBP(MutableClass) ⇒ // MutableObject) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); - case UBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container - if (someEPS.isFinal) dependees -= SuperClassKey - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + case LBP(DeepImmutableClass) ⇒ //_:ImmutableObject) ⇒ // the super class + dependees -= SuperClassKey - case LBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container - if (minLocalImmutability != DeepImmutableClass && //ImmutableContainer && - !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + case UBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is at most immutable container + if (someEPS.isFinal) dependees -= SuperClassKey + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - case LUBP(MutableClass, DeepImmutableClass) => //_: MutableObject, ImmutableObject) ⇒ // No information about superclass + case LBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is a least immutable container + if (minLocalImmutability != DeepImmutableClass && //ImmutableContainer && + !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - // Properties related to the type of the class' fields. - // - case UBP(ShallowImmutableClass | MutableClass) => //ImmutableContainerType | MutableType) ⇒ - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + case LUBP(MutableClass, DeepImmutableClass) ⇒ //_: MutableObject, ImmutableObject) ⇒ // No information about superclass - case ELBP(e, ImmutableType) => // Immutable field type, no influence on mutability - dependees -= e + // Properties related to the type of the class' fields. + // + case UBP(ShallowImmutableClass | MutableClass) ⇒ //ImmutableContainerType | MutableType) ⇒ + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - case UBP(ImmutableType) => // No information about field type + case ELBP(e, ImmutableType) ⇒ // Immutable field type, no influence on mutability + dependees -= e - case FinalP(MutableType) => Result(t, MutableClass) //TODO check + case UBP(ImmutableType) ⇒ // No information about field type - case FinalP(DependentImmutableField) => maxLocalImmutability = DependentImmutableClass - // Field Mutability related dependencies: - // + case FinalP(MutableType) ⇒ Result(t, MutableClass) //TODO check - case FinalP(ShallowImmutableField) => { - maxLocalImmutability = ShallowImmutableClass - } - case FinalP(MutableField) => return Result(t, MutableClass); + case FinalP(DependentImmutableField) ⇒ maxLocalImmutability = DependentImmutableClass + // Field Mutability related dependencies: + // - case UBP(MutableField) => //_: NonFinalField) ⇒ - return Result(t, MutableClass); //MutableObjectByAnalysis); + case FinalP(ShallowImmutableField) ⇒ { + maxLocalImmutability = ShallowImmutableClass + } + case FinalP(MutableField) ⇒ return Result(t, MutableClass); - case ELBP(e, ShallowImmutableField | DeepImmutableField) => //FinalField) => - dependees -= e - if (minLocalImmutability != ShallowImmutableClass && // ImmutableContainer && - !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + case UBP(MutableField) ⇒ //_: NonFinalField) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); - case UBP(ShallowImmutableField | DeepImmutableField) => //_: FinalField) ⇒ // no information about field mutability + case ELBP(e, ShallowImmutableField | DeepImmutableField) ⇒ //FinalField) => + dependees -= e + if (minLocalImmutability != ShallowImmutableClass && // ImmutableContainer && + !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - case _ => Result(t, MutableClass) //TODO check + case UBP(ShallowImmutableField | DeepImmutableField) ⇒ //_: FinalField) ⇒ // no information about field mutability - } + case _ ⇒ Result(t, MutableClass) //TODO check + + } - if (someEPS.isRefinable) { - val entity = if (someEPS.pk == ClassImmutability_new.key) SuperClassKey else someEPS.e - dependees += (entity -> someEPS) - } + if (someEPS.isRefinable) { + val entity = if (someEPS.pk == ClassImmutability_new.key) SuperClassKey else someEPS.e + dependees += (entity -> someEPS) + } - /*[DEBUG] + /*[DEBUG] assert( oldDependees != dependees, s"dependees are not correctly updated $e($p)\n:old=$oldDependees\nnew=$dependees" ) */ - // Lift lower bound once no dependencies other than field type mutabilities are left - if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && - dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer + // Lift lower bound once no dependencies other than field type mutabilities are left + if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && + dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer - if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { - /*[DEBUG] + if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { + /*[DEBUG] assert( maxLocalImmutability == ConditionallyImmutableObject || maxLocalImmutability == ImmutableObject @@ -451,115 +451,115 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal ) */ - Result(t, maxLocalImmutability) + Result(t, maxLocalImmutability) - } else { - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - } - } + } else { + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + } + } - //[DEBUG] assert(initialImmutability.isRefinable) - println("minLocalImmutability: " + minLocalImmutability) - println("maxLocalImmutability: " + maxLocalImmutability) - val result = - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - if (lazyComputation) - result - else { - val isFinal = dependees.isEmpty - createIncrementalResult( - t, - EPS(t, minLocalImmutability, maxLocalImmutability), - isFinal, - result - ) + //[DEBUG] assert(initialImmutability.isRefinable) + println("minLocalImmutability: "+minLocalImmutability) + println("maxLocalImmutability: "+maxLocalImmutability) + val result = + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + if (lazyComputation) + result + else { + val isFinal = dependees.isEmpty + createIncrementalResult( + t, + EPS(t, minLocalImmutability, maxLocalImmutability), + isFinal, + result + ) + } } - } } trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) - - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, TypeImmutability, FieldImmutability) - - 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 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.map(cf => set(cf.thisType, DeepImmutableClass)) //ImmutableObject)) - - // 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) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability, FieldImmutability) + + 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 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.map(cf ⇒ set(cf.thisType, DeepImmutableClass)) //ImmutableObject)) + + // 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) } - // 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 = {} + 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 = {} } /** @@ -571,22 +571,22 @@ object EagerLxClassImmutabilityAnalysis_new extends ClassImmutabilityAnalysisScheduler_new 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 LxClassImmutabilityAnalysis_new(p) - ps.scheduleEagerComputationsForEntities(cfs)( - analysis.determineClassImmutability_new( - superClassType = null, - FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), - superClassMutabilityIsFinal = true, - lazyComputation = false - ) - ) - analysis - } + 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 LxClassImmutabilityAnalysis_new(p) + ps.scheduleEagerComputationsForEntities(cfs)( + analysis.determineClassImmutability_new( + superClassType = null, + FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), + superClassMutabilityIsFinal = true, + lazyComputation = false + ) + ) + analysis + } } /** @@ -598,18 +598,18 @@ object LazyLxClassImmutabilityAnalysis_new extends ClassImmutabilityAnalysisScheduler_new with FPCFLazyAnalysisScheduler { - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - - override def register( - p: SomeProject, - ps: PropertyStore, - unused: InitializationData - ): FPCFAnalysis = { - val analysis = new LxClassImmutabilityAnalysis_new(p) - ps.registerLazyPropertyComputation( - ClassImmutability_new.key, - analysis.doDetermineClassImmutability_new - ) - analysis - } + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + override def register( + p: SomeProject, + ps: PropertyStore, + unused: InitializationData + ): FPCFAnalysis = { + val analysis = new LxClassImmutabilityAnalysis_new(p) + ps.registerLazyPropertyComputation( + ClassImmutability_new.key, + analysis.doDetermineClassImmutability_new + ) + analysis + } } From 194645f210b87c5844c8abbc8d4b78a13d91064e Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 12 Nov 2019 15:55:01 +0100 Subject: [PATCH 055/327] format optimization --- .../analyses/ClassImmutabilityAnalysis.scala | 781 ++++---- .../analyses/TypeImmutabilityAnalysis.scala | 403 ++-- .../analyses/L2FieldMutabilityAnalysis.scala | 1781 +++++++++-------- 3 files changed, 1493 insertions(+), 1472 deletions(-) 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/ClassImmutabilityAnalysis.scala index c116c173ac..aba6712bc7 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/ClassImmutabilityAnalysis.scala @@ -44,6 +44,7 @@ import org.opalj.br.fpcf.properties.NonFinalField import org.opalj.br.fpcf.properties.TypeImmutability /** + * * 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 @@ -66,301 +67,307 @@ import org.opalj.br.fpcf.properties.TypeImmutability * @author Dominik Helm */ class ClassImmutabilityAnalysis(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: MutableObject - ): MultiResult = { - val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) - val r = allSubtypes.map { st ⇒ new FinalEP(st, immutability) }.toSeq - MultiResult(r) + /* + * 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: MutableObject + ): 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 ::= ( + ( + determineClassImmutability(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) + } } + IncrementalResult(Results(results), nextComputations.iterator) + } + + def doDetermineClassImmutability(e: Entity): ProperPropertyComputationResult = { + e match { + case t: ObjectType => + //this is safe + classHierarchy.superclassType(t) match { + case None => Result(t, MutableObjectDueToUnknownSupertypes) + case Some(superClassType) => + val cf = project.classFile(t) match { + case None => + return Result(t, MutableObjectByAnalysis) //TODO consider other lattice element + case Some(cf) => cf + } - @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 ::= ( - (determineClassImmutability(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) + propertyStore(superClassType, ClassImmutability.key) match { + case UBP(p: MutableObject) => Result(t, p) + case eps: EPS[ObjectType, ClassImmutability] => + determineClassImmutability( + superClassType, + eps, + eps.isFinal, + lazyComputation = true + )(cf) + case epk => + determineClassImmutability( + superClassType, + epk, + superClassMutabilityIsFinal = false, + lazyComputation = true + )(cf) } - } - IncrementalResult(Results(results), nextComputations.iterator) - } - def doDetermineClassImmutability(e: Entity): ProperPropertyComputationResult = { - e match { - case t: ObjectType ⇒ - //this is safe - classHierarchy.superclassType(t) match { - case None ⇒ Result(t, MutableObjectDueToUnknownSupertypes) - case Some(superClassType) ⇒ - val cf = project.classFile(t) match { - case None ⇒ - return Result(t, MutableObjectByAnalysis) //TODO consider other lattice element - case Some(cf) ⇒ cf - } - - propertyStore(superClassType, ClassImmutability.key) match { - case UBP(p: MutableObject) ⇒ Result(t, p) - case eps: EPS[ObjectType, ClassImmutability] ⇒ - determineClassImmutability( - superClassType, - eps, - eps.isFinal, - lazyComputation = true - )(cf) - case epk ⇒ - determineClassImmutability( - superClassType, - epk, - superClassMutabilityIsFinal = false, - lazyComputation = true - )(cf) - } - - } - case _ ⇒ - val m = e.getClass.getSimpleName+" is not an org.opalj.br.ObjectType" - throw new IllegalArgumentException(m) } + 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 determineClassImmutability( + superClassType: ObjectType, + superClassInformation: EOptionP[Entity, Property], + superClassMutabilityIsFinal: Boolean, + lazyComputation: Boolean + )( + cf: ClassFile + ): ProperPropertyComputationResult = { + // assert(superClassMutability.isMutable.isNoOrUnknown) + val t = cf.thisType + + var dependees = Map.empty[Entity, EOptionP[Entity, Property]] + + if (!superClassMutabilityIsFinal) { + dependees += (SuperClassKey -> superClassInformation) } - 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 determineClassImmutability( - superClassType: ObjectType, - superClassInformation: EOptionP[Entity, Property], - superClassMutabilityIsFinal: Boolean, - lazyComputation: Boolean - )( - cf: ClassFile - ): ProperPropertyComputationResult = { - // assert(superClassMutability.isMutable.isNoOrUnknown) - 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.filter { f ⇒ !f.isStatic } - dependees ++= (propertyStore(instanceFields, FieldMutability) collect { - case FinalP(_: NonFinalField) ⇒ - // <=> The class is definitively mutable and therefore also all subclasses. - if (lazyComputation) - return Result(t, MutableObjectByAnalysis); - else - return createResultForAllSubtypes(t, MutableObjectByAnalysis); - case ep @ InterimE(e) ⇒ - hasFieldsWithUnknownMutability = true - (e, ep) - case epk @ EPK(e: Entity, _) ⇒ - // <=> The mutability information is not yet available. - hasFieldsWithUnknownMutability = true - (e, epk) - - // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields - }).toMap - - var minLocalImmutability: ClassImmutability = - if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) - MutableObjectByAnalysis - else - ImmutableContainer - - // NOTE: maxLocalImmutability does not take the super classes' mutability into account! - var maxLocalImmutability: ClassImmutability = superClassInformation match { - case UBP(ImmutableContainer) ⇒ ImmutableContainer - case _ ⇒ ImmutableObject - } + // Collect all fields for which we need to determine the effective mutability! + var hasFieldsWithUnknownMutability = false - 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 - } + val instanceFields = cf.fields.filter { f => + !f.isStatic + } + dependees ++= (propertyStore(instanceFields, FieldMutability) collect { + case FinalP(_: NonFinalField) => + // <=> The class is definitively mutable and therefore also all subclasses. + if (lazyComputation) + return Result(t, MutableObjectByAnalysis); + else + return createResultForAllSubtypes(t, MutableObjectByAnalysis); + case ep @ InterimE(e) => + hasFieldsWithUnknownMutability = true + (e, ep) + case epk @ EPK(e: Entity, _) => + // <=> The mutability information is not yet available. + hasFieldsWithUnknownMutability = true + (e, epk) + + // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields + }).toMap + + var minLocalImmutability: ClassImmutability = + if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) + MutableObjectByAnalysis + else + ImmutableContainer + + // NOTE: maxLocalImmutability does not take the super classes' mutability into account! + var maxLocalImmutability: ClassImmutability = superClassInformation match { + case UBP(ImmutableContainer) => ImmutableContainer + case _ => ImmutableObject + } - var fieldTypes: Set[ObjectType] = Set.empty - if (maxLocalImmutability == ImmutableObject) { - fieldTypes = - // IMPROVE Use the precise type of the field (if available)! - cf.fields.collect { - case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType - }.toSet - } + 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 + } - // For each dependent class file we have to determine the mutability - // of instances of the respective type to determine the immutability - // of instances of this class. - // Basically, we have to distinguish the following cases: - // - A field's type is mutable or conditionally immutable=> - // This class is conditionally immutable. - // - A field's type is immutable => - // The field is ignored. - // (This class is as immutable as its superclass.) - // - A field's type is at least conditionally mutable or - // The immutability of the field's type is not yet known => - // This type is AtLeastConditionallyImmutable - // We have to declare a dependency on the respective type's immutability. - // - // Additional handling is required w.r.t. the supertype: - // If the supertype is Immutable => - // Nothing special to do. - // If the supertype is AtLeastConditionallyImmutable => - // We have to declare a dependency on the supertype. - // If the supertype is ConditionallyImmutable => - // This type is also at most conditionally immutable. - // - // We furthermore have to take the mutability of the fields into consideration: - // If a field is not effectively final => - // This type is mutable. - // If a field is effectively final => - // Nothing special to do. - - val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) - val hasMutableOrConditionallyImmutableField = - // IMPROVE Use the precise type of the field (if available)! - fieldTypesImmutability.exists { eOptP ⇒ - eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) - } + var fieldTypes: Set[ObjectType] = Set.empty + if (maxLocalImmutability == ImmutableObject) { + fieldTypes = // IMPROVE Use the precise type of the field (if available)! + cf.fields.collect { + case f if !f.isStatic && f.fieldType.isObjectType => f.fieldType.asObjectType + }.toSet + } - if (hasMutableOrConditionallyImmutableField) { - maxLocalImmutability = ImmutableContainer - } 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 - } - fieldTypesWithUndecidedMutability.foreach { eOptP ⇒ - dependees += (eOptP.e → eOptP) - } + // For each dependent class file we have to determine the mutability + // of instances of the respective type to determine the immutability + // of instances of this class. + // Basically, we have to distinguish the following cases: + // - A field's type is mutable or conditionally immutable=> + // This class is conditionally immutable. + // - A field's type is immutable => + // The field is ignored. + // (This class is as immutable as its superclass.) + // - A field's type is at least conditionally mutable or + // The immutability of the field's type is not yet known => + // This type is AtLeastConditionallyImmutable + // We have to declare a dependency on the respective type's immutability. + // + // Additional handling is required w.r.t. the supertype: + // If the supertype is Immutable => + // Nothing special to do. + // If the supertype is AtLeastConditionallyImmutable => + // We have to declare a dependency on the supertype. + // If the supertype is ConditionallyImmutable => + // This type is also at most conditionally immutable. + // + // We furthermore have to take the mutability of the fields into consideration: + // If a field is not effectively final => + // This type is mutable. + // If a field is effectively final => + // Nothing special to do. + + val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) + val hasMutableOrConditionallyImmutableField = + // IMPROVE Use the precise type of the field (if available)! + fieldTypesImmutability.exists { eOptP => + eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) + } + + if (hasMutableOrConditionallyImmutableField) { + maxLocalImmutability = ImmutableContainer + } 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 } + fieldTypesWithUndecidedMutability.foreach { eOptP => + dependees += (eOptP.e -> eOptP) + } + } - 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) - ); - } + 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 - someEPS match { - // Superclass related dependencies: - // - case UBP(_: MutableObject) ⇒ return Result(t, MutableObjectByAnalysis); + def c(someEPS: SomeEPS): ProperPropertyComputationResult = { + //[DEBUG] val oldDependees = dependees + someEPS match { + // Superclass related dependencies: + // + case UBP(_: MutableObject) => return Result(t, MutableObjectByAnalysis); - case LBP(ImmutableObject) ⇒ // the super class - dependees -= SuperClassKey + case LBP(ImmutableObject) => // the super class + dependees -= SuperClassKey - case UBP(ImmutableContainer) ⇒ // super class is at most immutable container - if (someEPS.isFinal) dependees -= SuperClassKey - maxLocalImmutability = ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + case UBP(ImmutableContainer) => // super class is at most immutable container + if (someEPS.isFinal) dependees -= SuperClassKey + maxLocalImmutability = ImmutableContainer + 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(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 LUBP(_: MutableObject, ImmutableObject) ⇒ // No information about superclass + case LUBP(_: MutableObject, ImmutableObject) => // No information about superclass - // Properties related to the type of the class' fields. - // - case UBP(ImmutableContainerType | MutableType) ⇒ - maxLocalImmutability = ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + // Properties related to the type of the class' fields. + // + case UBP(ImmutableContainerType | MutableType) => + maxLocalImmutability = ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - case ELBP(e, ImmutableType) ⇒ // Immutable field type, no influence on mutability - dependees -= e + case ELBP(e, ImmutableType) => // Immutable field type, no influence on mutability + dependees -= e - case UBP(ImmutableType) ⇒ // No information about field type + case UBP(ImmutableType) => // No information about field type - // Field Mutability related dependencies: - // - case UBP(_: NonFinalField) ⇒ return Result(t, MutableObjectByAnalysis); + // Field Mutability related dependencies: + // + case UBP(_: NonFinalField) => return Result(t, MutableObjectByAnalysis); - case ELBP(e, _: FinalField) ⇒ - dependees -= e - if (minLocalImmutability != ImmutableContainer && - !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) - minLocalImmutability = ImmutableContainer // Lift lower bound when possible + case ELBP(e, _: FinalField) => + dependees -= e + if (minLocalImmutability != ImmutableContainer && + !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) + minLocalImmutability = ImmutableContainer // Lift lower bound when possible - case UBP(_: FinalField) ⇒ // no information about field mutability + case UBP(_: FinalField) => // no information about field mutability - } + } - if (someEPS.isRefinable) { - val entity = if (someEPS.pk == ClassImmutability.key) SuperClassKey else someEPS.e - dependees += (entity → someEPS) - } + if (someEPS.isRefinable) { + val entity = if (someEPS.pk == ClassImmutability.key) SuperClassKey else someEPS.e + dependees += (entity -> someEPS) + } - /*[DEBUG] + /*[DEBUG] assert( oldDependees != dependees, s"dependees are not correctly updated $e($p)\n:old=$oldDependees\nnew=$dependees" ) - */ + */ - // Lift lower bound once no dependencies other than field type mutabilities are left - if (minLocalImmutability != ImmutableContainer && - dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) - minLocalImmutability = ImmutableContainer + // Lift lower bound once no dependencies other than field type mutabilities are left + if (minLocalImmutability != ImmutableContainer && + dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) + minLocalImmutability = ImmutableContainer - if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { - /*[DEBUG] + if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { + /*[DEBUG] assert( maxLocalImmutability == ConditionallyImmutableObject || maxLocalImmutability == ImmutableObject @@ -377,110 +384,115 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { s"maxLocalImmutability=$maxLocalImmutability - "+ s"(old dependees: ${oldDependees.mkString(",")}" ) - */ + */ - Result(t, maxLocalImmutability) + Result(t, maxLocalImmutability) - } else { - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - } - } + } else { + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + } + } - //[DEBUG] assert(initialImmutability.isRefinable) - val result = - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - if (lazyComputation) - result - else { - val isFinal = dependees.isEmpty - createIncrementalResult( - t, EPS(t, minLocalImmutability, maxLocalImmutability), isFinal, result - ) - } + //[DEBUG] assert(initialImmutability.isRefinable) + val result = + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + if (lazyComputation) + result + else { + val isFinal = dependees.isEmpty + createIncrementalResult( + t, + EPS(t, minLocalImmutability, maxLocalImmutability), + isFinal, + result + ) } + } } trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability) - - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability, TypeImmutability, FieldMutability) - - 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 immutable. - set(ObjectType.Object, ImmutableObject) - - // 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)) - - // 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, MutableObjectDueToUnknownSupertypes) - } - } + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability, TypeImmutability, FieldMutability) + + 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 immutable. + set(ObjectType.Object, ImmutableObject) + + // 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)) + + // 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, 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, 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 = {} + // 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, 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 = {} } /** @@ -492,22 +504,22 @@ object EagerClassImmutabilityAnalysis extends ClassImmutabilityAnalysisScheduler 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 ClassImmutabilityAnalysis(p) - ps.scheduleEagerComputationsForEntities(cfs)( - analysis.determineClassImmutability( - superClassType = null, - FinalEP(ObjectType.Object, ImmutableObject), - superClassMutabilityIsFinal = true, - lazyComputation = false - ) - ) - analysis - } + 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 ClassImmutabilityAnalysis(p) + ps.scheduleEagerComputationsForEntities(cfs)( + analysis.determineClassImmutability( + superClassType = null, + FinalEP(ObjectType.Object, ImmutableObject), + superClassMutabilityIsFinal = true, + lazyComputation = false + ) + ) + analysis + } } /** @@ -519,17 +531,18 @@ object LazyClassImmutabilityAnalysis extends ClassImmutabilityAnalysisScheduler with FPCFLazyAnalysisScheduler { - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - - override def register( - p: SomeProject, - ps: PropertyStore, - unused: InitializationData - ): FPCFAnalysis = { - val analysis = new ClassImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - ClassImmutability.key, analysis.doDetermineClassImmutability - ) - analysis - } + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + override def register( + p: SomeProject, + ps: PropertyStore, + unused: InitializationData + ): FPCFAnalysis = { + val analysis = new ClassImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + ClassImmutability.key, + analysis.doDetermineClassImmutability + ) + analysis + } } 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/TypeImmutabilityAnalysis.scala index 499e0586aa..bcc0458eca 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/TypeImmutabilityAnalysis.scala @@ -39,196 +39,194 @@ import org.opalj.br.fpcf.properties.TypeImmutability * * @author Michael Eichberg */ -class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnalysis { - - def doDetermineTypeMutability( - typeExtensibility: ObjectType ⇒ Answer - )( - e: Entity - ): ProperPropertyComputationResult = e match { - case t: ObjectType ⇒ step1(typeExtensibility)(t) - case _ ⇒ throw new IllegalArgumentException(s"$e is not an ObjectType") +class TypeImmutabilityAnalysis(final val project: SomeProject) extends FPCFAnalysis { + + def doDetermineTypeMutability( + 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 = { + typeExtensibility(t) match { + case Yes | Unknown => Result(t, MutableType) + case No => step2(t) } - - /** - * @param t An object type which is not `java.lang.Object`. - */ - def step1( - typeExtensibility: ObjectType ⇒ Answer - )( - t: ObjectType - ): ProperPropertyComputationResult = { - typeExtensibility(t) 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, Seq(eps), c) + } } - } - - 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, Seq(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, Seq(eps), c) - case epk ⇒ - InterimResult(t, MutableType, ImmutableType, Seq(epk), c) - } - } else { - var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] - var joinedImmutability: TypeImmutability = ImmutableType // this may become "Mutable..." - var maxImmutability: TypeImmutability = ImmutableType - - ps(t, ClassImmutability.key) match { - case FinalP(ImmutableObject) ⇒ - - case FinalP(_: MutableObject) ⇒ - return Result(t, MutableType); - - case FinalP(ImmutableContainer) ⇒ - joinedImmutability = ImmutableContainerType - maxImmutability = ImmutableContainerType - - 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(ImmutableType) ⇒ - - case UBP(MutableType) ⇒ - return Result(t, MutableType); - - case FinalP(ImmutableContainerType) ⇒ - joinedImmutability = joinedImmutability.meet(ImmutableContainerType) - maxImmutability = ImmutableContainerType - - case eps @ InterimLUBP(subtypeLB, subtypeUB) ⇒ - joinedImmutability = joinedImmutability.meet(subtypeLB) - maxImmutability = maxImmutability.meet(subtypeUB) - dependencies += ((subtype, eps)) - - case epk ⇒ - joinedImmutability = MutableType - dependencies += ((subtype, epk)) - - } - } + } + + 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, Seq(eps), c) + case epk => + InterimResult(t, MutableType, ImmutableType, Seq(epk), c) + } + } else { + var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] + var joinedImmutability: TypeImmutability = ImmutableType // this may become "Mutable..." + var maxImmutability: TypeImmutability = ImmutableType + + ps(t, ClassImmutability.key) match { + case FinalP(ImmutableObject) => + case FinalP(_: MutableObject) => + return Result(t, MutableType); + + case FinalP(ImmutableContainer) => + joinedImmutability = ImmutableContainerType + maxImmutability = ImmutableContainerType + + 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(ImmutableType) => + case UBP(MutableType) => + return Result(t, MutableType); + + case FinalP(ImmutableContainerType) => + joinedImmutability = joinedImmutability.meet(ImmutableContainerType) + maxImmutability = ImmutableContainerType + + 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 an ImmutableContainer, we are at most + // ImmutableContainer, even if all other subtype may even be 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 if (joinedImmutability == maxImmutability) { - // E.g., as soon as one subtype is an ImmutableContainer, we are at most - // ImmutableContainer, even if all other subtype may even be immutable! - Result(t, joinedImmutability) + Result(t, maxImmutability) } 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 == ImmutableContainerType) - Result(t, maxImmutability) - } else { - InterimResult( - t, joinedImmutability, maxImmutability, - dependencies.values, c - ) - } - } - } - - (eps: @unchecked) match { - case FinalEP(e, ImmutableType | ImmutableObject) ⇒ - dependencies = dependencies - e - nextResult() - - case UBP(MutableType | _: MutableObject) ⇒ - Result(t, MutableType) - - case FinalEP(e, ImmutableContainerType | ImmutableContainer) ⇒ - maxImmutability = ImmutableContainerType - 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() - } + 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 } - - InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) + } + if (joinedImmutability == maxImmutability) { + assert(maxImmutability == ImmutableContainerType) + Result(t, maxImmutability) + } else { + InterimResult( + t, + joinedImmutability, + maxImmutability, + dependencies.values, + c + ) + } } + } + + (eps: @unchecked) match { + case FinalEP(e, ImmutableType | ImmutableObject) => + dependencies = dependencies - e + nextResult() + + case UBP(MutableType | _: MutableObject) => + Result(t, MutableType) + + case FinalEP(e, ImmutableContainerType | ImmutableContainer) => + maxImmutability = ImmutableContainerType + 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, c) + } } + } } trait TypeImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability) - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability, TypeImmutability) + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability, TypeImmutability) } @@ -241,41 +239,42 @@ object EagerTypeImmutabilityAnalysis extends TypeImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + 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 TypeImmutabilityAnalysis(project) - val allClassFilesIterator = project.allClassFiles.iterator - val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) - - ps.scheduleEagerComputationsForEntities(types) { - analysis.step1(typeExtensibility) - } + override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val typeExtensibility = project.get(TypeExtensibilityKey) + val analysis = new TypeImmutabilityAnalysis(project) + val allClassFilesIterator = project.allClassFiles.iterator + val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) - analysis + ps.scheduleEagerComputationsForEntities(types) { + analysis.step1(typeExtensibility) } + analysis + } + } object LazyTypeImmutabilityAnalysis extends TypeImmutabilityAnalysisScheduler 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 TypeImmutabilityAnalysis(p) - val analysisRunner: ProperPropertyComputation[Entity] = - analysis.doDetermineTypeMutability(typeExtensibility) - ps.registerLazyPropertyComputation(TypeImmutability.key, analysisRunner) - analysis - - } + 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 TypeImmutabilityAnalysis(p) + val analysisRunner: ProperPropertyComputation[Entity] = + analysis.doDetermineTypeMutability(typeExtensibility) + ps.registerLazyPropertyComputation(TypeImmutability.key, analysisRunner) + 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/L2FieldMutabilityAnalysis.scala index 7c54548b27..571fb9bb6f 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/L2FieldMutabilityAnalysis.scala @@ -14,20 +14,13 @@ 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 @@ -35,8 +28,6 @@ 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 @@ -95,182 +86,200 @@ import org.opalj.tac.fpcf.properties.TACAI */ class L2FieldMutabilityAnalysis 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 - ) { - def hasDependees: Boolean = { - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || fieldMutabilityDependees.nonEmpty || - escapeDependees.nonEmpty || tacDependees.nonEmpty - } - - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - fieldMutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) - } + 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 + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || fieldMutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty } - 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) - - def doDetermineFieldMutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field ⇒ determineFieldMutability(field) - case _ ⇒ - val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + fieldMutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) } - - /** - * Analyzes the mutability of private non-final fields. - * - * 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 determineFieldMutability( - field: Field - ): ProperPropertyComputationResult = { - 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 (field.isFinal) - return createResult(); - - state.fieldMutability = EffectivelyFinalField - - val thisType = field.classFile.thisType - - if (field.isPublic) - return Result(field, NonFinalFieldByLackOfInformation) - - // 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) { - if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, NonFinalFieldByLackOfInformation); - } - project.classesPerPackage(thisType.packageName) - } else { - Set(field.classFile) - } - - val classesHavingAccess: Iterator[ClassFile] = - if (field.isProtected) { - if (typeExtensibility(thisType).isYesOrUnknown) { - return Result(field, NonFinalFieldByLackOfInformation); - } - 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))) - return Result(field, NonFinalFieldByLackOfInformation); - - // 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 - // of the class after the reference to the class and (an indirect) reference to the - // field has become available. Consider the following example: - // class X implements Y{ - // - // private Object o; - // - // public Object getO() { return o; } - // - // private static X instance; - // static { - // instance = new X(); - // Z.register(instance); - // // when we reach this point o is now (via getO) publically accessible and - // // X is properly initialized! - // o = new Object(); // o is mutated... - // } - // } - - for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode ← getTACAI(method, pcs) - } { - if (methodUpdatesField(method, taCode, pcs)) - return Result(field, NonFinalFieldByAnalysis); + } + + 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) + + def doDetermineFieldMutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field => determineFieldMutability(field) + case _ => + val m = entity.getClass.getSimpleName + " is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + + /** + * Analyzes the mutability of private non-final fields. + * + * 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 determineFieldMutability( + field: Field + ): ProperPropertyComputationResult = { + 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 (field.isFinal) + return createResult(); + + state.fieldMutability = EffectivelyFinalField + + val thisType = field.classFile.thisType + + if (field.isPublic) + return Result(field, NonFinalFieldByLackOfInformation) + + // 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) { + if (!closedPackages.isClosed(thisType.packageName)) { + return Result(field, NonFinalFieldByLackOfInformation); } - - if (state.lazyInitInvocation.isDefined) { - val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - handleCalls(calleesEOP) + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } + + val classesHavingAccess: Iterator[ClassFile] = + if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown) { + return Result(field, NonFinalFieldByLackOfInformation); } + 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))) + return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } - createResult() + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] => + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk => + state.tacDependees += method -> ((epk, pcs)) + None + } } - def handleCalls( - calleesEOP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: State - ): Boolean = { - calleesEOP match { - case FinalP(callees) ⇒ - state.calleesDependee = None - handleCallees(callees) - case InterimUBP(callees) ⇒ - state.calleesDependee = Some(calleesEOP) - handleCallees(callees) - case _ ⇒ - state.calleesDependee = Some(calleesEOP) - false - } + for { + (method, pcs) <- fieldAccessInformation.writeAccesses(field) + taCode <- getTACAI(method, pcs) + } { + if (methodUpdatesField(method, taCode, pcs)) + return Result(field, NonFinalFieldByAnalysis); } - def handleCallees(callees: Callees)(implicit state: State): Boolean = { - val pc = state.lazyInitInvocation.get._2 - if (callees.isIncompleteCallSite(pc)) { - state.fieldMutability = NonFinalFieldByAnalysis - true - } else { - val targets = callees.callees(pc).toTraversable - if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { - state.fieldMutability = NonFinalFieldByAnalysis - true - } else false - } + if (state.lazyInitInvocation.isDefined) { + val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + handleCalls(calleesEOP) } - /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. - */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field - * The code below can be used to identify such value, but is not yet adapted to using the - * TACAI property */ - /* + createResult() + } + + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: State + ): Boolean = { + calleesEOP match { + case FinalP(callees) => + state.calleesDependee = None + handleCallees(callees) + case InterimUBP(callees) => + state.calleesDependee = Some(calleesEOP) + handleCallees(callees) + case _ => + state.calleesDependee = Some(calleesEOP) + false + } + } + + def handleCallees(callees: Callees)(implicit state: State): Boolean = { + val pc = state.lazyInitInvocation.get._2 + if (callees.isIncompleteCallSite(pc)) { + state.fieldMutability = NonFinalFieldByAnalysis + true + } else { + val targets = callees.callees(pc).toTraversable + if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { + state.fieldMutability = NonFinalFieldByAnalysis + true + } else false + } + } + + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + * The code below can be used to identify such value, but is not yet adapted to using the + * TACAI property */ + /* var constantVal: Option[Any] = None var allInitializeConstant = true @@ -314,6 +323,7 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } } + for (constructor ← constructors) { // TODO iterate all statements val NonVirtualMethodCall(_, declClass, _, name, _, rcvr, _) = stmt @@ -328,754 +338,752 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } constantVal */ + } + + /** + * 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.fieldMutability ne NonFinalFieldByAnalysis)) + InterimResult( + state.field, + NonFinalFieldByAnalysis, + state.fieldMutability, + state.dependees, + c + ) + else + Result(state.field, state.fieldMutability) + } + + /** + * 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 = { + val isNotFinal = eps.pk match { + case EscapeProperty.key => + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + methodUpdatesField(method, newEP.ub.tac.get, pcs) + case Callees.key => + handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key => + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key => + 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) + !isFinalField(newEP) } - /** - * 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.fieldMutability ne NonFinalFieldByAnalysis)) - InterimResult( - state.field, - NonFinalFieldByAnalysis, - state.fieldMutability, - state.dependees, - c - ) - else - Result(state.field, state.fieldMutability) - } - - /** - * 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 = { - val isNotFinal = eps.pk match { - case EscapeProperty.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - 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)) - methodUpdatesField(method, newEP.ub.tac.get, pcs) - case Callees.key ⇒ - handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key ⇒ - isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key ⇒ - 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) - !isFinalField(newEP) - } - - if (isNotFinal) - Result(state.field, NonFinalFieldByAnalysis) - else - createResult() - } - - /** - * Checks whether a field write may be a lazy initialization. - * - * We only consider simple cases of lazy initialization where the single field write is guarded - * so it is executed only if the field still has its default value and where the written value - * is guaranteed to be the same even if the write is executed multiple times (may happen because - * of the initializing method being executed more than once on concurrent threads). - * - * Also, all field reads must be used only for a guard or their uses have to be guarded - * themselves. - */ - def isLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - )(implicit state: State): Boolean = { - val write = code(writeIndex).asFieldWriteAccessStmt - - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - // Only handle lazy initialization of ints and floats as they are guaranteed to be - // written atomically - return false; - } - - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return false; // Reads outside the (single) lazy initialization method - } - - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val (guardIndex, guardedIndex, readIndex) = - findGuard(writeIndex, defaultValue, code, cfg) match { - case Some((guard, guarded, read)) ⇒ (guard, guarded, read) - case None ⇒ return false; - } - - // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) - return false; - - // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - return false; - - // Field reads (except for the guard) may only be executed if the field's value is not the - // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - return false; - - true + if (isNotFinal) + Result(state.field, NonFinalFieldByAnalysis) + else + createResult() + } + + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + )(implicit state: State): Boolean = { + val write = code(writeIndex).asFieldWriteAccessStmt + + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + return false; } - def checkWrites( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val definitions = write.value.asVar.definedBy - - val isDeterministic = - if (definitions.size == 1) { - // The value written must be computed deterministically - checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) - } else { - // More than one definition site for the value might lead to differences between - // invocations, but not if this method has no parameters and is deterministic - // (in this case, the definition reaching the write will always be the same) - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - - // The field write must be guarded correctly - isDeterministic && - checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - } - - /** - * Checks if the field write for a lazy initialization is immediately followed by a return of - * the written value (possibly loaded by another field read). - */ - def checkImmediateReturn( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 - - var load = -1 - - while (index < code.length) { - val stmt = code(index) - stmt.astID match { - case Assignment.ASTID ⇒ - if (isReadOfCurrentField(stmt.asAssignment.expr)) - load = index - else - return false; // No field read or a field read of a different field - case ReturnValue.ASTID ⇒ - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) - return true; // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) - return true; // return of field value loaded by field read - else - return false; // return of different value - case _ ⇒ return false; // neither a field read nor a return - } - index += 1 - } - false + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs => (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method } - def lazyInitializerIsDeterministic( - method: Method, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) => (guard, guarded, read) + case None => return false; + } + + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) + return false; + + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + + true + } + + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val definitions = write.value.asVar.definedBy + + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + // The field write must be guarded correctly + isDeterministic && + checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID => + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID => + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ => return false; // neither a field read nor a return + } + index += 1 } - - /** - * 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 - for (pc ← pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID ⇒ - if (method.isInitializer) { - if (field.isStatic) { - if (method.isConstructor) - return true; - } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - 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; - - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - // A field written outside an initializer must be lazily - // initialized or it is non-final - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - )) - return true; - - state.fieldMutability = LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - // 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. - return true; - } - } - case _ ⇒ throw new RuntimeException("unexpected field access"); - } + false + } + + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + /** + * 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 + for (pc <- pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID => + if (method.isInitializer) { + if (field.isStatic) { + if (method.isConstructor) + return true; } else { - // nothing to do as the put field is dead + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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; + + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + // A field written outside an initializer must be lazily + // initialized or it is non-final + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )) + return true; + + state.fieldMutability = LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ => throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead } - false + } } - - /** - * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method → ((eps, pcs)) - eps.ub.tac - case epk ⇒ - state.tacDependees += method → ((epk, pcs)) - None - } + false + } + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] => + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk => + state.tacDependees += method -> ((epk, pcs)) + None } - - /** - * 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 // Must be locally created - else { - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - handleEscapeProperty(escape) - } - } + } + + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) } + } } - - /** - * 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 = 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 + } + + /** + * 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 = 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 + } + + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt => + stmt.astID == CaughtException.ASTID + } flatMap { exception => + exception.asCaughtException.origins.map { origin: Int => + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator } - /** - * Checks if the field write is only executed if the field's value was still the default value. - * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization - * and the field write. - */ - def checkWriteIsGuarded( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val startBB = cfg.bb(writeIndex).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt ⇒ - stmt.astID == CaughtException.ASTID - } flatMap { exception ⇒ - exception.asCaughtException.origins.map { origin: Int ⇒ - if (isImmediateVMException(origin)) - pcOfImmediateVMException(origin) - else - pcOfMethodExternalException(origin) - }.iterator - } + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail + val startPC = curBB.startPC + val endPC = curBB.endPC - val startPC = curBB.startPC - val endPC = curBB.endPC + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement - if (startPC == 0 || startPC == guardedIndex) - return false; // Reached method start or wrong branch of guarding if-Statement + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors + true + } + + /** + * 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]] + )(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(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) => + isFinalField(propertyStore(field, FieldMutability.key)) + case _ => // Unknown field + false + }) } + } - true + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) } - /** - * 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]] - )(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(defSites.head).asAssignment.expr.isConst) true - else { - val expr = code(index).asAssignment.expr - expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - isFinalField(propertyStore(field, FieldMutability.key)) - case _ ⇒ // Unknown field - false - }) - } - } + val value = origin.expr - defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID => + value.asFieldRead.resolveField(p) match { + case Some(field) => + isFinalField(propertyStore(field, FieldMutability.key)) + case _ => // Unknown field + false } - - val value = origin.expr - - val isNonConstDeterministic = value.astID match { - case GetStatic.ASTID | GetField.ASTID ⇒ - value.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - isFinalField(propertyStore(field, FieldMutability.key)) - case _ ⇒ // Unknown field - false - } - 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.exists(!isConstant(_))) { - false - } else { - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true - } - 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, code) + 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.exists(!isConstant(_))) { + false + } else { + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true } - - value.isConst || isNonConstDeterministic + 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, code) } - /** - * Checks that all non-dead field reads that are not used for the guarding if-Statement of a - * lazy initialization are only executed if the field did not have its default value or after - * the (single) field write. - */ - def checkReads( - reads: Seq[(Method, PCs)], - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - ): Boolean = { - // There is only a single method with reads aside from initializers (checked by - // isLazilyInitialized), so we have to check only reads from that one method. - reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ - val index = pcToIndex(readPC) - index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) - } + value.isConst || isNonConstDeterministic + } + + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC => + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) } - - /** - * Checks that a field read is only executed if the field did not have its default value or - * after the (single) field write. - */ - def checkRead( - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Boolean = { - val startBB = cfg.bb(readIndex).asBasicBlock - val writeBB = cfg.bb(writeIndex) - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - - if (startPC == 0) - return false; // Reached the start of the method but not the guard or field write - - if ((curBB eq writeBB) && writeIndex > readIndex) - return false; // In the basic block of the write, but before the write - - if (startPC != guardedIndex && // Did not reach the guard - (curBB ne writeBB) /* Not the basic block of the write */ ) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - true + } + + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } } - /** - * Gets all predecessor BasicBlocks of a CFGNode. - */ - def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode ⇒ - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) - } - result.toList + true + } + + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode => + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) } - - /** - * 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. - */ - def findGuard( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Option[(Int, Int, Int)] = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int)] = None - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID ⇒ - val ifStmt = cfStmt.asIf - ifStmt.condition match { - case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1)) - } - - case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None; - } else { - result = Some((endPC, ifStmt.targetStmt)) - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard - } - - None + result.toList + } + + /** + * 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. + */ + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID => + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) => + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) => + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ => + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ => + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } } - /** - * 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])(implicit state: State): Boolean = { - val field = expr.astID match { - case GetField.ASTID ⇒ - val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) None - else expr.asGetField.resolveField(project) - case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) - case _ ⇒ None - } - field.contains(state.field) + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use => + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard } - /** - * 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]] - )(implicit state: State): Boolean = { - - /** - * Checks if an expression is an IntConst or FloatConst with the corresponding default value. - */ - 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 || - expr.isFloatConst && defaultValue == expr.asFloatConst.value - } - } - - /** - * Checks whether the non-constant expression of the if-Statement is a read of the current - * field. - */ - def isGuardInternal(expr: V): Boolean = { - expr.definedBy forall { index ⇒ - if (index < 0) false // If the value is from a parameter, this can not be the guard - else isReadOfCurrentField(code(index).asAssignment.expr) - } - } - - if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { - isGuardInternal(ifStmt.rightExpr.asVar) - } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { - isGuardInternal(ifStmt.leftExpr.asVar) - } else false + None + } + + /** + * 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])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID => + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID => expr.asGetStatic.resolveField(project) + case _ => None } + field.contains(state.field) + } + + /** + * 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]] + )(implicit state: State): Boolean = { /** - * Checks if the field is prematurely read, i.e. read before it is initialized in the - * constructor, using the corresponding property. + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. */ - def isPrematurelyRead( - eop: EOptionP[Field, FieldPrematurelyRead] - )(implicit state: State): Boolean = - eop match { - case LBP(NotPrematurelyReadField) ⇒ - state.prematurelyReadDependee = None - false - case UBP(PrematurelyReadField) ⇒ true - case eps ⇒ - state.prematurelyReadDependee = Some(eps) - false - } + 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } /** - * Checkes 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. + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. */ - 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 isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index => + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } } - /** - * Checkes if 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 isFinalField( - eop: EOptionP[Field, FieldMutability] - )(implicit state: State): Boolean = eop match { - case LBP(_: FinalField) ⇒ - true - case UBP(_: NonFinalField) ⇒ false - case _ ⇒ - state.fieldMutabilityDependees += eop - true + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else false + } + + /** + * Checks if 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) => true + case eps => + state.prematurelyReadDependee = Some(eps) + false } + + /** + * Checkes 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. + */ + 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 + } + + /** + * Checkes if 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 isFinalField( + eop: EOptionP[Field, FieldMutability] + )(implicit state: State): Boolean = eop match { + case LBP(_: FinalField) => + true + case UBP(_: NonFinalField) => false + case _ => + state.fieldMutabilityDependees += eop + true + } } trait L2FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(FieldMutability), - PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(FieldMutability), + PropertyBounds.ub(EscapeProperty) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability) } @@ -1086,16 +1094,16 @@ object EagerL2FieldMutabilityAnalysis extends L2FieldMutabilityAnalysisScheduler 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) - analysis - } + 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) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -1105,17 +1113,18 @@ object LazyL2FieldMutabilityAnalysis extends L2FieldMutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L2FieldMutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - FieldMutability.key, analysis.determineFieldMutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L2FieldMutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldMutability.key, + analysis.determineFieldMutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } From 63c945e2ebb193c05f202bff51101facc2e18818 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 12 Nov 2019 16:27:09 +0100 Subject: [PATCH 056/327] Removing unnecessary trait in ReferenceImmutability Lattice --- .../properties/ReferenceImmutability.scala | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala index 54e86d106d..72526610f0 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -7,7 +7,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - type Self = ReferenceImmutability + type Self = ReferenceImmutability } @@ -28,25 +28,23 @@ sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaIn sealed trait ReferenceImmutability extends Property with ReferenceImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key + final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key } object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { - final val PropertyKeyName = "opalj.ReferenceImmutability" + final val PropertyKeyName = "opalj.ReferenceImmutability" - final val key: PropertyKey[ReferenceImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableReference - ) - } + final val key: PropertyKey[ReferenceImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableReference + ) + } } -sealed trait RefImm extends ReferenceImmutability +case object MutableReference extends ReferenceImmutability -case object MutableReference extends RefImm +case object LazyInitializedReference extends ReferenceImmutability -case object LazyInitializedReference extends RefImm - -case object ImmutableReference extends RefImm +case object ImmutableReference extends ReferenceImmutability From ae0438aa3a79cb198a404fa6d2759a85e48139e8 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 14 Nov 2019 00:48:01 +0100 Subject: [PATCH 057/327] first approach for the type immutability analysis with the new lattice nomenclature including demo and tests --- .../TypeImmutabilityAnalysisDemo.scala | 69 +++++ .../type_immutability/FinalEmptyClass.java | 10 + .../TrivialMutableClass.java | 18 ++ .../WithMutableAndImmutableFieldType.java | 12 + .../DeepImmutableTypeAnnotation.java | 24 ++ .../MutableTypeAnnotation.java | 30 ++ .../ShallowImmutableTypeAnnotation.java | 24 ++ .../opalj/fpcf/TypeImmutabilityTests.scala | 56 ++++ .../NewTypeImmutabilityMatcher.scala | 57 ++++ .../properties/TypeImmutability_new.scala | 112 +++++++ .../LxTypeImmutabilityAnalysis_new.scala | 288 ++++++++++++++++++ 11 files changed, 700 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/FinalEmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/TrivialMutableClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala 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..b78a2c409a --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo.scala @@ -0,0 +1,69 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0PurityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.MutableType_new +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new + +/** + * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result + * + * @author Tobias Peter Roth + */ +object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" + + override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0PurityAnalysis, + LazyL1FieldMutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new + ) + "Mutable Type: "+propertyStore + .finalEntities(MutableType_new) + .toList + .toString()+"\n"+ + "Shallow Immutable Type: "+propertyStore + .finalEntities(ShallowImmutableType) + .toList + .toString()+"\n"+ + "Deep Immutable Type: "+propertyStore + .finalEntities(DeepImmutableType) + .toList + .toString()+"\n" + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/FinalEmptyClass.java new file mode 100644 index 0000000000..37f683f188 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/FinalEmptyClass.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.type_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableTypeAnnotation("It has no fields and is final") +@DeepImmutableClassAnnotation("It has no fields and is final") +public final class FinalEmptyClass { + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/TrivialMutableClass.java new file mode 100644 index 0000000000..554b76d5c4 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/TrivialMutableClass.java @@ -0,0 +1,18 @@ +package org.opalj.fpcf.fixtures.type_immutability; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("is has mutable fields") +@MutableClassAnnotation("It has Mutable Fields") +public class TrivialMutableClass { + @MutableReferenceAnnotation("public") + @MutableFieldAnnotation("mutable reference") + public int n = 0; + + @MutableReferenceAnnotation("public") + @MutableFieldAnnotation("mutable reference") + public String name = "name"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java new file mode 100644 index 0000000000..db687c72e3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java @@ -0,0 +1,12 @@ +package org.opalj.fpcf.fixtures.type_immutability; + +import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; + +@ShallowImmutableTypeAnnotation("has shallow mutable fields") +public class WithMutableAndImmutableFieldType { + + private FinalEmptyClass fec = new FinalEmptyClass(); + + private TrivialMutableClass tmc = new TrivialMutableClass(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java new file mode 100644 index 0000000000..795a018d57 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.type_immutability; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated type deep immutable. + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "TypeImmutability_new", validator = DeepImmutableTypeMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface DeepImmutableTypeAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java new file mode 100644 index 0000000000..fa643933cf --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java @@ -0,0 +1,30 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.type_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.TypeImmutabilityAnalysis_new; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated type mutable. + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "TypeImmutability_new", validator = NewMutableTypeMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface MutableTypeAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default { + TypeImmutabilityAnalysis_new.class + }; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java new file mode 100644 index 0000000000..bd9ac4477c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.type_immutability; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated type shallow immutable. + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "TypeImmutability_new", validator = ShallowImmutableTypeMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ShallowImmutableTypeAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala new file mode 100644 index 0000000000..74a309ce90 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala @@ -0,0 +1,56 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis + +/** + * Tests the Type Immutability Analysis with the new lattice + * + * @author Tobias Peter Roth + */ +class TypeImmutabilityTests extends PropertiesTest { + + 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("ClassImmutability_new")) + } + + describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { + println(1) + val as = executeAnalyses( + Set( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala new file mode 100644 index 0000000000..0492c08aa2 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala @@ -0,0 +1,57 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.type_immutability + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.MutableType_new +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.br.fpcf.properties.TypeImmutability_new +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher + +/** + * @author Tobias Peter Roth + */ +class NewTypeImmutabilityMatcher(val property: TypeImmutability_new) + 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(MutableType_new) + +class ShallowImmutableTypeMatcher extends NewTypeImmutabilityMatcher(ShallowImmutableType) + +class DeepImmutableTypeMatcher extends NewTypeImmutabilityMatcher(DeepImmutableType) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala new file mode 100644 index 0000000000..fa1450bc81 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -0,0 +1,112 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.properties + +import org.opalj.fpcf.OrderedProperty +import org.opalj.fpcf.Entity +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +sealed trait TypeImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { + + final type Self = TypeImmutability_new +} + +/** + * 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]]. + * + * @author Tobias Peter Roth + */ +sealed trait TypeImmutability_new + extends OrderedProperty + with TypeImmutabilityPropertyMetaInformation_new { + + /** + * Returns the key used by all `TypeImmutability_new` properties. + */ + final def key = TypeImmutability_new.key + + def isDeepImmutable: Boolean + def isShallowImmutable: Boolean + + /** `true` if the immutability is unknown or if the type is mutable.*/ + def isMutable: Boolean + + def meet(other: TypeImmutability_new): TypeImmutability_new +} + +/** + * Common constants use by all [[TypeImmutability_new]] properties associated with methods. + */ +object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new { + + /** + * The key associated with every [[TypeImmutability_new]] property. + */ + final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( + "org.opalj.TypeImmutability_new", + MutableType_new + ) +} + +/** + * 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 DeepImmutableType extends TypeImmutability_new { + + override def isDeepImmutable: Boolean = true + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = false + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (this == that) + this + else + that + +} + +case object ShallowImmutableType extends TypeImmutability_new { + + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = true + override def isMutable: Boolean = false + + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (that == MutableType_new) + 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_new extends TypeImmutability_new { + + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = true + + def meet(other: TypeImmutability_new): this.type = this + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other != MutableType_new) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala new file mode 100644 index 0000000000..12d5ce8372 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -0,0 +1,288 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses + +import org.opalj.Answer +import org.opalj.No +import org.opalj.Unknown +import org.opalj.Yes +import org.opalj.br.ObjectType +import org.opalj.fpcf.ELUBP +import org.opalj.fpcf.Entity +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.EPS +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 +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_new +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.MutableClass +import org.opalj.br.fpcf.properties.MutableType_new +import org.opalj.br.fpcf.properties.ShallowImmutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.br.fpcf.properties.TypeImmutability_new + +/** + * Determines the mutability 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 + */ +class TypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPCFAnalysis { + + def doDetermineTypeMutability_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 = { + typeExtensibility(t) match { + case Yes | Unknown => Result(t, MutableType_new) // 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_new, ub: ClassImmutability_new) => + val thisLB = lb.correspondingTypeImmutability + val thisUB = ub.correspondingTypeImmutability + if (eps.isFinal) + Result(t, thisUB) + else + InterimResult(t, thisLB, thisUB, Seq(eps), c) + } + } + } + + ps(t, ClassImmutability_new.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, Seq(eps), c) + case epk => InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) + //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) + } + } else { + var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] + var joinedImmutability + : TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." + var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType + + ps(t, ClassImmutability_new.key) match { + case FinalP(DeepImmutableClass) => //ImmutableObject) => + case FinalP(MutableClass) => //(_: MutableObject) => + return Result(t, MutableType_new); //MutableType); + + case FinalP(ShallowImmutableClass) => //ImmutableContainer) => + joinedImmutability = ShallowImmutableType // ImmutableContainerType + maxImmutability = ShallowImmutableType //ImmutableContainerType + + case eps @ InterimLUBP(lb, ub) => + joinedImmutability = lb.correspondingTypeImmutability + maxImmutability = ub.correspondingTypeImmutability + dependencies += (t -> eps) + + case eOptP => + joinedImmutability = MutableType_new //MutableType + dependencies += (t -> eOptP) + } + + directSubtypes foreach { subtype => + ps(subtype, TypeImmutability_new.key) match { + case FinalP(DeepImmutableType) => //ImmutableType) => + case UBP(MutableType_new) => //MutableType) => + return Result(t, MutableType_new); //MutableType); + + case FinalP(ShallowImmutableType) => //ImmutableContainerType) => + joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) + maxImmutability = ShallowImmutableType //ImmutableContainerType + + case eps @ InterimLUBP(subtypeLB, subtypeUB) => + joinedImmutability = joinedImmutability.meet(subtypeLB) + maxImmutability = maxImmutability.meet(subtypeUB) + dependencies += ((subtype, eps)) + + case epk => + joinedImmutability = MutableType_new //MutableType + dependencies += ((subtype, epk)) + + } + } + + if (dependencies.isEmpty) { + Result(t, maxImmutability) + } else if (joinedImmutability == maxImmutability) { + // E.g., as soon as one subtype is an ImmutableContainer, we are at most + // ImmutableContainer, even if all other subtype may even be 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_new => + joinedImmutability = joinedImmutability.meet(lb) + case lb: ClassImmutability_new => + joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) + } else { + joinedImmutability = MutableType_new //MutableType + continue = false + } + } + if (joinedImmutability == maxImmutability) { + assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) + Result(t, maxImmutability) + } else { + InterimResult( + t, + joinedImmutability, + maxImmutability, + dependencies.values, + c + ) + } + } + } + + (eps: @unchecked) match { + case FinalEP(e, DeepImmutableType | DeepImmutableClass) => //ImmutableType | ImmutableObject) => + dependencies = dependencies - e + nextResult() + + case UBP(x) + if (x == MutableType_new || x == MutableClass) => //MutableType | _: MutableObject) => + Result(t, MutableType_new) //MutableType) + + case FinalEP(e, x) + if (x == ShallowImmutableType || x == ShallowImmutableClass) => //ImmutableContainerType | ImmutableContainer) => + maxImmutability = ShallowImmutableType //ImmutableContainerType + dependencies = dependencies - e + nextResult() + + case eps @ InterimEUBP(e, subtypeP) => + dependencies = dependencies.updated(e, eps) + subtypeP match { + case subtypeP: TypeImmutability_new => + maxImmutability = maxImmutability.meet(subtypeP) + case subtypeP: ClassImmutability_new => + maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) + } + nextResult() + } + } + + InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) + } + } + } +} + +trait TypeImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) + +} + +/** + * Starter for the '''type immutability analysis'''. + * + * @author Michael Eichberg + */ +object EagerLxTypeImmutabilityAnalysis_new + extends TypeImmutabilityAnalysisScheduler_new + 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 TypeImmutabilityAnalysis_new(project) + val allClassFilesIterator = project.allClassFiles.iterator + val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) + + ps.scheduleEagerComputationsForEntities(types) { + analysis.step1(typeExtensibility) + } + + analysis + } + +} + +object LazyLxTypeImmutabilityAnalysis_new + extends TypeImmutabilityAnalysisScheduler_new + 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 TypeImmutabilityAnalysis_new(p) + val analysisRunner: ProperPropertyComputation[Entity] = + analysis.doDetermineTypeMutability_new(typeExtensibility) + ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) + analysis + } +} From 265f42e1cb62070a1d7a884b37a55ddb52a0d2ed Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Mon, 18 Nov 2019 14:21:32 +0100 Subject: [PATCH 058/327] Type Immutability Analysis in the other new immutability Analysis integrated. With first approach of dependent classes and fields. 1. approach: A dependent class is always seen as a shallow immutable type --- .../ClassImmutabilityAnalysisDemo.scala | 92 +- .../FieldImmutabilityAnalysisDemo.scala | 44 +- .../ReferenceImmutabilityAnalysisDemo.scala | 80 +- .../TypeImmutabilityAnalysisDemo.scala | 9 +- .../WithMutableAndImmutableFieldType.java | 5 +- .../DeepImmutableTypeAnnotation.java | 4 + .../MutableTypeAnnotation.java | 6 +- .../ShallowImmutableTypeAnnotation.java | 4 + .../opalj/fpcf/ClassImmutabilityTests.scala | 126 +-- .../opalj/fpcf/FieldImmutabilityTests.scala | 6 +- .../opalj/fpcf/TypeImmutabilityTests.scala | 61 +- .../properties/ClassImmutability_new.scala | 36 +- .../properties/ReferenceImmutability.scala | 18 +- .../properties/TypeImmutability_new.scala | 92 +- .../L0FieldImmutabilityAnalysis.scala | 376 ++++---- .../LxClassImmutabilityAnalysis_new.scala | 912 +++++++++--------- .../LxTypeImmutabilityAnalysis_new.scala | 419 ++++---- 17 files changed, 1194 insertions(+), 1096 deletions(-) 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 index 4fa1ac4f0c..04946824b9 100644 --- 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 @@ -8,16 +8,18 @@ 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.EagerL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +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.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis; /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -26,47 +28,49 @@ import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis */ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" - override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(project: Project[URL]): String = { + def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) + val analysesManager = project.get(FPCFAnalysesManagerKey) - val (propertyStore, _) = analysesManager.runAll( - EagerTypeImmutabilityAnalysis, - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerL0ReferenceImmutabilityAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - EagerLxClassImmutabilityAnalysis_new - ) - "Mutable Class: " + propertyStore - .finalEntities(MutableClass) - .toList - .toString() + "\n" + - "Dependent Immutable Class: " + propertyStore - .finalEntities(DependentImmutableClass) - .toList - .toString() + "\n" + - "Shallow Immutable Class: " + propertyStore - .finalEntities(ShallowImmutableClass) - .toList - .toString() + "\n" + - "Deep Immutable Class: " + propertyStore - .finalEntities(DeepImmutableClass) - .toList - .toString() + "\n" - } + val (propertyStore, _) = analysesManager.runAll( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new + ) + "Mutable Class: "+propertyStore + .finalEntities(MutableClass) + .toList + .toString()+"\n"+ + "Dependent Immutable Class: "+propertyStore + .finalEntities(DependentImmutableClass) + .toList + .toString()+"\n"+ + "Shallow Immutable Class: "+propertyStore + .finalEntities(ShallowImmutableClass) + .toList + .toString()+"\n"+ + "Deep Immutable Class: "+propertyStore + .finalEntities(DeepImmutableClass) + .toList + .toString()+"\n" + } } 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 index 8a1fd29738..4da81148ed 100644 --- 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 @@ -7,19 +7,18 @@ 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.EagerClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis -import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +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.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.TACAITransformer -import org.opalj.tac.fpcf.analyses.escape.EagerSimpleEscapeAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. @@ -46,15 +45,26 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val analysesManager = project.get(FPCFAnalysesManagerKey) val (propertyStore, _) = analysesManager.runAll( - EagerTypeImmutabilityAnalysis, - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerClassImmutabilityAnalysis, - EagerL0ReferenceImmutabilityAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerSimpleEscapeAnalysis, - TACAITransformer, - EagerL0FieldImmutabilityAnalysis + /** + * LazyTypeImmutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyClassImmutabilityAnalysis, + * LazyL0ReferenceImmutabilityAnalysis, + * LazyL0PurityAnalysis, + * LazyL1FieldMutabilityAnalysis, + * LazySimpleEscapeAnalysis, + * TACAITransformer, + * LazyLxTypeImmutabilityAnalysis_new, + * EagerL0FieldImmutabilityAnalysis* + */ + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new ); "Mutable Fields: " + propertyStore diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index eb2d7a4b80..ba34ef0ad8 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -7,13 +7,13 @@ 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.EagerL0PurityAnalysis -import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.analyses.LazyL0PurityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.TACAITransformer /** * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. @@ -22,42 +22,42 @@ import org.opalj.tac.fpcf.analyses.EagerL1FieldMutabilityAnalysis */ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - - val (propertyStore, _) = analysesManager.runAll( - EagerUnsoundPrematurelyReadFieldsAnalysis, - EagerL0PurityAnalysis, - EagerL1FieldMutabilityAnalysis, - EagerL0ReferenceImmutabilityAnalysis - ); - - "Mutable References: " + propertyStore - .finalEntities(MutableReference) - .toList - .toString() + "\n" + - "Lazy Initialized Reference: " + propertyStore - .finalEntities(LazyInitializedReference) - .toList - .toString() + "\n" + - "Immutable References: " + propertyStore - .finalEntities(ImmutableReference) - .toList - .toString() - } + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + + val (propertyStore, _) = analysesManager.runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL0PurityAnalysis, + TACAITransformer, + EagerL0ReferenceImmutabilityAnalysis + ); + + "Mutable References: "+propertyStore + .finalEntities(MutableReference) + .toList + .toString()+"\n"+ + "Lazy Initialized Reference: "+propertyStore + .finalEntities(LazyInitializedReference) + .toList + .toString()+"\n"+ + "Immutable References: "+propertyStore + .finalEntities(ImmutableReference) + .toList + .toString() + } } 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 index b78a2c409a..bffe4c39e1 100644 --- 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 @@ -14,10 +14,11 @@ import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.DeepImmutableType import org.opalj.br.fpcf.properties.MutableType_new import org.opalj.br.fpcf.properties.ShallowImmutableType +//import org.opalj.br.fpcf.properties.ShallowImmutableType import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -48,9 +49,9 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL0ReferenceImmutabilityAnalysis, LazyL0PurityAnalysis, - LazyL1FieldMutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, EagerLxTypeImmutabilityAnalysis_new ) "Mutable Type: "+propertyStore diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java index db687c72e3..d9ddb71e0f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java @@ -1,12 +1,11 @@ package org.opalj.fpcf.fixtures.type_immutability; -import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@ShallowImmutableTypeAnnotation("has shallow mutable fields") +@MutableTypeAnnotation("has shallow mutable fields") public class WithMutableAndImmutableFieldType { private FinalEmptyClass fec = new FinalEmptyClass(); - private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java index 795a018d57..3400216ea3 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java @@ -1,7 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.type_immutability; +import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.LxTypeImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -21,4 +23,6 @@ * A short reasoning of this property. */ String value();// default = "N/A"; + + Class[] analyses() default {LxTypeImmutabilityAnalysis_new.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java index fa643933cf..a31dc77bcb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.TypeImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.LxTypeImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -24,7 +24,5 @@ */ String value();// default = "N/A"; - Class[] analyses() default { - TypeImmutabilityAnalysis_new.class - }; + Class[] analyses() default {LxTypeImmutabilityAnalysis_new.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java index bd9ac4477c..6bea537699 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java @@ -1,7 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.type_immutability; +import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.LxTypeImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -21,4 +23,6 @@ * A short reasoning of this property. */ String value();// default = "N/A"; + + Class[] analyses() default {LxTypeImmutabilityAnalysis_new.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala index a16da84d99..eaad2963d4 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -7,12 +7,14 @@ 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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** @@ -20,65 +22,71 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class ClassImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) - } - p.get(RTACallGraphKey) + 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("ClassImmutability_new")) - * }* - */ - describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { - println(1) - val as = executeAnalyses( - Set( -/****/ - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - EagerLxClassImmutabilityAnalysis_new - ) - ) - println(2) - as.propertyStore.shutdown() - println(3) - validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) //Set("ClassImmutability_new")) - println(4) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ + /** + * describe("no analysis is scheduled") { + * val as = executeAnalyses(Set.empty) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) + * }* + */ + describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { + println(1) + val as = executeAnalyses( + Set( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new + ) + ) + + println(2) + as.propertyStore.shutdown() + println(3) + validateProperties( + as, + classFilesWithAnnotations(as.project).map(tp => (tp._1.thisType, tp._2, tp._3)), + Set("ClassImmutability_new") + ) + println(4) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ } 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 index 442fda7ab6..a5cbceb83e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -13,6 +13,8 @@ import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis @@ -44,7 +46,9 @@ class FieldImmutabilityTests extends PropertiesTest { LazyL2PurityAnalysis, LazyInterProceduralEscapeAnalysis, EagerL0FieldImmutabilityAnalysis, - LazyClassImmutabilityAnalysis + LazyClassImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new // LazySimpleEscapeAnalysis, // TACAITransformer ) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala index 74a309ce90..85c3fd18b2 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala @@ -23,34 +23,41 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class TypeImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ => - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + } + p.get(RTACallGraphKey) } - p.get(RTACallGraphKey) - } - describe("no analysis is scheduled") { - val as = executeAnalyses(Set.empty) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) - } + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) + } - describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { - println(1) - val as = executeAnalyses( - Set( - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new - ) - ) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) - } + describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { + println(1) + val as = executeAnalyses( + Set( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new + ) + ) + as.propertyStore.shutdown() + val tmp = classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)) + print("===========================>>>>>>>>>>>>>>>>>>>>>>"+tmp) + validateProperties( + as, + tmp, + // fieldsWithAnnotations(as.project), + Set("TypeImmutability_new") + ) //TODO class files ... with annotation + } } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index 7a4602aedc..7605ac7268 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -6,7 +6,7 @@ import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = ClassImmutability_new + final type Self = ClassImmutability_new } /** @@ -27,24 +27,34 @@ sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaIn sealed trait ClassImmutability_new extends Property with ClassImmutabilityPropertyMetaInformation_new { - final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key + final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key + def correspondingTypeImmutability: TypeImmutability_new } object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[ClassImmutability_new]] property. - */ - final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( - "opalj.ClassImmutability_new", - MutableClass - ) + /** + * The key associated with every [[ClassImmutability_new]] property. + */ + final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( + "opalj.ClassImmutability_new", + MutableClass + ) } -case object MutableClass extends ClassImmutability_new +case object MutableClass extends ClassImmutability_new { + def correspondingTypeImmutability = MutableType_new +} -case object DependentImmutableClass extends ClassImmutability_new +case object DependentImmutableClass extends ClassImmutability_new { + override def correspondingTypeImmutability: TypeImmutability_new = + ShallowImmutableType //TODO check +} -case object ShallowImmutableClass extends ClassImmutability_new +case object ShallowImmutableClass extends ClassImmutability_new { + override def correspondingTypeImmutability: TypeImmutability_new = ShallowImmutableType +} -case object DeepImmutableClass extends ClassImmutability_new +case object DeepImmutableClass extends ClassImmutability_new { + override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala index 72526610f0..d4c57da5a7 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -7,7 +7,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - type Self = ReferenceImmutability + type Self = ReferenceImmutability } @@ -28,19 +28,19 @@ sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaIn sealed trait ReferenceImmutability extends Property with ReferenceImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key + final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key } object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { - final val PropertyKeyName = "opalj.ReferenceImmutability" + final val PropertyKeyName = "opalj.ReferenceImmutability" - final val key: PropertyKey[ReferenceImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableReference - ) - } + final val key: PropertyKey[ReferenceImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableReference + ) + } } case object MutableReference extends ReferenceImmutability diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala index fa1450bc81..3b4936ca3b 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -8,7 +8,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait TypeImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = TypeImmutability_new + final type Self = TypeImmutability_new } /** @@ -29,18 +29,18 @@ sealed trait TypeImmutability_new extends OrderedProperty with TypeImmutabilityPropertyMetaInformation_new { - /** - * Returns the key used by all `TypeImmutability_new` properties. - */ - final def key = TypeImmutability_new.key + /** + * Returns the key used by all `TypeImmutability_new` properties. + */ + final def key = TypeImmutability_new.key - def isDeepImmutable: Boolean - def isShallowImmutable: Boolean + def isDeepImmutable: Boolean + def isShallowImmutable: Boolean - /** `true` if the immutability is unknown or if the type is mutable.*/ - def isMutable: Boolean + /** `true` if the immutability is unknown or if the type is mutable.*/ + def isMutable: Boolean - def meet(other: TypeImmutability_new): TypeImmutability_new + def meet(other: TypeImmutability_new): TypeImmutability_new } /** @@ -48,13 +48,13 @@ sealed trait TypeImmutability_new */ object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[TypeImmutability_new]] property. - */ - final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( - "org.opalj.TypeImmutability_new", - MutableType_new - ) + /** + * The key associated with every [[TypeImmutability_new]] property. + */ + final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( + "org.opalj.TypeImmutability_new", + MutableType_new + ) } /** @@ -63,50 +63,50 @@ object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new */ case object DeepImmutableType extends TypeImmutability_new { - override def isDeepImmutable: Boolean = true - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = false + override def isDeepImmutable: Boolean = true + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = false - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (this == that) - this - else - that + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (this == that) + this + else + that } case object ShallowImmutableType extends TypeImmutability_new { - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = true - override def isMutable: Boolean = false + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = true + override def isMutable: Boolean = false - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (that == MutableType_new) - that - else - this + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (that == MutableType_new) + that + else + this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == ImmutableType) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other == DeepImmutableType) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } } - } } case object MutableType_new extends TypeImmutability_new { - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = true + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = true - def meet(other: TypeImmutability_new): this.type = this + def meet(other: TypeImmutability_new): this.type = this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other != MutableType_new) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other != MutableType_new) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } } - } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 1836727cfe..7ae9399292 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -12,18 +12,17 @@ import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.DeepImmutableField -import org.opalj.br.fpcf.properties.DependentImmutableField +import org.opalj.br.fpcf.properties.DeepImmutableType import org.opalj.br.fpcf.properties.FieldImmutability -import org.opalj.br.fpcf.properties.ImmutableContainerType import org.opalj.br.fpcf.properties.ImmutableReference -import org.opalj.br.fpcf.properties.ImmutableType import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableField import org.opalj.br.fpcf.properties.MutableReference -import org.opalj.br.fpcf.properties.MutableType +import org.opalj.br.fpcf.properties.MutableType_new import org.opalj.br.fpcf.properties.ReferenceImmutability import org.opalj.br.fpcf.properties.ShallowImmutableField -import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.br.fpcf.properties.TypeImmutability_new import org.opalj.fpcf.EOptionP import org.opalj.fpcf.Entity import org.opalj.fpcf.FinalEP @@ -39,8 +38,10 @@ import org.opalj.fpcf.SomeEPS import org.opalj.tac.fpcf.properties.TACAI case class State() { - var typeImmutability: Option[Boolean] = None - var referenceImmutability: Option[Boolean] = None + var typeImmutability: Option[Boolean] = None + var referenceImmutability: Option[Boolean] = None + var depencenciesTypes: scala.collection.mutable.Set[ObjectType] = + scala.collection.mutable.Set[ObjectType]() } /** @@ -55,175 +56,194 @@ case class State() { class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - final val typeExtensibility = project.get(TypeExtensibilityKey) - final val closedPackages = project.get(ClosedPackagesKey) - final val fieldAccessInformation = project.get(FieldAccessInformationKey) + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) - def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { - entity match { - case field: Field ⇒ determineFieldImmutability(field) - case _ ⇒ - val m = entity.getClass.getName+"is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { + entity match { + case field: Field => determineFieldImmutability(field) + case _ => + val m = entity.getClass.getName + "is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + } + + private[analyses] def determineFieldImmutability( + field: Field + ): ProperPropertyComputationResult = { + + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + + def handleTypeImmutability(objectType: ObjectType): Option[Boolean] = { + if (objectType.isArrayType) return Some(true); //TODO + if (objectType.isBaseType) return Some(true); + val result = propertyStore(objectType, TypeImmutability_new.key) + println("result: " + result) + result match { + case FinalEP(e, DeepImmutableType) => { //ImmutableType || t == ImmutableContainerType) ⇒ { + println("has deep immutable type") + Some(true) + } + case FinalEP(e, ShallowImmutableType) => { + println("has shallow immutable type") + Some(false) //TODO mindstorm if this approch is appropriate } + case FinalEP(e, t) if (t == MutableType_new) => { //MutableType) ⇒ { + println("has mutable type") + Some(false) + } + case x @ _ => { + dependencies += x + println(x) + println("immutability of type couldn't be determined") + None + } + } } - private[analyses] def determineFieldImmutability( - field: Field - ): ProperPropertyComputationResult = { - - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - - def hasImmutableType(field: Field): Option[Boolean] = { - println("Field:: "+field) - println(field.fieldType) - println(field.fieldType.isBaseType) - if (field.fieldType.isArrayType) return Some(true); //TODO - if (field.fieldType.isBaseType) return Some(true); - val result = propertyStore(field.fieldType, TypeImmutability.key) - println("result: "+result) - result match { - case FinalEP(e, t) if (t == ImmutableType || t == ImmutableContainerType) ⇒ { - println("has immutable type") - Some(true) - } - case FinalEP(e, t) if (t == MutableType) ⇒ { - println("has mutable type") - Some(false) - } - case x @ _ ⇒ { - dependencies += x - println(x) - println("immutability of type couldn't be determined") - None - } + + def hasImmutableType(field: Field)(state: State): Option[Boolean] = { + val hasFieldImmutableType = handleTypeImmutability(field.fieldType.asObjectType) + hasFieldImmutableType match { + case Some(false) => return Some(false); + case _ => { + state.depencenciesTypes.foreach( + t => { + val isImmutable = handleTypeImmutability(t) + isImmutable match { + case Some(false) => return Some(false); + case _ => + } } + ) + Some(true) } - def hasImmutableReference(field: Field): Option[Boolean] = { - - propertyStore(field, ReferenceImmutability.key) match { - case FinalEP(_, MutableReference) ⇒ { - println("has mutable reference") - Some(false) - } - case FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ { //TODO - println("has immutable Reference") - Some(true) - } - case x @ _ ⇒ { - dependencies += x - println(x) - println("immutability of reference couldn't be determined") - None - } - } + } + + } + def hasImmutableReference(field: Field): Option[Boolean] = { + + propertyStore(field, ReferenceImmutability.key) match { + case FinalEP(_, MutableReference) => { + println("has mutable reference") + Some(false) + } + case FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO + println("has immutable Reference") + Some(true) } + case x @ _ => { + dependencies += x + println(x) + println("immutability of reference couldn't be determined") + None + } + } + } - val state: State = new State() - - def createResult(state: State): ProperPropertyComputationResult = { - println("reference Immutability: "+state.referenceImmutability) - println("type immutabiliy: "+state.typeImmutability) - - state.referenceImmutability match { - case Some(false) ⇒ Result(field, MutableField) - case Some(true) ⇒ { - //If the field type is object. It is a generic field - if (field.fieldType == ObjectType("java/lang/Object")) - Result(field, DependentImmutableField) - else - state.typeImmutability match { - case Some(true) ⇒ Result(field, DeepImmutableField) - case Some(false) ⇒ Result(field, ShallowImmutableField) - case None if (dependencies.isEmpty) ⇒ Result(field, ShallowImmutableField) - case None ⇒ { - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } - } - } - case None if (dependencies.isEmpty) ⇒ Result(field, MutableField) - case None ⇒ { - println(dependencies) - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } + val state: State = new State() + def createResult(state: State): ProperPropertyComputationResult = { + println("reference Immutability: " + state.referenceImmutability) + println("type immutabiliy: " + state.typeImmutability) + + state.referenceImmutability match { + case Some(false) => Result(field, MutableField) + case Some(true) => { + //If the field type is object. It is a generic field + //if (field.fieldType == ObjectType("java/lang/Object")) + // Result(field, DependentImmutableField) + //else + state.typeImmutability match { + case Some(true) => Result(field, DeepImmutableField) + case Some(false) => Result(field, ShallowImmutableField) + case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) + case None => { + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) } + } } - def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - println("c function called") - dependencies = dependencies.filter(_.e ne eps.e) - (eps: @unchecked) match { - case x: InterimEP[_, _] ⇒ { - println("interim in continuation function") - println(x) - dependencies += eps - InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) - } - - case x @ FinalEP(_, t) if (t == ImmutableContainerType || t == ImmutableType) ⇒ { - println(x) - println("has immutable type. Determined by continuation function.") - state.typeImmutability = Some(true) - } - - case x @ FinalEP(_, MutableType) ⇒ { - println(x) - println("has mutable type. Determined by continuation function.") - state.typeImmutability = Some(false) - } - - case x @ FinalEP(_, MutableReference) ⇒ { - println(x) - println("has mutable reference. Determined by continuation function.") - state.referenceImmutability = Some(false) - } - - case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ { //TODO - println(x) - println("has immutable reference. Determined by continuation function.") - state.referenceImmutability = Some(true) - } - - case x @ _ ⇒ println("default value: "+x) - } - createResult(state) + case None if (dependencies.isEmpty) => Result(field, MutableField) + case None => { + println(dependencies) + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) } - //-- - state.referenceImmutability = hasImmutableReference(field) - state.typeImmutability = hasImmutableType(field); - println("after type immutability determination") - createResult(state) + } } + def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { + println("c function called") + dependencies = dependencies.filter(_.e ne eps.e) + (eps: @unchecked) match { + case x: InterimEP[_, _] => { + println("interim in continuation function") + println(x) + dependencies += eps + InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + } + + case x @ FinalEP(_, t) + if (t == DeepImmutableType || t == ShallowImmutableType) => { // ImmutableContainerType || t == ImmutableType) ⇒ { + println(x) + println("has immutable type. Determined by continuation function.") + state.typeImmutability = Some(true) + } + + case x @ FinalEP(_, MutableType_new) => { //MutableType) ⇒ { + println(x) + println("has mutable type. Determined by continuation function.") + state.typeImmutability = Some(false) + } + + case x @ FinalEP(_, MutableReference) => { + println(x) + println("has mutable reference. Determined by continuation function.") + state.referenceImmutability = Some(false) + } + + case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO + println(x) + println("has immutable reference. Determined by continuation function.") + state.referenceImmutability = Some(true) + } + + case x @ _ => println("default value: " + x) + } + createResult(state) + } + + //-- + state.depencenciesTypes = scala.collection.mutable.Set[ObjectType]() + state.referenceImmutability = hasImmutableReference(field) + state.typeImmutability = hasImmutableType(field)(state); + println("after type immutability determination") + createResult(state) + } } trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - //PropertyBounds.lub(Purity), - //PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - //PropertyBounds.lub(FieldMutability), - //PropertyBounds.lub(EscapeProperty), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(FieldImmutability) - //PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.ub(TACAI), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.lub(TypeImmutability_new), + PropertyBounds.lub(FieldImmutability) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) } @@ -234,16 +254,16 @@ object EagerL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -253,19 +273,19 @@ object LazyL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - FieldImmutability.key, - analysis.determineFieldImmutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldImmutability.key, + analysis.determineFieldImmutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 9779090536..4791874a69 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -38,13 +38,11 @@ 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.ImmutableType import org.opalj.br.fpcf.properties.MutableClass import org.opalj.br.fpcf.properties.MutableField -import org.opalj.br.fpcf.properties.MutableType import org.opalj.br.fpcf.properties.ShallowImmutableClass import org.opalj.br.fpcf.properties.ShallowImmutableField -import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.br.fpcf.properties.TypeImmutability_new /** * @@ -72,7 +70,7 @@ import org.opalj.br.fpcf.properties.TypeImmutability * */ class LxClassImmutabilityAnalysis_new(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. @@ -81,358 +79,379 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal * 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_new //MutableObject - ): MultiResult = { - val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) - val r = allSubtypes.map { st ⇒ - new FinalEP(st, immutability) - }.toSeq - MultiResult(r) + /** + * 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_new //MutableObject + ): 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 ::= ( + ( + determineClassImmutability_new(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, MutableClass) //MutableObjectDueToUnknownSupertypes) + } } + IncrementalResult(Results(results), nextComputations.iterator) + } + + def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { + e match { + case t: ObjectType => + //this is safe + val a = classHierarchy.superclassType(t) + println("AAA::::" + a) + a match { + case None => Result(t, MutableClass); //MutableObjectDueToUnknownSupertypes) + case Some(superClassType) => + val cf = project.classFile(t) match { + case None => + return Result(t, MutableClass); // MutableObjectByAnalysis) //TODO consider other lattice element + case Some(cf) => cf + } - @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 ::= ( - ( - determineClassImmutability_new(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, MutableClass) //MutableObjectDueToUnknownSupertypes) + propertyStore(superClassType, ClassImmutability_new.key) match { + case UBP(MutableClass) => //MutableObject) ⇒ + Result(t, MutableClass) + case eps: EPS[ObjectType, ClassImmutability_new] => + determineClassImmutability_new( + superClassType, + eps, + eps.isFinal, + lazyComputation = true + )(cf) + case epk => + determineClassImmutability_new( + superClassType, + epk, + superClassMutabilityIsFinal = false, + lazyComputation = true + )(cf) } } - IncrementalResult(Results(results), nextComputations.iterator) + case _ => + val m = e.getClass.getSimpleName + " is not an org.opalj.br.ObjectType" + throw new IllegalArgumentException(m) } + } - def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { - e match { - case t: ObjectType ⇒ - //this is safe - classHierarchy.superclassType(t) match { - case None ⇒ Result(t, MutableClass) //MutableObjectDueToUnknownSupertypes) - case Some(superClassType) ⇒ - val cf = project.classFile(t) match { - case None ⇒ - return Result(t, MutableClass) // MutableObjectByAnalysis) //TODO consider other lattice element - case Some(cf) ⇒ cf - } - - propertyStore(superClassType, ClassImmutability_new.key) match { - case UBP(MutableClass) ⇒ //MutableObject) ⇒ - Result(t, MutableClass) - case eps: EPS[ObjectType, ClassImmutability_new] ⇒ - determineClassImmutability_new( - superClassType, - eps, - eps.isFinal, - lazyComputation = true - )(cf) - case epk ⇒ - determineClassImmutability_new( - 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 determineClassImmutability_new( + superClassType: ObjectType, + superClassInformation: EOptionP[Entity, Property], + superClassMutabilityIsFinal: Boolean, + lazyComputation: Boolean + )( + cf: ClassFile + ): ProperPropertyComputationResult = { + // assert(superClassMutability.isMutable.isNoOrUnknown) + + //---generic classes handling + //printf(cf.asVirtualClass.asClassFile.classSignature.get.toString()) + //cf.asClassFile.classSignature.get.formalTypeParameters. + //--------------------------------------------- + + val t = cf.thisType + + var dependees = Map.empty[Entity, EOptionP[Entity, Property]] + + if (!superClassMutabilityIsFinal) { + dependees += (SuperClassKey -> superClassInformation) } - private[this] object SuperClassKey + // Collect all fields for which we need to determine the effective mutability! + var hasFieldsWithUnknownMutability = false + + val instanceFields = cf.fields.filter { f => + !f.isStatic + } + var hasShallowImmutableFields = false + var hasDependentImmutableFields = false + val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) + fieldsPropertyStoreInformation.foreach( + f => { + println("f:::" + f) + f match { + case FinalP(MutableField) => { + if (lazyComputation) + return Result(t, MutableClass); + else + return createResultForAllSubtypes(t, MutableClass); + } + case FinalP(ShallowImmutableField) => hasShallowImmutableFields = true + case FinalEP(f, DependentImmutableField) => { + println("FieldType: " + f.asField.fieldType) + println("FieldTypeSignature: " + f.asField.fieldTypeSignature.get.toJVMSignature) + 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); + } + } + ) /** - * Determines the immutability of instances of the given class type `t`. + * dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { + * case FinalP(MutableField) => //NonFinalField) ⇒ + * // <=> The class is definitively mutable and therefore also all subclasses. + * if (lazyComputation) + * return Result(t, MutableClass); //MutableObjectByAnalysis); // + * else + * return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); + * case ep @ InterimE(e) => + * hasFieldsWithUnknownMutability = true + * (e, ep) + * case epk @ EPK(e: Entity, _) => + * // <=> The mutability information is not yet available. + * hasFieldsWithUnknownMutability = true + * (e, epk) * - * @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. + * // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields + * }).toMap * */ - def determineClassImmutability_new( - superClassType: ObjectType, - superClassInformation: EOptionP[Entity, Property], - superClassMutabilityIsFinal: Boolean, - lazyComputation: Boolean - )( - cf: ClassFile - ): ProperPropertyComputationResult = { - // assert(superClassMutability.isMutable.isNoOrUnknown) - - //---generic classes handling - //printf(cf.asVirtualClass.asClassFile.classSignature.get.toString()) - //cf.asClassFile.classSignature.get.formalTypeParameters. - //--------------------------------------------- - - val t = cf.thisType - - var dependees = Map.empty[Entity, EOptionP[Entity, Property]] - - if (!superClassMutabilityIsFinal) { - dependees += (SuperClassKey -> superClassInformation) - } + var minLocalImmutability: ClassImmutability_new = + if (!superClassMutabilityIsFinal) { + println("OnE!") + MutableClass //MutableObjectByAnalysis + + } else { + println("Two!!") + ShallowImmutableClass //ImmutableContainer + } + + // NOTE: maxLocalImmutability does not take the super classes' mutability into account! + var maxLocalImmutability: ClassImmutability_new = superClassInformation match { + case UBP(ShallowImmutableClass) => ShallowImmutableClass //ImmutableContainer + case _ => DeepImmutableClass // ImmutableObject + } - // Collect all fields for which we need to determine the effective mutability! - var hasFieldsWithUnknownMutability = false + if (hasDependentImmutableFields) { + maxLocalImmutability = DependentImmutableClass //TODO possibly here + } else if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + } - val instanceFields = cf.fields.filter { f ⇒ - !f.isStatic - } - var hasShallowImmutableFields = false - var hasDependentImmutableFields = false - val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) - fieldsPropertyStoreInformation.foreach( - f ⇒ - f match { - 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 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); - } - ) + 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 //ImmutableContainer + } - /** - * dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { - * case FinalP(MutableField) => //NonFinalField) ⇒ - * // <=> The class is definitively mutable and therefore also all subclasses. - * if (lazyComputation) - * return Result(t, MutableClass); //MutableObjectByAnalysis); // - * else - * return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); - * case ep @ InterimE(e) => - * hasFieldsWithUnknownMutability = true - * (e, ep) - * case epk @ EPK(e: Entity, _) => - * // <=> The mutability information is not yet available. - * hasFieldsWithUnknownMutability = true - * (e, epk) - * - * // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields - * }).toMap * - */ - var minLocalImmutability: ClassImmutability_new = - if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) - MutableClass //MutableObjectByAnalysis - else - ShallowImmutableClass //ImmutableContainer + //--var fieldTypes: Set[ObjectType] = Set.empty + //--if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { + //-- fieldTypes = // IMPROVE Use the precise type of the field (if available)! + // cf.fields.collect { + // case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType + // }.toSet + //} + + // For each dependent class file we have to determine the mutability + // of instances of the respective type to determine the immutability + // of instances of this class. + // Basically, we have to distinguish the following cases: + // - A field's type is mutable or conditionally immutable=> + // This class is conditionally immutable. + // - A field's type is immutable => + // The field is ignored. + // (This class is as immutable as its superclass.) + // - A field's type is at least conditionally mutable or + // The immutability of the field's type is not yet known => + // This type is AtLeastConditionallyImmutable + // We have to declare a dependency on the respective type's immutability. + // + // Additional handling is required w.r.t. the supertype: + // If the supertype is Immutable => + // Nothing special to do. + // If the supertype is AtLeastConditionallyImmutable => + // We have to declare a dependency on the supertype. + // If the supertype is ConditionallyImmutable => + // This type is also at most conditionally immutable. + // + // We furthermore have to take the mutability of the fields into consideration: + // If a field is not effectively final => + // This type is mutable. + // If a field is effectively final => + // Nothing special to do. + + //val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) + //val hasMutableOrConditionallyImmutableField = + // IMPROVE Use the precise type of the field (if available)! + //-- fieldTypesImmutability.exists { eOptP ⇒ + //-- eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) + //-- } + + if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + println("shallow immutable fields.......!!!!!!!!!!!!!!!!!!") + } //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 + * } + * fieldTypesWithUndecidedMutability.foreach { eOptP => + * dependees += (eOptP.e -> eOptP) + * }* + */ + //} + + 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) + ); + } - // NOTE: maxLocalImmutability does not take the super classes' mutability into account! - var maxLocalImmutability: ClassImmutability_new = superClassInformation match { - case UBP(ShallowImmutableClass) ⇒ ShallowImmutableClass //ImmutableContainer - case _ ⇒ DeepImmutableClass // ImmutableObject - } + def c(someEPS: SomeEPS): ProperPropertyComputationResult = { + //[DEBUG] val oldDependees = dependees + dependees = dependees.filter(_._1 ne someEPS.e) + println("someEPS: " + someEPS) + someEPS match { + // Superclass related dependencies: + // + case UBP(MutableClass) => // MutableObject) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); - if (hasDependentImmutableFields) { - maxLocalImmutability = DependentImmutableClass - } else if (hasShallowImmutableFields) { - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - } + case LBP(DeepImmutableClass) => //_:ImmutableObject) ⇒ // the super class + dependees -= SuperClassKey - 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 //ImmutableContainer - } + case UBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container + if (someEPS.isFinal) dependees -= SuperClassKey + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) //TypeImmutability.key) - //--var fieldTypes: Set[ObjectType] = Set.empty - //--if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { - //-- fieldTypes = // IMPROVE Use the precise type of the field (if available)! - // cf.fields.collect { - // case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType - // }.toSet - //} - - // For each dependent class file we have to determine the mutability - // of instances of the respective type to determine the immutability - // of instances of this class. - // Basically, we have to distinguish the following cases: - // - A field's type is mutable or conditionally immutable=> - // This class is conditionally immutable. - // - A field's type is immutable => - // The field is ignored. - // (This class is as immutable as its superclass.) - // - A field's type is at least conditionally mutable or - // The immutability of the field's type is not yet known => - // This type is AtLeastConditionallyImmutable - // We have to declare a dependency on the respective type's immutability. - // - // Additional handling is required w.r.t. the supertype: - // If the supertype is Immutable => - // Nothing special to do. - // If the supertype is AtLeastConditionallyImmutable => - // We have to declare a dependency on the supertype. - // If the supertype is ConditionallyImmutable => - // This type is also at most conditionally immutable. + case LBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container + if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && + !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + + case LUBP(MutableClass, DeepImmutableClass) => //_: MutableObject, ImmutableObject) ⇒ // No information about superclass + + // Properties related to the type of the class' fields. // - // We furthermore have to take the mutability of the fields into consideration: - // If a field is not effectively final => - // This type is mutable. - // If a field is effectively final => - // Nothing special to do. - - //val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) - //val hasMutableOrConditionallyImmutableField = - // IMPROVE Use the precise type of the field (if available)! - //-- fieldTypesImmutability.exists { eOptP ⇒ - //-- eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) - //-- } - - if (hasShallowImmutableFields) { - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - } //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 - * } - * fieldTypesWithUndecidedMutability.foreach { eOptP => - * dependees += (eOptP.e -> eOptP) - * }* + * case UBP(ShallowImmutableType | MutableType_new) ⇒ //ImmutableContainerType | MutableType) ⇒ + * maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + * dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) // TypeImmutability.key) + * + * case ELBP(e, DeepImmutableType) ⇒ // ImmutableType) ⇒ // Immutable field type, no influence on mutability + * dependees -= e + * + * case UBP(DeepImmutableType) ⇒ //ImmutableType) ⇒ // No information about field type + * + * case FinalP(MutableType_new) ⇒ //MutableType) + * Result(t, MutableClass) //TODO check */ - //} - - 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) - ); + case FinalP(DependentImmutableField) => { + println( + ".........................................kkk......................................" + ) + maxLocalImmutability = ShallowImmutableClass // DependentImmutableClass //TODO possibly here? } - def c(someEPS: SomeEPS): ProperPropertyComputationResult = { - //[DEBUG] val oldDependees = dependees - dependees = dependees.filter(_._1 ne someEPS.e) - println("someEPS: "+someEPS) - someEPS match { - // Superclass related dependencies: - // - case UBP(MutableClass) ⇒ // MutableObject) ⇒ - return Result(t, MutableClass); //MutableObjectByAnalysis); - - case LBP(DeepImmutableClass) ⇒ //_:ImmutableObject) ⇒ // the super class - dependees -= SuperClassKey - - case UBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is at most immutable container - if (someEPS.isFinal) dependees -= SuperClassKey - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - - case LBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is a least immutable container - if (minLocalImmutability != DeepImmutableClass && //ImmutableContainer && - !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - - case LUBP(MutableClass, DeepImmutableClass) ⇒ //_: MutableObject, ImmutableObject) ⇒ // No information about superclass - - // Properties related to the type of the class' fields. - // - case UBP(ShallowImmutableClass | MutableClass) ⇒ //ImmutableContainerType | MutableType) ⇒ - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - - case ELBP(e, ImmutableType) ⇒ // Immutable field type, no influence on mutability - dependees -= e - - case UBP(ImmutableType) ⇒ // No information about field type - - case FinalP(MutableType) ⇒ Result(t, MutableClass) //TODO check - - case FinalP(DependentImmutableField) ⇒ maxLocalImmutability = DependentImmutableClass - // Field Mutability related dependencies: - // + // Field Mutability related dependencies: + // - case FinalP(ShallowImmutableField) ⇒ { - maxLocalImmutability = ShallowImmutableClass - } - case FinalP(MutableField) ⇒ return Result(t, MutableClass); + case FinalP(ShallowImmutableField) => { + maxLocalImmutability = ShallowImmutableClass + } + case FinalP(MutableField) => return Result(t, MutableClass); - case UBP(MutableField) ⇒ //_: NonFinalField) ⇒ - return Result(t, MutableClass); //MutableObjectByAnalysis); + case UBP(MutableField) => //_: NonFinalField) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); - case ELBP(e, ShallowImmutableField | DeepImmutableField) ⇒ //FinalField) => - dependees -= e - if (minLocalImmutability != ShallowImmutableClass && // ImmutableContainer && - !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + case ELBP(e, ShallowImmutableField | DeepImmutableField) => //FinalField) => + dependees -= e + if (minLocalImmutability != ShallowImmutableClass && // ImmutableContainer && + !dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - case UBP(ShallowImmutableField | DeepImmutableField) ⇒ //_: FinalField) ⇒ // no information about field mutability + case UBP(ShallowImmutableField | DeepImmutableField) => //_: FinalField) ⇒ // no information about field mutability - case _ ⇒ Result(t, MutableClass) //TODO check + case _ => Result(t, MutableClass) //TODO check - } + } - if (someEPS.isRefinable) { - val entity = if (someEPS.pk == ClassImmutability_new.key) SuperClassKey else someEPS.e - dependees += (entity -> someEPS) - } + if (someEPS.isRefinable) { + val entity = if (someEPS.pk == ClassImmutability_new.key) SuperClassKey else someEPS.e + dependees += (entity -> someEPS) + } - /*[DEBUG] + /*[DEBUG] assert( oldDependees != dependees, s"dependees are not correctly updated $e($p)\n:old=$oldDependees\nnew=$dependees" ) */ - // Lift lower bound once no dependencies other than field type mutabilities are left - if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && - dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer - - if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { - /*[DEBUG] + // Lift lower bound once no dependencies other than field type mutabilities are left + if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && + dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer + println("minLocalImmutability: " + minLocalImmutability) + println("maxLocalImmutability: " + maxLocalImmutability) + if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { + /*[DEBUG] assert( maxLocalImmutability == ConditionallyImmutableObject || maxLocalImmutability == ImmutableObject @@ -451,115 +470,114 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal ) */ - Result(t, maxLocalImmutability) + Result(t, maxLocalImmutability) - } else { - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - } - } + } else { + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + } + } - //[DEBUG] assert(initialImmutability.isRefinable) - println("minLocalImmutability: "+minLocalImmutability) - println("maxLocalImmutability: "+maxLocalImmutability) - val result = - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - if (lazyComputation) - result - else { - val isFinal = dependees.isEmpty - createIncrementalResult( - t, - EPS(t, minLocalImmutability, maxLocalImmutability), - isFinal, - result - ) - } + //[DEBUG] assert(initialImmutability.isRefinable) + + val result = + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + if (lazyComputation) + result + else { + val isFinal = dependees.isEmpty + createIncrementalResult( + t, + EPS(t, minLocalImmutability, maxLocalImmutability), + isFinal, + result + ) } + } } trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) - - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, TypeImmutability, FieldImmutability) - - 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 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.map(cf ⇒ set(cf.thisType, DeepImmutableClass)) //ImmutableObject)) - - // 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) - } - } + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new, FieldImmutability) //TypeImmutability, FieldImmutability) + + 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 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.map(cf => set(cf.thisType, DeepImmutableClass)) //ImmutableObject)) + + // 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 = {} + // 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 = {} } /** @@ -571,22 +589,22 @@ object EagerLxClassImmutabilityAnalysis_new extends ClassImmutabilityAnalysisScheduler_new 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 LxClassImmutabilityAnalysis_new(p) - ps.scheduleEagerComputationsForEntities(cfs)( - analysis.determineClassImmutability_new( - superClassType = null, - FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), - superClassMutabilityIsFinal = true, - lazyComputation = false - ) - ) - analysis - } + 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 LxClassImmutabilityAnalysis_new(p) + ps.scheduleEagerComputationsForEntities(cfs)( + analysis.determineClassImmutability_new( + superClassType = null, + FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), + superClassMutabilityIsFinal = true, + lazyComputation = false + ) + ) + analysis + } } /** @@ -598,18 +616,18 @@ object LazyLxClassImmutabilityAnalysis_new extends ClassImmutabilityAnalysisScheduler_new with FPCFLazyAnalysisScheduler { - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - - override def register( - p: SomeProject, - ps: PropertyStore, - unused: InitializationData - ): FPCFAnalysis = { - val analysis = new LxClassImmutabilityAnalysis_new(p) - ps.registerLazyPropertyComputation( - ClassImmutability_new.key, - analysis.doDetermineClassImmutability_new - ) - analysis - } + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + override def register( + p: SomeProject, + ps: PropertyStore, + unused: InitializationData + ): FPCFAnalysis = { + val analysis = new LxClassImmutabilityAnalysis_new(p) + ps.registerLazyPropertyComputation( + ClassImmutability_new.key, + analysis.doDetermineClassImmutability_new + ) + analysis + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index 12d5ce8372..1143ec8aec 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -33,6 +33,7 @@ import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.ClassImmutability_new 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.MutableClass import org.opalj.br.fpcf.properties.MutableType_new import org.opalj.br.fpcf.properties.ShallowImmutableClass @@ -44,198 +45,209 @@ import org.opalj.br.fpcf.properties.TypeImmutability_new * type are immutable and checking that the set of types is closed. * * @author Michael Eichberg + * @author Tobias Peter Roth */ -class TypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPCFAnalysis { - - def doDetermineTypeMutability_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 = { - typeExtensibility(t) match { - case Yes | Unknown => Result(t, MutableType_new) // MutableType) - case No => step2(t) +class LxTypeImmutabilityAnalysis_new( 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") } - } - - 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_new, ub: ClassImmutability_new) => - val thisLB = lb.correspondingTypeImmutability - val thisUB = ub.correspondingTypeImmutability - if (eps.isFinal) - Result(t, thisUB) - else - InterimResult(t, thisLB, thisUB, Seq(eps), c) - } - } - } - - ps(t, ClassImmutability_new.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, Seq(eps), c) - case epk => InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) - //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) - } - } else { - var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] - var joinedImmutability - : TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." - var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType - - ps(t, ClassImmutability_new.key) match { - case FinalP(DeepImmutableClass) => //ImmutableObject) => - case FinalP(MutableClass) => //(_: MutableObject) => - return Result(t, MutableType_new); //MutableType); - - case FinalP(ShallowImmutableClass) => //ImmutableContainer) => - joinedImmutability = ShallowImmutableType // ImmutableContainerType - maxImmutability = ShallowImmutableType //ImmutableContainerType - - case eps @ InterimLUBP(lb, ub) => - joinedImmutability = lb.correspondingTypeImmutability - maxImmutability = ub.correspondingTypeImmutability - dependencies += (t -> eps) - - case eOptP => - joinedImmutability = MutableType_new //MutableType - dependencies += (t -> eOptP) - } - - directSubtypes foreach { subtype => - ps(subtype, TypeImmutability_new.key) match { - case FinalP(DeepImmutableType) => //ImmutableType) => - case UBP(MutableType_new) => //MutableType) => - return Result(t, MutableType_new); //MutableType); - - case FinalP(ShallowImmutableType) => //ImmutableContainerType) => - joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) - maxImmutability = ShallowImmutableType //ImmutableContainerType - - case eps @ InterimLUBP(subtypeLB, subtypeUB) => - joinedImmutability = joinedImmutability.meet(subtypeLB) - maxImmutability = maxImmutability.meet(subtypeUB) - dependencies += ((subtype, eps)) - - case epk => - joinedImmutability = MutableType_new //MutableType - dependencies += ((subtype, epk)) + /** + * @param t An object type which is not `java.lang.Object`. + */ + def step1( + typeExtensibility: ObjectType ⇒ Answer + )( + t: ObjectType + ): ProperPropertyComputationResult = { + typeExtensibility(t) match { + case Yes | Unknown ⇒ + println("Yes Unknown"); Result(t, MutableType_new) // MutableType) + case No ⇒ step2(t) } - } - - if (dependencies.isEmpty) { - Result(t, maxImmutability) - } else if (joinedImmutability == maxImmutability) { - // E.g., as soon as one subtype is an ImmutableContainer, we are at most - // ImmutableContainer, even if all other subtype may even be 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 = { + } + + 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 ⇒ + println("c function called") + def apply(eps: SomeEPS): ProperPropertyComputationResult = { + println("EPS: "+eps) + eps match { + case ELUBP(_, lb: ClassImmutability_new, ub: ClassImmutability_new) ⇒ + val thisLB = lb.correspondingTypeImmutability + val thisUB = ub.correspondingTypeImmutability + if (eps.isFinal) + Result(t, thisUB) + else + InterimResult(t, thisLB, thisUB, Seq(eps), c) + } + } + } + val resultToMatch = ps(t, ClassImmutability_new.key) + println("Result1: "+resultToMatch) + resultToMatch match { + case x @ FinalP(p) ⇒ { + println("x::: "+x.p.correspondingTypeImmutability) + Result(t, p.correspondingTypeImmutability); + } + + case eps @ InterimLUBP(lb, ub) ⇒ + val thisUB = ub.correspondingTypeImmutability + val thisLB = lb.correspondingTypeImmutability + InterimResult(t, thisLB, thisUB, Seq(eps), c) + case epk ⇒ { + println("here ") + InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) + } + //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) + } + } else { + var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] + var joinedImmutability: TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." + var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType + + val resultToMatch2 = ps(t, ClassImmutability_new.key) + println("Result2: "+resultToMatch2) + resultToMatch2 match { + case FinalP(DeepImmutableClass) ⇒ //ImmutableObject) => + case FinalP(MutableClass) ⇒ //(_: MutableObject) => + return Result(t, MutableType_new); //MutableType); + case FinalP(ShallowImmutableClass) ⇒ //ImmutableContainer) => + joinedImmutability = ShallowImmutableType // ImmutableContainerType + maxImmutability = ShallowImmutableType //ImmutableContainerType + case FinalP(DependentImmutableClass) ⇒ + joinedImmutability = ShallowImmutableType + maxImmutability = ShallowImmutableType + + case eps @ InterimLUBP(lb, ub) ⇒ + joinedImmutability = lb.correspondingTypeImmutability + maxImmutability = ub.correspondingTypeImmutability + dependencies += (t -> eps) + + case eOptP ⇒ + joinedImmutability = MutableType_new //MutableType + dependencies += (t -> eOptP) + } + + directSubtypes foreach { subtype ⇒ + ps(subtype, TypeImmutability_new.key) match { + case FinalP(DeepImmutableType) ⇒ //ImmutableType) => + case UBP(MutableType_new) ⇒ //MutableType) => + return Result(t, MutableType_new); //MutableType); + + case FinalP(ShallowImmutableType) ⇒ //ImmutableContainerType) => + joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) + maxImmutability = ShallowImmutableType //ImmutableContainerType + + case eps @ InterimLUBP(subtypeLB, subtypeUB) ⇒ + joinedImmutability = joinedImmutability.meet(subtypeLB) + maxImmutability = maxImmutability.meet(subtypeUB) + dependencies += ((subtype, eps)) + + case epk ⇒ + joinedImmutability = MutableType_new //MutableType + dependencies += ((subtype, epk)) + } + } + if (dependencies.isEmpty) { - Result(t, maxImmutability) + Result(t, maxImmutability) + } else if (joinedImmutability == maxImmutability) { + // E.g., as soon as one subtype is an ImmutableContainer, we are at most + // ImmutableContainer, even if all other subtype may even be immutable! + Result(t, joinedImmutability) } 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_new => - joinedImmutability = joinedImmutability.meet(lb) - case lb: ClassImmutability_new => - joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) - } else { - joinedImmutability = MutableType_new //MutableType - continue = false + // 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_new ⇒ + joinedImmutability = joinedImmutability.meet(lb) + case lb: ClassImmutability_new ⇒ + joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) + } + else { + joinedImmutability = MutableType_new //MutableType + continue = false + } + } + if (joinedImmutability == maxImmutability) { + assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) + Result(t, maxImmutability) + } else { + InterimResult( + t, + joinedImmutability, + maxImmutability, + dependencies.values, + c + ) + } + } + } + + (eps: @unchecked) match { + case FinalEP(e, DeepImmutableType | DeepImmutableClass) ⇒ //ImmutableType | ImmutableObject) => + dependencies = dependencies - e + nextResult() + + case UBP(x) if (x == MutableType_new || x == MutableClass) ⇒ //MutableType | _: MutableObject) => + Result(t, MutableType_new) //MutableType) + + case FinalEP(e, x) if (x == ShallowImmutableType || x == DependentImmutableClass || x == ShallowImmutableClass) ⇒ //ImmutableContainerType | ImmutableContainer) => + maxImmutability = ShallowImmutableType //ImmutableContainerType + dependencies = dependencies - e + nextResult() + + case eps @ InterimEUBP(e, subtypeP) ⇒ + dependencies = dependencies.updated(e, eps) + subtypeP match { + case subtypeP: TypeImmutability_new ⇒ + maxImmutability = maxImmutability.meet(subtypeP) + case subtypeP: ClassImmutability_new ⇒ + maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) + } + nextResult() + } } - } - if (joinedImmutability == maxImmutability) { - assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) - Result(t, maxImmutability) - } else { - InterimResult( - t, - joinedImmutability, - maxImmutability, - dependencies.values, - c - ) - } + InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) } - } - - (eps: @unchecked) match { - case FinalEP(e, DeepImmutableType | DeepImmutableClass) => //ImmutableType | ImmutableObject) => - dependencies = dependencies - e - nextResult() - - case UBP(x) - if (x == MutableType_new || x == MutableClass) => //MutableType | _: MutableObject) => - Result(t, MutableType_new) //MutableType) - - case FinalEP(e, x) - if (x == ShallowImmutableType || x == ShallowImmutableClass) => //ImmutableContainerType | ImmutableContainer) => - maxImmutability = ShallowImmutableType //ImmutableContainerType - dependencies = dependencies - e - nextResult() - - case eps @ InterimEUBP(e, subtypeP) => - dependencies = dependencies.updated(e, eps) - subtypeP match { - case subtypeP: TypeImmutability_new => - maxImmutability = maxImmutability.meet(subtypeP) - case subtypeP: ClassImmutability_new => - maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) - } - nextResult() - } } - - InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) - } } - } } trait TypeImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) } @@ -248,41 +260,40 @@ object EagerLxTypeImmutabilityAnalysis_new extends TypeImmutabilityAnalysisScheduler_new with BasicFPCFEagerAnalysisScheduler { - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val typeExtensibility = project.get(TypeExtensibilityKey) - val analysis = new TypeImmutabilityAnalysis_new(project) - val allClassFilesIterator = project.allClassFiles.iterator - val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty - ps.scheduleEagerComputationsForEntities(types) { - analysis.step1(typeExtensibility) - } + override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val typeExtensibility = project.get(TypeExtensibilityKey) + val analysis = new LxTypeImmutabilityAnalysis_new(project) + val allClassFilesIterator = project.allClassFiles.iterator + val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) - analysis - } + ps.scheduleEagerComputationsForEntities(types) { + analysis.step1(typeExtensibility) + } + analysis + } } object LazyLxTypeImmutabilityAnalysis_new extends TypeImmutabilityAnalysisScheduler_new 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 TypeImmutabilityAnalysis_new(p) - val analysisRunner: ProperPropertyComputation[Entity] = - analysis.doDetermineTypeMutability_new(typeExtensibility) - ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) - analysis - } + 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 LxTypeImmutabilityAnalysis_new(p) + val analysisRunner: ProperPropertyComputation[Entity] = + analysis.doDetermineTypeImmutability_new(typeExtensibility) + ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) + analysis + } } From 9e21bb35ac9a32b4e86cb363a8970d6394af0292 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Mon, 25 Nov 2019 14:57:39 +0100 Subject: [PATCH 059/327] Introducing dependent immutable type including tests --- .../ClassImmutabilityAnalysisDemo.scala | 3 + .../ReferenceImmutabilityAnalysisDemo.scala | 18 ++- .../TypeImmutabilityAnalysisDemo.scala | 5 + .../GenericAndDeepImmutableFields.java | 31 ++++++ .../GenericAndMutableFields.java | 24 ++++ .../GenericAndShallowImmutableFields.java | 27 +++++ .../class_immutability/Generic_class1.java | 4 +- .../WithMutableAndImmutableFieldType.java | 15 ++- .../DependentImmutableTypeAnnotation.java | 28 +++++ .../opalj/fpcf/TypeImmutabilityTests.scala | 70 ++++++------ .../NewTypeImmutabilityMatcher.scala | 3 + .../properties/ClassImmutability_new.scala | 2 +- .../properties/TypeImmutability_new.scala | 50 +++++++-- .../L0FieldImmutabilityAnalysis.scala | 104 +++++++++++------- .../LxClassImmutabilityAnalysis_new.scala | 4 +- .../LxTypeImmutabilityAnalysis_new.scala | 17 ++- 16 files changed, 306 insertions(+), 99 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java 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 index 04946824b9..d7ba9e7954 100644 --- 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 @@ -16,6 +16,7 @@ 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.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new @@ -45,6 +46,8 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + val (propertyStore, _) = analysesManager.runAll( LazyTypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index ba34ef0ad8..25dcf825c8 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -7,13 +7,15 @@ 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.LazyL0PurityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.TACAITransformer +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. @@ -38,12 +40,16 @@ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { def analyze(project: Project[URL]): String = { val analysesManager = project.get(FPCFAnalysesManagerKey) - + analysesManager.project.get(RTACallGraphKey) val (propertyStore, _) = analysesManager.runAll( + //EagerUnsoundPrematurelyReadFieldsAnalysis, + //EagerL2PurityAnalysis, + //EagerL2FieldMutabilityAnalysis, + EagerL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL0PurityAnalysis, - TACAITransformer, - EagerL0ReferenceImmutabilityAnalysis + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis ); "Mutable References: "+propertyStore 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 index bffe4c39e1..5b54015926 100644 --- 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 @@ -12,6 +12,7 @@ import org.opalj.br.fpcf.analyses.LazyL0PurityAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis 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_new import org.opalj.br.fpcf.properties.ShallowImmutableType //import org.opalj.br.fpcf.properties.ShallowImmutableType @@ -62,6 +63,10 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .finalEntities(ShallowImmutableType) .toList .toString()+"\n"+ + "Dependent Immutable Type: "+propertyStore + .finalEntities(DependentImmutableType) + .toList + .toString()+"\n"+ "Deep Immutable Type: "+propertyStore .finalEntities(DeepImmutableType) .toList diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java new file mode 100644 index 0000000000..4510b6b51c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java @@ -0,0 +1,31 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_mutability.MutableType; + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class GenericAndDeepImmutableFields { + + @DependentImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private T1 t1; + + @DependentImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private T2 t2; + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private FinalEmptyClass fec; + + GenericAndDeepImmutableFields(T1 t1, T2 t2, FinalEmptyClass fec){ + this.t1 = t1; + this.t2 = t2; + this.fec = fec; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java new file mode 100644 index 0000000000..05fcf78ccb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java @@ -0,0 +1,24 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@MutableClassAnnotation("Because of mutable field") +public class GenericAndMutableFields { + @MutableFieldAnnotation("Because of mutable reference") + @MutableReferenceAnnotation("Because of public field") + public T1 t1; + @DependentImmutableFieldAnnotation("Because of generic type") + @ImmutableReferenceAnnotation("Because of effectively immutable final") + private T2 t2; + GenericAndMutableFields(T1 t1, T2 t2){ + this.t1 = t1; + this.t2 = t2; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java new file mode 100644 index 0000000000..1905bbc540 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java @@ -0,0 +1,27 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +import org.opalj.fpcf.properties.type_mutability.MutableType; + +@MutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +public class GenericAndShallowImmutableFields { + + @DependentImmutableFieldAnnotation("") + private T1 t1; + @DependentImmutableFieldAnnotation("") + private T2 t2; + @ShallowImmutableFieldAnnotation("") + private TrivialMutableClass tmc; + GenericAndShallowImmutableFields(T1 t1, T2 t2, TrivialMutableClass tmc){ + this.t1 = t1; + this.t2 = t2; + this.tmc = tmc; + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java index 5ec7f3f1c8..0e7cfcf3d3 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java @@ -3,9 +3,11 @@ import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; +@DependentImmutableTypeAnnotation("Dependent Immutability of T1,...,T5") @DependentImmutableClassAnnotation("Dependent Immutability of T1,...,T5") -public class Generic_class1 { +public final class Generic_class1 { @DependentImmutableFieldAnnotation("T1") @ImmutableReferenceAnnotation("effectively") private T1 t1; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java index d9ddb71e0f..658f54d05d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java @@ -1,11 +1,22 @@ package org.opalj.fpcf.fixtures.type_immutability; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; -@MutableTypeAnnotation("has shallow mutable fields") -public class WithMutableAndImmutableFieldType { +@ShallowImmutableTypeAnnotation("has shallow mutable fields") +@ShallowImmutableClassAnnotation("has shallow imm fields") +public final class WithMutableAndImmutableFieldType { + @DeepImmutableFieldAnnotation("imm reference and deep immutable type") + @ImmutableReferenceAnnotation("private") private FinalEmptyClass fec = new FinalEmptyClass(); + + @ShallowImmutableFieldAnnotation("imm reference and mutable type") + @ImmutableReferenceAnnotation("private") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java new file mode 100644 index 0000000000..2781c70abb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.type_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.LxTypeImmutabilityAnalysis_new; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * 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 DependentImmutableTypeAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {LxTypeImmutabilityAnalysis_new.class}; +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala index 85c3fd18b2..f6531f392a 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala @@ -7,6 +7,7 @@ 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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey @@ -23,41 +24,42 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class TypeImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) - } - p.get(RTACallGraphKey) + 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("TypeImmutability_new")) - } + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) + } - describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { - println(1) - val as = executeAnalyses( - Set( - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new - ) - ) - as.propertyStore.shutdown() - val tmp = classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)) - print("===========================>>>>>>>>>>>>>>>>>>>>>>"+tmp) - validateProperties( - as, - tmp, - // fieldsWithAnnotations(as.project), - Set("TypeImmutability_new") - ) //TODO class files ... with annotation - } + describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { + println(1) + val as = executeAnalyses( + Set( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new + ) + ) + as.propertyStore.shutdown() + val tmp = classFilesWithAnnotations(as.project).map(tp => (tp._1.thisType, tp._2, tp._3)) + print("===========================>>>>>>>>>>>>>>>>>>>>>>" + tmp) + validateProperties( + as, + tmp, + // fieldsWithAnnotations(as.project), + Set("TypeImmutability_new") + ) //TODO class files ... with annotation + } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala index 0492c08aa2..2611624620 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala @@ -5,6 +5,7 @@ import org.opalj.br.AnnotationLike import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.DependentImmutableType import org.opalj.br.fpcf.properties.MutableType_new import org.opalj.br.fpcf.properties.ShallowImmutableType import org.opalj.br.fpcf.properties.TypeImmutability_new @@ -54,4 +55,6 @@ class NewMutableTypeMatcher extends NewTypeImmutabilityMatcher(MutableType_new) class ShallowImmutableTypeMatcher extends NewTypeImmutabilityMatcher(ShallowImmutableType) +class DependentImmutableTypeMatcher extends NewTypeImmutabilityMatcher(DependentImmutableType) + class DeepImmutableTypeMatcher extends NewTypeImmutabilityMatcher(DeepImmutableType) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index 7605ac7268..50239f389b 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -48,7 +48,7 @@ case object MutableClass extends ClassImmutability_new { case object DependentImmutableClass extends ClassImmutability_new { override def correspondingTypeImmutability: TypeImmutability_new = - ShallowImmutableType //TODO check + DependentImmutableType //TODO check } case object ShallowImmutableClass extends ClassImmutability_new { diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala index 3b4936ca3b..6b43e42c51 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -36,6 +36,7 @@ sealed trait TypeImmutability_new def isDeepImmutable: Boolean def isShallowImmutable: Boolean + def isDependentImmutable: Boolean /** `true` if the immutability is unknown or if the type is mutable.*/ def isMutable: Boolean @@ -66,6 +67,7 @@ case object DeepImmutableType extends TypeImmutability_new { 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 = {} @@ -82,6 +84,7 @@ case object ShallowImmutableType extends TypeImmutability_new { override def isDeepImmutable: Boolean = false override def isShallowImmutable: Boolean = true override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = false def meet(that: TypeImmutability_new): TypeImmutability_new = if (that == MutableType_new) @@ -89,11 +92,35 @@ case object ShallowImmutableType extends TypeImmutability_new { else this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == DeepImmutableType) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - } - } + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + /** + * if (other == DeepImmutableType) { + * throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + * } + * }* + */ +} + +case object DependentImmutableType extends TypeImmutability_new { + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = true + + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (that == MutableType_new) + that + else + this + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + /** + * if (other == DependentImmutableType || other == ShallowImmutableType) { + * throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + * } + * }* + */ + } case object MutableType_new extends TypeImmutability_new { @@ -101,12 +128,15 @@ case object MutableType_new extends TypeImmutability_new { override def isDeepImmutable: Boolean = false override def isShallowImmutable: Boolean = false override def isMutable: Boolean = true + override def isDependentImmutable: Boolean = false def meet(other: TypeImmutability_new): this.type = this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other != MutableType_new) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - } - } + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + /** + * if (other != MutableType_new) { + * throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + * } + * }* + */ } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 7ae9399292..994809a9cb 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -2,6 +2,7 @@ package org.opalj.br.fpcf.analyses import org.opalj.br.Field +import org.opalj.br.FieldType import org.opalj.br.ObjectType import org.opalj.br.analyses.FieldAccessInformationKey import org.opalj.br.analyses.SomeProject @@ -13,6 +14,8 @@ import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.FPCFAnalysisScheduler 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.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference @@ -42,6 +45,7 @@ case class State() { var referenceImmutability: Option[Boolean] = None var depencenciesTypes: scala.collection.mutable.Set[ObjectType] = scala.collection.mutable.Set[ObjectType]() + var dependentTypeImmutability: Option[Boolean] = None } /** @@ -75,7 +79,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - def handleTypeImmutability(objectType: ObjectType): Option[Boolean] = { + def handleTypeImmutability(objectType: FieldType)(state: State): Option[Boolean] = { if (objectType.isArrayType) return Some(true); //TODO if (objectType.isBaseType) return Some(true); val result = propertyStore(objectType, TypeImmutability_new.key) @@ -83,42 +87,52 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) result match { case FinalEP(e, DeepImmutableType) => { //ImmutableType || t == ImmutableContainerType) ⇒ { println("has deep immutable type") - Some(true) + return Some(true); + } + case FinalEP(e, DependentImmutableType) => { + println("has dependent immutable type") + state.dependentTypeImmutability = Some(true) + return Some(false); } + case FinalEP(e, ShallowImmutableType) => { println("has shallow immutable type") - Some(false) //TODO mindstorm if this approch is appropriate + return Some(false); //TODO mindstorm if this approch is appropriate } case FinalEP(e, t) if (t == MutableType_new) => { //MutableType) ⇒ { println("has mutable type") - Some(false) + return Some(false); } case x @ _ => { dependencies += x println(x) println("immutability of type couldn't be determined") - None + return None; } } } def hasImmutableType(field: Field)(state: State): Option[Boolean] = { - val hasFieldImmutableType = handleTypeImmutability(field.fieldType.asObjectType) - hasFieldImmutableType match { - case Some(false) => return Some(false); - case _ => { - state.depencenciesTypes.foreach( - t => { - val isImmutable = handleTypeImmutability(t) - isImmutable match { - case Some(false) => return Some(false); - case _ => - } - } - ) - Some(true) - } - } + //val hasFieldImmutableType = + handleTypeImmutability(field.fieldType.asFieldType)(state) + + /** + * hasFieldImmutableType match { + * case Some(false) => return Some(false); + * case _ => { + * state.depencenciesTypes.foreach( + * t => { + * val isImmutable = handleTypeImmutability(t)(state) + * isImmutable match { + * case Some(false) => return Some(false); + * case _ => + * } + * } + * ) + * Some(true) + * } + * } * + */ } def hasImmutableReference(field: Field): Option[Boolean] = { @@ -146,28 +160,34 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def createResult(state: State): ProperPropertyComputationResult = { println("reference Immutability: " + state.referenceImmutability) println("type immutabiliy: " + state.typeImmutability) + println("dependent immutability: " + state.dependentTypeImmutability) state.referenceImmutability match { case Some(false) => Result(field, MutableField) case Some(true) => { //If the field type is object. It is a generic field - //if (field.fieldType == ObjectType("java/lang/Object")) - // Result(field, DependentImmutableField) - //else - state.typeImmutability match { - case Some(true) => Result(field, DeepImmutableField) - case Some(false) => Result(field, ShallowImmutableField) - case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) - case None => { - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) + if (field.fieldType == ObjectType("java/lang/Object")) + Result(field, DependentImmutableField) + else + state.typeImmutability match { + case Some(true) => Result(field, DeepImmutableField) + case Some(false) => { + state.dependentTypeImmutability match { + case Some(true) => Result(field, DependentImmutableField) + case _ => Result(field, ShallowImmutableField) + } + } + case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) + case None => { + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } } - } } case None if (dependencies.isEmpty) => Result(field, MutableField) case None => { @@ -195,17 +215,23 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case x @ FinalEP(_, t) - if (t == DeepImmutableType || t == ShallowImmutableType) => { // ImmutableContainerType || t == ImmutableType) ⇒ { + if (t == DeepImmutableType) => { // || t == ShallowImmutableType) ⇒ { // ImmutableContainerType || t == ImmutableType) ⇒ { println(x) println("has immutable type. Determined by continuation function.") state.typeImmutability = Some(true) } - case x @ FinalEP(_, MutableType_new) => { //MutableType) ⇒ { + case x @ FinalEP(_, MutableType_new | ShallowImmutableType) => { //MutableType) ⇒ { println(x) println("has mutable type. Determined by continuation function.") state.typeImmutability = Some(false) } + case x @ FinalEP(_, DependentImmutableType) => { + println(x) + println("has dependent immutable type. Determined by continuation function.") + state.typeImmutability = Some(false) + state.dependentTypeImmutability = Some(true) + } case x @ FinalEP(_, MutableReference) => { println(x) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 4791874a69..13cf4ebc3f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -217,8 +217,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } case FinalP(ShallowImmutableField) => hasShallowImmutableFields = true case FinalEP(f, DependentImmutableField) => { - println("FieldType: " + f.asField.fieldType) - println("FieldTypeSignature: " + f.asField.fieldTypeSignature.get.toJVMSignature) + //println("FieldType: "+f.asField.fieldType) + //println("FieldTypeSignature: "+f.asField.fieldTypeSignature.get.toJVMSignature) hasDependentImmutableFields = true } case FinalP(DeepImmutableField) => diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index 1143ec8aec..848733f5aa 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -34,6 +34,7 @@ import org.opalj.br.fpcf.properties.ClassImmutability_new 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_new import org.opalj.br.fpcf.properties.ShallowImmutableClass @@ -127,8 +128,8 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP joinedImmutability = ShallowImmutableType // ImmutableContainerType maxImmutability = ShallowImmutableType //ImmutableContainerType case FinalP(DependentImmutableClass) ⇒ - joinedImmutability = ShallowImmutableType - maxImmutability = ShallowImmutableType + joinedImmutability = DependentImmutableType + maxImmutability = DependentImmutableType case eps @ InterimLUBP(lb, ub) ⇒ joinedImmutability = lb.correspondingTypeImmutability @@ -150,6 +151,10 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) maxImmutability = ShallowImmutableType //ImmutableContainerType + case FinalP(DependentImmutableType) ⇒ + joinedImmutability = joinedImmutability.meet(DependentImmutableType) + maxImmutability = DependentImmutableType + case eps @ InterimLUBP(subtypeLB, subtypeUB) ⇒ joinedImmutability = joinedImmutability.meet(subtypeLB) maxImmutability = maxImmutability.meet(subtypeUB) @@ -220,11 +225,15 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP case UBP(x) if (x == MutableType_new || x == MutableClass) ⇒ //MutableType | _: MutableObject) => Result(t, MutableType_new) //MutableType) - case FinalEP(e, x) if (x == ShallowImmutableType || x == DependentImmutableClass || x == ShallowImmutableClass) ⇒ //ImmutableContainerType | ImmutableContainer) => + case FinalEP(e, x) if (x == ShallowImmutableType || x == ShallowImmutableClass) ⇒ //ImmutableContainerType | ImmutableContainer) => maxImmutability = ShallowImmutableType //ImmutableContainerType dependencies = dependencies - e nextResult() - + case FinalEP(e, x) if (x == DependentImmutableClass || x == DependentImmutableType) ⇒ { + maxImmutability = DependentImmutableType + dependencies = dependencies - e + nextResult() + } case eps @ InterimEUBP(e, subtypeP) ⇒ dependencies = dependencies.updated(e, eps) subtypeP match { From e1b028aa50d7674d1047e082f452a70d25100e12 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Mon, 25 Nov 2019 14:59:47 +0100 Subject: [PATCH 060/327] formatting respecting scalafmt --- .../opalj/fpcf/ComputationSpecification.scala | 295 +++++++++--------- 1 file changed, 145 insertions(+), 150 deletions(-) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala index 4f4ecc259c..cefd90111a 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala @@ -17,160 +17,155 @@ case class SpecificationViolation(message: String) extends Exception(message) */ trait ComputationSpecification[A] { - // - // PROPERTIES OF COMPUTATION SPECIFICATIONS - // - - /** - * Identifies this computation specification; typically the name of the class - * which implements the underlying analysis. - * - * The default name is the name of `this` class. - * - * '''This method should be overridden.''' - */ - def name: String = { - val nameCandidate = this.getClass.getSimpleName - if (nameCandidate.endsWith("$")) - nameCandidate.substring(0, nameCandidate.length() - 1) - else - nameCandidate - } - - /** - * Returns the kinds of properties which are queried by this analysis. - * - * @note This set consists only of property kinds which are directly used by the analysis. - * - * @note Self usages should also be documented. - * - * @note This method is called after - * [[org.opalj.fpcf.ComputationSpecification#init(ps:org\.opalj\.fpcf\.PropertyStore)*]] - * was called for all analyses belonging to an analysis scenario. - * (E.g., it can be used to collect the set of used property bounds based on the - * configuration choices made in other analyses.) - */ - def uses(ps: PropertyStore): Set[PropertyBounds] - - /** - * Returns the kind of the property that is lazily (on-demand) derived. - */ - def derivesLazily: Option[PropertyBounds] - - /** - * Returns the set of property kinds eagerly derived by the underlying analysis. - */ - def derivesEagerly: Set[PropertyBounds] - - def derivesCollaboratively: Set[PropertyBounds] - - def derives: Iterator[PropertyBounds] = { - derivesEagerly.iterator ++ derivesCollaboratively.iterator ++ derivesLazily.iterator - } - - require( - (derivesCollaboratively intersect derivesEagerly).isEmpty, - "a property either has to be derived eagerly or collaboratively, but not both: "+ - (derivesCollaboratively intersect derivesEagerly).mkString(", ") - ) - - require( - derivesLazily.isDefined || derivesEagerly.nonEmpty || derivesCollaboratively.nonEmpty, - "the computation does not derive any information" - ) - - require( - derivesLazily.isEmpty || (derivesEagerly.isEmpty && derivesCollaboratively.isEmpty), - "a lazy analysis cannot also derive information eagerly and/or collaboratively " - ) - - /** - * Specifies the kind of the computation that is performed. The kind restricts in which - * way the analysis is allowed to interact with the property store/other analyses. - */ - def computationType: ComputationType - - def toString(ps: PropertyStore): String = { - val uses = this.uses(ps).iterator.map(_.toSpecification).mkString("uses={", ", ", "}") - - val derivesLazily = - this.derivesLazily.iterator. - map(_.toSpecification). - mkString("derivesLazily={", ", ", "}") - val derivesEagerly = - this.derivesEagerly.iterator. - map(_.toSpecification). - mkString("derivesEagerly={", ", ", "}") - val derivesCollaboratively = - this.derivesCollaboratively.iterator. - map(_.toSpecification). - mkString("derivesCollaboratively={", ", ", "}") - - s"ComputationSpecification(name=$name,type=$computationType,"+ - s"$uses,$derivesLazily,$derivesEagerly,$derivesCollaboratively)" - - } - - override def toString: String = { - s"ComputationSpecification(name=$name,type=$computationType)" - } - - // - // LIFECYCLE RELATED METHODS - // - - /** - * The type of the data used by the analysis at initialization time. - * For analyses without special initialization requirements this type is `Null`. - */ - type InitializationData - - /** - * Called directly after the analysis is registered with an analysis scheduler; in particular - * before any analysis belonging to the same analysis scenario is scheduled – - * independent of the batch in which it will run. - * - * This enables further initialization of the computations that will eventually be executed. - * For example to initialize global configuration information. - * - * A computation specification does not have to call any methods of the property store that - * may trigger or schedule computations; i.e., it must – in particular – not call - * the methods `apply`, `schedule*`, `register*` or `waitOnPhaseCompletion`. - * - * @return The initialization data that is later on passed to schedule. - */ - def init(ps: PropertyStore): InitializationData - - /** - * Called directly before the analyses belonging to a phase are effectively scheduled. I.e., - * after phase setup, but potentially after other analyses' `beforeSchedule` method is called. - */ - def beforeSchedule(ps: PropertyStore): Unit - - /** - * Called by the scheduler to let the analysis register itself or to start execution. - */ - def schedule(ps: PropertyStore, i: InitializationData): A - - /** - * Called back after all analyses of a specific phase have been - * schedule (i.e., before calling waitOnPhaseCompletion). - */ - def afterPhaseScheduling(ps: PropertyStore, analysis: A): Unit - - /** - * Called after phase completion. - */ - def afterPhaseCompletion(ps: PropertyStore, analysis: A): Unit + // + // PROPERTIES OF COMPUTATION SPECIFICATIONS + // + + /** + * Identifies this computation specification; typically the name of the class + * which implements the underlying analysis. + * + * The default name is the name of `this` class. + * + * '''This method should be overridden.''' + */ + def name: String = { + val nameCandidate = this.getClass.getSimpleName + if (nameCandidate.endsWith("$")) + nameCandidate.substring(0, nameCandidate.length() - 1) + else + nameCandidate + } + + /** + * Returns the kinds of properties which are queried by this analysis. + * + * @note This set consists only of property kinds which are directly used by the analysis. + * + * @note Self usages should also be documented. + * + * @note This method is called after + * [[org.opalj.fpcf.ComputationSpecification#init(ps:org\.opalj\.fpcf\.PropertyStore)*]] + * was called for all analyses belonging to an analysis scenario. + * (E.g., it can be used to collect the set of used property bounds based on the + * configuration choices made in other analyses.) + */ + def uses(ps: PropertyStore): Set[PropertyBounds] + + /** + * Returns the kind of the property that is lazily (on-demand) derived. + */ + def derivesLazily: Option[PropertyBounds] + + /** + * Returns the set of property kinds eagerly derived by the underlying analysis. + */ + def derivesEagerly: Set[PropertyBounds] + + def derivesCollaboratively: Set[PropertyBounds] + + def derives: Iterator[PropertyBounds] = { + derivesEagerly.iterator ++ derivesCollaboratively.iterator ++ derivesLazily.iterator + } + + require( + (derivesCollaboratively intersect derivesEagerly).isEmpty, + "a property either has to be derived eagerly or collaboratively, but not both: " + + (derivesCollaboratively intersect derivesEagerly).mkString(", ") + ) + + require( + derivesLazily.isDefined || derivesEagerly.nonEmpty || derivesCollaboratively.nonEmpty, + "the computation does not derive any information" + ) + + require( + derivesLazily.isEmpty || (derivesEagerly.isEmpty && derivesCollaboratively.isEmpty), + "a lazy analysis cannot also derive information eagerly and/or collaboratively " + ) + + /** + * Specifies the kind of the computation that is performed. The kind restricts in which + * way the analysis is allowed to interact with the property store/other analyses. + */ + def computationType: ComputationType + + def toString(ps: PropertyStore): String = { + val uses = this.uses(ps).iterator.map(_.toSpecification).mkString("uses={", ", ", "}") + + val derivesLazily = + this.derivesLazily.iterator.map(_.toSpecification).mkString("derivesLazily={", ", ", "}") + val derivesEagerly = + this.derivesEagerly.iterator.map(_.toSpecification).mkString("derivesEagerly={", ", ", "}") + val derivesCollaboratively = + this.derivesCollaboratively.iterator + .map(_.toSpecification) + .mkString("derivesCollaboratively={", ", ", "}") + + s"ComputationSpecification(name=$name,type=$computationType," + + s"$uses,$derivesLazily,$derivesEagerly,$derivesCollaboratively)" + } + + override def toString: String = { + s"ComputationSpecification(name=$name,type=$computationType)" + } + + // + // LIFECYCLE RELATED METHODS + // + + /** + * The type of the data used by the analysis at initialization time. + * For analyses without special initialization requirements this type is `Null`. + */ + type InitializationData + + /** + * Called directly after the analysis is registered with an analysis scheduler; in particular + * before any analysis belonging to the same analysis scenario is scheduled – + * independent of the batch in which it will run. + * + * This enables further initialization of the computations that will eventually be executed. + * For example to initialize global configuration information. + * + * A computation specification does not have to call any methods of the property store that + * may trigger or schedule computations; i.e., it must – in particular – not call + * the methods `apply`, `schedule*`, `register*` or `waitOnPhaseCompletion`. + * + * @return The initialization data that is later on passed to schedule. + */ + def init(ps: PropertyStore): InitializationData + + /** + * Called directly before the analyses belonging to a phase are effectively scheduled. I.e., + * after phase setup, but potentially after other analyses' `beforeSchedule` method is called. + */ + def beforeSchedule(ps: PropertyStore): Unit + + /** + * Called by the scheduler to let the analysis register itself or to start execution. + */ + def schedule(ps: PropertyStore, i: InitializationData): A + + /** + * Called back after all analyses of a specific phase have been + * schedule (i.e., before calling waitOnPhaseCompletion). + */ + def afterPhaseScheduling(ps: PropertyStore, analysis: A): Unit + + /** + * Called after phase completion. + */ + def afterPhaseCompletion(ps: PropertyStore, analysis: A): Unit } trait SimpleComputationSpecification[A] extends ComputationSpecification[A] { - final override type InitializationData = Null - final override def init(ps: PropertyStore): Null = null - final override def beforeSchedule(ps: PropertyStore): Unit = {} - final override def afterPhaseScheduling(ps: PropertyStore, analysis: A): Unit = {} - final override def afterPhaseCompletion(ps: PropertyStore, analysis: A): Unit = {} + final override type InitializationData = Null + final override def init(ps: PropertyStore): Null = null + final override def beforeSchedule(ps: PropertyStore): Unit = {} + final override def afterPhaseScheduling(ps: PropertyStore, analysis: A): Unit = {} + final override def afterPhaseCompletion(ps: PropertyStore, analysis: A): Unit = {} } From 099d7ecdb3e766ea6297d16daa46d54f2a362330 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 26 Nov 2019 11:30:33 +0100 Subject: [PATCH 061/327] test error fix --- .../main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala index f6b55077e3..95537737ac 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala @@ -4,14 +4,13 @@ package br package fpcf import com.typesafe.config.Config - import org.opalj.log.LogContext import org.opalj.log.OPALLogger import org.opalj.concurrent.NumberOfThreadsForCPUBoundTasks import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.PropertyStoreContext -import org.opalj.fpcf.par.PKECPropertyStore -//import org.opalj.fpcf.seq.PKESequentialPropertyStore +//import org.opalj.fpcf.par.PKECPropertyStore +import org.opalj.fpcf.seq.PKESequentialPropertyStore import org.opalj.br.analyses.ProjectInformationKey import org.opalj.br.analyses.SomeProject @@ -60,8 +59,8 @@ object PropertyStoreKey ) psFactory(context) case None ⇒ - //val ps = PKESequentialPropertyStore(context: _*) - val ps = PKECPropertyStore(context: _*) + val ps = PKESequentialPropertyStore(context: _*) + //val ps = PKECPropertyStore(context: _*) ps } } From 7daa8251c08beb4a618e98351a1641d5ff7d36b7 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 5 Dec 2019 22:12:08 +0100 Subject: [PATCH 062/327] Dependent field and dependent class approach using signature --- .../ClassImmutabilityAnalysisDemo.scala | 85 +- .../FieldImmutabilityAnalysisDemo.scala | 23 +- .../TypeImmutabilityAnalysisDemo.scala | 32 +- .../GenericAndDeepImmutableFields.java | 4 +- .../GenericAndMutableFields.java | 2 +- .../GenericAndShallowImmutableFields.java | 4 +- .../class_immutability/Generic_class1.java | 10 +- .../DependentImmutableClassAnnotation.java | 3 +- .../DependentImmutableFieldAnnotation.java | 2 + .../opalj/fpcf/FieldImmutabilityTests.scala | 2 - .../ClassImmutabilityMatcher.scala | 18 +- .../FieldImmutabilityMatcher.scala | 10 +- .../fpcf/properties/ClassImmutability.scala | 67 +- .../properties/ClassImmutability_new.scala | 33 +- .../fpcf/properties/FieldImmutability.scala | 15 +- .../L0FieldImmutabilityAnalysis.scala | 26 +- .../L0ReferenceImmutabilityAnalysis.scala | 95 +- .../analyses/L2FieldMutabilityAnalysis.scala | 1780 ++++++++--------- .../LxClassImmutabilityAnalysis_new.scala | 55 +- .../LxTypeImmutabilityAnalysis_new.scala | 444 ++-- 20 files changed, 1450 insertions(+), 1260 deletions(-) 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 index d7ba9e7954..447d52c55d 100644 --- 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 @@ -12,8 +12,8 @@ import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.ClassImmutability_new 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 @@ -29,51 +29,52 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis; */ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" - override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(project: Project[URL]): String = { + def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) + val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + analysesManager.project.get(RTACallGraphKey) - val (propertyStore, _) = analysesManager.runAll( - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new - ) - "Mutable Class: "+propertyStore - .finalEntities(MutableClass) - .toList - .toString()+"\n"+ - "Dependent Immutable Class: "+propertyStore - .finalEntities(DependentImmutableClass) - .toList - .toString()+"\n"+ - "Shallow Immutable Class: "+propertyStore - .finalEntities(ShallowImmutableClass) - .toList - .toString()+"\n"+ - "Deep Immutable Class: "+propertyStore - .finalEntities(DeepImmutableClass) - .toList - .toString()+"\n" - } + val (propertyStore, _) = analysesManager.runAll( + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new + ) + + "Mutable Class: " + propertyStore + .finalEntities(MutableClass) + .toList + .toString() + "\n" + + "Dependent Immutable Class: " + propertyStore + .entities(ClassImmutability_new.key) + .toList + .toString() + "\n" + + "Shallow Immutable Class: " + propertyStore + .finalEntities(ShallowImmutableClass) + .toList + .toString() + "\n" + + "Deep Immutable Class: " + propertyStore + .finalEntities(DeepImmutableClass) + .toList + .toString() + "\n" + } } 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 index 4da81148ed..dfc0b5aed7 100644 --- 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 @@ -13,10 +13,13 @@ import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis 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.FieldImmutability 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.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis @@ -44,6 +47,8 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { def analyze(project: Project[URL]): String = { val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + val (propertyStore, _) = analysesManager.runAll( /** * LazyTypeImmutabilityAnalysis, @@ -57,6 +62,8 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { * LazyLxTypeImmutabilityAnalysis_new, * EagerL0FieldImmutabilityAnalysis* */ + + LazyLxClassImmutabilityAnalysis_new, LazyTypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, @@ -76,12 +83,24 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .toList .toString() + "\n" + "Dependet Immutable Fields:" + propertyStore - .finalEntities(DependentImmutableField) + .finalEntities(DependentImmutableField(None)) .toList .toString() + "\n" + "Deep Immutable Fields: " + propertyStore .finalEntities(DeepImmutableField) .toList - .toString() + .toString() + "\n" + + propertyStore + .entities(FieldImmutability.key) + /** + * .entities(x ⇒ { + * x.asEPS.pk match { + * case DependentImmutableField(_) ⇒ true + * case _ ⇒ false + * } + * })* + */ + .toList + .toString } } 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 index 5b54015926..5793b66312 100644 --- 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 @@ -7,19 +7,20 @@ 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.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyL0PurityAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis 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_new import org.opalj.br.fpcf.properties.ShallowImmutableType -//import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +//import org.opalj.br.fpcf.properties.ShallowImmutableType import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -45,15 +46,28 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + val (propertyStore, _) = analysesManager.runAll( - LazyTypeImmutabilityAnalysis, + /** + * LazyTypeImmutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL0ReferenceImmutabilityAnalysis, + * LazyL0PurityAnalysis, + * LazyL2FieldMutabilityAnalysis, + * LazyL0FieldImmutabilityAnalysis, + * EagerLxClassImmutabilityAnalysis_new, + * EagerLxTypeImmutabilityAnalysis_new* + */ + //LazyTypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0PurityAnalysis, + LazyL2PurityAnalysis, LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, - EagerLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new ) "Mutable Type: "+propertyStore .finalEntities(MutableType_new) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java index 4510b6b51c..acd93a1b2d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java @@ -11,11 +11,11 @@ @DependentImmutableClassAnnotation("") public final class GenericAndDeepImmutableFields { - @DependentImmutableFieldAnnotation("") + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") @ImmutableReferenceAnnotation("") private T1 t1; - @DependentImmutableFieldAnnotation("") + @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") @ImmutableReferenceAnnotation("") private T2 t2; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java index 05fcf78ccb..d80fcf4ac2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java @@ -14,7 +14,7 @@ public class GenericAndMutableFields { @MutableFieldAnnotation("Because of mutable reference") @MutableReferenceAnnotation("Because of public field") public T1 t1; - @DependentImmutableFieldAnnotation("Because of generic type") + @DependentImmutableFieldAnnotation(value = "Because of generic type", genericString = "T2") @ImmutableReferenceAnnotation("Because of effectively immutable final") private T2 t2; GenericAndMutableFields(T1 t1, T2 t2){ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java index 1905bbc540..1ffd179a83 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java @@ -12,9 +12,9 @@ @ShallowImmutableClassAnnotation("") public class GenericAndShallowImmutableFields { - @DependentImmutableFieldAnnotation("") + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") private T1 t1; - @DependentImmutableFieldAnnotation("") + @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") private T2 t2; @ShallowImmutableFieldAnnotation("") private TrivialMutableClass tmc; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java index 0e7cfcf3d3..eef7c43847 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java @@ -8,19 +8,19 @@ @DependentImmutableTypeAnnotation("Dependent Immutability of T1,...,T5") @DependentImmutableClassAnnotation("Dependent Immutability of T1,...,T5") public final class Generic_class1 { - @DependentImmutableFieldAnnotation("T1") + @DependentImmutableFieldAnnotation(value = "T1",genericString = "T1") @ImmutableReferenceAnnotation("effectively") private T1 t1; - @DependentImmutableFieldAnnotation("T2") + @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") @ImmutableReferenceAnnotation("effectively") private T2 t2; - @DependentImmutableFieldAnnotation("T3") + @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") @ImmutableReferenceAnnotation("effectively") private T3 t3; - @DependentImmutableFieldAnnotation("T4") + @DependentImmutableFieldAnnotation(value = "T4", genericString = "T3") @ImmutableReferenceAnnotation("effectively") private T4 t4; - @DependentImmutableFieldAnnotation("T5") + @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") @ImmutableReferenceAnnotation("effectively") private T5 t5; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java index e1925a8bc5..d2f047a50b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java @@ -1,11 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability; +import javafx.util.Pair; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; - import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java index eb76bef20c..ed0bc13b0e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java @@ -24,6 +24,8 @@ */ String value() ; // default = "N/A"; + String genericString(); + Class[] analyses() default { L0FieldImmutabilityAnalysis.class }; 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 index a5cbceb83e..430b923e33 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -8,7 +8,6 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.Project import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis @@ -39,7 +38,6 @@ class FieldImmutabilityTests extends PropertiesTest { describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { val as = executeAnalyses( Set( - LazyTypeImmutabilityAnalysis, LazyL2FieldMutabilityAnalysis, LazyL0ReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala index 188a95ec8d..fac97feb4c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala @@ -44,24 +44,26 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) a: AnnotationLike, properties: Traversable[Property] ): Option[String] = { - println(11) - if (!properties.exists(p ⇒ p == property)) { + if (!properties.exists(p ⇒ { + p match { + case DependentImmutableClass(_) if property == DependentImmutableClass() ⇒ true + case _ if p == property ⇒ true + case _ ⇒ false + } + })) { //p == property)) { // ... when we reach this point the expected property was not found. - println(22) - val r = Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) - println(33) - r + Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) } else { - println(44) None } + } } class MutableClassMatcher extends ClassImmutabilityMatcher(MutableClass) -class DependentImmutableClassMatcher extends ClassImmutabilityMatcher(DependentImmutableClass) +class DependentImmutableClassMatcher extends ClassImmutabilityMatcher(DependentImmutableClass()) class ShallowImmutableClassMatcher extends ClassImmutabilityMatcher(ShallowImmutableClass) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala index 64e406fb58..5d27f81213 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala @@ -42,7 +42,13 @@ class FieldImmutabilityMatcher(val property: FieldImmutability) extends Abstract a: AnnotationLike, properties: Traversable[Property] ): Option[String] = { - if (!properties.exists(p ⇒ p == property)) { + if (!properties.exists(p ⇒ { + p match { + case DependentImmutableField(_) if property == DependentImmutableField() ⇒ true + case _ if p == property ⇒ true + case _ ⇒ false + } + })) { //p == property)) { // ... when we reach this point the expected property was not found. Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) } else { @@ -56,6 +62,6 @@ class MutableFieldMatcher extends FieldImmutabilityMatcher(MutableField) class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(ShallowImmutableField) -class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(DependentImmutableField) +class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(DependentImmutableField()) class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(DeepImmutableField) 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..3c085c8897 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 @@ -11,7 +11,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - final type Self = ClassImmutability + final type Self = ClassImmutability } @@ -86,25 +86,26 @@ sealed trait ClassImmutability extends OrderedProperty with ClassImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ClassImmutability] = ClassImmutability.key + final def key: PropertyKey[ClassImmutability] = ClassImmutability.key - def correspondingTypeImmutability: TypeImmutability + def correspondingTypeImmutability: TypeImmutability - /** `true` if instances of the class are mutable. */ - def isMutable: Boolean + /** `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 { - /** - * The key associated with every [[ClassImmutability]] property. - */ - final val key: PropertyKey[ClassImmutability] = PropertyKey.create( - "opalj.ClassImmutability", - MutableObjectDueToUnresolvableDependency - ) + /** + * The key associated with every [[ClassImmutability]] property. + */ + final val key: PropertyKey[ClassImmutability] = PropertyKey.create( + "opalj.ClassImmutability", + MutableObjectDueToUnresolvableDependency + ) } /** @@ -117,11 +118,11 @@ object ClassImmutability extends ClassImmutabilityPropertyMetaInformation { */ case object ImmutableObject extends ClassImmutability { - final val correspondingTypeImmutability = ImmutableType + final val correspondingTypeImmutability = ImmutableType - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - final def isMutable: Boolean = false + final def isMutable: Boolean = false } /** @@ -131,45 +132,45 @@ case object ImmutableObject extends ClassImmutability { */ case object ImmutableContainer extends ClassImmutability { - final val correspondingTypeImmutability = ImmutableContainerType + final val correspondingTypeImmutability = ImmutableContainerType - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == ImmutableObject) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - } + 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 + final def isMutable: Boolean = false } sealed trait MutableObject extends ClassImmutability { - def reason: String - final val correspondingTypeImmutability = MutableType + def reason: String + final val correspondingTypeImmutability = MutableType - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == ImmutableObject || other == ImmutableContainer) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") - } + 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 def isMutable: Boolean = true - final override def toString: String = s"MutableObject(reason=$reason)" + final override def toString: String = s"MutableObject(reason=$reason)" } case object MutableObjectDueToIncompleteAnalysis extends MutableObject { - final def reason = "analysis has not yet completed" + final def reason = "analysis has not yet completed" } case object MutableObjectByAnalysis extends MutableObject { - final def reason = "determined by analysis" + final def reason = "determined by analysis" } case object MutableObjectDueToUnknownSupertypes extends MutableObject { - final def reason = "the type hierarchy is upwards incomplete" + final def reason = "the type hierarchy is upwards incomplete" } case object MutableObjectDueToUnresolvableDependency extends MutableObject { - final def reason = "a dependency cannot be resolved" + final def reason = "a dependency cannot be resolved" } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index 50239f389b..b4977729a9 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -6,7 +6,7 @@ import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = ClassImmutability_new + final type Self = ClassImmutability_new } /** @@ -27,34 +27,35 @@ sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaIn sealed trait ClassImmutability_new extends Property with ClassImmutabilityPropertyMetaInformation_new { - final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key - def correspondingTypeImmutability: TypeImmutability_new + final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key + def correspondingTypeImmutability: TypeImmutability_new } object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[ClassImmutability_new]] property. - */ - final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( - "opalj.ClassImmutability_new", - MutableClass - ) + /** + * The key associated with every [[ClassImmutability_new]] property. + */ + final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( + "opalj.ClassImmutability_new", + MutableClass + ) } case object MutableClass extends ClassImmutability_new { - def correspondingTypeImmutability = MutableType_new + def correspondingTypeImmutability = MutableType_new } -case object DependentImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = - DependentImmutableType //TODO check +case class DependentImmutableClass(typeBounds: Set[(String, String)] = Set.empty) + extends ClassImmutability_new { + override def correspondingTypeImmutability: TypeImmutability_new = + DependentImmutableType //TODO check } case object ShallowImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = ShallowImmutableType + override def correspondingTypeImmutability: TypeImmutability_new = ShallowImmutableType } case object DeepImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType + override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType } 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 index f6bbffb40b..23b7699fad 100644 --- 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 @@ -46,6 +46,17 @@ sealed trait ImmutableField extends FieldImmutability case object ShallowImmutableField extends ImmutableField -case object DependentImmutableField extends ImmutableField - +case class DependentImmutableField(var genericString: Option[String] = None) + extends ImmutableField + with FieldImmutability //{ +// def genericString: Option[String] = None +//} +/** + * case object DependentImmutableField extends DependentImmutableField_ { + * def setGenericString(s: Option[String]) = { + * genericString = s + * this + * } + * }* + */ case object DeepImmutableField extends ImmutableField diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 994809a9cb..79b8f1f129 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -4,6 +4,7 @@ package org.opalj.br.fpcf.analyses import org.opalj.br.Field import org.opalj.br.FieldType import org.opalj.br.ObjectType +import org.opalj.br.TypeVariableSignature import org.opalj.br.analyses.FieldAccessInformationKey import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.ClosedPackagesKey @@ -77,6 +78,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) field: Field ): ProperPropertyComputationResult = { + println("Field Attributes: " + field.asField.attributes.toList.collectFirst({ + case TypeVariableSignature(t) => t + })) + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty def handleTypeImmutability(objectType: FieldType)(state: State): Option[Boolean] = { @@ -91,6 +96,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case FinalEP(e, DependentImmutableType) => { println("has dependent immutable type") + //TODO under construction state.dependentTypeImmutability = Some(true) return Some(false); } @@ -166,14 +172,26 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(false) => Result(field, MutableField) case Some(true) => { //If the field type is object. It is a generic field - if (field.fieldType == ObjectType("java/lang/Object")) - Result(field, DependentImmutableField) - else + //Test Dependent... //TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //if (field.fieldType == ObjectType("java/lang/Object")) { + val genericString = field.asField.attributes.toList.collectFirst({ + case TypeVariableSignature(t) => t + }) + if (!field.attributes.isEmpty && genericString != None) { + + //if(genericString!=None) + // println("Generic String: "+genericString) + // println( + // "test: "+DependentImmutableField(genericString).genericString + // } + Result(field, DependentImmutableField(genericString)) //genericString)) + + } else state.typeImmutability match { case Some(true) => Result(field, DeepImmutableField) case Some(false) => { state.dependentTypeImmutability match { - case Some(true) => Result(field, DependentImmutableField) + case Some(true) => Result(field, DependentImmutableField()) case _ => Result(field, ShallowImmutableField) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index 6fc3cc9170..c2ad3ac191 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -601,7 +601,14 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec taCode.stmts, taCode.cfg, taCode.pcToIndex - )) + ) /**&& !isDoubleCheckedLocking( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )**/ ) return true; state.referenceImmutability = LazyInitializedReference //LazyInitializedField @@ -910,15 +917,16 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec defaultValue: Any, code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Option[(Int, Int, Int)] = { + ): Option[(Int, Int, Int)] = { val startBB = cfg.bb(fieldWrite).asBasicBlock var enqueuedBBs: Set[CFGNode] = startBB.predecessors var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) var result: Option[(Int, Int)] = None - + println("worklist initial: "+worklist) while (worklist.nonEmpty) { + println("worklist "+worklist) val curBB = worklist.head worklist = worklist.tail @@ -929,8 +937,13 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec (cfStmt.astID: @switch) match { case If.ASTID ⇒ val ifStmt = cfStmt.asIf + println("ifstmt: "+ifStmt) + println("curBB: "+curBB) + println("startBB: "+startBB) + println("result.isDefined "+result.isDefined) + println("result: "+result) ifStmt.condition match { - case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + case EQ if curBB != startBB ⇒ //&& isGuard(ifStmt, defaultValue, code) ⇒ if (result.isDefined) { if (result.get._1 != endPC || result.get._2 != endPC + 1) return None; @@ -938,7 +951,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec result = Some((endPC, endPC + 1)) } - case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + case NE if curBB != startBB ⇒ //&& isGuard(ifStmt, defaultValue, code) ⇒ if (result.isDefined) { if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) return None; @@ -1037,7 +1050,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec isGuardInternal(ifStmt.rightExpr.asVar) } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { isGuardInternal(ifStmt.leftExpr.asVar) - } else false + } else + isGuardInternal(ifStmt.leftExpr.asVar) || isGuardInternal(ifStmt.rightExpr.asVar) // || false //TODO } /** @@ -1087,6 +1101,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec /** * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ + * * true * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * */ @@ -1094,8 +1109,74 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec state.referenceImmutabilityDependees += eop true } -} + //-------------------------------------------------- double checked locking + def isDoubleCheckedLocking( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + )(implicit state: State): Boolean = { + //val a = state.field + //val write = code(writeIndex).asFieldWriteAccessStmt + //println("Purity Information: "+propertyStore(declaredMethods(method), Purity.key)) + //println(isNonDeterministic(propertyStore(declaredMethods(method), Purity.key))) + if (!method.isStatic || !state.field.isStatic) { + false + } else { + //----------------------------- + /** + * val reads = fieldAccessInformation.readAccesses(state.field) + * if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + * return false; // Reads outside the (single) lazy initialization method + * } + * + * // There must be a guarding if-Statement + * // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + * // first statement that is executed after the if-Statement if the field's value was not the + * // default value + * val (guardIndex, guardedIndex, readIndex) = + * findGuard(writeIndex, defaultValue, code, cfg) match { + * case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + * case None ⇒ return false; + * } + * // Detect only simple patterns where the lazily initialized value is returned immediately + * // if (!checkImmediateReturn(write, writeIndex, readIndex, code)) + * // return false; + * + * println( + * "Check writes (is double checked locking): "+checkWrites( + * write, + * writeIndex, + * guardIndex, + * guardedIndex, + * method, + * code, + * cfg + * ) + * ) + * // The value written must be computed deterministically and the writes guarded correctly + * if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + * return false; + * + * // Field reads (except for the guard) may only be executed if the field's value is not the + * // default value + * if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + * return false; + * + * true + */ + //----------------------------- + + //} + + false + } + } + //-------------------------------------------------- double checked locking +} trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { final override def uses: Set[PropertyBounds] = Set( 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/L2FieldMutabilityAnalysis.scala index 571fb9bb6f..c703fb9427 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/L2FieldMutabilityAnalysis.scala @@ -86,200 +86,200 @@ import org.opalj.tac.fpcf.properties.TACAI */ class L2FieldMutabilityAnalysis 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 - ) { - def hasDependees: Boolean = { - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || fieldMutabilityDependees.nonEmpty || - escapeDependees.nonEmpty || tacDependees.nonEmpty + 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 + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || fieldMutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty + } + + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + fieldMutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + } } - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - fieldMutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + 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) + + def doDetermineFieldMutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field ⇒ determineFieldMutability(field) + case _ ⇒ + val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) } - } - - 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) - - def doDetermineFieldMutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field => determineFieldMutability(field) - case _ => - val m = entity.getClass.getSimpleName + " is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } - - /** - * Analyzes the mutability of private non-final fields. - * - * 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 determineFieldMutability( - field: Field - ): ProperPropertyComputationResult = { - 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 (field.isFinal) - return createResult(); - - state.fieldMutability = EffectivelyFinalField - - val thisType = field.classFile.thisType - - if (field.isPublic) - return Result(field, NonFinalFieldByLackOfInformation) - - // 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) { - if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, NonFinalFieldByLackOfInformation); - } - project.classesPerPackage(thisType.packageName) - } else { - Set(field.classFile) - } - - val classesHavingAccess: Iterator[ClassFile] = - if (field.isProtected) { - if (typeExtensibility(thisType).isYesOrUnknown) { - return Result(field, NonFinalFieldByLackOfInformation); - } - 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))) - return Result(field, NonFinalFieldByLackOfInformation); - - // 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 - // of the class after the reference to the class and (an indirect) reference to the - // field has become available. Consider the following example: - // class X implements Y{ - // - // private Object o; - // - // public Object getO() { return o; } - // - // private static X instance; - // static { - // instance = new X(); - // Z.register(instance); - // // when we reach this point o is now (via getO) publically accessible and - // // X is properly initialized! - // o = new Object(); // o is mutated... - // } - // } /** - * Returns the TACode for a method if available, registering dependencies as necessary. + * Analyzes the mutability of private non-final fields. + * + * 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. */ - 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 eps: InterimEP[Method, TACAI] => - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk => - state.tacDependees += method -> ((epk, pcs)) - None - } - } + private[analyses] def determineFieldMutability( + field: Field + ): ProperPropertyComputationResult = { + implicit val state: State = State(field) - for { - (method, pcs) <- fieldAccessInformation.writeAccesses(field) - taCode <- getTACAI(method, pcs) - } { - if (methodUpdatesField(method, taCode, pcs)) - return Result(field, NonFinalFieldByAnalysis); - } + // Fields are not final if they are read prematurely! + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) + return Result(field, NonFinalFieldByAnalysis); + + if (field.isFinal) + return createResult(); + + state.fieldMutability = EffectivelyFinalField + + val thisType = field.classFile.thisType + + if (field.isPublic) + return Result(field, NonFinalFieldByLackOfInformation) + + // 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) { + if (!closedPackages.isClosed(thisType.packageName)) { + return Result(field, NonFinalFieldByLackOfInformation); + } + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } + + val classesHavingAccess: Iterator[ClassFile] = + if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown) { + return Result(field, NonFinalFieldByLackOfInformation); + } + 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))) + return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode ← getTACAI(method, pcs) + } { + if (methodUpdatesField(method, taCode, pcs)) + return Result(field, NonFinalFieldByAnalysis); + } - if (state.lazyInitInvocation.isDefined) { - val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - handleCalls(calleesEOP) + if (state.lazyInitInvocation.isDefined) { + val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + handleCalls(calleesEOP) + } + + createResult() } - createResult() - } - - def handleCalls( - calleesEOP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: State - ): Boolean = { - calleesEOP match { - case FinalP(callees) => - state.calleesDependee = None - handleCallees(callees) - case InterimUBP(callees) => - state.calleesDependee = Some(calleesEOP) - handleCallees(callees) - case _ => - state.calleesDependee = Some(calleesEOP) - false + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: State + ): Boolean = { + calleesEOP match { + case FinalP(callees) ⇒ + state.calleesDependee = None + handleCallees(callees) + case InterimUBP(callees) ⇒ + state.calleesDependee = Some(calleesEOP) + handleCallees(callees) + case _ ⇒ + state.calleesDependee = Some(calleesEOP) + false + } } - } - - def handleCallees(callees: Callees)(implicit state: State): Boolean = { - val pc = state.lazyInitInvocation.get._2 - if (callees.isIncompleteCallSite(pc)) { - state.fieldMutability = NonFinalFieldByAnalysis - true - } else { - val targets = callees.callees(pc).toTraversable - if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { - state.fieldMutability = NonFinalFieldByAnalysis - true - } else false + + def handleCallees(callees: Callees)(implicit state: State): Boolean = { + val pc = state.lazyInitInvocation.get._2 + if (callees.isIncompleteCallSite(pc)) { + state.fieldMutability = NonFinalFieldByAnalysis + true + } else { + val targets = callees.callees(pc).toTraversable + if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { + state.fieldMutability = NonFinalFieldByAnalysis + true + } else false + } } - } - /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. - */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field * The code below can be used to identify such value, but is not yet adapted to using the * TACAI property */ - /* + /* var constantVal: Option[Any] = None var allInitializeConstant = true @@ -338,752 +338,752 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } constantVal */ - } - - /** - * 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.fieldMutability ne NonFinalFieldByAnalysis)) - InterimResult( - state.field, - NonFinalFieldByAnalysis, - state.fieldMutability, - state.dependees, - c - ) - else - Result(state.field, state.fieldMutability) - } - - /** - * 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 = { - val isNotFinal = eps.pk match { - case EscapeProperty.key => - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - 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)) - methodUpdatesField(method, newEP.ub.tac.get, pcs) - case Callees.key => - handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key => - isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key => - 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) - !isFinalField(newEP) } - if (isNotFinal) - Result(state.field, NonFinalFieldByAnalysis) - else - createResult() - } - - /** - * Checks whether a field write may be a lazy initialization. - * - * We only consider simple cases of lazy initialization where the single field write is guarded - * so it is executed only if the field still has its default value and where the written value - * is guaranteed to be the same even if the write is executed multiple times (may happen because - * of the initializing method being executed more than once on concurrent threads). - * - * Also, all field reads must be used only for a guard or their uses have to be guarded - * themselves. - */ - def isLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - )(implicit state: State): Boolean = { - val write = code(writeIndex).asFieldWriteAccessStmt - - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - // Only handle lazy initialization of ints and floats as they are guaranteed to be - // written atomically - return false; + /** + * 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.fieldMutability ne NonFinalFieldByAnalysis)) + InterimResult( + state.field, + NonFinalFieldByAnalysis, + state.fieldMutability, + state.dependees, + c + ) + else + Result(state.field, state.fieldMutability) + } + + /** + * 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 = { + val isNotFinal = eps.pk match { + case EscapeProperty.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + methodUpdatesField(method, newEP.ub.tac.get, pcs) + case Callees.key ⇒ + handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key ⇒ + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key ⇒ + 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) + !isFinalField(newEP) + } + + if (isNotFinal) + Result(state.field, NonFinalFieldByAnalysis) + else + createResult() + } + + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + )(implicit state: State): Boolean = { + val write = code(writeIndex).asFieldWriteAccessStmt + + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + return false; + } + + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method + } + + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + case None ⇒ return false; + } + + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) + return false; + + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + + true } - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs => (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return false; // Reads outside the (single) lazy initialization method + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val definitions = write.value.asVar.definedBy + + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + // The field write must be guarded correctly + isDeterministic && + checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID ⇒ + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID ⇒ + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ ⇒ return false; // neither a field read nor a return + } + index += 1 + } + false } - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val (guardIndex, guardedIndex, readIndex) = - findGuard(writeIndex, defaultValue, code, cfg) match { - case Some((guard, guarded, read)) => (guard, guarded, read) - case None => return false; - } - - // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) - return false; - - // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - return false; - - // Field reads (except for the guard) may only be executed if the field's value is not the - // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - return false; - - true - } - - def checkWrites( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val definitions = write.value.asVar.definedBy - - val isDeterministic = - if (definitions.size == 1) { - // The value written must be computed deterministically - checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) - } else { - // More than one definition site for the value might lead to differences between - // invocations, but not if this method has no parameters and is deterministic - // (in this case, the definition reaching the write will always be the same) + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - - // The field write must be guarded correctly - isDeterministic && - checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - } - - /** - * Checks if the field write for a lazy initialization is immediately followed by a return of - * the written value (possibly loaded by another field read). - */ - def checkImmediateReturn( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 - - var load = -1 - - while (index < code.length) { - val stmt = code(index) - stmt.astID match { - case Assignment.ASTID => - if (isReadOfCurrentField(stmt.asAssignment.expr)) - load = index - else - return false; // No field read or a field read of a different field - case ReturnValue.ASTID => - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) - return true; // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) - return true; // return of field value loaded by field read - else - return false; // return of different value - case _ => return false; // neither a field read nor a return - } - index += 1 + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) } - false - } - - def lazyInitializerIsDeterministic( - method: Method, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - - /** - * 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 - for (pc <- pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID => - if (method.isInitializer) { - if (field.isStatic) { - if (method.isConstructor) - return true; + + /** + * 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 + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + if (field.isStatic) { + if (method.isConstructor) + return true; + } else { + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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; + + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + // A field written outside an initializer must be lazily + // initialized or it is non-final + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )) + return true; + + state.fieldMutability = LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - if (receiverDefs != SelfReferenceParameter) - return true; + // nothing to do as the put field is dead } - } 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; - - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - // A field written outside an initializer must be lazily - // initialized or it is non-final - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - )) - return true; - - state.fieldMutability = LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - // 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. - return true; - } - } - case _ => throw new RuntimeException("unexpected field access"); - } - } else { - // nothing to do as the put field is dead + } } - } - } - false - } - - /** - * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] => - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk => - state.tacDependees += method -> ((epk, pcs)) - None + 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 // Must be locally created - else { - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - handleEscapeProperty(escape) + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, 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 handleEscapeProperty( - ep: EOptionP[DefinitionSite, EscapeProperty] - )(implicit state: State): Boolean = 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 - } - - /** - * Checks if the field write is only executed if the field's value was still the default value. - * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization - * and the field write. - */ - def checkWriteIsGuarded( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val startBB = cfg.bb(writeIndex).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt => - stmt.astID == CaughtException.ASTID - } flatMap { exception => - exception.asCaughtException.origins.map { origin: Int => - if (isImmediateVMException(origin)) - pcOfImmediateVMException(origin) - else - pcOfMethodExternalException(origin) - }.iterator + + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) + } + } + } } - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail + /** + * 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 = ep match { + case FinalP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ + false - val startPC = curBB.startPC - val endPC = curBB.endPC + case FinalP(AtMost(_)) ⇒ + true - if (startPC == 0 || startPC == guardedIndex) - return false; // Reached method start or wrong branch of guarding if-Statement + case _: FinalEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + case InterimUBP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ + state.escapeDependees += ep + false + + case InterimUBP(AtMost(_)) ⇒ + true - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + case _: InterimEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors + case _ ⇒ + state.escapeDependees += ep + false } - true - } - - /** - * 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]] - )(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(defSites.head).asAssignment.expr.isConst) true - else { - val expr = code(index).asAssignment.expr - expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { - case Some(field) => - isFinalField(propertyStore(field, FieldMutability.key)) - case _ => // Unknown field - false - }) + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt ⇒ + stmt.astID == CaughtException.ASTID + } flatMap { exception ⇒ + exception.asCaughtException.origins.map { origin: Int ⇒ + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator + } + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement + + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors } - } - defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) + true } - val value = origin.expr + /** + * 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]] + )(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(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isFinalField(propertyStore(field, FieldMutability.key)) + case _ ⇒ // Unknown field + false + }) + } + } - val isNonConstDeterministic = value.astID match { - case GetStatic.ASTID | GetField.ASTID => - value.asFieldRead.resolveField(p) match { - case Some(field) => - isFinalField(propertyStore(field, FieldMutability.key)) - case _ => // Unknown field - false + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) } - 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.exists(!isConstant(_))) { - false - } else { - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true + + val value = origin.expr + + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID ⇒ + value.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isFinalField(propertyStore(field, FieldMutability.key)) + case _ ⇒ // Unknown field + false + } + 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.exists(!isConstant(_))) { + false + } else { + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true + } + 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, code) } - 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, code) - } - value.isConst || isNonConstDeterministic - } - - /** - * Checks that all non-dead field reads that are not used for the guarding if-Statement of a - * lazy initialization are only executed if the field did not have its default value or after - * the (single) field write. - */ - def checkReads( - reads: Seq[(Method, PCs)], - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - ): Boolean = { - // There is only a single method with reads aside from initializers (checked by - // isLazilyInitialized), so we have to check only reads from that one method. - reads.filter(!_._1.isInitializer).head._2 forall { readPC => - val index = pcToIndex(readPC) - index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) - } - } - - /** - * Checks that a field read is only executed if the field did not have its default value or - * after the (single) field write. - */ - def checkRead( - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Boolean = { - val startBB = cfg.bb(readIndex).asBasicBlock - val writeBB = cfg.bb(writeIndex) - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - - if (startPC == 0) - return false; // Reached the start of the method but not the guard or field write - - if ((curBB eq writeBB) && writeIndex > readIndex) - return false; // In the basic block of the write, but before the write - - if (startPC != guardedIndex && // Did not reach the guard - (curBB ne writeBB) /* Not the basic block of the write */ ) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } + value.isConst || isNonConstDeterministic } - true - } - - /** - * Gets all predecessor BasicBlocks of a CFGNode. - */ - def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode => - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) + } } - result.toList - } - - /** - * 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. - */ - def findGuard( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Option[(Int, Int, Int)] = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int)] = None - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID => - val ifStmt = cfStmt.asIf - ifStmt.condition match { - case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) => - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1)) - } - - case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) => - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None; - } else { - result = Some((endPC, ifStmt.targetStmt)) - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ => - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ => - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } + + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + true } - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use => - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode ⇒ + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) + } + result.toList } - None - } - - /** - * 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])(implicit state: State): Boolean = { - val field = expr.astID match { - case GetField.ASTID => - val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) None - else expr.asGetField.resolveField(project) - case GetStatic.ASTID => expr.asGetStatic.resolveField(project) - case _ => None + /** + * 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. + */ + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID ⇒ + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + } + + None } - field.contains(state.field) - } - - /** - * 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]] - )(implicit state: State): Boolean = { /** - * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + * 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 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 || - expr.isFloatConst && defaultValue == expr.asFloatConst.value - } + def isReadOfCurrentField(expr: Expr[V])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID ⇒ + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) + case _ ⇒ None + } + field.contains(state.field) } /** - * Checks whether the non-constant expression of the if-Statement is a read of the current - * field. + * 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 isGuardInternal(expr: V): Boolean = { - expr.definedBy forall { index => - if (index < 0) false // If the value is from a parameter, this can not be the guard - else isReadOfCurrentField(code(index).asAssignment.expr) - } + def isGuard( + ifStmt: If[V], + defaultValue: Any, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + + /** + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + */ + 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } + + /** + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. + */ + def isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index ⇒ + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } + } + + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else false } - if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { - isGuardInternal(ifStmt.rightExpr.asVar) - } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { - isGuardInternal(ifStmt.leftExpr.asVar) - } else false - } - - /** - * Checks if 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) => true - case eps => - state.prematurelyReadDependee = Some(eps) - false + /** + * Checks if 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) ⇒ true + case eps ⇒ + state.prematurelyReadDependee = Some(eps) + false + } + + /** + * Checkes 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. + */ + 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 } - /** - * Checkes 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. - */ - 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 - } - - /** - * Checkes if 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 isFinalField( - eop: EOptionP[Field, FieldMutability] - )(implicit state: State): Boolean = eop match { - case LBP(_: FinalField) => - true - case UBP(_: NonFinalField) => false - case _ => - state.fieldMutabilityDependees += eop - true - } + /** + * Checkes if 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 isFinalField( + eop: EOptionP[Field, FieldMutability] + )(implicit state: State): Boolean = eop match { + case LBP(_: FinalField) ⇒ + true + case UBP(_: NonFinalField) ⇒ false + case _ ⇒ + state.fieldMutabilityDependees += eop + true + } } trait L2FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(FieldMutability), - PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(FieldMutability), + PropertyBounds.ub(EscapeProperty) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldMutability) } @@ -1094,16 +1094,16 @@ object EagerL2FieldMutabilityAnalysis extends L2FieldMutabilityAnalysisScheduler 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) - analysis - } + 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) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -1113,18 +1113,18 @@ object LazyL2FieldMutabilityAnalysis extends L2FieldMutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L2FieldMutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - FieldMutability.key, - analysis.determineFieldMutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L2FieldMutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldMutability.key, + analysis.determineFieldMutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 13cf4ebc3f..395e0da63e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -2,7 +2,11 @@ package org.opalj.tac.fpcf.analyses 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.log.LogContext import org.opalj.log.OPALLogger import org.opalj.fpcf.ELBP @@ -122,6 +126,37 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal 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(identifier, classBound, interfaceBound) => ftp + }) + .foreach({ + case FormalTypeParameter(identifier, classBound, interfaceBound) => + classBound match { + case Some( + ClassTypeSignature( + packageIdentifier, + SimpleClassTypeSignature(simpleName, typeArguments), + classTypeSignatureSuffix + ) + ) => { + genericTypeBounds += ((identifier, simpleName)) //+ typeBounds + } + case _ => + } + + }) + }) + genericTypeBounds.toList.foreach({ x => + println("Bound: " + x) + }) + genericTypeBounds + } + def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { e match { case t: ObjectType => @@ -203,7 +238,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal !f.isStatic } var hasShallowImmutableFields = false - var hasDependentImmutableFields = false + var dependentImmutableFields: Set[Option[String]] = Set.empty val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) fieldsPropertyStoreInformation.foreach( f => { @@ -216,10 +251,10 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal return createResultForAllSubtypes(t, MutableClass); } case FinalP(ShallowImmutableField) => hasShallowImmutableFields = true - case FinalEP(f, DependentImmutableField) => { + case FinalEP(f, DependentImmutableField(x)) => { //println("FieldType: "+f.asField.fieldType) //println("FieldTypeSignature: "+f.asField.fieldTypeSignature.get.toJVMSignature) - hasDependentImmutableFields = true + dependentImmutableFields = dependentImmutableFields + x } case FinalP(DeepImmutableField) => case ep @ InterimE(e) => @@ -270,11 +305,11 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability_new = superClassInformation match { case UBP(ShallowImmutableClass) => ShallowImmutableClass //ImmutableContainer - case _ => DeepImmutableClass // ImmutableObject + case _ => DeepImmutableClass // ImmutableObject } - if (hasDependentImmutableFields) { - maxLocalImmutability = DependentImmutableClass //TODO possibly here + if (!dependentImmutableFields.isEmpty) { + maxLocalImmutability = DependentImmutableClass(determineGenericTypeBounds(cf)) //TODO possibly here } else if (hasShallowImmutableFields) { maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } @@ -402,11 +437,11 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal * case FinalP(MutableType_new) ⇒ //MutableType) * Result(t, MutableClass) //TODO check */ - case FinalP(DependentImmutableField) => { + case FinalP(DependentImmutableField(x)) => { println( - ".........................................kkk......................................" + ".........................................kkk......................................: " + x.get ) - maxLocalImmutability = ShallowImmutableClass // DependentImmutableClass //TODO possibly here? + maxLocalImmutability = DependentImmutableClass() // DependentImmutableClass //TODO possibly here? } // Field Mutability related dependencies: @@ -548,7 +583,7 @@ trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { .map(ot => (ot, project.classFile(ot))) .foreach { case (_, Some(cf)) => cfs ::= cf - case (t, None) => + 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 diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index 848733f5aa..0529ed6b85 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -48,215 +48,217 @@ import org.opalj.br.fpcf.properties.TypeImmutability_new * @author Michael Eichberg * @author Tobias Peter Roth */ -class LxTypeImmutabilityAnalysis_new( 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") +class LxTypeImmutabilityAnalysis_new(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 = { + typeExtensibility(t) match { + case Yes | Unknown => + println("Yes Unknown"); Result(t, MutableType_new) // MutableType) + case No => step2(t) } - - /** - * @param t An object type which is not `java.lang.Object`. - */ - def step1( - typeExtensibility: ObjectType ⇒ Answer - )( - t: ObjectType - ): ProperPropertyComputationResult = { - typeExtensibility(t) match { - case Yes | Unknown ⇒ - println("Yes Unknown"); Result(t, MutableType_new) // 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 => + println("c function called") + def apply(eps: SomeEPS): ProperPropertyComputationResult = { + println("EPS: " + eps) + eps match { + case ELUBP(_, lb: ClassImmutability_new, ub: ClassImmutability_new) => + val thisLB = lb.correspondingTypeImmutability + val thisUB = ub.correspondingTypeImmutability + if (eps.isFinal) + Result(t, thisUB) + else + InterimResult(t, thisLB, thisUB, Seq(eps), c) + } + } + } + val resultToMatch = ps(t, ClassImmutability_new.key) + println("Result1: " + resultToMatch) + resultToMatch match { + case x @ FinalP(p) => { + println("x::: " + x.p.correspondingTypeImmutability) + Result(t, p.correspondingTypeImmutability); } - } - - 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 ⇒ - println("c function called") - def apply(eps: SomeEPS): ProperPropertyComputationResult = { - println("EPS: "+eps) - eps match { - case ELUBP(_, lb: ClassImmutability_new, ub: ClassImmutability_new) ⇒ - val thisLB = lb.correspondingTypeImmutability - val thisUB = ub.correspondingTypeImmutability - if (eps.isFinal) - Result(t, thisUB) - else - InterimResult(t, thisLB, thisUB, Seq(eps), c) - } - } - } - val resultToMatch = ps(t, ClassImmutability_new.key) - println("Result1: "+resultToMatch) - resultToMatch match { - case x @ FinalP(p) ⇒ { - println("x::: "+x.p.correspondingTypeImmutability) - Result(t, p.correspondingTypeImmutability); - } - - case eps @ InterimLUBP(lb, ub) ⇒ - val thisUB = ub.correspondingTypeImmutability - val thisLB = lb.correspondingTypeImmutability - InterimResult(t, thisLB, thisUB, Seq(eps), c) - case epk ⇒ { - println("here ") - InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) - } - //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) - } - } else { - var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] - var joinedImmutability: TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." - var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType - - val resultToMatch2 = ps(t, ClassImmutability_new.key) - println("Result2: "+resultToMatch2) - resultToMatch2 match { - case FinalP(DeepImmutableClass) ⇒ //ImmutableObject) => - case FinalP(MutableClass) ⇒ //(_: MutableObject) => - return Result(t, MutableType_new); //MutableType); - case FinalP(ShallowImmutableClass) ⇒ //ImmutableContainer) => - joinedImmutability = ShallowImmutableType // ImmutableContainerType - maxImmutability = ShallowImmutableType //ImmutableContainerType - case FinalP(DependentImmutableClass) ⇒ - joinedImmutability = DependentImmutableType - maxImmutability = DependentImmutableType - - case eps @ InterimLUBP(lb, ub) ⇒ - joinedImmutability = lb.correspondingTypeImmutability - maxImmutability = ub.correspondingTypeImmutability - dependencies += (t -> eps) - - case eOptP ⇒ - joinedImmutability = MutableType_new //MutableType - dependencies += (t -> eOptP) - } - - directSubtypes foreach { subtype ⇒ - ps(subtype, TypeImmutability_new.key) match { - case FinalP(DeepImmutableType) ⇒ //ImmutableType) => - case UBP(MutableType_new) ⇒ //MutableType) => - return Result(t, MutableType_new); //MutableType); - - case FinalP(ShallowImmutableType) ⇒ //ImmutableContainerType) => - joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) - maxImmutability = ShallowImmutableType //ImmutableContainerType - - case FinalP(DependentImmutableType) ⇒ - joinedImmutability = joinedImmutability.meet(DependentImmutableType) - maxImmutability = DependentImmutableType - - case eps @ InterimLUBP(subtypeLB, subtypeUB) ⇒ - joinedImmutability = joinedImmutability.meet(subtypeLB) - maxImmutability = maxImmutability.meet(subtypeUB) - dependencies += ((subtype, eps)) - - case epk ⇒ - joinedImmutability = MutableType_new //MutableType - dependencies += ((subtype, epk)) - } - } + case eps @ InterimLUBP(lb, ub) => + val thisUB = ub.correspondingTypeImmutability + val thisLB = lb.correspondingTypeImmutability + InterimResult(t, thisLB, thisUB, Seq(eps), c) + case epk => { + println("here ") + InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) + } + //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) + } + } else { + var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] + var joinedImmutability + : TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." + var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType + + val resultToMatch2 = ps(t, ClassImmutability_new.key) + println("Result2: " + resultToMatch2) + resultToMatch2 match { + case FinalP(DeepImmutableClass) => //ImmutableObject) => + case FinalP(MutableClass) => //(_: MutableObject) => + return Result(t, MutableType_new); //MutableType); + case FinalP(ShallowImmutableClass) => //ImmutableContainer) => + joinedImmutability = ShallowImmutableType // ImmutableContainerType + maxImmutability = ShallowImmutableType //ImmutableContainerType + case FinalP(DependentImmutableClass(_)) => + joinedImmutability = DependentImmutableType + maxImmutability = DependentImmutableType + + case eps @ InterimLUBP(lb, ub) => + joinedImmutability = lb.correspondingTypeImmutability + maxImmutability = ub.correspondingTypeImmutability + dependencies += (t -> eps) + + case eOptP => + joinedImmutability = MutableType_new //MutableType + dependencies += (t -> eOptP) + } + + directSubtypes foreach { subtype => + ps(subtype, TypeImmutability_new.key) match { + case FinalP(DeepImmutableType) => //ImmutableType) => + case UBP(MutableType_new) => //MutableType) => + return Result(t, MutableType_new); //MutableType); + + case FinalP(ShallowImmutableType) => //ImmutableContainerType) => + joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) + maxImmutability = ShallowImmutableType //ImmutableContainerType + + case FinalP(DependentImmutableType) => + joinedImmutability = joinedImmutability.meet(DependentImmutableType) + maxImmutability = DependentImmutableType + + case eps @ InterimLUBP(subtypeLB, subtypeUB) => + joinedImmutability = joinedImmutability.meet(subtypeLB) + maxImmutability = maxImmutability.meet(subtypeUB) + dependencies += ((subtype, eps)) + + case epk => + joinedImmutability = MutableType_new //MutableType + dependencies += ((subtype, epk)) + } + } + + if (dependencies.isEmpty) { + Result(t, maxImmutability) + } else if (joinedImmutability == maxImmutability) { + // E.g., as soon as one subtype is an ImmutableContainer, we are at most + // ImmutableContainer, even if all other subtype may even be 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 if (joinedImmutability == maxImmutability) { - // E.g., as soon as one subtype is an ImmutableContainer, we are at most - // ImmutableContainer, even if all other subtype may even be immutable! - Result(t, joinedImmutability) + Result(t, maxImmutability) } 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_new ⇒ - joinedImmutability = joinedImmutability.meet(lb) - case lb: ClassImmutability_new ⇒ - joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) - } - else { - joinedImmutability = MutableType_new //MutableType - continue = false - } - } - if (joinedImmutability == maxImmutability) { - assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) - Result(t, maxImmutability) - } else { - InterimResult( - t, - joinedImmutability, - maxImmutability, - dependencies.values, - c - ) - } - } - } - - (eps: @unchecked) match { - case FinalEP(e, DeepImmutableType | DeepImmutableClass) ⇒ //ImmutableType | ImmutableObject) => - dependencies = dependencies - e - nextResult() - - case UBP(x) if (x == MutableType_new || x == MutableClass) ⇒ //MutableType | _: MutableObject) => - Result(t, MutableType_new) //MutableType) - - case FinalEP(e, x) if (x == ShallowImmutableType || x == ShallowImmutableClass) ⇒ //ImmutableContainerType | ImmutableContainer) => - maxImmutability = ShallowImmutableType //ImmutableContainerType - dependencies = dependencies - e - nextResult() - case FinalEP(e, x) if (x == DependentImmutableClass || x == DependentImmutableType) ⇒ { - maxImmutability = DependentImmutableType - dependencies = dependencies - e - nextResult() - } - case eps @ InterimEUBP(e, subtypeP) ⇒ - dependencies = dependencies.updated(e, eps) - subtypeP match { - case subtypeP: TypeImmutability_new ⇒ - maxImmutability = maxImmutability.meet(subtypeP) - case subtypeP: ClassImmutability_new ⇒ - maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) - } - nextResult() - } + 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_new => + joinedImmutability = joinedImmutability.meet(lb) + case lb: ClassImmutability_new => + joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) + } else { + joinedImmutability = MutableType_new //MutableType + continue = false } - InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) + } + if (joinedImmutability == maxImmutability) { + assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) + Result(t, maxImmutability) + } else { + InterimResult( + t, + joinedImmutability, + maxImmutability, + dependencies.values, + c + ) + } + } + } + + (eps: @unchecked) match { + case FinalEP(e, DeepImmutableType | DeepImmutableClass) => //ImmutableType | ImmutableObject) => + dependencies = dependencies - e + nextResult() + + case UBP(x) + if (x == MutableType_new || x == MutableClass) => //MutableType | _: MutableObject) => + Result(t, MutableType_new) //MutableType) + + case FinalEP(e, x) + if (x == ShallowImmutableType || x == ShallowImmutableClass) => //ImmutableContainerType | ImmutableContainer) => + maxImmutability = ShallowImmutableType //ImmutableContainerType + dependencies = dependencies - e + nextResult() + case FinalEP(e, x) if (x == DependentImmutableClass || x == DependentImmutableType) => { + maxImmutability = DependentImmutableType + dependencies = dependencies - e + nextResult() } + case eps @ InterimEUBP(e, subtypeP) => + dependencies = dependencies.updated(e, eps) + subtypeP match { + case subtypeP: TypeImmutability_new => + maxImmutability = maxImmutability.meet(subtypeP) + case subtypeP: ClassImmutability_new => + maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) + } + nextResult() + } } + InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) + } } + } } trait TypeImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) } @@ -269,40 +271,40 @@ object EagerLxTypeImmutabilityAnalysis_new extends TypeImmutabilityAnalysisScheduler_new with BasicFPCFEagerAnalysisScheduler { - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val typeExtensibility = project.get(TypeExtensibilityKey) - val analysis = new LxTypeImmutabilityAnalysis_new(project) - val allClassFilesIterator = project.allClassFiles.iterator - val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty - ps.scheduleEagerComputationsForEntities(types) { - analysis.step1(typeExtensibility) - } + override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val typeExtensibility = project.get(TypeExtensibilityKey) + val analysis = new LxTypeImmutabilityAnalysis_new(project) + val allClassFilesIterator = project.allClassFiles.iterator + val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) - analysis + ps.scheduleEagerComputationsForEntities(types) { + analysis.step1(typeExtensibility) } + + analysis + } } object LazyLxTypeImmutabilityAnalysis_new extends TypeImmutabilityAnalysisScheduler_new 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 LxTypeImmutabilityAnalysis_new(p) - val analysisRunner: ProperPropertyComputation[Entity] = - analysis.doDetermineTypeImmutability_new(typeExtensibility) - ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) - analysis - } + 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 LxTypeImmutabilityAnalysis_new(p) + val analysisRunner: ProperPropertyComputation[Entity] = + analysis.doDetermineTypeImmutability_new(typeExtensibility) + ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) + analysis + } } From 2c5652c9d1d037f64461f0a5b0b9b48578896362 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 11 Dec 2019 12:10:12 +0100 Subject: [PATCH 063/327] works with the current test suite for reference immutability without double checked locking with lazy instantiation --- .../L0ReferenceImmutabilityAnalysis.scala | 1915 ++++++++--------- 1 file changed, 917 insertions(+), 998 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index c2ad3ac191..c220646e22 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -83,206 +83,206 @@ import org.opalj.value.ValueInformation class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - case class State( - field: Field, - var referenceImmutability: ReferenceImmutability = ImmutableReference, - 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, - var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, - var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty - ) { - def hasDependees: Boolean = { - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || - escapeDependees.nonEmpty || tacDependees.nonEmpty - } - - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) - } + case class State( + field: Field, + var referenceImmutability: ReferenceImmutability = ImmutableReference, + 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, + var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty } - 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) - - def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field ⇒ determineReferenceImmutability(field) - case _ ⇒ - val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) } + } + + 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) + + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field => determineReferenceImmutability(field) + case _ => + val m = entity.getClass.getSimpleName + " is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + + /** + * Analyzes the mutability of private non-final fields. + * + * 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 determineReferenceImmutability( + field: Field + ): ProperPropertyComputationResult = { + implicit val state: State = State(field) + println( + "field.isPrematurelyRead " + isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key)) + ) + // Fields are not final if they are read prematurely! + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) + return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); + println("field.isFinal " + field.isFinal) + if (field.isFinal) + return createResult(); + + state.referenceImmutability = ImmutableReference //EffectivelyFinalField + + val thisType = field.classFile.thisType + + if (field.isPublic) + return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) + + // 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) { + if (!closedPackages.isClosed(thisType.packageName)) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } + + val classesHavingAccess: Iterator[ClassFile] = + if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + 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))) + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } /** - * Analyzes the mutability of private non-final fields. - * - * 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. + * Returns the TACode for a method if available, registering dependencies as necessary. */ - private[analyses] def determineReferenceImmutability( - field: Field - ): ProperPropertyComputationResult = { - implicit val state: State = State(field) - println( - "field.isPrematurelyRead "+isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key)) - ) - // Fields are not final if they are read prematurely! - if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) - return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); - println("field.isFinal "+field.isFinal) - if (field.isFinal) - return createResult(); - - state.referenceImmutability = ImmutableReference //EffectivelyFinalField - - val thisType = field.classFile.thisType - - if (field.isPublic) - return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) - - // 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) { - if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - } - project.classesPerPackage(thisType.packageName) - } else { - Set(field.classFile) - } - - val classesHavingAccess: Iterator[ClassFile] = - if (field.isProtected) { - if (typeExtensibility(thisType).isYesOrUnknown) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - } - 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))) - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - - // 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 - // of the class after the reference to the class and (an indirect) reference to the - // field has become available. Consider the following example: - // class X implements Y{ - // - // private Object o; - // - // public Object getO() { return o; } - // - // private static X instance; - // static { - // instance = new X(); - // Z.register(instance); - // // when we reach this point o is now (via getO) publically accessible and - // // X is properly initialized! - // o = new Object(); // o is mutated... - // } - // } - - /** - * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) - None - } - } + 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 eps: InterimEP[Method, TACAI] => + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk => + state.tacDependees += method -> ((epk, pcs)) + None + } + } - for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode ← getTACAI(method, pcs) - } { - if (methodUpdatesField(method, taCode, pcs)) { - println("method updates field: true") - return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); - } else - println("method updates field: false") + for { + (method, pcs) <- fieldAccessInformation.writeAccesses(field) + taCode <- getTACAI(method, pcs) + } { + if (methodUpdatesField(method, taCode, pcs)) { + println("method updates field: true") + return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); + } else + println("method updates field: false") - } - - if (state.lazyInitInvocation.isDefined) { - val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - handleCalls(calleesEOP) - } - - createResult() } - def handleCalls( - calleesEOP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: State - ): Boolean = { - calleesEOP match { - case FinalP(callees) ⇒ - state.calleesDependee = None - handleCallees(callees) - case InterimUBP(callees) ⇒ - state.calleesDependee = Some(calleesEOP) - handleCallees(callees) - case _ ⇒ - state.calleesDependee = Some(calleesEOP) - false - } + if (state.lazyInitInvocation.isDefined) { + val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + handleCalls(calleesEOP) } - def handleCallees(callees: Callees)(implicit state: State): Boolean = { - val pc = state.lazyInitInvocation.get._2 - if (callees.isIncompleteCallSite(pc)) { - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else { - val targets = callees.callees(pc).toTraversable - if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else false - } + createResult() + } + + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: State + ): Boolean = { + calleesEOP match { + case FinalP(callees) => + state.calleesDependee = None + handleCallees(callees) + case InterimUBP(callees) => + state.calleesDependee = Some(calleesEOP) + handleCallees(callees) + case _ => + state.calleesDependee = Some(calleesEOP) + false } + } + + def handleCallees(callees: Callees)(implicit state: State): Boolean = { + val pc = state.lazyInitInvocation.get._2 + if (callees.isIncompleteCallSite(pc)) { + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else { + val targets = callees.callees(pc).toTraversable + if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else false + } + } - /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. - */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field * The code below can be used to identify such value, but is not yet adapted to using the * TACAI property */ - /* + /* var constantVal: Option[Any] = None var allInitializeConstant = true @@ -340,854 +340,773 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } constantVal */ + } + + /** + * 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 MutableReference)) //NonFinalFieldByAnalysis)) + InterimResult( + state.field, + MutableReference, //NonFinalFieldByAnalysis, + 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 = { + val isNotFinal = eps.pk match { + case EscapeProperty.key => + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + methodUpdatesField(method, newEP.ub.tac.get, pcs) + case Callees.key => + handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key => + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key => + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + isNonDeterministic(newEP) + case ReferenceImmutability.key => + val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + !isImmutableReference(newEP) } - /** - * 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 MutableReference)) //NonFinalFieldByAnalysis)) - InterimResult( - state.field, - MutableReference, //NonFinalFieldByAnalysis, - state.referenceImmutability, - state.dependees, - c - ) - else - Result(state.field, state.referenceImmutability) + if (isNotFinal) + Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) + else + createResult() + } + + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + )(implicit state: State): Boolean = { + val write = code(writeIndex).asFieldWriteAccessStmt + + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + return false; } - /** - * 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 = { - val isNotFinal = eps.pk match { - case EscapeProperty.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - 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)) - methodUpdatesField(method, newEP.ub.tac.get, pcs) - case Callees.key ⇒ - handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key ⇒ - isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] - state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) - isNonDeterministic(newEP) - case ReferenceImmutability.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] - state.referenceImmutabilityDependees = - state.referenceImmutabilityDependees.filter(_.e ne newEP.e) - !isImmutableReference(newEP) - } - - if (isNotFinal) - Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) - else - createResult() + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs => (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method } - /** - * Checks whether a field write may be a lazy initialization. - * - * We only consider simple cases of lazy initialization where the single field write is guarded - * so it is executed only if the field still has its default value and where the written value - * is guaranteed to be the same even if the write is executed multiple times (may happen because - * of the initializing method being executed more than once on concurrent threads). - * - * Also, all field reads must be used only for a guard or their uses have to be guarded - * themselves. - */ - def isLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - )(implicit state: State): Boolean = { - val write = code(writeIndex).asFieldWriteAccessStmt - - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - // Only handle lazy initialization of ints and floats as they are guaranteed to be - // written atomically - return false; - } - - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return false; // Reads outside the (single) lazy initialization method - } - - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val (guardIndex, guardedIndex, readIndex) = - findGuard(writeIndex, defaultValue, code, cfg) match { - case Some((guard, guarded, read)) ⇒ (guard, guarded, read) - case None ⇒ return false; - } - - // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) - return false; - - println( - "Check writes: "+checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg) - ) - // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - return false; - - // Field reads (except for the guard) may only be executed if the field's value is not the - // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - return false; - - true - } - - def checkWrites( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val definitions = write.value.asVar.definedBy - - val isDeterministic = - if (definitions.size == 1) { - // The value written must be computed deterministically - checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) - } else { - // More than one definition site for the value might lead to differences between - // invocations, but not if this method has no parameters and is deterministic - // (in this case, the definition reaching the write will always be the same) - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - - // The field write must be guarded correctly - isDeterministic && - checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - } - - /** - * Checks if the field write for a lazy initialization is immediately followed by a return of - * the written value (possibly loaded by another field read). - */ - def checkImmediateReturn( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 - - var load = -1 - - while (index < code.length) { - val stmt = code(index) - stmt.astID match { - case Assignment.ASTID ⇒ - if (isReadOfCurrentField(stmt.asAssignment.expr)) - load = index - else - return false; // No field read or a field read of a different field - case ReturnValue.ASTID ⇒ - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) - return true; // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) - return true; // return of field value loaded by field read - else - return false; // return of different value - case _ ⇒ return false; // neither a field read nor a return - } - index += 1 - } - false - } - - def lazyInitializerIsDeterministic( - method: Method, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( - propertyStore(declaredMethods(method), Purity.key) - )) - println("paramsCount: "+method.descriptor.parametersCount == 0) - println( - "isnondeterministic-result: "+isNonDeterministic( - propertyStore(declaredMethods(method), Purity.key) - ).toString - ) - println(propertyStore(declaredMethods(method), Purity.key)) - println("lazyInitializerIsDeterministic: "+result) - result + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) => (guard, guarded, read) + case None => return false; + } + + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) + return false; + + println( + "Check writes: " + checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg) + ) + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + + true + } + + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val definitions = write.value.asVar.definedBy + + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + // The field write must be guarded correctly + isDeterministic && + checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID => + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID => + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ => return false; // neither a field read nor a return + } + index += 1 } - - /** - * 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 = { - println("xxxxxx") - val field = state.field - val stmts = taCode.stmts - for (pc ← pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID ⇒ - if (method.isInitializer) { - if (field.isStatic) { - if (method.isConstructor) - return true; - } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - 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.referenceImmutability == LazyInitializedReference) //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; - - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - // A field written outside an initializer must be lazily - // initialized or it is non-final - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - ) /**&& !isDoubleCheckedLocking( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - )**/ ) - return true; - - state.referenceImmutability = LazyInitializedReference //LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - // 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. - return true; - } - } - case _ ⇒ throw new RuntimeException("unexpected field access"); - } + false + } + + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + )) + println("paramsCount: " + method.descriptor.parametersCount == 0) + println( + "isnondeterministic-result: " + isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + ).toString + ) + println(propertyStore(declaredMethods(method), Purity.key)) + println("lazyInitializerIsDeterministic: " + result) + result + } + + /** + * 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 = { + println("xxxxxx") + val field = state.field + val stmts = taCode.stmts + for (pc <- pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID => + if (method.isInitializer) { + if (field.isStatic) { + if (method.isConstructor) + return true; } else { - // nothing to do as the put field is dead + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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.referenceImmutability == LazyInitializedReference) //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; + + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + // A field written outside an initializer must be lazily + // initialized or it is non-final + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )) + return true; + + state.referenceImmutability = LazyInitializedReference //LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ => throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead } - false + } } - - /** - * 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 eps: InterimEP[Method, TACAI] ⇒ - * state.tacDependees += method → ((eps, pcs)) - * eps.ub.tac - * case epk ⇒ - * state.tacDependees += method → ((epk, pcs)) - * None - * } - * } - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - /** - * 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 // Must be locally created - else { - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - handleEscapeProperty(escape) - } - } + false + } + + /** + * 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 eps: InterimEP[Method, TACAI] ⇒ + * state.tacDependees += method → ((eps, pcs)) + * eps.ub.tac + * case epk ⇒ + * state.tacDependees += method → ((epk, pcs)) + * None + * } + * } + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) } + } } - - /** - * 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 = 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 + } + + /** + * 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 = 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 + } + + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt => + stmt.astID == CaughtException.ASTID + } flatMap { exception => + exception.asCaughtException.origins.map { origin: Int => + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator } - /** - * Checks if the field write is only executed if the field's value was still the default value. - * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization - * and the field write. - */ - def checkWriteIsGuarded( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val startBB = cfg.bb(writeIndex).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt ⇒ - stmt.astID == CaughtException.ASTID - } flatMap { exception ⇒ - exception.asCaughtException.origins.map { origin: Int ⇒ - if (isImmediateVMException(origin)) - pcOfImmediateVMException(origin) - else - pcOfMethodExternalException(origin) - }.iterator - } + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail + val startPC = curBB.startPC + val endPC = curBB.endPC - val startPC = curBB.startPC - val endPC = curBB.endPC + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement - if (startPC == 0 || startPC == guardedIndex) - return false; // Reached method start or wrong branch of guarding if-Statement + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors + true + } + + /** + * 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]] + )(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(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) => + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ => // Unknown field + false + }) } + } - true + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) } - /** - * 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]] - )(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(defSites.head).asAssignment.expr.isConst) true - else { - val expr = code(index).asAssignment.expr - expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ ⇒ // Unknown field - false - }) - } - } + val value = origin.expr - defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID => + value.asFieldRead.resolveField(p) match { + case Some(field) => + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ => // Unknown field + false } - - val value = origin.expr - - val isNonConstDeterministic = value.astID match { - case GetStatic.ASTID | GetField.ASTID ⇒ - value.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ ⇒ // Unknown field - false - } - 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.exists(!isConstant(_))) { - false - } else { - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true - } - 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, code) + 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.exists(!isConstant(_))) { + false + } else { + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true } - - value.isConst || isNonConstDeterministic + 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, code) } - /** - * Checks that all non-dead field reads that are not used for the guarding if-Statement of a - * lazy initialization are only executed if the field did not have its default value or after - * the (single) field write. - */ - def checkReads( - reads: Seq[(Method, PCs)], - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - ): Boolean = { - // There is only a single method with reads aside from initializers (checked by - // isLazilyInitialized), so we have to check only reads from that one method. - reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ - val index = pcToIndex(readPC) - index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) - } + value.isConst || isNonConstDeterministic + } + + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC => + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) } - - /** - * Checks that a field read is only executed if the field did not have its default value or - * after the (single) field write. - */ - def checkRead( - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Boolean = { - val startBB = cfg.bb(readIndex).asBasicBlock - val writeBB = cfg.bb(writeIndex) - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - - if (startPC == 0) - return false; // Reached the start of the method but not the guard or field write - - if ((curBB eq writeBB) && writeIndex > readIndex) - return false; // In the basic block of the write, but before the write - - if (startPC != guardedIndex && // Did not reach the guard - (curBB ne writeBB) /* Not the basic block of the write */ ) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - true + } + + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } } - /** - * Gets all predecessor BasicBlocks of a CFGNode. - */ - def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode ⇒ - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) - } - result.toList + true + } + + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode => + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) } - - /** - * 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. - */ - def findGuard( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - ): Option[(Int, Int, Int)] = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int)] = None - println("worklist initial: "+worklist) - while (worklist.nonEmpty) { - println("worklist "+worklist) - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID ⇒ - val ifStmt = cfStmt.asIf - println("ifstmt: "+ifStmt) - println("curBB: "+curBB) - println("startBB: "+startBB) - println("result.isDefined "+result.isDefined) - println("result: "+result) - ifStmt.condition match { - case EQ if curBB != startBB ⇒ //&& isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1)) - } - - case NE if curBB != startBB ⇒ //&& isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None; - } else { - result = Some((endPC, ifStmt.targetStmt)) - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard - } - - None + result.toList + } + + /** + * 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. + */ + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID => + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) => + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) => + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ => + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ => + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } } - /** - * 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])(implicit state: State): Boolean = { - val field = expr.astID match { - case GetField.ASTID ⇒ - val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) None - else expr.asGetField.resolveField(project) - case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) - case _ ⇒ None - } - field.contains(state.field) + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use => + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard } - /** - * 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]] - )(implicit state: State): Boolean = { - - /** - * Checks if an expression is an IntConst or FloatConst with the corresponding default value. - */ - 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 || - expr.isFloatConst && defaultValue == expr.asFloatConst.value - } - } - - /** - * Checks whether the non-constant expression of the if-Statement is a read of the current - * field. - */ - def isGuardInternal(expr: V): Boolean = { - expr.definedBy forall { index ⇒ - if (index < 0) false // If the value is from a parameter, this can not be the guard - else isReadOfCurrentField(code(index).asAssignment.expr) - } - } - - if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { - isGuardInternal(ifStmt.rightExpr.asVar) - } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { - isGuardInternal(ifStmt.leftExpr.asVar) - } else - isGuardInternal(ifStmt.leftExpr.asVar) || isGuardInternal(ifStmt.rightExpr.asVar) // || false //TODO + None + } + + /** + * 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])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID => + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID => expr.asGetStatic.resolveField(project) + case _ => None } + field.contains(state.field) + } + + /** + * 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]] + )(implicit state: State): Boolean = { /** - * Checks if the field is prematurely read, i.e. read before it is initialized in the - * constructor, using the corresponding property. + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. */ - def isPrematurelyRead( - eop: EOptionP[Field, FieldPrematurelyRead] - )(implicit state: State): Boolean = - eop match { - case LBP(NotPrematurelyReadField) ⇒ - state.prematurelyReadDependee = None - false - case UBP(PrematurelyReadField) ⇒ true - case eps ⇒ - state.prematurelyReadDependee = Some(eps) - false - } + 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } /** - * Checkes 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. + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. */ - 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 isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index => + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } } - /** - * Checkes if 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, ReferenceImmutability] - )(implicit state: State): Boolean = eop match { - case FinalEP(e, ImmutableReference) ⇒ true - case FinalEP(e, MutableReference) ⇒ false - // - case LBP(ImmutableReference) ⇒ true - case UBP(MutableReference) ⇒ false - - /** - * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ - * - * true - * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * - */ - case _ ⇒ - state.referenceImmutabilityDependees += eop - true + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else false + } + + /** + * Checks if 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) => true + case eps => + state.prematurelyReadDependee = Some(eps) + false } - //-------------------------------------------------- double checked locking - def isDoubleCheckedLocking( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - )(implicit state: State): Boolean = { - //val a = state.field - //val write = code(writeIndex).asFieldWriteAccessStmt - //println("Purity Information: "+propertyStore(declaredMethods(method), Purity.key)) - //println(isNonDeterministic(propertyStore(declaredMethods(method), Purity.key))) - if (!method.isStatic || !state.field.isStatic) { - false - } else { - //----------------------------- - /** - * val reads = fieldAccessInformation.readAccesses(state.field) - * if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - * return false; // Reads outside the (single) lazy initialization method - * } - * - * // There must be a guarding if-Statement - * // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - * // first statement that is executed after the if-Statement if the field's value was not the - * // default value - * val (guardIndex, guardedIndex, readIndex) = - * findGuard(writeIndex, defaultValue, code, cfg) match { - * case Some((guard, guarded, read)) ⇒ (guard, guarded, read) - * case None ⇒ return false; - * } - * // Detect only simple patterns where the lazily initialized value is returned immediately - * // if (!checkImmediateReturn(write, writeIndex, readIndex, code)) - * // return false; - * - * println( - * "Check writes (is double checked locking): "+checkWrites( - * write, - * writeIndex, - * guardIndex, - * guardedIndex, - * method, - * code, - * cfg - * ) - * ) - * // The value written must be computed deterministically and the writes guarded correctly - * if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - * return false; - * - * // Field reads (except for the guard) may only be executed if the field's value is not the - * // default value - * if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - * return false; - * - * true - */ - //----------------------------- - - //} + /** + * Checkes 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. + */ + 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 + } + + /** + * Checkes if 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, ReferenceImmutability] + )(implicit state: State): Boolean = eop match { + case FinalEP(e, ImmutableReference) => true + case FinalEP(e, MutableReference) => false + // + case LBP(ImmutableReference) => true + case UBP(MutableReference) => false - false - } - } - //-------------------------------------------------- double checked locking + /** + * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ + * true + * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * + */ + case _ => + state.referenceImmutabilityDependees += eop + true + } } + trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.ub(EscapeProperty) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) } @@ -1198,16 +1117,16 @@ object EagerL0ReferenceImmutabilityAnalysis extends L0ReferenceImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -1217,18 +1136,18 @@ object LazyL0ReferenceImmutabilityAnalysis extends L0ReferenceImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - ReferenceImmutability.key, - analysis.determineReferenceImmutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + ReferenceImmutability.key, + analysis.determineReferenceImmutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } From 491a0b1f1fcffb2293123712514f01eded2a5fa6 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 18 Dec 2019 14:28:46 +0100 Subject: [PATCH 064/327] formatting, optimization and removal of printlns in tests and matcher --- .../opalj/fpcf/ClassImmutabilityTests.scala | 4 - .../opalj/fpcf/FieldImmutabilityTests.scala | 112 +++++++++--------- .../fpcf/ReferenceImmutabilityTests.scala | 100 ++++++++-------- .../ClassImmutabilityMatcher.scala | 5 +- .../FieldImmutabilityMatcher.scala | 6 +- 5 files changed, 110 insertions(+), 117 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala index eaad2963d4..3edc1ed830 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -51,16 +51,12 @@ class ClassImmutabilityTests extends PropertiesTest { EagerLxClassImmutabilityAnalysis_new ) ) - - println(2) as.propertyStore.shutdown() - println(3) validateProperties( as, classFilesWithAnnotations(as.project).map(tp => (tp._1.thisType, tp._2, tp._3)), Set("ClassImmutability_new") ) - println(4) } /** * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { 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 index 430b923e33..944ba41571 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -8,13 +8,13 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.Project import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** @@ -22,64 +22,62 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class FieldImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) - } - p.get(RTACallGraphKey) + 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")) - } + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } - describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { - val as = executeAnalyses( - Set( - LazyL2FieldMutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - EagerL0FieldImmutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new - // LazySimpleEscapeAnalysis, - // TACAITransformer - ) - ) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ + describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + val as = executeAnalyses( + Set( + LazyTypeImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index 3e5b2ea958..5cb5df6328 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -18,58 +18,58 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class ReferenceImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) - } - p.get(RTACallGraphKey) + 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("ReferenceImmutability")) - } + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + } - describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { - val as = executeAnalyses( - Set( - EagerL0ReferenceImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis - ) - ) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ + describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { + val as = executeAnalyses( + Set( + EagerL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala index fac97feb4c..4a15bb6dcd 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala @@ -46,9 +46,8 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) ): Option[String] = { if (!properties.exists(p ⇒ { p match { - case DependentImmutableClass(_) if property == DependentImmutableClass() ⇒ true - case _ if p == property ⇒ true - case _ ⇒ false + case DependentImmutableClass(_) ⇒ property == DependentImmutableClass() + case _ ⇒ p == property } })) { //p == property)) { // ... when we reach this point the expected property was not found. diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala index 5d27f81213..1871e2971d 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala @@ -44,9 +44,9 @@ class FieldImmutabilityMatcher(val property: FieldImmutability) extends Abstract ): Option[String] = { if (!properties.exists(p ⇒ { p match { - case DependentImmutableField(_) if property == DependentImmutableField() ⇒ true - case _ if p == property ⇒ true - case _ ⇒ false + case DependentImmutableField(_) ⇒ property == DependentImmutableField() //TODO optimize + case _ ⇒ p == property + } })) { //p == property)) { // ... when we reach this point the expected property was not found. From 52fdc86549efbc6fff55553eed5594f40e2ef80f Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 18 Dec 2019 14:32:11 +0100 Subject: [PATCH 065/327] removed printlns in ref imm analysis --- .../L0ReferenceImmutabilityAnalysis.scala | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index c220646e22..95da9cdc5d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -132,13 +132,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec field: Field ): ProperPropertyComputationResult = { implicit val state: State = State(field) - println( - "field.isPrematurelyRead " + isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key)) - ) // Fields are not final if they are read prematurely! if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); - println("field.isFinal " + field.isFinal) if (field.isFinal) return createResult(); @@ -224,10 +220,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec taCode <- getTACAI(method, pcs) } { if (methodUpdatesField(method, taCode, pcs)) { - println("method updates field: true") return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); - } else - println("method updates field: false") + } } @@ -445,9 +439,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (!checkImmediateReturn(write, writeIndex, readIndex, code)) return false; - println( - "Check writes: " + checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg) - ) // The value written must be computed deterministically and the writes guarded correctly if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) return false; @@ -535,14 +526,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( propertyStore(declaredMethods(method), Purity.key) )) - println("paramsCount: " + method.descriptor.parametersCount == 0) - println( - "isnondeterministic-result: " + isNonDeterministic( - propertyStore(declaredMethods(method), Purity.key) - ).toString - ) - println(propertyStore(declaredMethods(method), Purity.key)) - println("lazyInitializerIsDeterministic: " + result) result } @@ -555,7 +538,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec taCode: TACode[TACMethodParameter, V], pcs: PCs )(implicit state: State): Boolean = { - println("xxxxxx") val field = state.field val stmts = taCode.stmts for (pc <- pcs) { From 371e8b28263e32dbb9eee3757f030306d77c8f36 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 18 Dec 2019 14:49:26 +0100 Subject: [PATCH 066/327] optimization of dependent immutability. Introducing the recognition of generic classes where the a field with a generic types is influenced by the generic type of the generic class. --- .../FieldImmutabilityAnalysisDemo.scala | 21 --- .../ClassWithGenericField_deep.java | 46 ++++++ .../ClassWithGenericField_mutable.java | 24 +++ .../ClassWithGenericField_shallow.java | 18 +++ .../DependentClassWithGenericField_deep1.java | 31 ++++ .../DependentClassWithGenericField_deep2.java | 25 +++ .../L0FieldImmutabilityAnalysis.scala | 152 ++++++++++++------ .../LxClassImmutabilityAnalysis_new.scala | 152 ++++-------------- .../LxTypeImmutabilityAnalysis_new.scala | 9 +- 9 files changed, 279 insertions(+), 199 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_mutable.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep1.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep2.java 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 index dfc0b5aed7..1efd2e07a6 100644 --- 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 @@ -50,19 +50,6 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { analysesManager.project.get(RTACallGraphKey) val (propertyStore, _) = analysesManager.runAll( - /** - * LazyTypeImmutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyClassImmutabilityAnalysis, - * LazyL0ReferenceImmutabilityAnalysis, - * LazyL0PurityAnalysis, - * LazyL1FieldMutabilityAnalysis, - * LazySimpleEscapeAnalysis, - * TACAITransformer, - * LazyLxTypeImmutabilityAnalysis_new, - * EagerL0FieldImmutabilityAnalysis* - */ - LazyLxClassImmutabilityAnalysis_new, LazyTypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, @@ -92,14 +79,6 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .toString() + "\n" + propertyStore .entities(FieldImmutability.key) - /** - * .entities(x ⇒ { - * x.asEPS.pk match { - * case DependentImmutableField(_) ⇒ true - * case _ ⇒ false - * } - * })* - */ .toList .toString } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_deep.java new file mode 100644 index 0000000000..d549417ad5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_deep.java @@ -0,0 +1,46 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public final class ClassWithGenericField_deep { + @DeepImmutableFieldAnnotation("deep imm field") + @ImmutableReferenceAnnotation("eff imm ref") + private Generic_class2 gc = + new Generic_class2 + (new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2()); +} + + +final class Generic_class2 { + + private T1 t1; + + private T2 t2; + + private T3 t3; + + private T4 t4; + + private T5 t5; + + public Generic_class2(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + } + +} + +final class FinalEmptyClass2 { + +} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_mutable.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_mutable.java new file mode 100644 index 0000000000..d550757bce --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_mutable.java @@ -0,0 +1,24 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +import org.opalj.fpcf.properties.type_mutability.MutableType; + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +public final class ClassWithGenericField_mutable { + @MutableFieldAnnotation("deep imm field") + @MutableReferenceAnnotation("eff imm ref") + public Generic_class1 gc = + new Generic_class1 + (new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); +} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_shallow.java new file mode 100644 index 0000000000..2edd749fea --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_shallow.java @@ -0,0 +1,18 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; + +@ShallowImmutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +public final class ClassWithGenericField_shallow { + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Generic_class1 gc = + new Generic_class1 + (new TrivialMutableClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); +} + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep1.java new file mode 100644 index 0000000000..b88188748b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep1.java @@ -0,0 +1,31 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class DependentClassWithGenericField_deep1 { + + @DependentImmutableFieldAnnotation(value = "", genericString = "T1") + @ImmutableReferenceAnnotation("") + private T1 t1; + + @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") + @ImmutableReferenceAnnotation("eff imm ref") + private Generic_class1 gc; + + public DependentClassWithGenericField_deep1(T1 t1) { + this.t1 = t1; + gc = new Generic_class1 + (t1, new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); + } + + +} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep2.java new file mode 100644 index 0000000000..b12357c698 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep2.java @@ -0,0 +1,25 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class DependentClassWithGenericField_deep2 { + + @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") + @ImmutableReferenceAnnotation("eff imm ref") + private Generic_class1 gc; + + public DependentClassWithGenericField_deep2(T1 t1) { + gc = new Generic_class1 + (t1, new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); + } + +} + + + diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 79b8f1f129..fbd8cf1f3f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -1,9 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.br.fpcf.analyses +import org.opalj.br.ClassTypeSignature import org.opalj.br.Field import org.opalj.br.FieldType import org.opalj.br.ObjectType +import org.opalj.br.ProperTypeArgument +import org.opalj.br.SimpleClassTypeSignature import org.opalj.br.TypeVariableSignature import org.opalj.br.analyses.FieldAccessInformationKey import org.opalj.br.analyses.SomeProject @@ -30,6 +33,7 @@ import org.opalj.br.fpcf.properties.TypeImmutability_new 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.ProperPropertyComputationResult @@ -41,8 +45,9 @@ import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS import org.opalj.tac.fpcf.properties.TACAI -case class State() { - var typeImmutability: Option[Boolean] = None +case class State(f: Field) { + var field: Field = f + var typeImmutability: Option[Boolean] = Some(true) var referenceImmutability: Option[Boolean] = None var depencenciesTypes: scala.collection.mutable.Set[ObjectType] = scala.collection.mutable.Set[ObjectType]() @@ -78,42 +83,87 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) field: Field ): ProperPropertyComputationResult = { - println("Field Attributes: " + field.asField.attributes.toList.collectFirst({ - case TypeVariableSignature(t) => t - })) - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + def determineGenericFieldImmutability(state: State): Option[Boolean] = { + println( + "determine generic field imm-------------------------------------------------------------------" + ) + var genericFields: Set[String] = Set.empty + state.field.fieldTypeSignature.head match { + case ClassTypeSignature(_, SimpleClassTypeSignature(name, typeArguments), _) => { + typeArguments.foreach( + ta => { + ta match { + case ProperTypeArgument( + varianceIndicator, + ClassTypeSignature( + packageIdentifier1, + SimpleClassTypeSignature( + packageIdentifier2, + typeArguments2 + ), + _ + ) + ) => { + genericFields += packageIdentifier2 + } + case _ => + } + + } + ) + } + case _ => + } + genericFields.foreach(f => println("generic Field: " + f)) + //state.typeImmutability = Some(true) + + genericFields.toList.foreach(s => { + val objectType = ObjectType(s) + val result = propertyStore(objectType, TypeImmutability_new.key) + println("Result generic field with objtype: " + objectType + " result: " + result) + result match { + case FinalP(DeepImmutableType) => + case FinalP(ShallowImmutableType | MutableType_new) => { + return Some(false); //state.typeImmutability = Some(false) + } + case ep @ _ => { + dependencies += ep + } + } + }) + if (dependencies.size > 0) None; + else Some(true); + } def handleTypeImmutability(objectType: FieldType)(state: State): Option[Boolean] = { if (objectType.isArrayType) return Some(true); //TODO if (objectType.isBaseType) return Some(true); val result = propertyStore(objectType, TypeImmutability_new.key) - println("result: " + result) + println("Result: " + result) result match { - case FinalEP(e, DeepImmutableType) => { //ImmutableType || t == ImmutableContainerType) ⇒ { - println("has deep immutable type") + case FinalEP(e, DeepImmutableType) => { return Some(true); } - case FinalEP(e, DependentImmutableType) => { - println("has dependent immutable type") + case FinalEP(f, DependentImmutableType) => { + println(f + " has dependent imm type") //TODO under construction - state.dependentTypeImmutability = Some(true) - return Some(false); + //--------------------------------------------------------------------------------------- + state.typeImmutability = determineGenericFieldImmutability(state) + //--------------------------------------------------------------------------------------- + //state.dependentTypeImmutability = Some(true) + return state.typeImmutability; } case FinalEP(e, ShallowImmutableType) => { - println("has shallow immutable type") return Some(false); //TODO mindstorm if this approch is appropriate } case FinalEP(e, t) if (t == MutableType_new) => { //MutableType) ⇒ { - println("has mutable type") return Some(false); } case x @ _ => { dependencies += x - println(x) - println("immutability of type couldn't be determined") - return None; + return None; //TODO check!!!!! None; } } } @@ -145,46 +195,64 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) propertyStore(field, ReferenceImmutability.key) match { case FinalEP(_, MutableReference) => { - println("has mutable reference") Some(false) } case FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO - println("has immutable Reference") Some(true) } case x @ _ => { dependencies += x - println(x) - println("immutability of reference couldn't be determined") None } } } - val state: State = new State() + val state: State = new State(field) def createResult(state: State): ProperPropertyComputationResult = { - println("reference Immutability: " + state.referenceImmutability) - println("type immutabiliy: " + state.typeImmutability) - println("dependent immutability: " + state.dependentTypeImmutability) - state.referenceImmutability match { case Some(false) => Result(field, MutableField) case Some(true) => { //If the field type is object. It is a generic field //Test Dependent... //TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //if (field.fieldType == ObjectType("java/lang/Object")) { + println("field attributes: " + field.asField.attributes) val genericString = field.asField.attributes.toList.collectFirst({ case TypeVariableSignature(t) => t + case ClassTypeSignature( + packageIdentifier, + SimpleClassTypeSignature(simpleName, typeArguments), + classTypeSignatureSuffix + ) => { + val tA = typeArguments + .find(typeArgument => { + typeArgument match { + case ProperTypeArgument(variance, signature) => { + signature match { + case TypeVariableSignature(identifier) => true + case _ => false + } + } + case _ => false + } + + }) + if (tA.size > 0) + tA.head match { + case ProperTypeArgument(variance, TypeVariableSignature(identifier)) => identifier + case _ => "" + } else "" + + } }) - if (!field.attributes.isEmpty && genericString != None) { + if (!field.attributes.isEmpty && genericString != None && genericString != Some("")) { //if(genericString!=None) // println("Generic String: "+genericString) // println( // "test: "+DependentImmutableField(genericString).genericString // } - Result(field, DependentImmutableField(genericString)) //genericString)) + Result(field, DependentImmutableField(genericString)) } else state.typeImmutability match { @@ -209,7 +277,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case None if (dependencies.isEmpty) => Result(field, MutableField) case None => { - println(dependencies) InterimResult( field, MutableField, @@ -222,48 +289,38 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - println("c function called") dependencies = dependencies.filter(_.e ne eps.e) (eps: @unchecked) match { case x: InterimEP[_, _] => { - println("interim in continuation function") - println(x) dependencies += eps InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) } case x @ FinalEP(_, t) if (t == DeepImmutableType) => { // || t == ShallowImmutableType) ⇒ { // ImmutableContainerType || t == ImmutableType) ⇒ { - println(x) - println("has immutable type. Determined by continuation function.") state.typeImmutability = Some(true) } case x @ FinalEP(_, MutableType_new | ShallowImmutableType) => { //MutableType) ⇒ { - println(x) - println("has mutable type. Determined by continuation function.") state.typeImmutability = Some(false) } - case x @ FinalEP(_, DependentImmutableType) => { - println(x) - println("has dependent immutable type. Determined by continuation function.") - state.typeImmutability = Some(false) - state.dependentTypeImmutability = Some(true) + case x @ FinalEP(f, DependentImmutableType) => { + //------------------------------------------------------------------------------------- + state.typeImmutability = determineGenericFieldImmutability(state) + //------------------------------------------------------------------------------------- + //state.typeImmutability = Some(false) + //state.dependentTypeImmutability = Some(true) } case x @ FinalEP(_, MutableReference) => { - println(x) - println("has mutable reference. Determined by continuation function.") state.referenceImmutability = Some(false) } case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO - println(x) - println("has immutable reference. Determined by continuation function.") state.referenceImmutability = Some(true) } - case x @ _ => println("default value: " + x) + case x @ _ => dependencies = dependencies + x } createResult(state) } @@ -272,7 +329,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.depencenciesTypes = scala.collection.mutable.Set[ObjectType]() state.referenceImmutability = hasImmutableReference(field) state.typeImmutability = hasImmutableType(field)(state); - println("after type immutability determination") createResult(state) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 395e0da63e..264e39b48b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -151,9 +151,9 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal }) }) - genericTypeBounds.toList.foreach({ x => - println("Bound: " + x) - }) + //genericTypeBounds.toList.foreach({ x ⇒ + // println("Bound: "+x) + //}) genericTypeBounds } @@ -162,7 +162,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case t: ObjectType => //this is safe val a = classHierarchy.superclassType(t) - println("AAA::::" + a) a match { case None => Result(t, MutableClass); //MutableObjectDueToUnknownSupertypes) case Some(superClassType) => @@ -216,12 +215,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal )( cf: ClassFile ): ProperPropertyComputationResult = { - // assert(superClassMutability.isMutable.isNoOrUnknown) - - //---generic classes handling - //printf(cf.asVirtualClass.asClassFile.classSignature.get.toString()) - //cf.asClassFile.classSignature.get.formalTypeParameters. - //--------------------------------------------- val t = cf.thisType @@ -238,11 +231,13 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal !f.isStatic } var hasShallowImmutableFields = false - var dependentImmutableFields: Set[Option[String]] = Set.empty + val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) + + var dependentImmutableFields: Set[String] = Set.empty + fieldsPropertyStoreInformation.foreach( f => { - println("f:::" + f) f match { case FinalP(MutableField) => { if (lazyComputation) @@ -251,11 +246,13 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal return createResultForAllSubtypes(t, MutableClass); } case FinalP(ShallowImmutableField) => hasShallowImmutableFields = true - case FinalEP(f, DependentImmutableField(x)) => { - //println("FieldType: "+f.asField.fieldType) - //println("FieldTypeSignature: "+f.asField.fieldTypeSignature.get.toJVMSignature) - dependentImmutableFields = dependentImmutableFields + x + case FinalP(DependentImmutableField(x)) => { + x match { + case Some(v) => dependentImmutableFields = dependentImmutableFields + v + case _ => + } } + case FinalP(DeepImmutableField) => case ep @ InterimE(e) => hasFieldsWithUnknownMutability = true @@ -273,44 +270,25 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } ) - /** - * dependees ++= (propertyStore(instanceFields, FieldImmutability) collect { - * case FinalP(MutableField) => //NonFinalField) ⇒ - * // <=> The class is definitively mutable and therefore also all subclasses. - * if (lazyComputation) - * return Result(t, MutableClass); //MutableObjectByAnalysis); // - * else - * return createResultForAllSubtypes(t, MutableClass); //MutableObjectByAnalysis); - * case ep @ InterimE(e) => - * hasFieldsWithUnknownMutability = true - * (e, ep) - * case epk @ EPK(e: Entity, _) => - * // <=> The mutability information is not yet available. - * hasFieldsWithUnknownMutability = true - * (e, epk) - * - * // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields - * }).toMap * - */ var minLocalImmutability: ClassImmutability_new = if (!superClassMutabilityIsFinal) { - println("OnE!") MutableClass //MutableObjectByAnalysis } else { - println("Two!!") ShallowImmutableClass //ImmutableContainer } // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability_new = superClassInformation match { case UBP(ShallowImmutableClass) => ShallowImmutableClass //ImmutableContainer - case _ => DeepImmutableClass // ImmutableObject + case _ => DeepImmutableClass // ImmutableObject } if (!dependentImmutableFields.isEmpty) { - maxLocalImmutability = DependentImmutableClass(determineGenericTypeBounds(cf)) //TODO possibly here - } else if (hasShallowImmutableFields) { + maxLocalImmutability = DependentImmutableClass() + } + + if (hasShallowImmutableFields) { maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } @@ -321,65 +299,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } - //--var fieldTypes: Set[ObjectType] = Set.empty - //--if (maxLocalImmutability == DeepImmutableClass) { //ImmutableObject) { - //-- fieldTypes = // IMPROVE Use the precise type of the field (if available)! - // cf.fields.collect { - // case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType - // }.toSet - //} - - // For each dependent class file we have to determine the mutability - // of instances of the respective type to determine the immutability - // of instances of this class. - // Basically, we have to distinguish the following cases: - // - A field's type is mutable or conditionally immutable=> - // This class is conditionally immutable. - // - A field's type is immutable => - // The field is ignored. - // (This class is as immutable as its superclass.) - // - A field's type is at least conditionally mutable or - // The immutability of the field's type is not yet known => - // This type is AtLeastConditionallyImmutable - // We have to declare a dependency on the respective type's immutability. - // - // Additional handling is required w.r.t. the supertype: - // If the supertype is Immutable => - // Nothing special to do. - // If the supertype is AtLeastConditionallyImmutable => - // We have to declare a dependency on the supertype. - // If the supertype is ConditionallyImmutable => - // This type is also at most conditionally immutable. - // - // We furthermore have to take the mutability of the fields into consideration: - // If a field is not effectively final => - // This type is mutable. - // If a field is effectively final => - // Nothing special to do. - - //val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) - //val hasMutableOrConditionallyImmutableField = - // IMPROVE Use the precise type of the field (if available)! - //-- fieldTypesImmutability.exists { eOptP ⇒ - //-- eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) - //-- } - - if (hasShallowImmutableFields) { - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - println("shallow immutable fields.......!!!!!!!!!!!!!!!!!!") - } //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 - * } - * fieldTypesWithUndecidedMutability.foreach { eOptP => - * dependees += (eOptP.e -> eOptP) - * }* - */ - //} - if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { // <=> the super classes' immutability is final // (i.e., ImmutableObject or ImmutableContainer) @@ -400,7 +319,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal def c(someEPS: SomeEPS): ProperPropertyComputationResult = { //[DEBUG] val oldDependees = dependees dependees = dependees.filter(_._1 ne someEPS.e) - println("someEPS: " + someEPS) someEPS match { // Superclass related dependencies: // @@ -422,26 +340,16 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case LUBP(MutableClass, DeepImmutableClass) => //_: MutableObject, ImmutableObject) ⇒ // No information about superclass - // Properties related to the type of the class' fields. - // - /** - * case UBP(ShallowImmutableType | MutableType_new) ⇒ //ImmutableContainerType | MutableType) ⇒ - * maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - * dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) // TypeImmutability.key) - * - * case ELBP(e, DeepImmutableType) ⇒ // ImmutableType) ⇒ // Immutable field type, no influence on mutability - * dependees -= e - * - * case UBP(DeepImmutableType) ⇒ //ImmutableType) ⇒ // No information about field type - * - * case FinalP(MutableType_new) ⇒ //MutableType) - * Result(t, MutableClass) //TODO check - */ - case FinalP(DependentImmutableField(x)) => { - println( - ".........................................kkk......................................: " + x.get - ) - maxLocalImmutability = DependentImmutableClass() // DependentImmutableClass //TODO possibly here? + case FinalEP(f, DependentImmutableField(x)) => { + //determineGenericFieldImmutability(f.asInstanceOf[Field]) + if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass + } else { + maxLocalImmutability = DependentImmutableClass() + } + + //} else + // maxLocalImmutability = DependentImmutableClass // DependentImmutableClass //TODO possibly here? } // Field Mutability related dependencies: @@ -483,8 +391,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) minLocalImmutability = ShallowImmutableClass //ImmutableContainer - println("minLocalImmutability: " + minLocalImmutability) - println("maxLocalImmutability: " + maxLocalImmutability) if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { /*[DEBUG] assert( @@ -583,7 +489,7 @@ trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { .map(ot => (ot, project.classFile(ot))) .foreach { case (_, Some(cf)) => cfs ::= cf - case (t, None) => + 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 diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index 0529ed6b85..ec5d976859 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -69,7 +69,7 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC ): ProperPropertyComputationResult = { typeExtensibility(t) match { case Yes | Unknown => - println("Yes Unknown"); Result(t, MutableType_new) // MutableType) + Result(t, MutableType_new) // MutableType) case No => step2(t) } } @@ -81,9 +81,7 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC if (cf.exists(_.isFinal) || directSubtypes.isEmpty /*... the type is not extensible*/ ) { val c = new ProperOnUpdateContinuation { c => - println("c function called") def apply(eps: SomeEPS): ProperPropertyComputationResult = { - println("EPS: " + eps) eps match { case ELUBP(_, lb: ClassImmutability_new, ub: ClassImmutability_new) => val thisLB = lb.correspondingTypeImmutability @@ -95,11 +93,10 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC } } } + val resultToMatch = ps(t, ClassImmutability_new.key) - println("Result1: " + resultToMatch) resultToMatch match { case x @ FinalP(p) => { - println("x::: " + x.p.correspondingTypeImmutability) Result(t, p.correspondingTypeImmutability); } @@ -108,7 +105,6 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC val thisLB = lb.correspondingTypeImmutability InterimResult(t, thisLB, thisUB, Seq(eps), c) case epk => { - println("here ") InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) } //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) @@ -120,7 +116,6 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType val resultToMatch2 = ps(t, ClassImmutability_new.key) - println("Result2: " + resultToMatch2) resultToMatch2 match { case FinalP(DeepImmutableClass) => //ImmutableObject) => case FinalP(MutableClass) => //(_: MutableObject) => From 8220354680acdf4c35938166031b2b23ffe87500 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 24 Dec 2019 16:47:34 +0100 Subject: [PATCH 067/327] handling generic types of fields with recognition of the package name of the inserted types. --- .../L0FieldImmutabilityAnalysis.scala | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index fbd8cf1f3f..82fe3670d2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -89,7 +89,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) println( "determine generic field imm-------------------------------------------------------------------" ) - var genericFields: Set[String] = Set.empty + var genericFields: Set[ObjectType] = Set.empty state.field.fieldTypeSignature.head match { case ClassTypeSignature(_, SimpleClassTypeSignature(name, typeArguments), _) => { typeArguments.foreach( @@ -106,7 +106,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) _ ) ) => { - genericFields += packageIdentifier2 + packageIdentifier1 match { + case Some(pid1) => genericFields += ObjectType(pid1 + packageIdentifier2) + case _ => genericFields += ObjectType(packageIdentifier2) + } + } case _ => } @@ -119,10 +123,8 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) genericFields.foreach(f => println("generic Field: " + f)) //state.typeImmutability = Some(true) - genericFields.toList.foreach(s => { - val objectType = ObjectType(s) + genericFields.toList.foreach(objectType => { val result = propertyStore(objectType, TypeImmutability_new.key) - println("Result generic field with objtype: " + objectType + " result: " + result) result match { case FinalP(DeepImmutableType) => case FinalP(ShallowImmutableType | MutableType_new) => { @@ -146,7 +148,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) return Some(true); } case FinalEP(f, DependentImmutableType) => { - println(f + " has dependent imm type") //TODO under construction //--------------------------------------------------------------------------------------- state.typeImmutability = determineGenericFieldImmutability(state) @@ -230,17 +231,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case ProperTypeArgument(variance, signature) => { signature match { case TypeVariableSignature(identifier) => true - case _ => false + case _ => false } } case _ => false } - }) if (tA.size > 0) tA.head match { case ProperTypeArgument(variance, TypeVariableSignature(identifier)) => identifier - case _ => "" + case _ => "" } else "" } @@ -260,7 +260,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(false) => { state.dependentTypeImmutability match { case Some(true) => Result(field, DependentImmutableField()) - case _ => Result(field, ShallowImmutableField) + case _ => Result(field, ShallowImmutableField) } } case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) From cb5a514226b4b0c343fff483779f5ef8fc644c8e Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 24 Dec 2019 16:50:57 +0100 Subject: [PATCH 068/327] New test cases for handling the immutability of generic types. Even of nested ones --- .../ClassImmutabilityAnalysisDemo.scala | 3 +- .../TypeImmutabilityAnalysisDemo.scala | 10 ------ ...DependentClassWithGenericField_deep01.java | 27 ++++++++++++++ ...DependentClassWithGenericField_deep11.java | 30 ++++++++++++++++ .../FinalEmptyClass.java | 10 ++++++ .../Generic_class1.java | 34 ++++++++++++++++++ .../Generic_class2.java | 35 +++++++++++++++++++ .../Generic_class3.java | 25 +++++++++++++ .../Generic_class4_deep.java | 21 +++++++++++ .../Generic_class4_shallow.java | 19 ++++++++++ .../TrivialMutableClass.java | 19 ++++++++++ .../analyses/ClassImmutabilityAnalysis.scala | 4 +-- .../LxTypeImmutabilityAnalysis_new.scala | 2 +- 13 files changed, 224 insertions(+), 15 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep01.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep11.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/FinalEmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class1.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class2.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class3.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_shallow.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/TrivialMutableClass.java 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 index 447d52c55d..a5c34b7557 100644 --- 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 @@ -10,7 +10,6 @@ import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ClassImmutability_new import org.opalj.br.fpcf.properties.DeepImmutableClass @@ -49,7 +48,7 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { analysesManager.project.get(RTACallGraphKey) val (propertyStore, _) = analysesManager.runAll( - LazyTypeImmutabilityAnalysis, + //LazyTypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, LazyL2FieldMutabilityAnalysis, 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 index 5793b66312..1739638a81 100644 --- 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 @@ -49,16 +49,6 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { analysesManager.project.get(RTACallGraphKey) val (propertyStore, _) = analysesManager.runAll( - /** - * LazyTypeImmutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL0ReferenceImmutabilityAnalysis, - * LazyL0PurityAnalysis, - * LazyL2FieldMutabilityAnalysis, - * LazyL0FieldImmutabilityAnalysis, - * EagerLxClassImmutabilityAnalysis_new, - * EagerLxTypeImmutabilityAnalysis_new* - */ //LazyTypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep01.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep01.java new file mode 100644 index 0000000000..b018a847e9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep01.java @@ -0,0 +1,27 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public final class DependentClassWithGenericField_deep01 { + + @DeepImmutableFieldAnnotation(value = "") + private FinalEmptyClass fec; + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("eff imm ref") + private Generic_class1 gc; + + public DependentClassWithGenericField_deep01(FinalEmptyClass fec) { + this.fec = fec; + gc = new Generic_class1 + (fec, new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); + } +} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep11.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep11.java new file mode 100644 index 0000000000..b85faef3ce --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep11.java @@ -0,0 +1,30 @@ +package org.opalj.fpcf.fixtures.class_immutability; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class DependentClassWithGenericField_deep11 { + + @DependentImmutableFieldAnnotation(value = "", genericString = "T1") + @ImmutableReferenceAnnotation("") + private T1 t1; + + @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") + @ImmutableReferenceAnnotation("eff imm ref") + private DependentClassWithGenericField_deep1 gc; + + public DependentClassWithGenericField_deep11(T1 t1) { + this.t1 = t1; + gc = new DependentClassWithGenericField_deep1(t1); + } + + +} + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/FinalEmptyClass.java new file mode 100644 index 0000000000..e347c4e5ea --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/FinalEmptyClass.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public final class FinalEmptyClass { + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class1.java new file mode 100644 index 0000000000..d539ee2c7a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class1.java @@ -0,0 +1,34 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class Generic_class1 { + + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") + private T1 t1; + + @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") + private T2 t2; + + @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") + private T3 t3; + + @DependentImmutableFieldAnnotation(value = "T4", genericString = "T4") + private T4 t4; + + @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") + private T5 t5; + + public Generic_class1(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + } + +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class2.java new file mode 100644 index 0000000000..d61328c0b9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class2.java @@ -0,0 +1,35 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class Generic_class2 { + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value="T1", genericString = "T1") + private T1 t1; + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value="T2", genericString = "T2") + private T2 t2; + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value="T3", genericString = "T3") + private T3 t3; + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value = "", genericString = "") + private Generic_class1 gc; + + public Generic_class2(T1 t1, T2 t2, T3 t3, FinalEmptyClass fec1, FinalEmptyClass fec2){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + gc = new Generic_class1(fec1, fec2, t1,t2,t3); + } + +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class3.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class3.java new file mode 100644 index 0000000000..5afd485a96 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class3.java @@ -0,0 +1,25 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.FinalE; +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class Generic_class3 { + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") + private T1 t1; + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value = "", genericString = "") + private Generic_class2 gc; + + public Generic_class3(T1 t1, FinalEmptyClass fec1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4){ + this.t1 = t1; + gc = new Generic_class2(t1, fec1, fec2, fec3, fec4); + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_deep.java new file mode 100644 index 0000000000..4795b818cc --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_deep.java @@ -0,0 +1,21 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.FinalE; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public final class Generic_class4_deep { + + @ImmutableReferenceAnnotation("") + @DeepImmutableFieldAnnotation("") + private Generic_class3 gc; + + public Generic_class4_deep(FinalEmptyClass fec1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4, FinalEmptyClass fec5){ + gc = new Generic_class3(fec1, fec2, fec3, fec4, fec5); + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_shallow.java new file mode 100644 index 0000000000..92a17f3951 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_shallow.java @@ -0,0 +1,19 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; + +@ShallowImmutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +public final class Generic_class4_shallow { + + @ImmutableReferenceAnnotation("") + @ShallowImmutableFieldAnnotation("") + private Generic_class3 gc; + + public Generic_class4_shallow(TrivialMutableClass tmc1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4, FinalEmptyClass fec5){ + gc = new Generic_class3(tmc1, fec2, fec3, fec4, fec5); + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/TrivialMutableClass.java new file mode 100644 index 0000000000..2997ac5e50 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/TrivialMutableClass.java @@ -0,0 +1,19 @@ +package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +public class TrivialMutableClass { + + @MutableFieldAnnotation("") + @MutableReferenceAnnotation("") + public int n = 0; + + @MutableFieldAnnotation("") + @MutableReferenceAnnotation("") + public String name = "name"; +} 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/ClassImmutabilityAnalysis.scala index aba6712bc7..07c2d07b91 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/ClassImmutabilityAnalysis.scala @@ -219,7 +219,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability = superClassInformation match { case UBP(ImmutableContainer) => ImmutableContainer - case _ => ImmutableObject + case _ => ImmutableObject } if (cf.fields.exists(f => !f.isStatic && f.fieldType.isArrayType)) { @@ -463,7 +463,7 @@ trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { .map(ot => (ot, project.classFile(ot))) .foreach { case (_, Some(cf)) => cfs ::= cf - case (t, None) => + 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 diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index ec5d976859..cc122f9762 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -56,7 +56,7 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC e: Entity ): ProperPropertyComputationResult = e match { case t: ObjectType => step1(typeExtensibility)(t) - case _ => throw new IllegalArgumentException(s"$e is not an ObjectType") + case _ => throw new IllegalArgumentException(s"$e is not an ObjectType") } /** From 86dc0d5e19f88694039d6660322f077e096a35e3 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 16 Jan 2020 16:43:20 +0100 Subject: [PATCH 069/327] Extending Stmt with isxxstmts ... --- .../src/main/scala/org/opalj/tac/Stmt.scala | 1715 +++++++++-------- 1 file changed, 870 insertions(+), 845 deletions(-) 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..a5e375ac93 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala @@ -18,72 +18,76 @@ import org.opalj.value.ValueInformation */ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { - /** - * The program counter of the original '''underyling bytecode instruction'''. - * - * This `pc` is independent of the (implicit) `index` of the statement - * in the generated statements array! This pc is, e.g., useful for - * getting line number information. - */ - def pc: UShort - - def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean - - /** - * Called by the framework to enable each statement/expression to re-map the target - * `pc` of a(n unconditional) jump instruction to the index of the respective quadruple - * statement in the statements array. - * - * ==Example== - * The bytecode instruction: `5: goto 10` (where 5 is the original `pc` and `10` is - * the branchoffset) is re-mapped to a `goto pcToIndex(5+10)` quadruples statement. - */ - private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit - - override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] - - // TYPE CONVERSION METHODS - - def asIf: If[V] = throw new ClassCastException(); - def asGoto: Goto = throw new ClassCastException(); - def asRet: Ret = throw new ClassCastException(); - def asJSR: JSR = throw new ClassCastException(); - def asSwitch: Switch[V] = throw new ClassCastException(); - def asAssignmentLike: AssignmentLikeStmt[V] = throw new ClassCastException(); - def asAssignment: Assignment[V] = throw new ClassCastException(); - def asReturnValue: ReturnValue[V] = throw new ClassCastException(); - def asReturn: Return = throw new ClassCastException(); - def asNop: Nop = throw new ClassCastException(); - def asSynchronizationStmt: SynchronizationStmt[V] = throw new ClassCastException(); - def asMonitorEnter: MonitorEnter[V] = throw new ClassCastException(); - def asMonitorExit: MonitorExit[V] = throw new ClassCastException(); - def asArrayStore: ArrayStore[V] = throw new ClassCastException(); - def asThrow: Throw[V] = throw new ClassCastException(); - def asFieldWriteAccessStmt: FieldWriteAccessStmt[V] = throw new ClassCastException(); - def asPutStatic: PutStatic[V] = throw new ClassCastException(); - def asPutField: PutField[V] = throw new ClassCastException(); - /*inner type*/ def asMethodCall: MethodCall[V] = throw new ClassCastException(); - /*inner type*/ def asInstanceMethodCall: InstanceMethodCall[V] = throw new ClassCastException(); - def asNonVirtualMethodCall: NonVirtualMethodCall[V] = throw new ClassCastException(); - def asVirtualMethodCall: VirtualMethodCall[V] = throw new ClassCastException(); - def asStaticMethodCall: StaticMethodCall[V] = throw new ClassCastException(); - def asInvokedynamicMethodCall: InvokedynamicMethodCall[V] = throw new ClassCastException(); - def asExprStmt: ExprStmt[V] = throw new ClassCastException(); - def asCaughtException: CaughtException[V] = throw new ClassCastException(); - def asCheckcast: Checkcast[V] = throw new ClassCastException(); - - def isAssignment: Boolean = false - def isExprStmt: Boolean = false - def isNonVirtualMethodCall: Boolean = false - def isVirtualMethodCall: Boolean = false - def isStaticMethodCall: Boolean = false - + /** + * The program counter of the original '''underyling bytecode instruction'''. + * + * This `pc` is independent of the (implicit) `index` of the statement + * in the generated statements array! This pc is, e.g., useful for + * getting line number information. + */ + def pc: UShort + + def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean + + /** + * Called by the framework to enable each statement/expression to re-map the target + * `pc` of a(n unconditional) jump instruction to the index of the respective quadruple + * statement in the statements array. + * + * ==Example== + * The bytecode instruction: `5: goto 10` (where 5 is the original `pc` and `10` is + * the branchoffset) is re-mapped to a `goto pcToIndex(5+10)` quadruples statement. + */ + private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit + + override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] + + // TYPE CONVERSION METHODS + def asIf: If[V] = throw new ClassCastException(); + def asGoto: Goto = throw new ClassCastException(); + def asRet: Ret = throw new ClassCastException(); + def asJSR: JSR = throw new ClassCastException(); + def asSwitch: Switch[V] = throw new ClassCastException(); + def asAssignmentLike: AssignmentLikeStmt[V] = throw new ClassCastException(); + def asAssignment: Assignment[V] = throw new ClassCastException(); + def asReturnValue: ReturnValue[V] = throw new ClassCastException(); + def asReturn: Return = throw new ClassCastException(); + def asNop: Nop = throw new ClassCastException(); + def asSynchronizationStmt: SynchronizationStmt[V] = throw new ClassCastException(); + def asMonitorEnter: MonitorEnter[V] = throw new ClassCastException(); + def asMonitorExit: MonitorExit[V] = throw new ClassCastException(); + def asArrayStore: ArrayStore[V] = throw new ClassCastException(); + def asThrow: Throw[V] = throw new ClassCastException(); + def asFieldWriteAccessStmt: FieldWriteAccessStmt[V] = throw new ClassCastException(); + def asPutStatic: PutStatic[V] = throw new ClassCastException(); + def asPutField: PutField[V] = throw new ClassCastException(); + /*inner type*/ + def asMethodCall: MethodCall[V] = throw new ClassCastException(); + /*inner type*/ + def asInstanceMethodCall: InstanceMethodCall[V] = throw new ClassCastException(); + def asNonVirtualMethodCall: NonVirtualMethodCall[V] = throw new ClassCastException(); + def asVirtualMethodCall: VirtualMethodCall[V] = throw new ClassCastException(); + def asStaticMethodCall: StaticMethodCall[V] = throw new ClassCastException(); + def asInvokedynamicMethodCall: InvokedynamicMethodCall[V] = throw new ClassCastException(); + def asExprStmt: ExprStmt[V] = throw new ClassCastException(); + def asCaughtException: CaughtException[V] = throw new ClassCastException(); + def asCheckcast: Checkcast[V] = throw new ClassCastException(); + + def isAssignment: Boolean = false + def isExprStmt: Boolean = false + def isNonVirtualMethodCall: Boolean = false + def isVirtualMethodCall: Boolean = false + def isStaticMethodCall: Boolean = false + def isIfStmt: Boolean = false + def isMonitorEnter: Boolean = false + def isMonitorExit: Boolean = false + def isPutStatic: Boolean = false } /** @@ -101,62 +105,64 @@ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { * generating source code. */ case class If[+V <: Var[V]]( - pc: PC, - left: Expr[V], - condition: RelationalOperator, - right: Expr[V], - private[tac] var target: Int + pc: PC, + left: Expr[V], + condition: RelationalOperator, + right: Expr[V], + private[tac] var target: Int ) extends Stmt[V] { - final override def asIf: this.type = this - final override def astID: Int = If.ASTID - final def leftExpr: Expr[V] = left - final def rightExpr: Expr[V] = right - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(left) && p(right) - } - - /** - * The target statement that is executed if the condition evaluates to `true`. - */ - def targetStmt: Int = target - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - left.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - right.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - target = pcToIndex(target) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - If(pc, left.toCanonicalForm, condition, right.toCanonicalForm, target) - } - - final override def isSideEffectFree: Boolean = { - assert(left.isValueExpression && right.isValueExpression) - true - } - - override def toString: String = s"If(pc=$pc,$left,$condition,$right,target=$target)" + final override def asIf: this.type = this + + final override def isIfStmt: Boolean = true + final override def astID: Int = If.ASTID + final def leftExpr: Expr[V] = left + final def rightExpr: Expr[V] = right + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(left) && p(right) + } + + /** + * The target statement that is executed if the condition evaluates to `true`. + */ + def targetStmt: Int = target + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + left.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + right.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + target = pcToIndex(target) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + If(pc, left.toCanonicalForm, condition, right.toCanonicalForm, target) + } + + final override def isSideEffectFree: Boolean = { + assert(left.isValueExpression && right.isValueExpression) + true + } + + override def toString: String = s"If(pc=$pc,$left,$condition,$right,target=$target)" } object If { - final val ASTID = 0 + final val ASTID = 0 } trait VariableFreeStmt extends Stmt[Nothing] { - final override def toCanonicalForm( - implicit - ev: Nothing <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - this - } + final override def toCanonicalForm( + implicit + ev: Nothing <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + this + } } @@ -167,30 +173,31 @@ trait VariableFreeStmt extends Stmt[Nothing] { */ 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 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 - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - target = pcToIndex(target) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + target = pcToIndex(target) + } - final override def isSideEffectFree: Boolean = true + final override def isSideEffectFree: Boolean = true - /** - * @note Calling this method is only supported after the quadruples representation - * is created and the re-mapping of `pc`s to instruction indexes has happened! - */ - def targetStmt: Int = target + /** + * @note Calling this method is only supported after the quadruples representation + * is created and the re-mapping of `pc`s to instruction indexes has happened! + */ + def targetStmt: Int = target - override def toString: String = s"Goto(pc=$pc,target=$target)" + override def toString: String = s"Goto(pc=$pc,target=$target)" } object Goto { - final val ASTID = 1 + final val ASTID = 1 } /** @@ -205,26 +212,27 @@ object Goto { */ case class Ret(pc: PC, private var returnAddresses: PCs) extends VariableFreeStmt { - 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 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 - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - returnAddresses = returnAddresses map { pcToIndex } - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + returnAddresses = returnAddresses map { pcToIndex } + } - final override def isSideEffectFree: Boolean = true + final override def isSideEffectFree: Boolean = true - override def toString: String = { - s"Ret(pc=$pc,returnAddresses=${returnAddresses.mkString("(", ",", ")")})" - } + override def toString: String = { + s"Ret(pc=$pc,returnAddresses=${returnAddresses.mkString("(", ",", ")")})" + } } object Ret { - final val ASTID = 2 + final val ASTID = 2 } /** @@ -239,32 +247,33 @@ object Ret { */ 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 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 - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - target = pcToIndex(target) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + target = pcToIndex(target) + } - final override def isSideEffectFree: Boolean = true + final override def isSideEffectFree: Boolean = true - /** - * The first statement of the called subroutine. - * - * @note Calling this method is only supported after the quadruples representation - * is created and the re-mapping of `pc`s to instruction indexes has happened! - */ - def targetStmt: Int = target + /** + * The first statement of the called subroutine. + * + * @note Calling this method is only supported after the quadruples representation + * is created and the re-mapping of `pc`s to instruction indexes has happened! + */ + def targetStmt: Int = target - override def toString: String = s"JSR(pc=$pc,target=$target)" + override def toString: String = s"JSR(pc=$pc,target=$target)" } object JSR { - final val ASTID = 3 + final val ASTID = 3 } /** @@ -277,176 +286,177 @@ object JSR { * were determined to be dead. */ case class Switch[+V <: Var[V]]( - pc: PC, - private var defaultTarget: Int, - index: Expr[V], - private var npairs: RefArray[IntIntPair /*(Case Value, Jump Target)*/ ] + pc: PC, + private var defaultTarget: Int, + index: Expr[V], + private var npairs: RefArray[IntIntPair /*(Case Value, Jump Target)*/ ] ) extends Stmt[V] { - final override def asSwitch: this.type = this - final override def astID: Int = Switch.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(index) - } - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - npairs._UNSAFE_mapped { x ⇒ - val newIndex = pcToIndex(x._2) - // assert(newIndex >= 0) - x.copy(_2 = newIndex) - } - defaultTarget = pcToIndex(defaultTarget) - index.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - Switch(pc, defaultTarget, index.toCanonicalForm, npairs) - } - - final override def isSideEffectFree: Boolean = { - assert(index.isValueExpression) - true - } - - // Calling this method is only supported after the three-address code representation - // is created and the remapping of pcs to instruction indexes has happened! - def caseStmts: IntArray = npairs.map(x ⇒ x._2) - - // Calling this method is only supported after the quadruples representation - // is created and the remapping of pcs to instruction indexes has happened! - def defaultStmt: Int = defaultTarget - - override def toString: String = { - val npairs = this.npairs.mkString("(", ",", ")") - s"Switch(pc=$pc,defaultTarget=$defaultTarget,index=$index,npairs=$npairs" - } + final override def asSwitch: this.type = this + final override def astID: Int = Switch.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(index) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + npairs._UNSAFE_mapped { x => + val newIndex = pcToIndex(x._2) + // assert(newIndex >= 0) + x.copy(_2 = newIndex) + } + defaultTarget = pcToIndex(defaultTarget) + index.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + Switch(pc, defaultTarget, index.toCanonicalForm, npairs) + } + + final override def isSideEffectFree: Boolean = { + assert(index.isValueExpression) + true + } + + // Calling this method is only supported after the three-address code representation + // is created and the remapping of pcs to instruction indexes has happened! + def caseStmts: IntArray = npairs.map(x => x._2) + + // Calling this method is only supported after the quadruples representation + // is created and the remapping of pcs to instruction indexes has happened! + def defaultStmt: Int = defaultTarget + + override def toString: String = { + val npairs = this.npairs.mkString("(", ",", ")") + s"Switch(pc=$pc,defaultTarget=$defaultTarget,index=$index,npairs=$npairs" + } } object Switch { - final val ASTID = 4 + final val ASTID = 4 } sealed abstract class AssignmentLikeStmt[+V <: Var[V]] extends Stmt[V] { - def expr: Expr[V] - final override def asAssignmentLike: AssignmentLikeStmt[V] = this + def expr: Expr[V] + final override def asAssignmentLike: AssignmentLikeStmt[V] = this } object AssignmentLikeStmt { - def unapply[V <: Var[V]](stmt: Stmt[V]): Option[(PC, Expr[V])] = { - stmt match { - case s: AssignmentLikeStmt[V] ⇒ Some((s.pc, s.expr)) - case _ ⇒ None - } + def unapply[V <: Var[V]](stmt: Stmt[V]): Option[(PC, Expr[V])] = { + stmt match { + case s: AssignmentLikeStmt[V] => Some((s.pc, s.expr)) + case _ => None } + } } case class Assignment[+V <: Var[V]]( - pc: PC, - targetVar: V, - expr: Expr[V] + pc: PC, + targetVar: V, + expr: Expr[V] ) extends AssignmentLikeStmt[V] { - final override def asAssignment: this.type = this - final override def isAssignment: Boolean = true + final override def asAssignment: this.type = this + final override def isAssignment: Boolean = true - final override def astID: Int = Assignment.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(expr) - } + final override def astID: Int = Assignment.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(expr) + } - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - targetVar.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + targetVar.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - Assignment(pc, ev(targetVar).toCanonicalForm, expr.toCanonicalForm) - } + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + Assignment(pc, ev(targetVar).toCanonicalForm, expr.toCanonicalForm) + } - final override def isSideEffectFree: Boolean = expr.isSideEffectFree + final override def isSideEffectFree: Boolean = expr.isSideEffectFree - override def hashCode(): Opcode = (Assignment.ASTID * 1171 + pc) * 31 + expr.hashCode + override def hashCode(): Opcode = (Assignment.ASTID * 1171 + pc) * 31 + expr.hashCode - override def toString: String = s"Assignment(pc=$pc,$targetVar,$expr)" + override def toString: String = s"Assignment(pc=$pc,$targetVar,$expr)" } object Assignment { - final val ASTID = 5 + final val ASTID = 5 } case class ReturnValue[+V <: Var[V]](pc: Int, expr: Expr[V]) extends Stmt[V] { - final override def asReturnValue: this.type = this - 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( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - ReturnValue(pc, expr.toCanonicalForm) - } - - 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 - false - } - - override def toString: String = s"ReturnValue(pc=$pc,$expr)" + final override def asReturnValue: this.type = this + final override def astID: Int = ReturnValue.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(expr) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + ReturnValue(pc, expr.toCanonicalForm) + } + + 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 + false + } + + override def toString: String = s"ReturnValue(pc=$pc,$expr)" } object ReturnValue { - final val ASTID = 6 + final val ASTID = 6 } sealed abstract class SimpleStmt extends VariableFreeStmt { - /** - * Nothing to do. - */ - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = {} + /** + * Nothing to do. + */ + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = {} } 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 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 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 - false - } + 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 + false + } - override def toString: String = s"Return(pc=$pc)" + override def toString: String = s"Return(pc=$pc)" } object Return { - final val ASTID = 7 + final val ASTID = 7 } /** @@ -473,305 +483,314 @@ object Return { */ case class Nop(pc: Int) extends SimpleStmt { - final override def asNop: this.type = this - final override def astID: Int = Nop.ASTID - final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = true + final override def asNop: this.type = this + final override def astID: Int = Nop.ASTID + final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] => Boolean): Boolean = + true - final override def isSideEffectFree: Boolean = true + final override def isSideEffectFree: Boolean = true - override def toString: String = s"Nop(pc=$pc)" + override def toString: String = s"Nop(pc=$pc)" } object Nop { - final val ASTID = 8 + final val ASTID = 8 } sealed abstract class SynchronizationStmt[+V <: Var[V]] extends Stmt[V] { - final override def asSynchronizationStmt: this.type = this + final override def asSynchronizationStmt: this.type = this - def objRef: Expr[V] + def objRef: Expr[V] - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - objRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + objRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } } case class MonitorEnter[+V <: Var[V]](pc: PC, objRef: Expr[V]) extends SynchronizationStmt[V] { - final override def asMonitorEnter: this.type = this - final override def astID: Int = MonitorEnter.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(objRef) - } + final override def asMonitorEnter: this.type = this - final override def isSideEffectFree: Boolean = { - // IMPROVE Is the lock as such ever used (do we potentially have concurrency)? - false - } + final override def isMonitorEnter: Boolean = true - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - MonitorEnter(pc, objRef.toCanonicalForm) - } + final override def astID: Int = MonitorEnter.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(objRef) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE Is the lock as such ever used (do we potentially have concurrency)? + false + } - override def toString: String = s"MonitorEnter(pc=$pc,$objRef)" + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + MonitorEnter(pc, objRef.toCanonicalForm) + } + + override def toString: String = s"MonitorEnter(pc=$pc,$objRef)" } object MonitorEnter { - final val ASTID = 9 + final val ASTID = 9 } case class MonitorExit[+V <: Var[V]](pc: PC, objRef: Expr[V]) extends SynchronizationStmt[V] { - final override def asMonitorExit: this.type = this - final override def astID: Int = MonitorExit.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(objRef) - } + final override def asMonitorExit: this.type = this - final override def isSideEffectFree: Boolean = { - // IMPROVE Is the lock as such ever used (do we potentially have concurrency)? - false - } + 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) + } - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - MonitorExit(pc, objRef.toCanonicalForm) - } + final override def isSideEffectFree: Boolean = { + // IMPROVE Is the lock as such ever used (do we potentially have concurrency)? + false + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + MonitorExit(pc, objRef.toCanonicalForm) + } - override def toString: String = s"MonitorExit(pc=$pc,$objRef)" + override def toString: String = s"MonitorExit(pc=$pc,$objRef)" } object MonitorExit { - final val ASTID = 10 + final val ASTID = 10 } case class ArrayStore[+V <: Var[V]]( - pc: PC, - arrayRef: Expr[V], - index: Expr[V], - value: Expr[V] + pc: PC, + arrayRef: Expr[V], + index: Expr[V], + value: Expr[V] ) extends Stmt[V] { - final override def asArrayStore: this.type = this - 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) - } - - final override def isSideEffectFree: Boolean = { - // IMPROVE Is it a redundant write? - false - } - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - arrayRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - index.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - ArrayStore(pc, arrayRef.toCanonicalForm, index.toCanonicalForm, value.toCanonicalForm) - } - - override def toString: String = s"ArrayStore(pc=$pc,$arrayRef,$index,$value)" + final override def asArrayStore: this.type = this + 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) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE Is it a redundant write? + false + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + arrayRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + index.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + ArrayStore(pc, arrayRef.toCanonicalForm, index.toCanonicalForm, value.toCanonicalForm) + } + + override def toString: String = s"ArrayStore(pc=$pc,$arrayRef,$index,$value)" } object ArrayStore { - final val ASTID = 11 + final val ASTID = 11 } case class Throw[+V <: Var[V]](pc: PC, exception: Expr[V]) extends Stmt[V] { - final override def asThrow: this.type = this - final override def astID: Int = Throw.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(exception) - } + final override def asThrow: this.type = this + final override def astID: Int = Throw.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(exception) + } - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - exception.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + exception.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - Throw(pc, exception.toCanonicalForm) - } + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + Throw(pc, exception.toCanonicalForm) + } - final override def isSideEffectFree: Boolean = false + final override def isSideEffectFree: Boolean = false - override def hashCode(): Opcode = (Throw.ASTID * 1171 + pc) * 31 + exception.hashCode + override def hashCode(): Opcode = (Throw.ASTID * 1171 + pc) * 31 + exception.hashCode - override def toString: String = s"Throw(pc=$pc,$exception)" + override def toString: String = s"Throw(pc=$pc,$exception)" } object Throw { - final val ASTID = 12 + final val ASTID = 12 } sealed abstract class FieldWriteAccessStmt[+V <: Var[V]] extends Stmt[V] { - def declaringClass: ObjectType - def name: String - def declaredFieldType: FieldType - def value: Expr[V] - - final override def asFieldWriteAccessStmt: this.type = this - - /** - * Identifies the field if it can be found. - */ - def resolveField(implicit p: ProjectLike): Option[Field] = { - p.resolveFieldReference(declaringClass, name, declaredFieldType) - } + def declaringClass: ObjectType + def name: String + def declaredFieldType: FieldType + def value: Expr[V] + + final override def asFieldWriteAccessStmt: this.type = this + + /** + * Identifies the field if it can be found. + */ + def resolveField(implicit p: ProjectLike): Option[Field] = { + p.resolveFieldReference(declaringClass, name, declaredFieldType) + } } case class PutStatic[+V <: Var[V]]( - pc: PC, - declaringClass: ObjectType, - name: String, - declaredFieldType: FieldType, - value: Expr[V] + pc: PC, + declaringClass: ObjectType, + name: String, + declaredFieldType: FieldType, + value: Expr[V] ) extends FieldWriteAccessStmt[V] { - final override def asPutStatic: this.type = this - 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( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - PutStatic(pc, declaringClass, name, declaredFieldType, value.toCanonicalForm) - } - - final override def isSideEffectFree: Boolean = { - // IMPROVE Is it a redundant write? - false - } - - override def hashCode(): Opcode = { - ((PutStatic.ASTID * 1171 + pc) * 31 + declaringClass.hashCode) * 31 + name.hashCode - } - - override def toString: String = { - s"PutStatic(pc=$pc,${declaringClass.toJava},$name,${declaredFieldType.toJava},$value)" - } + 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) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + PutStatic(pc, declaringClass, name, declaredFieldType, value.toCanonicalForm) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE Is it a redundant write? + false + } + + override def hashCode(): Opcode = { + ((PutStatic.ASTID * 1171 + pc) * 31 + declaringClass.hashCode) * 31 + name.hashCode + } + + override def toString: String = { + s"PutStatic(pc=$pc,${declaringClass.toJava},$name,${declaredFieldType.toJava},$value)" + } } object PutStatic { - final val ASTID = 13 + final val ASTID = 13 } case class PutField[+V <: Var[V]]( - pc: Int, - declaringClass: ObjectType, - name: String, - declaredFieldType: FieldType, - objRef: Expr[V], - value: Expr[V] + pc: Int, + declaringClass: ObjectType, + name: String, + declaredFieldType: FieldType, + objRef: Expr[V], + value: Expr[V] ) extends FieldWriteAccessStmt[V] { - 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( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - objRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - val newObjRef = objRef.toCanonicalForm - val newValue = value.toCanonicalForm - PutField(pc, declaringClass, name, declaredFieldType, newObjRef, newValue) - } - - final override def isSideEffectFree: Boolean = { - // IMPROVE Is it a redundant write? - false - } - - override def hashCode(): Opcode = { - ((PutField.ASTID * 1171 + pc) * 31 + declaringClass.hashCode) * 31 + name.hashCode - } - - override def toString: String = { - s"PutField(pc=$pc,${declaringClass.toJava},$name,${declaredFieldType.toJava},$objRef,$value)" - } + 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) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + objRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + val newObjRef = objRef.toCanonicalForm + val newValue = value.toCanonicalForm + PutField(pc, declaringClass, name, declaredFieldType, newObjRef, newValue) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE Is it a redundant write? + false + } + + override def hashCode(): Opcode = { + ((PutField.ASTID * 1171 + pc) * 31 + declaringClass.hashCode) * 31 + name.hashCode + } + + override def toString: String = { + s"PutField(pc=$pc,${declaringClass.toJava},$name,${declaredFieldType.toJava},$objRef,$value)" + } } object PutField { - final val ASTID = 14 + final val ASTID = 14 } sealed abstract class MethodCall[+V <: Var[V]] extends Stmt[V] with Call[V] { - final override def isSideEffectFree: Boolean = false // IMPROVE Check if a call has no side-effect + final override def isSideEffectFree: Boolean = false // IMPROVE Check if a call has no side-effect - final override def asMethodCall: this.type = this + final override def asMethodCall: this.type = this } sealed abstract class InstanceMethodCall[+V <: Var[V]] extends MethodCall[V] { - final override def allParams: Seq[Expr[V]] = receiver +: params + final override def allParams: Seq[Expr[V]] = receiver +: params - def receiver: Expr[V] - final override def receiverOption: Some[Expr[V]] = Some(receiver) - final override def asInstanceMethodCall: this.type = this - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(receiver) && params.forall(param ⇒ p(param)) - } + def receiver: Expr[V] + final override def receiverOption: Some[Expr[V]] = Some(receiver) + final override def asInstanceMethodCall: this.type = this + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(receiver) && params.forall(param => p(param)) + } - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - receiver.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - params foreach { p ⇒ p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + receiver.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + params foreach { p => + p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) } + } } object InstanceMethodCall { - def unapply[V <: Var[V]]( - call: InstanceMethodCall[V] - ): Some[(Int, ReferenceType, Boolean, String, MethodDescriptor, Expr[V], Seq[Expr[V]])] = { - import call._ - Some((pc, declaringClass, isInterface, name, descriptor, receiver, params)) - } + def unapply[V <: Var[V]]( + call: InstanceMethodCall[V] + ): Some[(Int, ReferenceType, Boolean, String, MethodDescriptor, Expr[V], Seq[Expr[V]])] = { + import call._ + Some((pc, declaringClass, isInterface, name, descriptor, receiver, params)) + } } /** @@ -779,175 +798,177 @@ object InstanceMethodCall { * I.e., it is either a super-call, a private instance method call or a constructor call. */ case class NonVirtualMethodCall[+V <: Var[V]]( - pc: Int, - declaringClass: ObjectType, - isInterface: Boolean, - name: String, - descriptor: MethodDescriptor, - receiver: Expr[V], - params: Seq[Expr[V]] + pc: Int, + declaringClass: ObjectType, + isInterface: Boolean, + name: String, + descriptor: MethodDescriptor, + receiver: Expr[V], + params: Seq[Expr[V]] ) extends InstanceMethodCall[V] { - final override def asNonVirtualMethodCall: this.type = this - final override def isNonVirtualMethodCall: Boolean = true - final override def astID: Int = NonVirtualMethodCall.ASTID - - /** - * Identifies the potential call target if it can be found. - * - * @see [ProjectLike#specialCall] for further details. - */ - def resolveCallTarget(callerClassType: ObjectType)(implicit p: ProjectLike): Result[Method] = { - p.specialCall(callerClassType, declaringClass, isInterface, name, descriptor) - } - - // convenience method to enable Call to define a single method to handle all kinds of calls - def resolveCallTargets( - callingContext: ObjectType - )( - implicit - p: ProjectLike, - ev: V <:< DUVar[ValueInformation] - ): Set[Method] = { - resolveCallTarget(callingContext)(p).toSet - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - NonVirtualMethodCall( - pc, - declaringClass, - isInterface, - name, - descriptor, - receiver.toCanonicalForm, - params.map(_.toCanonicalForm) - ) - } - - override def toString: String = { - val sig = descriptor.toJava(name) - val declClass = declaringClass.toJava - val params = this.params.mkString("(", ",", ")") - s"NonVirtualMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$receiver,$params)" - } + final override def asNonVirtualMethodCall: this.type = this + final override def isNonVirtualMethodCall: Boolean = true + final override def astID: Int = NonVirtualMethodCall.ASTID + + /** + * Identifies the potential call target if it can be found. + * + * @see [ProjectLike#specialCall] for further details. + */ + def resolveCallTarget(callerClassType: ObjectType)(implicit p: ProjectLike): Result[Method] = { + p.specialCall(callerClassType, declaringClass, isInterface, name, descriptor) + } + + // convenience method to enable Call to define a single method to handle all kinds of calls + def resolveCallTargets( + callingContext: ObjectType + )( + implicit + p: ProjectLike, + ev: V <:< DUVar[ValueInformation] + ): Set[Method] = { + resolveCallTarget(callingContext)(p).toSet + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + NonVirtualMethodCall( + pc, + declaringClass, + isInterface, + name, + descriptor, + receiver.toCanonicalForm, + params.map(_.toCanonicalForm) + ) + } + + override def toString: String = { + val sig = descriptor.toJava(name) + val declClass = declaringClass.toJava + val params = this.params.mkString("(", ",", ")") + s"NonVirtualMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$receiver,$params)" + } } object NonVirtualMethodCall { - final val ASTID = 15 + final val ASTID = 15 } case class VirtualMethodCall[+V <: Var[V]]( - pc: Int, - declaringClass: ReferenceType, - isInterface: Boolean, - name: String, - descriptor: MethodDescriptor, - receiver: Expr[V], - params: Seq[Expr[V]] + pc: Int, + declaringClass: ReferenceType, + isInterface: Boolean, + name: String, + descriptor: MethodDescriptor, + receiver: Expr[V], + params: Seq[Expr[V]] ) extends InstanceMethodCall[V] with VirtualCall[V] { - final override def asVirtualMethodCall: this.type = this - final override def isVirtualMethodCall: Boolean = true - final override def astID: Int = VirtualMethodCall.ASTID - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - VirtualMethodCall( - pc, - declaringClass, - isInterface, - name, - descriptor, - receiver.toCanonicalForm, - params.map(_.toCanonicalForm) - ) - } - - override def toString: String = { - val sig = descriptor.toJava(name) - val declClass = declaringClass.toJava - val params = this.params.mkString("(", ",", ")") - s"VirtualMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$receiver,$params)" - } + final override def asVirtualMethodCall: this.type = this + final override def isVirtualMethodCall: Boolean = true + final override def astID: Int = VirtualMethodCall.ASTID + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + VirtualMethodCall( + pc, + declaringClass, + isInterface, + name, + descriptor, + receiver.toCanonicalForm, + params.map(_.toCanonicalForm) + ) + } + + override def toString: String = { + val sig = descriptor.toJava(name) + val declClass = declaringClass.toJava + val params = this.params.mkString("(", ",", ")") + s"VirtualMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$receiver,$params)" + } } object VirtualMethodCall { - final val ASTID = 16 + final val ASTID = 16 } case class StaticMethodCall[+V <: Var[V]]( - pc: Int, - declaringClass: ObjectType, - isInterface: Boolean, - name: String, - descriptor: MethodDescriptor, - params: Seq[Expr[V]] + pc: Int, + declaringClass: ObjectType, + isInterface: Boolean, + name: String, + descriptor: MethodDescriptor, + params: Seq[Expr[V]] ) extends MethodCall[V] { - final override def allParams: Seq[Expr[V]] = params - - final override def asStaticMethodCall: this.type = this - final override def isStaticMethodCall: Boolean = true - final override def astID: Int = StaticMethodCall.ASTID - final override def receiverOption: Option[Expr[V]] = None - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - params.forall(param ⇒ p(param)) - } - - /** - * Identifies the potential call target if it can be found. - * - * @see [ProjectLike#staticCall] for further details. - */ - def resolveCallTarget(implicit p: ProjectLike): Result[Method] = { - p.staticCall(declaringClass, isInterface, name, descriptor) - } - - // convenience method to enable Call to define a single method to handle all kinds of calls - def resolveCallTargets( - callingContext: ObjectType - )( - implicit - p: ProjectLike, - ev: V <:< DUVar[ValueInformation] - ): Set[Method] = { - resolveCallTarget(p).toSet - } - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - params.foreach { p ⇒ p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) } - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - StaticMethodCall( - pc, - declaringClass, - isInterface, - name, - descriptor, - params.map(_.toCanonicalForm) - ) - } - - override def toString: String = { - val sig = descriptor.toJava(name) - val declClass = declaringClass.toJava - val params = this.params.mkString("(", ",", ")") - s"StaticMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$params)" - } + final override def allParams: Seq[Expr[V]] = params + + final override def asStaticMethodCall: this.type = this + final override def isStaticMethodCall: Boolean = true + final override def astID: Int = StaticMethodCall.ASTID + final override def receiverOption: Option[Expr[V]] = None + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + params.forall(param => p(param)) + } + + /** + * Identifies the potential call target if it can be found. + * + * @see [ProjectLike#staticCall] for further details. + */ + def resolveCallTarget(implicit p: ProjectLike): Result[Method] = { + p.staticCall(declaringClass, isInterface, name, descriptor) + } + + // convenience method to enable Call to define a single method to handle all kinds of calls + def resolveCallTargets( + callingContext: ObjectType + )( + implicit + p: ProjectLike, + ev: V <:< DUVar[ValueInformation] + ): Set[Method] = { + resolveCallTarget(p).toSet + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + params.foreach { p => + p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + StaticMethodCall( + pc, + declaringClass, + isInterface, + name, + descriptor, + params.map(_.toCanonicalForm) + ) + } + + override def toString: String = { + val sig = descriptor.toJava(name) + val declClass = declaringClass.toJava + val params = this.params.mkString("(", ",", ")") + s"StaticMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$params)" + } } object StaticMethodCall { - final val ASTID = 17 + final val ASTID = 17 } /** @@ -957,96 +978,98 @@ object StaticMethodCall { * @tparam V The type of the [[Var]]s. */ case class InvokedynamicMethodCall[+V <: Var[V]]( - pc: PC, - bootstrapMethod: BootstrapMethod, - name: String, - descriptor: MethodDescriptor, - params: Seq[Expr[V]] + pc: PC, + bootstrapMethod: BootstrapMethod, + name: String, + descriptor: MethodDescriptor, + params: Seq[Expr[V]] ) extends Stmt[V] { - final override def astID: Int = InvokedynamicMethodCall.ASTID - final override def asInvokedynamicMethodCall: this.type = this - // IMPROVE [FUTURE] Use some analysis to determine if a method call is side effect free - final override def isSideEffectFree: Boolean = false - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - params.forall(param ⇒ p(param)) - } - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - params.foreach { p ⇒ p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) } - } - - override def hashCode(): Int = { - (((InvokedynamicMethodCall.ASTID * 1171 + - pc) * 31 + - bootstrapMethod.hashCode) * 31 + - name.hashCode) * 31 + - descriptor.hashCode - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - InvokedynamicMethodCall( - pc, - bootstrapMethod, - name, - descriptor, - params.map(_.toCanonicalForm) - ) - } - - override def toString: String = { - val sig = descriptor.toJava(name) - val params = this.params.mkString("(", ",", ")") - s"InvokedynamicMethodCall(pc=$pc,$bootstrapMethod,$sig,$params)" - } + final override def astID: Int = InvokedynamicMethodCall.ASTID + final override def asInvokedynamicMethodCall: this.type = this + // IMPROVE [FUTURE] Use some analysis to determine if a method call is side effect free + final override def isSideEffectFree: Boolean = false + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + params.forall(param => p(param)) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + params.foreach { p => + p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + } + + override def hashCode(): Int = { + (((InvokedynamicMethodCall.ASTID * 1171 + + pc) * 31 + + bootstrapMethod.hashCode) * 31 + + name.hashCode) * 31 + + descriptor.hashCode + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + InvokedynamicMethodCall( + pc, + bootstrapMethod, + name, + descriptor, + params.map(_.toCanonicalForm) + ) + } + + override def toString: String = { + val sig = descriptor.toJava(name) + val params = this.params.mkString("(", ",", ")") + s"InvokedynamicMethodCall(pc=$pc,$bootstrapMethod,$sig,$params)" + } } object InvokedynamicMethodCall { final val ASTID = 18 } /** An expression where the value is not further used. */ case class ExprStmt[+V <: Var[V]](pc: Int, expr: Expr[V]) extends AssignmentLikeStmt[V] { - final override def asExprStmt: this.type = this - final override def isExprStmt: Boolean = true - final override def astID: Int = ExprStmt.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(expr) - } - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def isSideEffectFree: Boolean = { - assert( - !expr.isSideEffectFree, - "useless ExprStmt - the referenced expression is side-effect free" - ) - false - } - - override def hashCode(): Opcode = (ExprStmt.ASTID * 1171 + pc) * 31 + expr.hashCode - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - ExprStmt(pc, expr.toCanonicalForm) - } - - override def toString: String = s"ExprStmt(pc=$pc,$expr)" + final override def asExprStmt: this.type = this + final override def isExprStmt: Boolean = true + final override def astID: Int = ExprStmt.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(expr) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def isSideEffectFree: Boolean = { + assert( + !expr.isSideEffectFree, + "useless ExprStmt - the referenced expression is side-effect free" + ) + false + } + + override def hashCode(): Opcode = (ExprStmt.ASTID * 1171 + pc) * 31 + expr.hashCode + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + ExprStmt(pc, expr.toCanonicalForm) + } + + override def toString: String = s"ExprStmt(pc=$pc,$expr)" } object ExprStmt { - final val ASTID = 19 + final val ASTID = 19 } /** @@ -1055,13 +1078,13 @@ object ExprStmt { */ object VirtualFunctionCallStatement { - def unapply[V <: Var[V]](stmt: Stmt[V]): Option[VirtualFunctionCall[V]] = { - stmt match { - case ExprStmt(_, vfc: VirtualFunctionCall[V]) ⇒ Some(vfc) - case Assignment(_, _, vfc: VirtualFunctionCall[V]) ⇒ Some(vfc) - case _ ⇒ None - } + def unapply[V <: Var[V]](stmt: Stmt[V]): Option[VirtualFunctionCall[V]] = { + stmt match { + case ExprStmt(_, vfc: VirtualFunctionCall[V]) => Some(vfc) + case Assignment(_, _, vfc: VirtualFunctionCall[V]) => Some(vfc) + case _ => None } + } } /** @@ -1070,13 +1093,13 @@ object VirtualFunctionCallStatement { */ object NonVirtualFunctionCallStatement { - def unapply[V <: Var[V]](stmt: Stmt[V]): Option[NonVirtualFunctionCall[V]] = { - stmt match { - case ExprStmt(_, vfc: NonVirtualFunctionCall[V]) ⇒ Some(vfc) - case Assignment(_, _, vfc: NonVirtualFunctionCall[V]) ⇒ Some(vfc) - case _ ⇒ None - } + def unapply[V <: Var[V]](stmt: Stmt[V]): Option[NonVirtualFunctionCall[V]] = { + stmt match { + case ExprStmt(_, vfc: NonVirtualFunctionCall[V]) => Some(vfc) + case Assignment(_, _, vfc: NonVirtualFunctionCall[V]) => Some(vfc) + case _ => None } + } } /** @@ -1085,13 +1108,13 @@ object NonVirtualFunctionCallStatement { */ object StaticFunctionCallStatement { - def unapply[V <: Var[V]](stmt: Stmt[V]): Option[StaticFunctionCall[V]] = { - stmt match { - case ExprStmt(_, vfc: StaticFunctionCall[V]) ⇒ Some(vfc) - case Assignment(_, _, vfc: StaticFunctionCall[V]) ⇒ Some(vfc) - case _ ⇒ None - } + def unapply[V <: Var[V]](stmt: Stmt[V]): Option[StaticFunctionCall[V]] = { + stmt match { + case ExprStmt(_, vfc: StaticFunctionCall[V]) => Some(vfc) + case Assignment(_, _, vfc: StaticFunctionCall[V]) => Some(vfc) + case _ => None } + } } /** @@ -1101,76 +1124,78 @@ object StaticFunctionCallStatement { * @note `CaughtException` expression are only created by [[TACAI]]! */ case class CaughtException[+V <: Var[V]]( - pc: PC, - exceptionType: Option[ObjectType], - private var throwingStmts: IntTrieSet + pc: PC, + exceptionType: Option[ObjectType], + private var throwingStmts: IntTrieSet ) extends Stmt[V] { - final override def asCaughtException: CaughtException[V] = this - final override def astID: Int = CaughtException.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = true - - final override def isSideEffectFree: Boolean = false - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - throwingStmts = throwingStmts map { pc ⇒ ai.remapPC(pcToIndex)(pc) } - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - this.asInstanceOf[Stmt[DUVar[ValueInformation]]] - } - - /** - * The origin(s) of the caught exception(s). An origin identifies the instruction - * that ex- or implicitly created the exception: - * - If the exception is created locally (`new XXXException`) and also caught within the - * same method, then the origin identifies a normal variable definition site. - * - If the exception is a parameter the parameter's origin (-1,... -n) is returned. - * - If the exception was raised due to a sideeffect of evaluating an expression, then the - * origin is smaller or equal to [[org.opalj.ai.ImmediateVMExceptionsOriginOffset]] and can be - * tranformed to the index of the responsible instruction using - * [[org.opalj.ai#pcOfImmediateVMException]]. - */ - def origins: IntTrieSet = throwingStmts - - /** - * Textual description of the sources of the caught exceptions. If the exception was - * thrown by the JVM due to the evaluation of an expression (e.g., NullPointerException, - * DivisionByZero,..) then the string will be `exception@` where index identifies - * the failing expression. In case an exception is caught that was thrown using `ATHROW` - * the local variable/parameter which stores the local variable is returned. - */ - final def exceptionLocations: Iterator[String] = { - throwingStmts.iterator.map { defSite ⇒ - if (defSite < 0) { - if (ai.isImmediateVMException(defSite)) - "exception[VM]@"+ai.pcOfImmediateVMException(defSite) - else if (ai.isMethodExternalExceptionOrigin(defSite)) - "exception@"+ai.pcOfMethodExternalException(defSite) - else - "param"+(-defSite - 1).toHexString - } else { - "lv"+defSite.toHexString - } - } - } - - override def toString: String = { - val exceptionType = this.exceptionType.map(_.toJava).getOrElse("") - val exceptionLocations = this.exceptionLocations.mkString("{", ",", "}") - s"CaughtException(pc=$pc,$exceptionType,caused by=$exceptionLocations)" - } + final override def asCaughtException: CaughtException[V] = this + final override def astID: Int = CaughtException.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = true + + final override def isSideEffectFree: Boolean = false + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + throwingStmts = throwingStmts map { pc => + ai.remapPC(pcToIndex)(pc) + } + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + this.asInstanceOf[Stmt[DUVar[ValueInformation]]] + } + + /** + * The origin(s) of the caught exception(s). An origin identifies the instruction + * that ex- or implicitly created the exception: + * - If the exception is created locally (`new XXXException`) and also caught within the + * same method, then the origin identifies a normal variable definition site. + * - If the exception is a parameter the parameter's origin (-1,... -n) is returned. + * - If the exception was raised due to a sideeffect of evaluating an expression, then the + * origin is smaller or equal to [[org.opalj.ai.ImmediateVMExceptionsOriginOffset]] and can be + * tranformed to the index of the responsible instruction using + * [[org.opalj.ai#pcOfImmediateVMException]]. + */ + def origins: IntTrieSet = throwingStmts + + /** + * Textual description of the sources of the caught exceptions. If the exception was + * thrown by the JVM due to the evaluation of an expression (e.g., NullPointerException, + * DivisionByZero,..) then the string will be `exception@` where index identifies + * the failing expression. In case an exception is caught that was thrown using `ATHROW` + * the local variable/parameter which stores the local variable is returned. + */ + final def exceptionLocations: Iterator[String] = { + throwingStmts.iterator.map { defSite => + if (defSite < 0) { + if (ai.isImmediateVMException(defSite)) + "exception[VM]@" + ai.pcOfImmediateVMException(defSite) + else if (ai.isMethodExternalExceptionOrigin(defSite)) + "exception@" + ai.pcOfMethodExternalException(defSite) + else + "param" + (-defSite - 1).toHexString + } else { + "lv" + defSite.toHexString + } + } + } + + override def toString: String = { + val exceptionType = this.exceptionType.map(_.toJava).getOrElse("") + val exceptionLocations = this.exceptionLocations.mkString("{", ",", "}") + s"CaughtException(pc=$pc,$exceptionType,caused by=$exceptionLocations)" + } } object CaughtException { - final val ASTID = 20 + final val ASTID = 20 } @@ -1179,38 +1204,38 @@ object CaughtException { */ case class Checkcast[+V <: Var[V]](pc: PC, value: Expr[V], cmpTpe: ReferenceType) extends Stmt[V] { - final override def asCheckcast: this.type = this - final override def astID: Int = Checkcast.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { - p(value) - } - - private[tac] override def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int ⇒ Boolean - ): Unit = { - value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - Checkcast(pc, value.toCanonicalForm, cmpTpe) - } - - final override def isSideEffectFree: Boolean = { - // IMPROVE identify (from the JVM verifiers point-of-view) truly useless checkcasts - // A useless checkcast is one where the static intra-procedural type information which - // is available in the bytecode is sufficient to determine that the type is a subtype - // of the tested type (i.e., only those check casts are truly usefull that would not - // lead to a failing validation of the bytecode by the JVM!) - false - } - - override def toString: String = s"Checkcast(pc=$pc,$value,${cmpTpe.toJava})" + final override def asCheckcast: this.type = this + final override def astID: Int = Checkcast.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { + p(value) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int => Boolean + ): Unit = { + value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + Checkcast(pc, value.toCanonicalForm, cmpTpe) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE identify (from the JVM verifiers point-of-view) truly useless checkcasts + // A useless checkcast is one where the static intra-procedural type information which + // is available in the bytecode is sufficient to determine that the type is a subtype + // of the tested type (i.e., only those check casts are truly usefull that would not + // lead to a failing validation of the bytecode by the JVM!) + false + } + + override def toString: String = s"Checkcast(pc=$pc,$value,${cmpTpe.toJava})" } object Checkcast { - final val ASTID = 21 + final val ASTID = 21 } From 190a4bc448ee9a92d784caeaf2e5db5e19dc3326 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 16 Jan 2020 17:01:07 +0100 Subject: [PATCH 070/327] First approach for recognizing double checked locking pattern. Not yet works with all tests. --- ...bilityLazyInitializationAnalysisDemo.scala | 65 + .../DoubleCheckedLockingClass1.java | 22 + .../SimpleLazyInstantiation.java | 14 + .../SimpleLazyIntInstantiation.java | 13 + .../SimpleLazyObjectsInstantiation.java | 14 + .../LazyInitializationAnnotation.java | 31 + .../NoLazyInitializationAnnotation.java | 31 + ...hreadSafeLazyInitializationAnnotation.java | 38 + ...eImmutabilityLazyInitializationTests.scala | 85 ++ ...mmutabilityLazyInitializationMatcher.scala | 62 + ...erenceImmutabilityLazyInitialization.scala | 50 + ...mutabilityLazyInitializationAnalysis.scala | 1328 +++++++++++++++++ 12 files changed, 1753 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutabilityLazyInitialization.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala new file mode 100644 index 0000000000..a9b3b9b7ac --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala @@ -0,0 +1,65 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.br.fpcf.properties.LazyInitialization +import org.opalj.br.fpcf.properties.NoLazyInitialization +import org.opalj.br.fpcf.properties.NotThreadSafeLazyInitialization +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis + +/** + * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object ReferenceImmutabilityLazyInitializationAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + val (propertyStore, _) = analysesManager.runAll( + EagerL0ReferenceImmutabilityLazyInitializationAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis + ); + + "Not lazy initialized References: " + propertyStore + .finalEntities(NoLazyInitialization) + .toList + .toString() + "\n" + + "Not thread safe lazy initialization: " + propertyStore + .finalEntities(NotThreadSafeLazyInitialization) + .toList + .toString() + "\n" + + "Lazy Initialization: " + propertyStore + .finalEntities(LazyInitialization) + .toList + .toString() + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java new file mode 100644 index 0000000000..dd566fb057 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java @@ -0,0 +1,22 @@ +package org.opalj.fpcf.fixtures.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.LazyInitializationAnnotation; + +public class DoubleCheckedLockingClass1{ + + @LazyInitializationAnnotation("") + private static DoubleCheckedLockingClass1 instance; + public static DoubleCheckedLockingClass1 getInstance() { + if(instance==null){ + synchronized(DoubleCheckedLockingClass1.class) { + if(instance==null){ + instance = new DoubleCheckedLockingClass1(); + } + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java new file mode 100644 index 0000000000..f92be000bb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java @@ -0,0 +1,14 @@ +package org.opalj.fpcf.fixtures.reference_immutability_lazy_initialization; + +import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.NotThreadSafeLazyInitializationAnnotation; + +public class SimpleLazyInstantiation{ + @NotThreadSafeLazyInitializationAnnotation("") + private SimpleLazyInstantiation instance; + + public SimpleLazyInstantiation init() { + SimpleLazyInstantiation result; + result = instance == null ? new SimpleLazyInstantiation() : instance; + return result; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java new file mode 100644 index 0000000000..b5c65fb42f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java @@ -0,0 +1,13 @@ +package org.opalj.fpcf.fixtures.reference_immutability_lazy_initialization; + +import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.LazyInitializationAnnotation; + +public class SimpleLazyIntInstantiation{ + @LazyInitializationAnnotation("") + 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/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java new file mode 100644 index 0000000000..f8eafab1b3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java @@ -0,0 +1,14 @@ +package org.opalj.fpcf.fixtures.reference_immutability_lazy_initialization; + +import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.NotThreadSafeLazyInitializationAnnotation; + +public class SimpleLazyObjectsInstantiation{ + @NotThreadSafeLazyInitializationAnnotation("") + private SimpleLazyObjectsInstantiation instance; + public SimpleLazyObjectsInstantiation getInstance() { + if(instance==null) + instance = new SimpleLazyObjectsInstantiation(); + return instance; + } +} + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java new file mode 100644 index 0000000000..bcd4e1dcaf --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability_lazy_initialization; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceMatcher; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is lazy initialized + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = LazyInitializedReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface LazyInitializationAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityLazyInitializationAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java new file mode 100644 index 0000000000..3a7eb903b5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability_lazy_initialization; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceMatcher; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated reference is immutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = NoLazyInitializationMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface NoLazyInitializationAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityLazyInitializationAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java new file mode 100644 index 0000000000..d26cfd01e9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java @@ -0,0 +1,38 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability_lazy_initialization; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceMatcher; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated reference is mutable + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = NotThreadSafeInitializationMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface NotThreadSafeLazyInitializationAnnotation { + + + /** + * True if the field is non-final because it is read prematurely. + * Tests may ignore @NonFinal annotations if the FieldPrematurelyRead property for the field + * did not identify the premature read. + */ + boolean prematurelyRead() default false; + /** + * A short reasoning of this property. + */ + String value() default "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityLazyInitializationAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala new file mode 100644 index 0000000000..53723d03b7 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala @@ -0,0 +1,85 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis + +/** + * @author Tobias Peter Roth + */ +class ReferenceImmutabilityLazyInitializationTests extends PropertiesTest { + + 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("ReferenceImmutabilityLazyInitialization") + ) + } + + describe("the org.opalj.fpcf.analyses.ReferenceImmutabilityLazyInitialization is executed") { + val as = executeAnalyses( + Set( + EagerL0ReferenceImmutabilityLazyInitializationAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties( + as, + fieldsWithAnnotations(as.project), + Set("ReferenceImmutabilityLazyInitialization") + ) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala new file mode 100644 index 0000000000..78eb7a2a70 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala @@ -0,0 +1,62 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability_lazy_initialization + +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.LazyInitialization +import org.opalj.br.fpcf.properties.NoLazyInitialization +import org.opalj.br.fpcf.properties.NotThreadSafeLazyInitialization +import org.opalj.br.fpcf.properties.ReferenceImmutabilityLazyInitialization +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher + +/** + * @author Tobias Peter Roth + */ +class ReferenceImmutabilityLazyInitializationMatcher( + val property: ReferenceImmutabilityLazyInitialization +) 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 NoLazyInitializationMatcher + extends ReferenceImmutabilityLazyInitializationMatcher(NoLazyInitialization) + +class NotThreadSafeInitializationMatcher + extends ReferenceImmutabilityLazyInitializationMatcher(NotThreadSafeLazyInitialization) + +class LazyInitializationMatcher + extends ReferenceImmutabilityLazyInitializationMatcher(LazyInitialization) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutabilityLazyInitialization.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutabilityLazyInitialization.scala new file mode 100644 index 0000000000..1f0a796ec7 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutabilityLazyInitialization.scala @@ -0,0 +1,50 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.fpcf.properties + +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyMetaInformation + +sealed trait ReferenceImmutabilityLazyInstantiationPropertyMetaInformation + extends PropertyMetaInformation { + + type Self = ReferenceImmutabilityLazyInitialization + +} + +/** + * Describes if the reference of a org.opalj.br.Field was lazyliy initialized. + * + * [[NoLazyInitialization]] The reference is not lazily initialized + * + * [[NotThreadSafeLazyInitialization]] The reference is lazily initialized, but not threadsafe + * + * [[LazyInitialization]] The reference is lazy initialized + * + * @author Tobias Peter Roth + */ +sealed trait ReferenceImmutabilityLazyInitialization + extends Property + with ReferenceImmutabilityLazyInstantiationPropertyMetaInformation { + final def key: PropertyKey[ReferenceImmutabilityLazyInitialization] = + ReferenceImmutabilityLazyInitialization.key +} + +object ReferenceImmutabilityLazyInitialization + extends ReferenceImmutabilityLazyInstantiationPropertyMetaInformation { + + final val PropertyKeyName = "opalj.ReferenceImmutabilityLazyInitialization" + + final val key: PropertyKey[ReferenceImmutabilityLazyInitialization] = { + PropertyKey.create( + PropertyKeyName, + NoLazyInitialization + ) + } +} + +case object NoLazyInitialization extends ReferenceImmutabilityLazyInitialization + +case object NotThreadSafeLazyInitialization extends ReferenceImmutabilityLazyInitialization + +case object LazyInitialization extends ReferenceImmutabilityLazyInitialization diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala new file mode 100644 index 0000000000..11b5112836 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala @@ -0,0 +1,1328 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses + +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.BasicFPCFEagerAnalysisScheduler +import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.FieldAccessInformationKey +import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.cg.ClosedPackagesKey +import org.opalj.br.analyses.cg.TypeExtensibilityKey +import org.opalj.br.cfg.BasicBlock +import org.opalj.br.cfg.CFG +import org.opalj.br.cfg.CFGNode +import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.fpcf.properties.EscapeProperty +import org.opalj.br.fpcf.properties.FieldPrematurelyRead +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.ai.isImmediateVMException +import org.opalj.ai.pcOfImmediateVMException +import org.opalj.ai.pcOfMethodExternalException +import org.opalj.br.ComputationalTypeFloat +import org.opalj.br.ComputationalTypeInt +import org.opalj.br.DeclaredMethod +import org.opalj.br.Field +import org.opalj.br.FloatType +import org.opalj.br.Method +import org.opalj.br.ObjectType +import org.opalj.br.PC +import org.opalj.br.PCs +import org.opalj.br.fpcf.properties.AtMost +import org.opalj.br.fpcf.properties.EscapeInCallee +import org.opalj.br.fpcf.properties.EscapeViaReturn +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitialization +import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.br.fpcf.properties.NoEscape +import org.opalj.br.fpcf.properties.NoLazyInitialization +import org.opalj.br.fpcf.properties.NotPrematurelyReadField +import org.opalj.br.fpcf.properties.NotThreadSafeLazyInitialization +import org.opalj.br.fpcf.properties.PrematurelyReadField +import org.opalj.br.fpcf.properties.ReferenceImmutability +import org.opalj.br.fpcf.properties.ReferenceImmutabilityLazyInitialization +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.LBP +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyComputationResult +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.SomeEPS +import org.opalj.fpcf.UBP +import org.opalj.fpcf.Result +import org.opalj.tac.Assignment +import org.opalj.tac.CaughtException +import org.opalj.tac.DUVar +import org.opalj.tac.Expr +import org.opalj.tac.FieldWriteAccessStmt +import org.opalj.tac.GetField +import org.opalj.tac.GetStatic +import org.opalj.tac.If +import org.opalj.tac.NonVirtualFunctionCall +import org.opalj.tac.PutField +import org.opalj.tac.PutStatic +import org.opalj.tac.ReturnValue +import org.opalj.tac.StaticFunctionCall +import org.opalj.tac.Stmt +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACStmts +import org.opalj.tac.TACode +import org.opalj.tac.VirtualFunctionCall +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.tac.SelfReferenceParameter + +import scala.collection.mutable + +/** + * + * Implementation is used from the old L2FieldMutability implementation + * but the lattice is mapped to the new reference immutability lattice. + * + * @note Requires that the 3-address code's expressions are not deeply nested. + * + * @author Dominik Helm + * @author Florian Kübler + * @author Michael Eichberg + * @author Tobias Peter Roth + */ +class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val project: SomeProject) + extends FPCFAnalysis { + + case class State( + field: Field, + var referenceImmutability: ReferenceImmutabilityLazyInitialization = NoLazyInitialization, + 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, + var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty, + var isThreadSafeType: Boolean = true + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty + } + + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + } + } + + 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) + + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field ⇒ determineReferenceImmutabilityLazyInitialization(field) + case _ ⇒ + val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + + /** + * Analyzes the mutability of private non-final fields. + * + * 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 determineReferenceImmutabilityLazyInitialization( + field: Field + ): ProperPropertyComputationResult = { + implicit val state: State = State(field) + // Fields are not final if they are read prematurely! + //if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) + // return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); + //if (field.isFinal) + // return createResult(); + + //state.referenceImmutability = ImmutableReference //EffectivelyFinalField + + //val thisType = field.classFile.thisType + + //if (field.isPublic) + // return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) + + // 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) { + * if (!closedPackages.isClosed(thisType.packageName)) { + * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + * } + * project.classesPerPackage(thisType.packageName) + * } else { + * Set(field.classFile) + * } + * + * val classesHavingAccess: Iterator[ClassFile] = + * if (field.isProtected) { + * if (typeExtensibility(thisType).isYesOrUnknown) { + * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + * } + * 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))) + /// return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode ← getTACAI(method, pcs) + } { + //if ( + checkMethod(method, taCode, pcs) //) { + // return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); + //} + + } + + ///if (state.lazyInitInvocation.isDefined) { + /// val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + /// handleCalls(calleesEOP) + ///} + + createResult() + } + + /** + * def handleCalls( + * calleesEOP: EOptionP[DeclaredMethod, Callees] + * )( + * implicit + * state: State + * ): Boolean = { + * calleesEOP match { + * case FinalP(callees) => + * state.calleesDependee = None + * handleCallees(callees) + * case InterimUBP(callees) => + * state.calleesDependee = Some(calleesEOP) + * handleCallees(callees) + * case _ => + * state.calleesDependee = Some(calleesEOP) + * false + * } + * }* + */ + /** + * + * def handleCallees(callees: Callees)(implicit state: State): Boolean = { + * val pc = state.lazyInitInvocation.get._2 + * if (callees.isIncompleteCallSite(pc)) { + * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + * true + * } else { + * val targets = callees.callees(pc).toTraversable + * if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { + * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + * true + * } else false + * } + * }* + */ + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + * The code below can be used to identify such value, but is not yet adapted to using the + * TACAI property */ + /* + var constantVal: Option[Any] = None + var allInitializeConstant = true + + val field = state.field + var constructors: Set[Method] = + if(field.isStatic) Set.empty else field.classFile.constructors.toSet + + val writesIterator = fieldAccessInformation.writeAccesses(field).iterator + while (writesIterator.hasNext && allInitializeConstant) { + val (method, pc) = writesIterator.next() + constructors -= method + val code = tacai(method).stmts + + val index = pcToIndex(pc) + val stmt = code(index) + if (stmt.astID == PutStatic.ASTID || + stmt.asPutField.objRef.asVar.definedBy == SelfReferenceParameter) { + val write = stmt.asFieldWriteAccessStmt + if (write.resolveField(p).contains(state.field)) { + val defs = write.value.asVar.definedBy + if (defs.size == 1 && defs.head >= 0) { + val defSite = code(defs.head).asAssignment.expr + val const = if (defSite.isIntConst) + Some(defSite.asIntConst.value) + else if (defSite.isFloatConst) + Some(defSite.asFloatConst.value) + else None + if (const.isDefined) { + if (constantVal.isDefined) { + if (constantVal != const) { + allInitializeConstant = false + constantVal = None + } + } else constantVal = const + } else { + allInitializeConstant = false + constantVal = None + } + } + } + } + } + + for (constructor ← constructors) { + // TODO iterate all statements + val NonVirtualMethodCall(_, declClass, _, name, _, rcvr, _) = stmt + // Consider calls to other constructors as initializations as either + // the called constructor will initialize the field or delegate to yet + // another constructor + if (declClass != state.field.classFile.thisType || name != "" || + rcvr.asVar.definedBy != SelfReferenceParameter) { + if (constantVal.isDefined) allInitializeConstant = false + else constantVal = Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + } + } + + constantVal */ + } + + /** + * 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 NoLazyInitialization)) //NonFinalFieldByAnalysis)) + InterimResult( + state.field, + NoLazyInitialization, //MutableReference, //NonFinalFieldByAnalysis, + state.referenceImmutability, + state.dependees, + c + ) + else { + if (state.referenceImmutability == LazyInitialization && !state.isThreadSafeType) + Result(state.field, NotThreadSafeLazyInitialization) + 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 = { + val isNotFinal: Boolean = eps.pk match { + case EscapeProperty.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + checkMethod(method, newEP.ub.tac.get, pcs) + //case Callees.key => + // handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key ⇒ + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + isNonDeterministic(newEP) + case ReferenceImmutabilityLazyInitialization.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + !isImmutableReference(newEP) + } + + if (isNotFinal) { + Result(state.field, NoLazyInitialization) //Result(state.field, NonFinalFieldByAnalysis) + } else + createResult() + } + + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int], + pcs: PCs + )(implicit state: State): Boolean = { + println("PS: "+propertyStore(declaredMethods(method), Purity.key)) + val write = code(writeIndex).asFieldWriteAccessStmt + println("1") + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + ////return false; + state.isThreadSafeType = false + } + println("2") + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method + } + println("3") + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + case None ⇒ return false; + } + println("4") + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) //return false; + // possibly double checked locking + { + //TODO + if (isDoubleCheckedLocking(method, pcs)) { + state.isThreadSafeType = true; + return true; + } else + return false; + } + println("5") + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + println("6") + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + println("7") + true + } + + 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + def isDoubleCheckedLocking(method: Method, pcs: PCs)(implicit state: State): Boolean = { + val fieldAccessInformation = project.get(FieldAccessInformationKey) + var guards: List[(Int, Int)] = Nil + var assignments: List[Int] = Nil + var monitorEnter: Option[(Int, Option[ObjectType])] = None + var monitorExit: Option[(Int, Option[ObjectType])] = None + var result = true + val accessingPcs = fieldAccessInformation.allWriteAccesses + .filter(p ⇒ p._1 == state.field) + .head + ._2 + .filter(p ⇒ p._1 == method) + .head + ._2 + + if (state.field.fieldType.isObjectType && method.asMethod.isStatic) { + val tacCode = getTACAI(method, pcs) + tacCode match { + case Some(tac) ⇒ { + val hm = new mutable.HashMap[Int, Assignment[V]]() + //index for tac-code + var i: Int = -1 + tac.instructions.foreach(instr ⇒ { + i = i + 1 + if (instr.isIfStmt) { + val currentFieldsClassType = state.field.fieldType.asObjectType // method.classFile.thisType + val ifLeftType = instr.asIf.left.asVar.value.asReferenceValue.asReferenceType + if ( //is non null check + instr.asIf.condition == NE && + // has same type as field + ifLeftType.equals(currentFieldsClassType) //guards field? + ) { + // => guards the value + guards = (i, instr.asIf.target) :: guards + } + } + if (instr.isAssignment) { + hm += (i -> instr.asAssignment) + if (accessingPcs.contains(instr.pc)) + assignments = instr.pc.toInt :: assignments + } + + if (instr.isPutStatic && accessingPcs.contains(instr.pc)) { + assignments = i :: assignments + } + + if (instr.isMonitorEnter) { + val defB = instr.asMonitorEnter.objRef.asVar.definedBy.head + var objTypeOfMonitorEnter: Option[ObjectType] = None + try { + objTypeOfMonitorEnter = + Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) + } catch { + case _: Throwable ⇒ + } + monitorEnter = Some((i, objTypeOfMonitorEnter)) + hm.foreach(x ⇒ println(x)) + } + + if (instr.isMonitorExit) { + val defB = instr.asMonitorExit.objRef.asVar.definedBy.head + var objTypeOfMonitorExit: Option[ObjectType] = None + try { + objTypeOfMonitorExit = + Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) + } catch { + case _: Throwable ⇒ + } + monitorExit = Some((i, objTypeOfMonitorExit)) + } + }) + } + case _ ⇒ result = false + } + assignments.foreach(a ⇒ guards.foreach(g ⇒ result = result && a > g._1 && a < g._2)) + result = result && assignments.size >= 1 + result = result && monitorEnter != None && monitorExit != None + monitorEnter match { + case Some((n, Some(ot: ObjectType))) ⇒ + ot == state.field.fieldType.asObjectType && + //outer guard(s) + guards.filter(x ⇒ n > x._1).size >= 1 && + //inner guard(s) + guards.filter(x ⇒ n < x._1).size >= 1 + case None ⇒ result = false + case _ ⇒ result = false + } + monitorExit match { + case Some((n, Some(ot: ObjectType))) if (ot == state.field.fieldType.asObjectType) ⇒ + case None ⇒ result = false + case _ ⇒ result = false + } + result + } else + false + } + + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + println("51") + val definitions = write.value.asVar.definedBy + println("52") + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + println("53") + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) + println("54") + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + println("55") + // The field write must be guarded correctly + val r1 = isDeterministic + val r2 = checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + println("isDeterministic: "+r1) + println("checkWriteIsGuarded: "+r2) + //r1 && r2 //TODO check + if (!(r1 && r2) && !state.isThreadSafeType) { + //state.isThreadSafeType = false + true + } else + r1 && r2 + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID ⇒ + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID ⇒ + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ ⇒ return false; // neither a field read nor a return + } + index += 1 + } + false + } + + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + )) + result + } + + /** + * Analyzes field writes for a single method, returning false if the field may still be + * effectively final and true otherwise. + */ + def checkMethod( + method: Method, + taCode: TACode[TACMethodParameter, V], + pcs: PCs + )(implicit state: State): Boolean = { + val field = state.field + val stmts = taCode.stmts + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + + if (field.isStatic) { + if (method.isConstructor) + return true; + } else { + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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.referenceImmutability == NotThreadSafeLazyInitialization || state.referenceImmutability == LazyInitialization) //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; + + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + // A field written outside an initializer must be lazily + // initialized or it is non-final + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex, + pcs + )) + return true; + state.referenceImmutability = LazyInitialization //LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead + } + } + } + false + } + + /** + * 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 eps: InterimEP[Method, TACAI] ⇒ + * state.tacDependees += method → ((eps, pcs)) + * eps.ub.tac + * case epk ⇒ + * state.tacDependees += method → ((epk, pcs)) + * None + * } + * } + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) + } + } + } + } + + /** + * 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 = 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 + } + + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt ⇒ + stmt.astID == CaughtException.ASTID + } flatMap { exception ⇒ + exception.asCaughtException.origins.map { origin: Int ⇒ + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator + } + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement + + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + true + } + + /** + * 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]] + )(implicit state: State): Boolean = { + println("begin checkwriteisdeterministic") + def isConstant(uvar: Expr[V]): Boolean = { + val defSites = uvar.asVar.definedBy + + def isConstantDef(index: Int) = { + println("index: "+index) + if (index < 0) false + else if (code(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + println("expr: "+expr) + println("resolveField(p): "+expr.asFieldRead.resolveField(p)) + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + false + }) + } + } + + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) + } + + val value = origin.expr + + println("value.astID: "+value.astID) + + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID ⇒ + println("a") + value.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + println("b") + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + println("c") + false + } + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + println("d") + // 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.exists(!isConstant(_))) { + println("e") + false + } else { + println("f") + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true + } + case _ ⇒ + println("g") + // 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, code) + } + + value.isConst || isNonConstDeterministic + } + + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) + } + } + + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + true + } + + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode ⇒ + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) + } + result.toList + } + + /** + * 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. + */ + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID ⇒ + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + } + + None + } + + /** + * 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])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID ⇒ + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) + case _ ⇒ None + } + field.contains(state.field) + } + + /** + * 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]] + )(implicit state: State): Boolean = { + + /** + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + */ + def isDefaultConst(expr: Expr[V]): Boolean = { + println("Expression: "+expr) + 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } + + /** + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. + */ + def isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index ⇒ + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } + } + + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else //TODO check + //false + { + + //TODO if expression = Nullexpr + println("----------------------------------------<<<<") + state.isThreadSafeType = false + true //TODO check + } + } + + /** + * Checks if 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) ⇒ true + case eps ⇒ + state.prematurelyReadDependee = Some(eps) + false + } + + /** + * Checkes 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. + */ + 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 + } + + /** + * Checkes if 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, ReferenceImmutability] + )(implicit state: State): Boolean = eop match { + case FinalEP(e, ImmutableReference) ⇒ true + case FinalEP(e, MutableReference) ⇒ false + // + case LBP(ImmutableReference) ⇒ true + case UBP(MutableReference) ⇒ false + + /** + * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ + * true + * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * + */ + case _ ⇒ + state.referenceImmutabilityDependees += eop + true + } +} + +trait L0ReferenceImmutabilityLazyInitializationAnalysisScheduler extends FPCFAnalysisScheduler { + + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(ReferenceImmutabilityLazyInitialization), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.ub(EscapeProperty) + ) + + final def derivedProperty: PropertyBounds = + PropertyBounds.lub(ReferenceImmutabilityLazyInitialization) + +} + +/** + * Executor for the field mutability analysis. + */ +object EagerL0ReferenceImmutabilityLazyInitializationAnalysis + extends L0ReferenceImmutabilityLazyInitializationAnalysisScheduler + with BasicFPCFEagerAnalysisScheduler { + + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)( + analysis.determineReferenceImmutabilityLazyInitialization + ) + analysis + } + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty +} + +/** + * Executor for the lazy field mutability analysis. + */ +object LazyL0ReferenceImmutabilityLazyInitializationAnalysis + extends L0ReferenceImmutabilityLazyInitializationAnalysisScheduler + with BasicFPCFLazyAnalysisScheduler { + + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) + ps.registerLazyPropertyComputation( + ReferenceImmutabilityLazyInitialization.key, + analysis.determineReferenceImmutabilityLazyInitialization + ) + analysis + } + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) +} From b8840c3734fe67f16ca74944828e75293e3edf56 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 16 Jan 2020 20:02:36 +0100 Subject: [PATCH 071/327] double checked locking tests improvements. Now all should run --- .../SimpleLazyInstantiation.java | 10 +++++----- .../SimpleLazyObjectsInstantiation.java | 4 ++-- .../LazyInitializationAnnotation.java | 4 +--- .../ReferenceImmutabilityLazyInitializationTests.scala | 2 -- .../fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala | 2 +- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java index f92be000bb..aabf03c0bb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java @@ -4,11 +4,11 @@ public class SimpleLazyInstantiation{ @NotThreadSafeLazyInitializationAnnotation("") - private SimpleLazyInstantiation instance; + private static SimpleLazyInstantiation instance; - public SimpleLazyInstantiation init() { - SimpleLazyInstantiation result; - result = instance == null ? new SimpleLazyInstantiation() : instance; - return result; + public static SimpleLazyInstantiation init() { + if(instance==null) + instance = new SimpleLazyInstantiation(); + return instance; } } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java index f8eafab1b3..a53170923b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java @@ -4,8 +4,8 @@ public class SimpleLazyObjectsInstantiation{ @NotThreadSafeLazyInitializationAnnotation("") - private SimpleLazyObjectsInstantiation instance; - public SimpleLazyObjectsInstantiation getInstance() { + private static SimpleLazyObjectsInstantiation instance; + public static SimpleLazyObjectsInstantiation getInstance() { if(instance==null) instance = new SimpleLazyObjectsInstantiation(); return instance; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java index bcd4e1dcaf..cd5dbd96cb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java @@ -3,8 +3,6 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceMatcher; -import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; import java.lang.annotation.Documented; @@ -16,7 +14,7 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = LazyInitializedReferenceMatcher.class) +@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = LazyInitializationMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface LazyInitializationAnnotation { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala index 53723d03b7..4d23a7d223 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala @@ -9,7 +9,6 @@ import org.opalj.br.analyses.Project import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis @@ -40,7 +39,6 @@ class ReferenceImmutabilityLazyInitializationTests extends PropertiesTest { val as = executeAnalyses( Set( EagerL0ReferenceImmutabilityLazyInitializationAnalysis, - LazyL0ReferenceImmutabilityAnalysis, LazyL2FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index cc122f9762..ec5d976859 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -56,7 +56,7 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC e: Entity ): ProperPropertyComputationResult = e match { case t: ObjectType => step1(typeExtensibility)(t) - case _ => throw new IllegalArgumentException(s"$e is not an ObjectType") + case _ => throw new IllegalArgumentException(s"$e is not an ObjectType") } /** From e5302357ca4d5a7b245d8b03aa762f49a5252a2a Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Mon, 20 Jan 2020 10:37:18 +0100 Subject: [PATCH 072/327] state before merging with current dev branch. Including improvements for running on jdk. --- .../analyses/ClassImmutabilityAnalysis.scala | 4 +- .../src/main/scala/org/opalj/tac/Stmt.scala | 14 ++-- .../L0FieldImmutabilityAnalysis.scala | 64 +++++++++++-------- .../L0ReferenceImmutabilityAnalysis.scala | 22 ++++++- .../LxTypeImmutabilityAnalysis_new.scala | 3 +- 5 files changed, 67 insertions(+), 40 deletions(-) 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/ClassImmutabilityAnalysis.scala index 07c2d07b91..aba6712bc7 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/ClassImmutabilityAnalysis.scala @@ -219,7 +219,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability = superClassInformation match { case UBP(ImmutableContainer) => ImmutableContainer - case _ => ImmutableObject + case _ => ImmutableObject } if (cf.fields.exists(f => !f.isStatic && f.fieldType.isArrayType)) { @@ -463,7 +463,7 @@ trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { .map(ot => (ot, project.classFile(ot))) .foreach { case (_, Some(cf)) => cfs ::= cf - case (t, None) => + 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 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 a5e375ac93..f2d0421a8a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala @@ -350,7 +350,7 @@ object AssignmentLikeStmt { def unapply[V <: Var[V]](stmt: Stmt[V]): Option[(PC, Expr[V])] = { stmt match { case s: AssignmentLikeStmt[V] => Some((s.pc, s.expr)) - case _ => None + case _ => None } } @@ -1080,9 +1080,9 @@ object VirtualFunctionCallStatement { def unapply[V <: Var[V]](stmt: Stmt[V]): Option[VirtualFunctionCall[V]] = { stmt match { - case ExprStmt(_, vfc: VirtualFunctionCall[V]) => Some(vfc) + case ExprStmt(_, vfc: VirtualFunctionCall[V]) => Some(vfc) case Assignment(_, _, vfc: VirtualFunctionCall[V]) => Some(vfc) - case _ => None + case _ => None } } } @@ -1095,9 +1095,9 @@ object NonVirtualFunctionCallStatement { def unapply[V <: Var[V]](stmt: Stmt[V]): Option[NonVirtualFunctionCall[V]] = { stmt match { - case ExprStmt(_, vfc: NonVirtualFunctionCall[V]) => Some(vfc) + case ExprStmt(_, vfc: NonVirtualFunctionCall[V]) => Some(vfc) case Assignment(_, _, vfc: NonVirtualFunctionCall[V]) => Some(vfc) - case _ => None + case _ => None } } } @@ -1110,9 +1110,9 @@ object StaticFunctionCallStatement { def unapply[V <: Var[V]](stmt: Stmt[V]): Option[StaticFunctionCall[V]] = { stmt match { - case ExprStmt(_, vfc: StaticFunctionCall[V]) => Some(vfc) + case ExprStmt(_, vfc: StaticFunctionCall[V]) => Some(vfc) case Assignment(_, _, vfc: StaticFunctionCall[V]) => Some(vfc) - case _ => None + case _ => None } } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 82fe3670d2..1eff352c8a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -90,34 +90,39 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) "determine generic field imm-------------------------------------------------------------------" ) var genericFields: Set[ObjectType] = Set.empty - state.field.fieldTypeSignature.head match { - case ClassTypeSignature(_, SimpleClassTypeSignature(name, typeArguments), _) => { - typeArguments.foreach( - ta => { - ta match { - case ProperTypeArgument( - varianceIndicator, - ClassTypeSignature( - packageIdentifier1, - SimpleClassTypeSignature( - packageIdentifier2, - typeArguments2 - ), - _ - ) - ) => { - packageIdentifier1 match { - case Some(pid1) => genericFields += ObjectType(pid1 + packageIdentifier2) - case _ => genericFields += ObjectType(packageIdentifier2) - } + state.field.fieldTypeSignature match { + case Some(fts) => + //} + //state.field.fieldTypeSignature.head + fts match { + case ClassTypeSignature(_, SimpleClassTypeSignature(name, typeArguments), _) => { + typeArguments.foreach( + ta => { + ta match { + case ProperTypeArgument( + varianceIndicator, + ClassTypeSignature( + packageIdentifier1, + SimpleClassTypeSignature( + packageIdentifier2, + typeArguments2 + ), + _ + ) + ) => { + packageIdentifier1 match { + case Some(pid1) => genericFields += ObjectType(pid1 + packageIdentifier2) + case _ => genericFields += ObjectType(packageIdentifier2) + } + } + case _ => + } } - case _ => - } - + ) } - ) - } + case _ => + } case _ => } genericFields.foreach(f => println("generic Field: " + f)) @@ -152,7 +157,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) //--------------------------------------------------------------------------------------- state.typeImmutability = determineGenericFieldImmutability(state) //--------------------------------------------------------------------------------------- - //state.dependentTypeImmutability = Some(true) + //statfielde.dependentTypeImmutability = Some(true) return state.typeImmutability; } @@ -163,6 +168,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) return Some(false); } case x @ _ => { + println("x: " + x) dependencies += x return None; //TODO check!!!!! None; } @@ -277,6 +283,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case None if (dependencies.isEmpty) => Result(field, MutableField) case None => { + println("last step: ") + println("field: " + field) + println("MutableField: " + MutableField) + println("DeepImmutableField: " + DeepImmutableField) + println("dependencies: " + dependencies) InterimResult( field, MutableField, @@ -285,7 +296,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) c(state) ) } - } } def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index 95da9cdc5d..f6cf200319 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -42,6 +42,7 @@ import org.opalj.br.fpcf.properties.EscapeInCallee import org.opalj.br.fpcf.properties.EscapeViaReturn import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.LazyInitializedField import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.NoEscape import org.opalj.br.fpcf.properties.NotPrematurelyReadField @@ -386,9 +387,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec !isImmutableReference(newEP) } - if (isNotFinal) + if (isNotFinal) { Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) - else + } else createResult() } @@ -411,6 +412,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec cfg: CFG[Stmt[V], TACStmts[V]], pcToIndex: Array[Int] )(implicit state: State): Boolean = { + println("PS: " + propertyStore(declaredMethods(method), Purity.key)) val write = code(writeIndex).asFieldWriteAccessStmt if (state.field.fieldType.computationalType != ComputationalTypeInt && @@ -574,8 +576,21 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (defaultValue.isEmpty) return true; + //TODO Lazy Initialization here + /** + * val liResult = propertyStore(field, ReferenceImmutabilityLazyInitialization.key) + * + * liResult match { + * case FinalP(NoLazyInitialization) => return true; + * case FinalP(NotThreadSafeLazyInitialization) => return true; + * case FinalP(LazyInitialization) => + * state.referenceImmutability = LazyInitializedReference + * case _ => return true; + * } + */ // A field written outside an initializer must be lazily // initialized or it is non-final + if (!isLazyInitialization( index, defaultValue.get, @@ -586,7 +601,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec )) return true; - state.referenceImmutability = LazyInitializedReference //LazyInitializedField + state.referenceImmutability = LazyInitializedReference + LazyInitializedField } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { // note that here we assume real three address code (flat hierarchy) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index ec5d976859..09fef956d2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -226,7 +226,8 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC maxImmutability = ShallowImmutableType //ImmutableContainerType dependencies = dependencies - e nextResult() - case FinalEP(e, x) if (x == DependentImmutableClass || x == DependentImmutableType) => { + case FinalEP(e, DependentImmutableClass(_) | DependentImmutableType) => { + //if (x == DependentImmutableClass() || x == DependentImmutableType) => { maxImmutability = DependentImmutableType dependencies = dependencies - e nextResult() From 3e097187cab8e703d529392a644efae4bdf0fd2e Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 28 Jan 2020 16:26:22 +0100 Subject: [PATCH 073/327] Improving methods guaranteeing the ordering in the type immutability lattice --- .../properties/TypeImmutability_new.scala | 153 +++++++++--------- 1 file changed, 75 insertions(+), 78 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala index 6b43e42c51..68d4a303b6 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -8,7 +8,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait TypeImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = TypeImmutability_new + final type Self = TypeImmutability_new } /** @@ -29,19 +29,19 @@ sealed trait TypeImmutability_new extends OrderedProperty with TypeImmutabilityPropertyMetaInformation_new { - /** - * Returns the key used by all `TypeImmutability_new` properties. - */ - final def key = TypeImmutability_new.key + /** + * Returns the key used by all `TypeImmutability_new` properties. + */ + final def key = TypeImmutability_new.key - def isDeepImmutable: Boolean - def isShallowImmutable: Boolean - def isDependentImmutable: Boolean + def isDeepImmutable: Boolean + def isShallowImmutable: Boolean + def isDependentImmutable: Boolean - /** `true` if the immutability is unknown or if the type is mutable.*/ - def isMutable: Boolean + /** `true` if the immutability is unknown or if the type is mutable.*/ + def isMutable: Boolean - def meet(other: TypeImmutability_new): TypeImmutability_new + def meet(other: TypeImmutability_new): TypeImmutability_new } /** @@ -49,13 +49,13 @@ sealed trait TypeImmutability_new */ object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[TypeImmutability_new]] property. - */ - final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( - "org.opalj.TypeImmutability_new", - MutableType_new - ) + /** + * The key associated with every [[TypeImmutability_new]] property. + */ + final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( + "org.opalj.TypeImmutability_new", + MutableType_new + ) } /** @@ -64,79 +64,76 @@ object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new */ case object DeepImmutableType extends TypeImmutability_new { - override def isDeepImmutable: Boolean = true - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = false - override def isDependentImmutable: 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 = {} + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (this == that) - this - else - that + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (this == that) + this + else + that } -case object ShallowImmutableType extends TypeImmutability_new { +case object DependentImmutableType extends TypeImmutability_new { + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = true + + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (that == MutableType_new || 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"); + } + } - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = true - override def isMutable: Boolean = false - override def isDependentImmutable: Boolean = false - - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (that == MutableType_new) - 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 DependentImmutableType extends TypeImmutability_new { - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = false - override def isDependentImmutable: Boolean = true - - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (that == MutableType_new) - that - else - this - - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - /** - * if (other == DependentImmutableType || other == ShallowImmutableType) { - * throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - * } - * }* - */ +case object ShallowImmutableType extends TypeImmutability_new { + + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = true + override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = false + + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (that == MutableType_new) + that + else + this + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + + if (other == DeepImmutableType || other == DependentImmutableType) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } + } } case object MutableType_new extends TypeImmutability_new { - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = true - override def isDependentImmutable: 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_new): this.type = this - def meet(other: TypeImmutability_new): this.type = this + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - /** - * if (other != MutableType_new) { - * throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - * } - * }* - */ + if (other != MutableType_new) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } + } } From 8f318d22d0828b981acffe082a2852e31493bef4 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 28 Jan 2020 17:00:12 +0100 Subject: [PATCH 074/327] prepare analysis for running on jdk --- .../L0FieldImmutabilityAnalysis.scala | 58 +----- .../L0ReferenceImmutabilityAnalysis.scala | 1 - ...mutabilityLazyInitializationAnalysis.scala | 168 ++++++++++-------- .../LxClassImmutabilityAnalysis_new.scala | 18 +- 4 files changed, 107 insertions(+), 138 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 1eff352c8a..d115616740 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -43,7 +43,6 @@ import org.opalj.fpcf.PropertyComputationResult import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS -import org.opalj.tac.fpcf.properties.TACAI case class State(f: Field) { var field: Field = f @@ -86,14 +85,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var dependencies: Set[EOptionP[Entity, Property]] = Set.empty def determineGenericFieldImmutability(state: State): Option[Boolean] = { - println( - "determine generic field imm-------------------------------------------------------------------" - ) var genericFields: Set[ObjectType] = Set.empty state.field.fieldTypeSignature match { case Some(fts) => - //} - //state.field.fieldTypeSignature.head fts match { case ClassTypeSignature(_, SimpleClassTypeSignature(name, typeArguments), _) => { typeArguments.foreach( @@ -112,7 +106,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) => { packageIdentifier1 match { case Some(pid1) => genericFields += ObjectType(pid1 + packageIdentifier2) - case _ => genericFields += ObjectType(packageIdentifier2) + case _ => genericFields += ObjectType(packageIdentifier2) } } @@ -125,9 +119,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case _ => } - genericFields.foreach(f => println("generic Field: " + f)) - //state.typeImmutability = Some(true) - genericFields.toList.foreach(objectType => { val result = propertyStore(objectType, TypeImmutability_new.key) result match { @@ -147,7 +138,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (objectType.isArrayType) return Some(true); //TODO if (objectType.isBaseType) return Some(true); val result = propertyStore(objectType, TypeImmutability_new.key) - println("Result: " + result) result match { case FinalEP(e, DeepImmutableType) => { return Some(true); @@ -168,7 +158,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) return Some(false); } case x @ _ => { - println("x: " + x) dependencies += x return None; //TODO check!!!!! None; } @@ -176,27 +165,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } def hasImmutableType(field: Field)(state: State): Option[Boolean] = { - //val hasFieldImmutableType = handleTypeImmutability(field.fieldType.asFieldType)(state) - - /** - * hasFieldImmutableType match { - * case Some(false) => return Some(false); - * case _ => { - * state.depencenciesTypes.foreach( - * t => { - * val isImmutable = handleTypeImmutability(t)(state) - * isImmutable match { - * case Some(false) => return Some(false); - * case _ => - * } - * } - * ) - * Some(true) - * } - * } * - */ - } def hasImmutableReference(field: Field): Option[Boolean] = { @@ -222,8 +191,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(true) => { //If the field type is object. It is a generic field //Test Dependent... //TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //var genericString: Option[String] = None //if (field.fieldType == ObjectType("java/lang/Object")) { - println("field attributes: " + field.asField.attributes) + //++ println("field attributes: "+field.asField.attributes) val genericString = field.asField.attributes.toList.collectFirst({ case TypeVariableSignature(t) => t case ClassTypeSignature( @@ -237,7 +207,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case ProperTypeArgument(variance, signature) => { signature match { case TypeVariableSignature(identifier) => true - case _ => false + case _ => false } } case _ => false @@ -245,28 +215,21 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) }) if (tA.size > 0) tA.head match { - case ProperTypeArgument(variance, TypeVariableSignature(identifier)) => identifier + case ProperTypeArgument(variance, TypeVariableSignature(identifier)) => + identifier case _ => "" } else "" - } }) if (!field.attributes.isEmpty && genericString != None && genericString != Some("")) { - - //if(genericString!=None) - // println("Generic String: "+genericString) - // println( - // "test: "+DependentImmutableField(genericString).genericString - // } Result(field, DependentImmutableField(genericString)) - } else state.typeImmutability match { case Some(true) => Result(field, DeepImmutableField) case Some(false) => { state.dependentTypeImmutability match { case Some(true) => Result(field, DependentImmutableField()) - case _ => Result(field, ShallowImmutableField) + case _ => Result(field, ShallowImmutableField) } } case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) @@ -283,11 +246,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case None if (dependencies.isEmpty) => Result(field, MutableField) case None => { - println("last step: ") - println("field: " + field) - println("MutableField: " + MutableField) - println("DeepImmutableField: " + DeepImmutableField) - println("dependencies: " + dependencies) InterimResult( field, MutableField, @@ -335,7 +293,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) createResult(state) } - //-- state.depencenciesTypes = scala.collection.mutable.Set[ObjectType]() state.referenceImmutability = hasImmutableReference(field) state.typeImmutability = hasImmutableType(field)(state); @@ -347,7 +304,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.ub(TACAI), PropertyBounds.lub(ReferenceImmutability), PropertyBounds.lub(TypeImmutability_new), PropertyBounds.lub(FieldImmutability) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index f6cf200319..03bfaecc4b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -412,7 +412,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec cfg: CFG[Stmt[V], TACStmts[V]], pcToIndex: Array[Int] )(implicit state: State): Boolean = { - println("PS: " + propertyStore(declaredMethods(method), Purity.key)) val write = code(writeIndex).asFieldWriteAccessStmt if (state.field.fieldType.computationalType != ComputationalTypeInt && diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala index 11b5112836..3e33cbeb0f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala @@ -34,6 +34,7 @@ import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.PC import org.opalj.br.PCs +import org.opalj.br.ReferenceType import org.opalj.br.fpcf.properties.AtMost import org.opalj.br.fpcf.properties.EscapeInCallee import org.opalj.br.fpcf.properties.EscapeViaReturn @@ -443,9 +444,9 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p pcToIndex: Array[Int], pcs: PCs )(implicit state: State): Boolean = { - println("PS: "+propertyStore(declaredMethods(method), Purity.key)) + //xx println("PS: "+propertyStore(declaredMethods(method), Purity.key)) val write = code(writeIndex).asFieldWriteAccessStmt - println("1") + //xx println("1") if (state.field.fieldType.computationalType != ComputationalTypeInt && state.field.fieldType.computationalType != ComputationalTypeFloat) { // Only handle lazy initialization of ints and floats as they are guaranteed to be @@ -453,12 +454,12 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p ////return false; state.isThreadSafeType = false } - println("2") + //xx println("2") val reads = fieldAccessInformation.readAccesses(state.field) if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { return false; // Reads outside the (single) lazy initialization method } - println("3") + //xx println("3") // There must be a guarding if-Statement // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the // first statement that is executed after the if-Statement if the field's value was not the @@ -468,7 +469,7 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p case Some((guard, guarded, read)) ⇒ (guard, guarded, read) case None ⇒ return false; } - println("4") + //xx println("4") // Detect only simple patterns where the lazily initialized value is returned immediately if (!checkImmediateReturn(write, writeIndex, readIndex, code)) //return false; // possibly double checked locking @@ -480,16 +481,16 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p } else return false; } - println("5") + //xx println("5") // The value written must be computed deterministically and the writes guarded correctly if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) return false; - println("6") + //xx println("6") // Field reads (except for the guard) may only be executed if the field's value is not the // default value if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) return false; - println("7") + //xx println("7") true } @@ -531,55 +532,67 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p val hm = new mutable.HashMap[Int, Assignment[V]]() //index for tac-code var i: Int = -1 - tac.instructions.foreach(instr ⇒ { - i = i + 1 - if (instr.isIfStmt) { - val currentFieldsClassType = state.field.fieldType.asObjectType // method.classFile.thisType - val ifLeftType = instr.asIf.left.asVar.value.asReferenceValue.asReferenceType - if ( //is non null check - instr.asIf.condition == NE && - // has same type as field - ifLeftType.equals(currentFieldsClassType) //guards field? - ) { - // => guards the value - guards = (i, instr.asIf.target) :: guards + if (tac != null && tac.instructions != null) + tac.instructions.foreach(instr ⇒ { + i = i + 1 + if (instr.isIfStmt) { + var currentFieldsClassType: ObjectType = null + if (state.field.fieldType.isObjectType) + currentFieldsClassType = state.field.fieldType.asObjectType // method.classFile.thisType + var ifLeftType: ReferenceType = null + if (instr.asIf.left.isVar && instr.asIf.left.asVar.value.isReferenceValue) // && instr.asIf.left.asVar.value.asReferenceValue) + try { + ifLeftType = instr.asIf.left.asVar.value.asReferenceValue.asReferenceType + } catch { + case _: Throwable ⇒ + } + + if ( //is non null check + instr.asIf.condition == NE && + // has same type as field + currentFieldsClassType != null && + ifLeftType != null && + ifLeftType.equals(currentFieldsClassType) //guards field? + ) { + // => guards the value + guards = (i, instr.asIf.target) :: guards + } + } + if (instr.isAssignment) { + hm += (i -> instr.asAssignment) + if (accessingPcs.contains(instr.pc)) + assignments = instr.pc.toInt :: assignments } - } - if (instr.isAssignment) { - hm += (i -> instr.asAssignment) - if (accessingPcs.contains(instr.pc)) - assignments = instr.pc.toInt :: assignments - } - if (instr.isPutStatic && accessingPcs.contains(instr.pc)) { - assignments = i :: assignments - } + if (instr.isPutStatic && accessingPcs.contains(instr.pc)) { + assignments = i :: assignments + } - if (instr.isMonitorEnter) { - val defB = instr.asMonitorEnter.objRef.asVar.definedBy.head - var objTypeOfMonitorEnter: Option[ObjectType] = None - try { - objTypeOfMonitorEnter = - Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) - } catch { - case _: Throwable ⇒ + if (instr.isMonitorEnter) { + val defB = instr.asMonitorEnter.objRef.asVar.definedBy.head + var objTypeOfMonitorEnter: Option[ObjectType] = None + try { + objTypeOfMonitorEnter = + Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) + } catch { + case _: Throwable ⇒ + } + monitorEnter = Some((i, objTypeOfMonitorEnter)) + //hm.foreach(x ⇒ println(x)) } - monitorEnter = Some((i, objTypeOfMonitorEnter)) - hm.foreach(x ⇒ println(x)) - } - if (instr.isMonitorExit) { - val defB = instr.asMonitorExit.objRef.asVar.definedBy.head - var objTypeOfMonitorExit: Option[ObjectType] = None - try { - objTypeOfMonitorExit = - Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) - } catch { - case _: Throwable ⇒ + if (instr.isMonitorExit) { + val defB = instr.asMonitorExit.objRef.asVar.definedBy.head + var objTypeOfMonitorExit: Option[ObjectType] = None + try { + objTypeOfMonitorExit = + Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) + } catch { + case _: Throwable ⇒ + } + monitorExit = Some((i, objTypeOfMonitorExit)) } - monitorExit = Some((i, objTypeOfMonitorExit)) - } - }) + }) } case _ ⇒ result = false } @@ -588,7 +601,8 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p result = result && monitorEnter != None && monitorExit != None monitorEnter match { case Some((n, Some(ot: ObjectType))) ⇒ - ot == state.field.fieldType.asObjectType && + result = result && + ot == state.field.fieldType.asObjectType && //outer guard(s) guards.filter(x ⇒ n > x._1).size >= 1 && //inner guard(s) @@ -615,28 +629,28 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]] )(implicit state: State): Boolean = { - println("51") + //xx println("51") val definitions = write.value.asVar.definedBy - println("52") + //xx println("52") val isDeterministic = if (definitions.size == 1) { // The value written must be computed deterministically - println("53") + //xx println("53") checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) } else { // More than one definition site for the value might lead to differences between // invocations, but not if this method has no parameters and is deterministic // (in this case, the definition reaching the write will always be the same) - println("54") + //xx println("54") method.descriptor.parametersCount == 0 && !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) } - println("55") + //xx println("55") // The field write must be guarded correctly val r1 = isDeterministic val r2 = checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - println("isDeterministic: "+r1) - println("checkWriteIsGuarded: "+r2) + //xx println("isDeterministic: "+r1) + //xx println("checkWriteIsGuarded: "+r2) //r1 && r2 //TODO check if (!(r1 && r2) && !state.isThreadSafeType) { //state.isThreadSafeType = false @@ -924,18 +938,18 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p method: Method, code: Array[Stmt[V]] )(implicit state: State): Boolean = { - println("begin checkwriteisdeterministic") + //xx println("begin checkwriteisdeterministic") def isConstant(uvar: Expr[V]): Boolean = { val defSites = uvar.asVar.definedBy def isConstantDef(index: Int) = { - println("index: "+index) + //xx println("index: "+index) if (index < 0) false else if (code(defSites.head).asAssignment.expr.isConst) true else { val expr = code(index).asAssignment.expr - println("expr: "+expr) - println("resolveField(p): "+expr.asFieldRead.resolveField(p)) + //xx println("expr: "+expr) + //println("resolveField(p): " + expr.asFieldRead.resolveField(p)) expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { case Some(field) ⇒ isImmutableReference(propertyStore(field, ReferenceImmutability.key)) @@ -951,35 +965,35 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p val value = origin.expr - println("value.astID: "+value.astID) + //xx println("value.astID: "+value.astID) val isNonConstDeterministic = value.astID match { case GetStatic.ASTID | GetField.ASTID ⇒ - println("a") + //xx println("a") value.asFieldRead.resolveField(p) match { case Some(field) ⇒ - println("b") + //xx println("b") isImmutableReference(propertyStore(field, ReferenceImmutability.key)) case _ ⇒ // Unknown field - println("c") + //xx println("c") false } case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ - println("d") + //xx println("d") // 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.exists(!isConstant(_))) { - println("e") + //xx println("e") false } else { - println("f") + //xx println("f") state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) true } case _ ⇒ - println("g") + //xx println("g") // 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. @@ -1133,7 +1147,9 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p val ifStmt = code(result.get._1).asIf val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + var fieldReadUses: IntTrieSet = IntTrieSet.empty + if (definitions.head >= 0 && code(definitions.head).isAssignment) + fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ use == result.get._1 || use == result.get._2 } @@ -1174,8 +1190,10 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p * Checks if an expression is an IntConst or FloatConst with the corresponding default value. */ def isDefaultConst(expr: Expr[V]): Boolean = { - println("Expression: "+expr) - if (expr.isVar) { + //xx println("Expression: "+expr) + if (expr.isNullExpr) + true //-- + else if (expr.isVar) { val defSites = expr.asVar.definedBy val head = defSites.head defSites.size == 1 && head >= 0 && isDefaultConst(code(head).asAssignment.expr) @@ -1205,7 +1223,7 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p { //TODO if expression = Nullexpr - println("----------------------------------------<<<<") + //xx println("----------------------------------------<<<<") state.isThreadSafeType = false true //TODO check } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 264e39b48b..715084a6e6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -46,7 +46,6 @@ 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.br.fpcf.properties.TypeImmutability_new /** * @@ -151,9 +150,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal }) }) - //genericTypeBounds.toList.foreach({ x ⇒ - // println("Bound: "+x) - //}) genericTypeBounds } @@ -284,7 +280,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case _ => DeepImmutableClass // ImmutableObject } - if (!dependentImmutableFields.isEmpty) { + if (!dependentImmutableFields.isEmpty && maxLocalImmutability != ShallowImmutableClass) { maxLocalImmutability = DependentImmutableClass() } @@ -331,7 +327,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case UBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container if (someEPS.isFinal) dependees -= SuperClassKey maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) //TypeImmutability.key) + //dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) //TypeImmutability.key) case LBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && @@ -365,8 +361,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case ELBP(e, ShallowImmutableField | DeepImmutableField) => //FinalField) => dependees -= e - if (minLocalImmutability != ShallowImmutableClass && // ImmutableContainer && - !dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) + if (minLocalImmutability != ShallowImmutableClass) // && // ImmutableContainer && + //!dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible case UBP(ShallowImmutableField | DeepImmutableField) => //_: FinalField) ⇒ // no information about field mutability @@ -388,8 +384,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal */ // Lift lower bound once no dependencies other than field type mutabilities are left - if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && - dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) + if (minLocalImmutability != ShallowImmutableClass) // && //ImmutableContainer && + //dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) minLocalImmutability = ShallowImmutableClass //ImmutableContainer if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { /*[DEBUG] @@ -441,7 +437,7 @@ trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new, FieldImmutability) //TypeImmutability, FieldImmutability) + PropertyBounds.lubs(ClassImmutability_new, FieldImmutability) //TypeImmutability, FieldImmutability) override type InitializationData = TraversableOnce[ClassFile] From 321dd1464adacad76242cf8bd6cf9c2a67eeab91 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 28 Jan 2020 17:05:48 +0100 Subject: [PATCH 075/327] introducing performance measurements for evaluation purposes --- .../ClassImmutabilityAnalysisDemo.scala | 103 ++++++--- ...AnalysisDemo_performanceMeasurements.scala | 103 +++++++++ .../FieldImmutabilityAnalysisDemo.scala | 92 ++++++--- ...AnalysisDemo_performanceMeasurements.scala | 100 +++++++++ .../analyses/ImmutabilityAnalysisDemo.scala | 66 +++--- .../ImmutabilityAnalysisDemo_new.scala | 195 ++++++++++++++++++ .../ReferenceImmutabilityAnalysisDemo.scala | 110 ++++++---- ...AnalysisDemo_performanceMeasurements.scala | 97 +++++++++ ...bilityLazyInitializationAnalysisDemo.scala | 136 +++++++++--- ...AnalysisDemo_performanceMeasurements.scala | 117 +++++++++++ .../TypeImmutabilityAnalysisDemo.scala | 117 +++++++---- ...yAnalysisDemo_performanceMeasurement.scala | 105 ++++++++++ 12 files changed, 1147 insertions(+), 194 deletions(-) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala 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 index a5c34b7557..2918e919df 100644 --- 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 @@ -1,7 +1,11 @@ /* 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.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new import org.opalj.br.analyses.BasicReport @@ -10,16 +14,26 @@ import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ClassImmutability_new 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.fpcf.FinalEP +import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis; +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -47,33 +61,68 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { analysesManager.project.get(RTACallGraphKey) - val (propertyStore, _) = analysesManager.runAll( - //LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyStaticDataUsageAnalysis, + LazySimpleEscapeAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds + } + + val sb = new StringBuilder + sb.append("Mutable Class: \n") + sb.append( + propertyStore + .finalEntities(MutableClass) + .toList + .map(x => x.toString + "\n") + ) + sb.append("\nDependent Immutable Class: \n") + sb.append( + propertyStore + .entities(ClassImmutability_new.key) + .toList + .collect({ case x @ FinalEP(_, DependentImmutableClass(_)) => x }) + .map(x => x.toString + "\n") + ) + sb.append("\nShallow Immutable Class: \n") + sb.append( + propertyStore + .finalEntities(ShallowImmutableClass) + .toList + .map(x => x.toString + "\n") ) + sb.append("\nDeep Immutable Class: \n") + sb.append( + propertyStore + .finalEntities(DeepImmutableClass) + .toList + .map(x => x.toString + "\n") + ) + + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/classImm" + dateString + ".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() - "Mutable Class: " + propertyStore - .finalEntities(MutableClass) - .toList - .toString() + "\n" + - "Dependent Immutable Class: " + propertyStore - .entities(ClassImmutability_new.key) - .toList - .toString() + "\n" + - "Shallow Immutable Class: " + propertyStore - .finalEntities(ShallowImmutableClass) - .toList - .toString() + "\n" + - "Deep Immutable Class: " + propertyStore - .finalEntities(DeepImmutableClass) - .toList - .toString() + "\n" + " took : " + analysisTime + " seconds" } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala new file mode 100644 index 0000000000..39924dbd93 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -0,0 +1,103 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds; + +/** + * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result + * + * @author Tobias Peter Roth + */ +object ClassImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { + + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i <- 0 until 19) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyStaticDataUsageAnalysis, + LazySimpleEscapeAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + + /** + * "Mutable Class: "+propertyStore + * .finalEntities(MutableClass) + * .toList + * .toString()+"\n"+ + * "Dependent Immutable Class: "+propertyStore + * .entities(ClassImmutability_new.key) + * .toList + * .toString()+"\n"+ + * "Shallow Immutable Class: "+propertyStore + * .finalEntities(ShallowImmutableClass) + * .toList + * .toString()+"\n"+ + * "Deep Immutable Class: "+propertyStore + * .finalEntities(DeepImmutableClass) + * .toList + * .toString()+"\n" + */ + times.foreach(s => println(s + " seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size + f"took: $aver seconds on average" + } +} 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 index 1efd2e07a6..7ca9c569cc 100644 --- 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 @@ -1,7 +1,11 @@ /* 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.analyses.BasicReport import org.opalj.br.analyses.Project @@ -16,12 +20,15 @@ import org.opalj.br.fpcf.properties.DependentImmutableField import org.opalj.br.fpcf.properties.FieldImmutability import org.opalj.br.fpcf.properties.MutableField import org.opalj.br.fpcf.properties.ShallowImmutableField +import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds /** * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. @@ -49,37 +56,68 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { analysesManager.project.get(RTACallGraphKey) - val (propertyStore, _) = analysesManager.runAll( - LazyLxClassImmutabilityAnalysis_new, - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new - ); + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { - "Mutable Fields: " + propertyStore - .finalEntities(MutableField) - .toList - .toString() + "\n" + - "Shallow Immutable Fields: " + propertyStore - .finalEntities(ShallowImmutableField) - .toList - .toString() + "\n" + - "Dependet Immutable Fields:" + propertyStore - .finalEntities(DependentImmutableField(None)) - .toList - .toString() + "\n" + - "Deep Immutable Fields: " + propertyStore - .finalEntities(DeepImmutableField) - .toList - .toString() + "\n" + + propertyStore = analysesManager + .runAll( + LazyLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds + } + val sb: StringBuilder = new StringBuilder + sb.append("Mutable Fields: \n") + sb.append( + propertyStore + .finalEntities(MutableField) + .toList + .map(x => x.toString + "\n") + .toString() + ) + sb.append("\nShallow Immutable Fields: \n") + sb.append( + propertyStore + .finalEntities(ShallowImmutableField) + .toList + .map(x => x.toString + "\n") + .toString() + ) + sb.append("\nDependet Immutable Fields: \n") + sb.append( propertyStore .entities(FieldImmutability.key) .toList + .collect({ case x: DependentImmutableField => x }) + .map(x => x.toString + "\n") + .toString() + ) + sb.append("Deep Immutable Fields: ") + sb.append( + propertyStore + .finalEntities(DeepImmutableField) + .toList + .map(x => x.toString + "\n") .toString + ) + + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/refImm" + dateString + ".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + + " took : " + analysisTime + " seconds" } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala new file mode 100644 index 0000000000..be2e23b644 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -0,0 +1,100 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds + +/** + * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object FieldImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0FieldImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0FieldImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i <- 0 until 19) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + + /** + * "Mutable Fields: "+propertyStore + * .finalEntities(MutableField) + * .toList + * .toString()+"\n"+ + * "Shallow Immutable Fields: "+propertyStore + * .finalEntities(ShallowImmutableField) + * .toList + * .toString()+"\n"+ + * "Dependet Immutable Fields:"+propertyStore + * .finalEntities(DependentImmutableField(None)) + * .toList + * .toString()+"\n"+ + * "Deep Immutable Fields: "+propertyStore + * .finalEntities(DeepImmutableField) + * .toList + * .toString()+"\n"+ + * propertyStore + * .entities(FieldImmutability.key) + * .toList + * .toString* + */ + times.foreach(s => println(s + " seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size + f"took: $aver seconds on average" + } +} 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..067d5b6ed5 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 @@ -61,10 +61,12 @@ object ImmutabilityAnalysisDemo extends ProjectAnalysisApplication { r = time[() ⇒ String](10, 50, 15, analyze(project, parallelismLevel))(handleResults) println( s"Results with $parallelismLevel threads:\n"+ - performanceData.values. - map(v ⇒ v.map(_.toSeconds.toString(false))). - map(v ⇒ List("setup\t", "analysis\t").zip(v).map(e ⇒ e._1 + e._2).mkString("", "\n", "\n")). - mkString("\n") + performanceData.values + .map(v ⇒ v.map(_.toSeconds.toString(false))) + .map( + v ⇒ List("setup\t", "analysis\t").zip(v).map(e ⇒ e._1 + e._2).mkString("", "\n", "\n") + ) + .mkString("\n") ) gc() @@ -82,24 +84,32 @@ object ImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val propertyStore = project.get(PropertyStoreKey) time { - propertyStore.setupPhase(Set[PropertyKind]( - FieldMutability.key, ClassImmutability.key, TypeImmutability.key - )) + propertyStore.setupPhase( + Set[PropertyKind]( + FieldMutability.key, + ClassImmutability.key, + TypeImmutability.key + ) + ) LazyL0FieldMutabilityAnalysis.register(project, propertyStore, null) EagerClassImmutabilityAnalysis.start(project, propertyStore, null) EagerTypeImmutabilityAnalysis.start(project, propertyStore, null) propertyStore.waitOnPhaseCompletion() - } { r ⇒ analysisTime = r } + } { r ⇒ + analysisTime = r + } result += s"\t- analysis time: ${analysisTime.toSeconds}\n" () ⇒ { val immutableClasses = - propertyStore.entities(ClassImmutability.key). - filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration).toBuffer. - groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub). - map { kv ⇒ + propertyStore + .entities(ClassImmutability.key) + .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) + .toBuffer + .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) + .map { kv ⇒ ( kv._1, kv._2.toList.sortWith { (a, b) ⇒ @@ -111,27 +121,29 @@ object ImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } val immutableClassesPerCategory = - immutableClasses. - map(kv ⇒ "\t\t"+kv._1+": "+kv._2.size). - toBuffer.sorted. - mkString("\n") + immutableClasses.map(kv ⇒ "\t\t"+kv._1+": "+kv._2.size).toBuffer.sorted.mkString("\n") val immutableTypes = - propertyStore.entities(TypeImmutability.key). - filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration).toBuffer. - groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub). - map(kv ⇒ (kv._1, kv._2.size)) + propertyStore + .entities(TypeImmutability.key) + .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) + .toBuffer + .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) + .map(kv ⇒ (kv._1, kv._2.size)) val immutableTypesPerCategory = immutableTypes.map(kv ⇒ "\t\t"+kv._1+": "+kv._2).toBuffer.sorted.mkString("\n") val immutableClassesInfo = - immutableClasses.values.flatten.filter { ep ⇒ - !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration - }.map { eps ⇒ - eps.e.asInstanceOf[ClassFile].thisType.toJava+ - " => "+eps.ub+ - " => "+propertyStore(eps.e, TypeImmutability.key).ub - }.mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") + immutableClasses.values.flatten + .filter { ep ⇒ + !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration + } + .map { eps ⇒ + eps.e.asInstanceOf[ClassFile].thisType.toJava+ + " => "+eps.ub+ + " => "+propertyStore(eps.e, TypeImmutability.key).ub + } + .mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") "\t- details:\n"+ immutableClassesInfo+ diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala new file mode 100644 index 0000000000..36c8c4aab5 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala @@ -0,0 +1,195 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.analyses + +import java.net.URL + +import org.opalj.br.ClassFile +import org.opalj.br.analyses.BasicReport +import org.opalj.br.analyses.Project +import org.opalj.br.analyses.ProjectAnalysisApplication +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.ClassImmutability +import org.opalj.br.fpcf.properties.ClassImmutability_new +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.FieldMutability +import org.opalj.br.fpcf.properties.FieldPrematurelyRead +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.properties.ReferenceImmutability +import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.br.fpcf.properties.TypeImmutability_new +import org.opalj.fpcf.EPS +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyKind +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.Nanoseconds +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.gc + +/** + * Determines the immutability of the classes of a project. + * + * @author Michael Eichberg + * @author Tobias Peter Roth + * + */ +object ImmutabilityAnalysisDemo_new extends ProjectAnalysisApplication { + + override def title: String = "determines the immutability of objects and types" + + override def description: String = "determines the immutability of objects and types" + + private[this] var setupTime = Nanoseconds.None + private[this] var analysisTime = Nanoseconds.None + private[this] var performanceData: Map[Nanoseconds, List[Nanoseconds]] = Map.empty + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + var r: () ⇒ String = null + + def handleResults(t: Nanoseconds, ts: Seq[Nanoseconds]) = { + performanceData += ((t, List(setupTime, analysisTime))) + performanceData = performanceData.filter((t_ts) ⇒ ts.contains(t_ts._1)) + } + + List(1).foreach { parallelismLevel ⇒ + performanceData = Map.empty + gc() + + println(s"\nRunning analysis with $parallelismLevel thread(s):") + r = time[() ⇒ String](10, 50, 15, analyze(project, parallelismLevel))(handleResults) + println( + s"Results with $parallelismLevel threads:\n"+ + performanceData.values + .map(v ⇒ v.map(_.toSeconds.toString(false))) + .map( + v ⇒ List("setup\t", "analysis\t").zip(v).map(e ⇒ e._1 + e._2).mkString("", "\n", "\n") + ) + .mkString("\n") + ) + + gc() + } + BasicReport(r()) + } + + def analyze(theProject: Project[URL], parallelismLevel: Int): () ⇒ String = { + var result = "Results:\n" + val project = Project.recreate(theProject) // We need an empty project(!) + project.get(RTACallGraphKey) + + // The following measurements (t) are done such that the results are comparable with the + // reactive async approach developed by P. Haller and Simon Gries. + PropertyStoreKey.parallelismLevel = parallelismLevel + //PropertyStoreKey + val propertyStore = project.get(PropertyStoreKey) + + time { + propertyStore.setupPhase( + Set[PropertyKind]( + FieldMutability.key, + ClassImmutability.key, + TypeImmutability.key, + FieldPrematurelyRead.key, + Purity.key, + FieldImmutability.key, + ReferenceImmutability.key, + ClassImmutability_new.key, + TypeImmutability_new.key + ) + ) + //LazyL0FieldMutabilityAnalysis.register(project, propertyStore, null) // (project, propertyStore) + //EagerClassImmutabilityAnalysis.start(project, propertyStore, project.allClassFiles) + //EagerTypeImmutabilityAnalysis.start(project, propertyStore, null) //project.allClassFiles) + + LazyClassImmutabilityAnalysis.register(project, propertyStore, project.allClassFiles) + LazyL2FieldMutabilityAnalysis.register(project, propertyStore, null) + LazyTypeImmutabilityAnalysis.register(project, propertyStore, null) + LazyUnsoundPrematurelyReadFieldsAnalysis.register(project, propertyStore, null) + LazyL2PurityAnalysis.register(project, propertyStore, null) + + EagerL0ReferenceImmutabilityAnalysis.start(project, propertyStore, null) + EagerL0FieldImmutabilityAnalysis.start(project, propertyStore, null) + EagerLxClassImmutabilityAnalysis_new.start(project, propertyStore, project.allClassFiles) + EagerLxTypeImmutabilityAnalysis_new.start(project, propertyStore, null) + //propertyStore.suppressError = true + //propertyStore.waitOnPhaseCompletion() + } { r ⇒ + analysisTime = r + } + + result += s"\t- analysis time: ${analysisTime.toSeconds}\n" + + () ⇒ { + val immutableReferences = + propertyStore.entities(ReferenceImmutability.key) + val immutableClasses = + propertyStore + .entities(ClassImmutability_new.key) + .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) + .toBuffer + .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) + .map { kv ⇒ + ( + kv._1, + kv._2.toList.sortWith { (a, b) ⇒ + val cfA = a.e.asInstanceOf[ClassFile] + val cfB = b.e.asInstanceOf[ClassFile] + cfA.thisType.toJava < cfB.thisType.toJava + } + ) + } + + val immutableClassesPerCategory = + immutableClasses + .map(kv ⇒ "\t\t"+kv._1+": "+kv._2.size) + .toBuffer + .sorted + .mkString("\n") + + val immutableTypes = + propertyStore + .entities(TypeImmutability_new.key) + .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) + .toBuffer + .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) + .map(kv ⇒ (kv._1, kv._2.size)) + val immutableTypesPerCategory = + immutableTypes.map(kv ⇒ "\t\t"+kv._1+": "+kv._2).toBuffer.sorted.mkString("\n") + + val immutableClassesInfo = + immutableClasses.values.flatten + .filter { ep ⇒ + !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration + } + .map { eps ⇒ + eps.e.asInstanceOf[ClassFile].thisType.toJava+ + " => "+eps.ub+ + " => "+propertyStore(eps.e, TypeImmutability_new.key).ub + } + .mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") + + "immutable References: "+immutableReferences.size+"\n" + "\t- details:\n"+ + immutableClassesInfo+ + "\nSummary (w.r.t classes):\n"+ + "\tObject Immutability:\n"+ + immutableClassesPerCategory+"\n"+ + "\tType Immutability:\n"+ + immutableTypesPerCategory+"\n"+ + "\n"+propertyStore.toString(false) + } + } +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index 25dcf825c8..c9f7275191 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -11,11 +11,22 @@ import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds +import java.io._ +import java.util.Calendar + +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis /** * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. @@ -24,46 +35,73 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyTypeImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds } + var sb: StringBuilder = new StringBuilder() + sb = sb.append("Mutable References: \n") + sb = sb.append( + propertyStore.finalEntities(MutableReference).toList.map(x => x.toString + "\n").toString() + ) - def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - val (propertyStore, _) = analysesManager.runAll( - //EagerUnsoundPrematurelyReadFieldsAnalysis, - //EagerL2PurityAnalysis, - //EagerL2FieldMutabilityAnalysis, - EagerL0ReferenceImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis - ); + sb = sb.append("\n Lazy Initialized Reference: \n") + sb = sb.append( + propertyStore + .finalEntities(LazyInitializedReference) + .toList + .map(x => x.toString + "\n") + .toString() + ) - "Mutable References: "+propertyStore - .finalEntities(MutableReference) - .toList - .toString()+"\n"+ - "Lazy Initialized Reference: "+propertyStore - .finalEntities(LazyInitializedReference) - .toList - .toString()+"\n"+ - "Immutable References: "+propertyStore - .finalEntities(ImmutableReference) - .toList - .toString() - } + /** + * .toList + * .toString() + "\n" +* + */ + sb = sb.append("\nImmutable References: \n") + sb = sb.append( + propertyStore.finalEntities(ImmutableReference).toList.map(x => x.toString + "\n").toString() + ) + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/refLazyImm" + dateString + ".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + " took : " + analysisTime + " seconds" + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala new file mode 100644 index 0000000000..24ff667072 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -0,0 +1,97 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +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.util.PerformanceEvaluation.time +import org.opalj.util.Seconds + +/** + * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object ReferenceImmutabilityAnalysisDemo_performanceMeasurements + extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i <- 0 until 19) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyTypeImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + + /** + * "Mutable References: "+ + * propertyStore + * .finalEntities(MutableReference) + * .toList + * .toString()+"\n"+ + * "Lazy Initialized Reference: "+propertyStore + * .finalEntities(LazyInitializedReference) + * .toList + * .toString()+"\n"+ + * "Immutable References: "+propertyStore + * .finalEntities(ImmutableReference) + * .toList + * .toString()+"\n"+* + */ + times.foreach(s => println(s + " seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size + f"took: $aver seconds on average" + } + +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala index a9b3b9b7ac..4daa6d1b84 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala @@ -1,21 +1,34 @@ /* 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.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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.LazyInitialization import org.opalj.br.fpcf.properties.NoLazyInitialization import org.opalj.br.fpcf.properties.NotThreadSafeLazyInitialization +import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis 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.util.PerformanceEvaluation.time +import org.opalj.util.Seconds /** * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. @@ -24,42 +37,97 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ object ReferenceImmutabilityLazyInitializationAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - val (propertyStore, _) = analysesManager.runAll( - EagerL0ReferenceImmutabilityLazyInitializationAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis - ); + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityLazyInitializationAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyTypeImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyFieldLocalityAnalysis, + LazyReturnValueFreshnessAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + /*def printResult(string: String, property: Property)( + implicit + propertyStore: PropertyStore + ): Unit = {} **/ + val sb: StringBuilder = new StringBuilder() - "Not lazy initialized References: " + propertyStore - .finalEntities(NoLazyInitialization) - .toList - .toString() + "\n" + - "Not thread safe lazy initialization: " + propertyStore - .finalEntities(NotThreadSafeLazyInitialization) - .toList - .toString() + "\n" + - "Lazy Initialization: " + propertyStore - .finalEntities(LazyInitialization) - .toList - .toString() - } + sb.append("Not initialized References: \n") + sb.append( + propertyStore + .finalEntities(NoLazyInitialization) + .toList + .map(x ⇒ x.toString+"\n") + .toString() + ) + sb.append("\nNot threadsafe Lazy Initialization: \n") + sb.append( + propertyStore + .finalEntities(NotThreadSafeLazyInitialization) + .toList + .map(x ⇒ x.toString+"\n") + .toString() + ) + sb.append("\nLazy Initialization: \n") + sb.append( + propertyStore + .finalEntities(LazyInitialization) + .toList + .map(x ⇒ x.toString+"\n") + .toString() + ) + + /** + * "Not lazy initialized References: " + propertyStore + * .finalEntities(NoLazyInitialization) + * .toList + * .toString() + "\n" + + * "Not thread safe lazy initialization: " + propertyStore + * .finalEntities(NotThreadSafeLazyInitialization) + * .toList + * .toString() + "\n" + + * "Lazy Initialization: " + propertyStore + * .finalEntities(LazyInitialization) + * .toList + * .toString()* + */ + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/refDCLImm"+dateString+".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + + " took : "+analysisTime+" seconds" + s"took $analysisTime seconds " + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala new file mode 100644 index 0000000000..4c6f2c79d9 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala @@ -0,0 +1,117 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +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.util.PerformanceEvaluation.time +import org.opalj.util.Seconds + +/** + * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements + extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i ← 0 until 19) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityLazyInitializationAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyTypeImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyFieldLocalityAnalysis, + LazyReturnValueFreshnessAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + /*def printResult(string: String, property: Property)( + implicit + propertyStore: PropertyStore + ): Unit = {} **/ + /** + * val notInitializedReferencesString = propertyStore + * .finalEntities(NoLazyInitialization) + * .toList + * .toString() + * val notThreadSafeLazyInitializationString = propertyStore + * .finalEntities(NotThreadSafeLazyInitialization) + * .toList + * .toString() + * val lazyInitializationString = propertyStore + * .finalEntities(LazyInitialization) + * .toList + * .toString() + * println(s"Not initialized References $notInitializedReferencesString") + * println(s"Not threadsafe Lazy Initialization: $notThreadSafeLazyInitializationString") + * println(s"Lazy Initialization String: $lazyInitializationString") + */ + /** + * "Not lazy initialized References: " + propertyStore + * .finalEntities(NoLazyInitialization) + * .toList + * .toString() + "\n" + + * "Not thread safe lazy initialization: " + propertyStore + * .finalEntities(NotThreadSafeLazyInitialization) + * .toList + * .toString() + "\n" + + * "Lazy Initialization: " + propertyStore + * .finalEntities(LazyInitialization) + * .toList + * .toString()* + */ + times.foreach(s ⇒ println(s+" seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size + f"took: $aver seconds on average" + } +} 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 index 1739638a81..dcbcb5df3b 100644 --- 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 @@ -1,7 +1,11 @@ /* 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.analyses.BasicReport import org.opalj.br.analyses.Project @@ -9,18 +13,25 @@ import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis 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_new import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis -//import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -29,51 +40,71 @@ import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis */ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" + override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" - override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" + override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(project: Project[URL]): String = { - - val analysesManager = project.get(FPCFAnalysesManagerKey) + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - analysesManager.project.get(RTACallGraphKey) - - val (propertyStore, _) = analysesManager.runAll( - //LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - EagerLxTypeImmutabilityAnalysis_new, - LazyLxClassImmutabilityAnalysis_new + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyFieldLocalityAnalysis, + LazySimpleEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis ) - "Mutable Type: "+propertyStore - .finalEntities(MutableType_new) - .toList - .toString()+"\n"+ - "Shallow Immutable Type: "+propertyStore - .finalEntities(ShallowImmutableType) - .toList - .toString()+"\n"+ - "Dependent Immutable Type: "+propertyStore - .finalEntities(DependentImmutableType) - .toList - .toString()+"\n"+ - "Deep Immutable Type: "+propertyStore - .finalEntities(DeepImmutableType) - .toList - .toString()+"\n" + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds } + val sb: StringBuilder = new StringBuilder + sb.append("\nMutableTypes: \n") + sb.append( + propertyStore + .finalEntities(MutableType_new) + .toList + .map(x => x.toString + "\n") + .toString + ) + sb.append("\nShallow Immutable Types:\n") + sb.append(propertyStore.finalEntities(ShallowImmutableType).toList.map(x => x.toString + "\n")) + sb.append("\nDependent Immutable Types: \n") + sb.append( + propertyStore.finalEntities(DependentImmutableType).toList.map(x => x.toString + "\n") + ) + sb.append("\nDeep Immutable Types:\n") + sb.append(propertyStore.finalEntities(DeepImmutableType).toList.map(x => x.toString + "\n")) + sb.append(s"\nType immutability analysis took: $analysisTime on average") + + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/typeImm" + dateString + ".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + + " took : " + analysisTime + " seconds" + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala new file mode 100644 index 0000000000..996a182686 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala @@ -0,0 +1,105 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis +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.ShallowImmutableType +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis + +/** + * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result + * + * @author Tobias Peter Roth + */ +object TypeImmutabilityAnalysisDemo_performanceMeasurement extends ProjectAnalysisApplication { + + override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" + + override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(theProject: Project[URL]): String = { + + var times: List[Seconds] = Nil: List[Seconds] + for (i ← 0 until 19) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyFieldLocalityAnalysis, + LazySimpleEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + + /** + * "Mutable Type: "+propertyStore + * .finalEntities(MutableType_new) + * .toList + * .toString()+"\n"+ + * "Shallow Immutable Type: "+propertyStore + * .finalEntities(ShallowImmutableType) + * .toList + * .toString()+"\n"+ + * "Dependent Immutable Type: "+propertyStore + * .finalEntities(DependentImmutableType) + * .toList + * .toString()+"\n"+ + * "Deep Immutable Type: "+propertyStore + * .finalEntities(DeepImmutableType) + * .toList + * .toString()+"\n"* + */ + times.foreach(s ⇒ println(s+" seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size + f"took: $aver seconds on average" + } +} From 1382ef6ea63ceef7f222c8414622b7b1dcdfef60 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 28 Jan 2020 17:52:20 +0100 Subject: [PATCH 076/327] Fixing PropertiesTest Merge Problem --- .../scala/org/opalj/fpcf/PropertiesTest.scala | 586 +++++++++--------- 1 file changed, 297 insertions(+), 289 deletions(-) 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 300c3c6317..85e57f6ec3 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,316 +51,324 @@ import org.opalj.tac.common.DefinitionSitesKey */ abstract class PropertiesTest extends FunSpec with Matchers { - def withRT = false - - /** - * The representation of the fixture project. - */ - final val FixtureProject: Project[URL] = { - val classFileReader = Project.JavaClassFileReader() - import classFileReader.ClassFiles - val sourceFolder = s"DEVELOPING_OPAL/validate/target/scala-$ScalaMajorVersion/test-classes" - val fixtureFiles = new File(sourceFolder) - val fixtureClassFiles = ClassFiles(fixtureFiles) - if (fixtureClassFiles.isEmpty) fail(s"no class files at $fixtureFiles") - - val projectClassFiles = fixtureClassFiles.filter { cfSrc ⇒ - val (cf, _) = cfSrc - cf.thisType.packageName.startsWith("org/opalj/fpcf/fixtures") - } - - val propertiesClassFiles = fixtureClassFiles.filter { cfSrc ⇒ - val (cf, _) = cfSrc - cf.thisType.packageName.startsWith("org/opalj/fpcf/properties") - } - - val libraryClassFiles = (if (withRT) ClassFiles(RTJar) else List()) ++ propertiesClassFiles - - val configForEntryPoints = BaseConfig - .withValue( - InitialEntryPointsKey.ConfigKeyPrefix+"analysis", - ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllEntryPointsFinder") - ) - .withValue( - InitialEntryPointsKey.ConfigKeyPrefix+"AllEntryPointsFinder.projectMethodsOnly", - ConfigValueFactory.fromAnyRef(true) - ) - - implicit val config: Config = configForEntryPoints - .withValue( - InitialInstantiatedTypesKey.ConfigKeyPrefix+"analysis", - ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllInstantiatedTypesFinder") - ) - .withValue( - InitialInstantiatedTypesKey.ConfigKeyPrefix+ - "AllInstantiatedTypesFinder.projectClassesOnly", - ConfigValueFactory.fromAnyRef(true) - ) - - info(s"the test fixture project consists of ${projectClassFiles.size} class files") - Project( - projectClassFiles, - libraryClassFiles, - libraryClassFilesAreInterfacesOnly = false, - virtualClassFiles = Traversable.empty - ) + def withRT = false + + /** + * The representation of the fixture project. + */ + final val FixtureProject: Project[URL] = { + val classFileReader = Project.JavaClassFileReader() + import classFileReader.ClassFiles + val sourceFolder = s"DEVELOPING_OPAL/validate/target/scala-$ScalaMajorVersion/test-classes" + val fixtureFiles = new File(sourceFolder) + val fixtureClassFiles = ClassFiles(fixtureFiles) + if (fixtureClassFiles.isEmpty) fail(s"no class files at $fixtureFiles") + + val projectClassFiles = fixtureClassFiles.filter { cfSrc => + val (cf, _) = cfSrc + cf.thisType.packageName.startsWith("org/opalj/fpcf/fixtures") } - final val PropertyValidatorType = ObjectType("org/opalj/fpcf/properties/PropertyValidator") - - /** - * Returns the [[org.opalj.fpcf.properties.PropertyMatcher]] associated with the annotation - - * if the annotation specifies an expected property currently relevant; i.e, if the - * property kind specified by the annotation is in the set `propertyKinds`. - * - * @param p The current project. - * @param propertyKinds The property kinds which should be analyzed. - * @param annotation The annotation found in the project. - */ - def getPropertyMatcher( - p: Project[URL], - propertyKinds: Set[String] - )( - annotation: AnnotationLike - ): Option[(AnnotationLike, String, Type /* type of the matcher */ )] = { - println("inside getPropertyMatcher") - if (!annotation.annotationType.isObjectType) - return None; - - // Get the PropertyValidator meta-annotation of the given entity's annotation: - val annotationClassFile = p.classFile(annotation.annotationType.asObjectType).get - annotationClassFile.runtimeInvisibleAnnotations.collectFirst { - case Annotation( - PropertyValidatorType, - Seq( - ElementValuePair("key", StringValue(propertyKind)), - ElementValuePair("validator", ClassValue(propertyMatcherType)) - ) - ) if propertyKinds.contains(propertyKind) ⇒ - (annotation, propertyKind, propertyMatcherType) - } + val propertiesClassFiles = fixtureClassFiles.filter { cfSrc => + val (cf, _) = cfSrc + cf.thisType.packageName.startsWith("org/opalj/fpcf/properties") } - /** - * Called by tests to trigger the validation of the derived properties against the - * specified ones. - * - * @param context The validation context; typically the return value of [[executeAnalyses]]. - * @param eas An iterator over the relevant entities along with the found annotations. - * @param propertyKinds The kinds of properties (as specified by the annotations) that are - * to be tested. - */ - def validateProperties( - context: TestContext, - eas: TraversableOnce[(Entity, /*the processed annotation*/ String ⇒ String /* a String identifying the entity */ , Traversable[AnnotationLike])], - propertyKinds: Set[String] - ): Unit = { - val TestContext(p: Project[URL], ps: PropertyStore, as: List[FPCFAnalysis]) = context - val ats = - as.map(a ⇒ ObjectType(a.getClass.getName.replace('.', '/'))).toSet - - for { - (e, entityIdentifier, annotations) ← eas - augmentedAnnotations = annotations.flatMap(getPropertyMatcher(p, propertyKinds)) - - (annotation, propertyKind, matcherType) ← augmentedAnnotations - } { - val annotationTypeName = annotation.annotationType.asObjectType.simpleName - val matcherClass = Class.forName(matcherType.toJava) - val matcherClassConstructor = matcherClass.getDeclaredConstructor() - val matcher = matcherClassConstructor.newInstance().asInstanceOf[PropertyMatcher] - if (matcher.isRelevant(p, ats, e, annotation)) { - - it(entityIdentifier(s"$annotationTypeName")) { - info(s"validator: "+matcherClass.toString.substring(32)) - val epss = ps.properties(e).toIndexedSeq - val nonFinalPSs = epss.filter(_.isRefinable) - assert( - nonFinalPSs.isEmpty, - nonFinalPSs.mkString("some epss are not final:\n\t", "\n\t", "\n") - ) - val properties = epss.map(_.toFinalEP.p) - matcher.validateProperty(p, ats, e, annotation, properties) match { - case Some(error: String) ⇒ - val propertiesAsStrings = properties.map(_.toString) - val m = propertiesAsStrings.mkString( - "actual: ", - ", ", - "\nexpectation: "+error - ) - fail(m) - case None ⇒ /* OK */ - case result ⇒ fail("matcher returned unexpected result: "+result) - } - } - - } - } + val libraryClassFiles = (if (withRT) ClassFiles(RTJar) else List()) ++ propertiesClassFiles + + implicit val config: Config = createConfig() + + info(s"the test fixture project consists of ${projectClassFiles.size} class files") + Project( + projectClassFiles, + libraryClassFiles, + libraryClassFilesAreInterfacesOnly = false, + virtualClassFiles = Traversable.empty + ) + } + + def createConfig(): Config = { + 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( + InitialInstantiatedTypesKey.ConfigKeyPrefix + + "AllInstantiatedTypesFinder.projectClassesOnly", + ConfigValueFactory.fromAnyRef(true) + ) + } + + final val PropertyValidatorType = ObjectType("org/opalj/fpcf/properties/PropertyValidator") + + /** + * Returns the [[org.opalj.fpcf.properties.PropertyMatcher]] associated with the annotation - + * if the annotation specifies an expected property currently relevant; i.e, if the + * property kind specified by the annotation is in the set `propertyKinds`. + * + * @param p The current project. + * @param propertyKinds The property kinds which should be analyzed. + * @param annotation The annotation found in the project. + */ + def getPropertyMatcher( + p: Project[URL], + propertyKinds: Set[String] + )( + annotation: AnnotationLike + ): Option[(AnnotationLike, String, Type /* type of the matcher */ )] = { + if (!annotation.annotationType.isObjectType) + return None; + + // Get the PropertyValidator meta-annotation of the given entity's annotation: + val annotationClassFile = p.classFile(annotation.annotationType.asObjectType).get + annotationClassFile.runtimeInvisibleAnnotations.collectFirst { + case Annotation( + PropertyValidatorType, + Seq( + ElementValuePair("key", StringValue(propertyKind)), + ElementValuePair("validator", ClassValue(propertyMatcherType)) + ) + ) if propertyKinds.contains(propertyKind) => + (annotation, propertyKind, propertyMatcherType) } - - // - // CONVENIENCE METHODS - // - - def fieldsWithAnnotations( - recreatedFixtureProject: SomeProject - ): Traversable[(Field, String ⇒ String, Annotations)] = { - for { - f ← recreatedFixtureProject.allFields // cannot be parallelized; "it" is not thread safe - annotations = f.runtimeInvisibleAnnotations - if annotations.nonEmpty - } yield { - (f, (a: String) ⇒ f.toJava(s"@$a").substring(24), annotations) + } + + /** + * Called by tests to trigger the validation of the derived properties against the + * specified ones. + * + * @param context The validation context; typically the return value of [[executeAnalyses]]. + * @param eas An iterator over the relevant entities along with the found annotations. + * @param propertyKinds The kinds of properties (as specified by the annotations) that are + * to be tested. + */ + def validateProperties( + context: TestContext, + eas: TraversableOnce[ + ( + Entity, + /*the processed annotation*/ String => String /* a String identifying the entity */, + Traversable[AnnotationLike] + ) + ], + propertyKinds: Set[String] + ): Unit = { + val TestContext(p: Project[URL], ps: PropertyStore, as: List[FPCFAnalysis]) = context + val ats = + as.map(a => ObjectType(a.getClass.getName.replace('.', '/'))).toSet + + for { + (e, entityIdentifier, annotations) <- eas + augmentedAnnotations = annotations.flatMap(getPropertyMatcher(p, propertyKinds)) + (annotation, propertyKind, matcherType) <- augmentedAnnotations + } { + val annotationTypeName = annotation.annotationType.asObjectType.simpleName + val matcherClass = Class.forName(matcherType.toJava) + val matcherClassConstructor = matcherClass.getDeclaredConstructor() + val matcher = matcherClassConstructor.newInstance().asInstanceOf[PropertyMatcher] + if (matcher.isRelevant(p, ats, e, annotation)) { + + it(entityIdentifier(s"$annotationTypeName")) { + info(s"validator: " + matcherClass.toString.substring(32)) + val epss = ps.properties(e).toIndexedSeq + val nonFinalPSs = epss.filter(_.isRefinable) + assert( + nonFinalPSs.isEmpty, + nonFinalPSs.mkString("some epss are not final:\n\t", "\n\t", "\n") + ) + val properties = epss.map(_.toFinalEP.p) + matcher.validateProperty(p, ats, e, annotation, properties) match { + case Some(error: String) => + val propertiesAsStrings = properties.map(_.toString) + val m = propertiesAsStrings.mkString( + "actual: ", + ", ", + "\nexpectation: " + error + ) + fail(m) + case None => /* OK */ + case result => fail("matcher returned unexpected result: " + result) + } } - } - def methodsWithAnnotations( - recreatedFixtureProject: SomeProject - ): Traversable[(Method, String ⇒ String, Annotations)] = { - for { - // cannot be parallelized; "it" is not thread safe - m ← recreatedFixtureProject.allMethods - annotations = m.runtimeInvisibleAnnotations - if annotations.nonEmpty - } yield { - (m, (a: String) ⇒ m.toJava(s"@$a").substring(24), annotations) - } + } } - - def declaredMethodsWithAnnotations( - recreatedFixtureProject: SomeProject - ): Traversable[(DefinedMethod, String ⇒ String, Annotations)] = { - val declaredMethods = recreatedFixtureProject.get(DeclaredMethodsKey) - for { - // cannot be parallelized; "it" is not thread safe - m ← recreatedFixtureProject.allMethods - dm = declaredMethods(m) - annotations = m.runtimeInvisibleAnnotations - if annotations.nonEmpty - } yield { - ( - dm, - (a: String) ⇒ m.toJava(s"@$a").substring(24), - annotations - ) - } + } + + // + // CONVENIENCE METHODS + // + + def fieldsWithAnnotations( + recreatedFixtureProject: SomeProject + ): Traversable[(Field, String => String, Annotations)] = { + for { + f <- recreatedFixtureProject.allFields // cannot be parallelized; "it" is not thread safe + annotations = f.runtimeInvisibleAnnotations + if annotations.nonEmpty + } yield { + (f, (a: String) => f.toJava(s"@$a").substring(24), annotations) } - - def classFilesWithAnnotations( - recreatedFixtureProject: SomeProject - ): Traversable[(ClassFile, String ⇒ String, Annotations)] = { - for { - // cannot be parallelized; "it" is not thread safe - cf ← recreatedFixtureProject.allClassFiles - annotations = cf.runtimeInvisibleAnnotations - if annotations.nonEmpty - } yield { - (cf, (a: String) ⇒ cf.thisType.toJava.substring(24) + s"@$a", annotations) - } + } + + def methodsWithAnnotations( + recreatedFixtureProject: SomeProject + ): Traversable[(Method, String => String, Annotations)] = { + for { + // cannot be parallelized; "it" is not thread safe + m <- recreatedFixtureProject.allMethods + annotations = m.runtimeInvisibleAnnotations + if annotations.nonEmpty + } yield { + (m, (a: String) => m.toJava(s"@$a").substring(24), annotations) } - - // there can't be any annotations of the implicit "this" parameter... - def explicitFormalParametersWithAnnotations( - recreatedFixtureProject: SomeProject - ): Traversable[(VirtualFormalParameter, String ⇒ String, Annotations)] = { - val formalParameters = recreatedFixtureProject.get(VirtualFormalParametersKey) - val declaredMethods = recreatedFixtureProject.get(DeclaredMethodsKey) - for { - // cannot be parallelized; "it" is not thread safe - m ← recreatedFixtureProject.allMethods - parameterAnnotations = m.runtimeInvisibleParameterAnnotations - i ← parameterAnnotations.indices - annotations = parameterAnnotations(i) - if annotations.nonEmpty - dm = declaredMethods(m) - } yield { - val fp = formalParameters(dm)(i + 1) - ( - fp, - (a: String) ⇒ - s"VirtualFormalParameter: (origin ${fp.origin} in "+ - s"${dm.declaringClassType}#${m.toJava(s"@$a")}", - annotations - ) - } + } + + def declaredMethodsWithAnnotations( + recreatedFixtureProject: SomeProject + ): Traversable[(DefinedMethod, String => String, Annotations)] = { + val declaredMethods = recreatedFixtureProject.get(DeclaredMethodsKey) + for { + // cannot be parallelized; "it" is not thread safe + m <- recreatedFixtureProject.allMethods + dm = declaredMethods(m) + annotations = m.runtimeInvisibleAnnotations + if annotations.nonEmpty + } yield { + ( + dm, + (a: String) => m.toJava(s"@$a").substring(24), + annotations + ) } - - def allocationSitesWithAnnotations( - recreatedFixtureProject: SomeProject - ): Traversable[(DefinitionSite, String ⇒ String, Traversable[AnnotationLike])] = { - val allocationSites = recreatedFixtureProject.get(DefinitionSitesKey).getAllocationSites - for { - as ← allocationSites - m = as.method - pc = as.pc - code = m.body.get - annotations = code.runtimeInvisibleTypeAnnotations filter { ta ⇒ - ta.target match { - case TAOfNew(`pc`) ⇒ true - case _ ⇒ false - } - } - if annotations.nonEmpty - } yield { - ( - as, - (a: String) ⇒ - s"AllocationSite: (pc ${as.pc} in "+ - s"${m.toJava(s"@$a").substring(24)})", - annotations - ) - } + } + + def classFilesWithAnnotations( + recreatedFixtureProject: SomeProject + ): Traversable[(ClassFile, String => String, Annotations)] = { + for { + // cannot be parallelized; "it" is not thread safe + cf <- recreatedFixtureProject.allClassFiles + annotations = cf.runtimeInvisibleAnnotations + if annotations.nonEmpty + } yield { + (cf, (a: String) => cf.thisType.toJava.substring(24) + s"@$a", annotations) } - - def init(p: Project[URL]): Unit = {} - - def executeAnalyses( - analysisRunners: ComputationSpecification[FPCFAnalysis]* - ): TestContext = { - executeAnalyses(analysisRunners.toIterable) + } + + // there can't be any annotations of the implicit "this" parameter... + def explicitFormalParametersWithAnnotations( + recreatedFixtureProject: SomeProject + ): Traversable[(VirtualFormalParameter, String => String, Annotations)] = { + val formalParameters = recreatedFixtureProject.get(VirtualFormalParametersKey) + val declaredMethods = recreatedFixtureProject.get(DeclaredMethodsKey) + for { + // cannot be parallelized; "it" is not thread safe + m <- recreatedFixtureProject.allMethods + parameterAnnotations = m.runtimeInvisibleParameterAnnotations + i <- parameterAnnotations.indices + annotations = parameterAnnotations(i) + if annotations.nonEmpty + dm = declaredMethods(m) + } yield { + val fp = formalParameters(dm)(i + 1) + ( + fp, + (a: String) => + s"VirtualFormalParameter: (origin ${fp.origin} in " + + s"${dm.declaringClassType}#${m.toJava(s"@$a")}", + annotations + ) } - - def executeAnalyses( - analysisRunners: Iterable[ComputationSpecification[FPCFAnalysis]] - ): TestContext = { - try { - val p = FixtureProject.recreate { piKeyUnidueId ⇒ - piKeyUnidueId != PropertyStoreKey.uniqueId - } // to ensure that this project is not "polluted" - implicit val logContext: LogContext = p.logContext - init(p) - - PropertyStore.updateDebug(true) - - p.getOrCreateProjectInformationKeyInitializationData( - PropertyStoreKey, - (context: List[PropertyStoreContext[AnyRef]]) ⇒ { - /* + } + + def allocationSitesWithAnnotations( + recreatedFixtureProject: SomeProject + ): Traversable[(DefinitionSite, String => String, Traversable[AnnotationLike])] = { + val allocationSites = recreatedFixtureProject.get(DefinitionSitesKey).getAllocationSites + for { + as <- allocationSites + m = as.method + pc = as.pc + code = m.body.get + annotations = code.runtimeInvisibleTypeAnnotations filter { ta => + ta.target match { + case TAOfNew(`pc`) => true + case _ => false + } + } + if annotations.nonEmpty + } yield { + ( + as, + (a: String) => + s"AllocationSite: (pc ${as.pc} in " + + s"${m.toJava(s"@$a").substring(24)})", + annotations + ) + } + } + + def init(p: Project[URL]): Unit = {} + + def executeAnalyses( + analysisRunners: ComputationSpecification[FPCFAnalysis]* + ): TestContext = { + executeAnalyses(analysisRunners.toIterable) + } + + def executeAnalyses( + analysisRunners: Iterable[ComputationSpecification[FPCFAnalysis]] + ): TestContext = { + try { + val p = FixtureProject.recreate { piKeyUnidueId => + piKeyUnidueId != PropertyStoreKey.uniqueId + } // to ensure that this project is not "polluted" + implicit val logContext: LogContext = p.logContext + init(p) + + PropertyStore.updateDebug(true) + + p.getOrCreateProjectInformationKeyInitializationData( + PropertyStoreKey, + (context: List[PropertyStoreContext[AnyRef]]) => { + /* val ps = PKEParallelTasksPropertyStore.create( new RecordAllPropertyStoreTracer, context.iterator.map(_.asTuple).toMap ) */ - val ps = PKESequentialPropertyStore(context: _*) - ps - } - ) - - val ps = p.get(PropertyStoreKey) - - val (_, csas) = p.get(FPCFAnalysesManagerKey).runAll(analysisRunners) - TestContext(p, ps, csas.collect { case (_, as) ⇒ as }) - } catch { - case t: Throwable ⇒ - t.printStackTrace() - t.getSuppressed.foreach(e ⇒ e.printStackTrace()) - throw t; + val ps = PKESequentialPropertyStore(context: _*) + ps } + ) + + val ps = p.get(PropertyStoreKey) + + val (_, csas) = p.get(FPCFAnalysesManagerKey).runAll(analysisRunners) + TestContext(p, ps, csas.collect { case (_, as) => as }) + } catch { + case t: Throwable => + t.printStackTrace() + t.getSuppressed.foreach(e => e.printStackTrace()) + throw t; } + } } case class TestContext( - project: Project[URL], - propertyStore: PropertyStore, - analyses: List[FPCFAnalysis] + project: Project[URL], + propertyStore: PropertyStore, + analyses: List[FPCFAnalysis] ) From c192c3e9770122c21f378342bee9cc0c820a5ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Fri, 7 Feb 2020 08:41:16 +0100 Subject: [PATCH 077/327] fixed merge --- .../scala/org/opalj/fpcf/par/DHTPropertyStore.scala | 13 ------------- .../src/main/scala/org/opalj/fpcf/par/YAPPS.scala | 13 ------------- 2 files changed, 26 deletions(-) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/DHTPropertyStore.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/DHTPropertyStore.scala index d329d466f6..5454dbd0a4 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/DHTPropertyStore.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/DHTPropertyStore.scala @@ -325,19 +325,6 @@ class DHTPropertyStore( val PartialResult(e, pk, u) = r handlePartialResult(r, u, e, pk) - case PrecomputedPartialResult.id ⇒ - val PrecomputedPartialResult(expectedEOptionP, updatedInterimEP, u) = r - handlePartialResult( - r, - oldEOptP ⇒ if (oldEOptP eq expectedEOptionP) { - Some(updatedInterimEP) - } else { - u.asInstanceOf[SomeEOptionP ⇒ Option[SomeInterimEP]](oldEOptP) - }, - expectedEOptionP.e, - expectedEOptionP.pk - ) - case InterimPartialResult.id ⇒ val InterimPartialResult(prs, dependees, c) = r diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala index 60d5514b6d..e6556f84a8 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala @@ -284,19 +284,6 @@ class YAPPS( val PartialResult(e, pk, u) = r handlePartialResult(r, u, e, pk) - case PrecomputedPartialResult.id ⇒ - val PrecomputedPartialResult(expectedEOptionP, updatedInterimEP, u) = r - handlePartialResult( - r, - oldEOptP ⇒ if (oldEOptP eq expectedEOptionP) { - Some(updatedInterimEP) - } else { - u.asInstanceOf[SomeEOptionP ⇒ Option[SomeInterimEP]](oldEOptP) - }, - expectedEOptionP.e, - expectedEOptionP.pk - ) - case InterimPartialResult.id ⇒ val InterimPartialResult(prs, dependees, c) = r From c33377881104c3921374ec68ac755718ba460c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Thu, 13 Feb 2020 16:12:42 +0100 Subject: [PATCH 078/327] fixed equals implementation --- OPAL/si/src/main/scala/org/opalj/fpcf/EOptionP.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..dd9d6c3e74 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/EOptionP.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/EOptionP.scala @@ -562,7 +562,7 @@ 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 } From a496dfe615860d48df616807eb74c365a87b798a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Thu, 13 Feb 2020 16:25:43 +0100 Subject: [PATCH 079/327] formatting --- .../tools/src/main/scala/org/opalj/support/info/CallGraph.scala | 1 - 1 file changed, 1 deletion(-) 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 995d53c54c..ec5a451a0d 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 @@ -188,7 +188,6 @@ object CallGraph extends ProjectAnalysisApplication { if (cgAlgorithm == "PointsTo") { val ptss = ps.entities(AllocationSitePointsToSet.key).toList - println(s"PTSs ${ptss.size}") println(s"PTS entries ${ptss.map(p ⇒ p.ub.elements.size).sum}") From c891ec9b1355f6ccb9b03ac7ad5c47d87fc4c6fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Thu, 13 Feb 2020 16:25:56 +0100 Subject: [PATCH 080/327] fixed issues --- .../main/scala/org/opalj/fpcf/par/YAPPS.scala | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala index e6556f84a8..81f03534db 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala @@ -758,7 +758,7 @@ class YAPPS( dependersLock.lockInterruptibly() val theDependers = dependers // Clear all dependers that will be notified, they will re-register if required - dependers = dependers.filter(d => suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) + dependers = dependers.filter(d ⇒ suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) dependersLock.unlock() notifyDependers(interimEP, theEOptP, theDependers) } @@ -807,30 +807,45 @@ class YAPPS( case _ ⇒ null } - val theDependers = dependers lock.unlock() + if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) + + dependersLock.lockInterruptibly() + val theDependers = dependers + dependers = dependers.filter(d ⇒ suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) + dependersLock.unlock() + if (newEOptP ne null) notifyDependers(newEOptP, theEOptP, theDependers) } - def notifyDependers(theEOptP: SomeEPS, oldEOptP: SomeEOptionP, theDependers: Set[SomeEPK], unnotifiedPKs: Set[PropertyKind] = Set.empty): Unit = { + def notifyDependers( + theEOptP: SomeEPS, + oldEOptP: SomeEOptionP, + theDependers: Set[SomeEPK], + unnotifiedPKs: Set[PropertyKind] = Set.empty + ): Unit = { if (theDependers ne null) { theDependers.foreach { depender ⇒ - if (!unnotifiedPKs.contains(depender.pk) && (theEOptP.isFinal || !suppressInterimUpdates(depender.pk.id)(theEOptP.pk.id))) + if (!unnotifiedPKs.contains(depender.pk) && + (theEOptP.isFinal || !suppressInterimUpdates(depender.pk.id)(theEOptP.pk.id))) { scheduleTask(new YappsContinuationTask(depender, theEOptP, oldEOptP)) + } } } } - def applyContinuation(dependee: SomeEPS, oldDependee: SomeEOptionP /*TODO remove*/ ): Unit = { + def applyContinuation(dependee: SomeEPS, oldDependee: SomeEOptionP): Unit = { // IMPROVE: Use tryLock() instead + val isSuppressed = suppressInterimUpdates(eOptP.pk.id)(dependee.pk.id) + val epk = dependee.toEPK lock.lockInterruptibly() val theDependees = dependees // We are still interessted in that dependee? if (theDependees != null && theDependees.exists { - // IMPROVE: We should be able to avoid the toEPK. - _.toEPK == oldDependee.toEPK + // TODO: adjust aplly() method in order to guarantee eq for EPKs + d ⇒ (oldDependee.isEPK && epk == d) || (d eq oldDependee) || (isSuppressed && epk == d.toEPK) }) { // We always retrieve the most up-to-date state of the dependee. val currentDependee = ps(dependee.pk.id).get(dependee.e).eOptP.asEPS From 9675da22798fe1bbf19d9b6a3966ffc4f01f4784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Mon, 17 Feb 2020 15:14:51 +0100 Subject: [PATCH 081/327] fixed reading the projectConfig= parameter --- .../scala/org/opalj/br/analyses/AnalysisApplication.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 { From 7fb22bfe4a63949d372c8849eb29a5eefeaa6cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Mon, 17 Feb 2020 15:16:11 +0100 Subject: [PATCH 082/327] fixed concurrency issue in YAPPS and refactoring --- .../org/opalj/support/info/CallGraph.scala | 6 +- .../main/scala/org/opalj/fpcf/par/YAPPS.scala | 116 ++++++++++-------- 2 files changed, 66 insertions(+), 56 deletions(-) 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 ec5a451a0d..d5dd8c3969 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 @@ -57,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. @@ -147,8 +147,8 @@ object CallGraph extends ProjectAnalysisApplication { // FIXME: The PKECPropertyStore is broken org.opalj.fpcf.par.PKECPropertyStore(context: _*) }*/ - //org.opalj.fpcf.par.YAPPS(context: _*) - org.opalj.fpcf.par.DHTPropertyStore(context: _*) + org.opalj.fpcf.par.YAPPS(context: _*) + //org.opalj.fpcf.par.DHTPropertyStore(context: _*) //org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) //org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = 4 //org.opalj.fpcf.par.PKECPropertyStore(context: _*) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala index 81f03534db..f4559a735a 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala @@ -306,6 +306,7 @@ class YAPPS( ps(AnalysisKeyId).remove(e) + //println(s"interim partial result remove dependees (${dependees} from depender: $epk") dependees.foreach { dependee ⇒ ps(dependee.pk.id).get(dependee.e).removeDepender(epk) } @@ -355,7 +356,7 @@ class YAPPS( if (isFresh) { triggerComputations(e, pk.id) updateDependees(ePKState, dependees) - } else ePKState.update(interimEP, c, dependees) + } else ePKState.interimUpdate(interimEP, c, dependees) //TODO update depender status } @@ -367,7 +368,7 @@ class YAPPS( pk: PropertyKey[Property] ): Unit = { val ePKState = ps(pk.id).computeIfAbsent(e, _ ⇒ YappsEPKState(EPK(e, pk), null, null)) - ePKState.update(update) + ePKState.partialUpdate(update) } def updateDependees(depender: YappsEPKState, newDependees: Traversable[SomeEOptionP]): Unit = { @@ -397,8 +398,10 @@ class YAPPS( pkId ) ) + epk + } else { + previous.eOptP.asInstanceOf[EOptionP[E, P]] } - epk } else if (propertyKindsComputedInThisPhase(pkId)) { val transformer = transformersByTargetPK(pkId) if (transformer ne null) { @@ -505,6 +508,7 @@ class YAPPS( private[this] val successors: Array[YappsEPKState ⇒ Traversable[YappsEPKState]] = Array.fill(THREAD_COUNT)(null) + // executed on the main thread only private[this] def resolveCycles(): Unit = { val theInterimStates = new ArrayBuffer[YappsEPKState](interimStates.iterator.map(_.size).sum) var tId = 0 @@ -524,6 +528,7 @@ class YAPPS( val dependees = interimEPKState.dependees val epk = interimEPKState.eOptP.toEPK dependees.foreach { dependee ⇒ + // during execution, no other thread accesses the dependers of the EPKState ps(dependee.pk.id).get(dependee.e).dependers -= epk } scheduleTask(new YappsSetTask(interimEPKState.eOptP.toFinalEP)) @@ -699,13 +704,13 @@ class YAPPS( } } - class YappsContinuationTask(depender: SomeEPK, dependee: SomeEPS, oldDependee: SomeEOptionP) extends YappsTask { + class YappsContinuationTask(depender: SomeEPK, oldDependee: SomeEOptionP) extends YappsTask { val priority = 0 override def apply(): Unit = { val epkState = ps(depender.pk.id).get(depender.e) if (epkState ne null) - epkState.applyContinuation(dependee, oldDependee) + epkState.applyContinuation(oldDependee) } } @@ -715,8 +720,8 @@ class YAPPS( @volatile var dependees: Traversable[SomeEOptionP], @volatile var dependers: Set[SomeEPK] = Set.empty ) { - val lock: MyRRLock = new MyRRLock() - val dependersLock = new MyRRLock() + val lock = new ReentrantLock() + val dependersLock = new ReentrantLock() def setFinal(finalEP: FinalEP[Entity, Property], unnotifiedPKs: Set[PropertyKind]): Unit = { lock.lockInterruptibly() @@ -725,37 +730,45 @@ class YAPPS( throw new IllegalStateException(s"${theEOptP.e} already had the property $theEOptP") } else { if (debug) eOptP.checkIsValidPropertiesUpdate(finalEP, Nil) + dependersLock.lock() eOptP = finalEP } dependees = null lock.unlock() - if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) - dependersLock.lock() + val theDependers = dependers dependers = null dependersLock.unlock() + notifyDependers(finalEP, theEOptP, theDependers, unnotifiedPKs) + + if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) } - def update( + def interimUpdate( interimEP: InterimEP[Entity, Property], newC: OnUpdateContinuation, newDependees: Traversable[SomeEOptionP] ): Unit = { + var requiresNotification = false + lock.lockInterruptibly() val theEOptP = eOptP if (theEOptP.isFinal) { throw new IllegalStateException(s"${theEOptP.e} already had the property $theEOptP") } else { - if (debug) eOptP.checkIsValidPropertiesUpdate(interimEP, newDependees) - eOptP = interimEP + if (debug) theEOptP.checkIsValidPropertiesUpdate(interimEP, newDependees) + if (interimEP.isUpdatedComparedTo(theEOptP)) { + requiresNotification = true + dependersLock.lockInterruptibly() + eOptP = interimEP + } c = newC dependees = newDependees } lock.unlock() - if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) - if (interimEP.isUpdatedComparedTo(theEOptP)) { - dependersLock.lockInterruptibly() + + if (requiresNotification) { val theDependers = dependers // Clear all dependers that will be notified, they will re-register if required dependers = dependers.filter(d ⇒ suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) @@ -764,6 +777,33 @@ class YAPPS( } updateDependees(this, newDependees) + + if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) + } + + def partialUpdate(updateComputation: UpdateComputation[Entity, Property]): Unit = { + lock.lockInterruptibly() + val theEOptP = eOptP + val u = updateComputation(theEOptP) + val newEOptP = u match { + case Some(interimEP) ⇒ + if (debug) assert(eOptP != interimEP) + dependersLock.lockInterruptibly() + eOptP = interimEP + interimEP + case _ ⇒ + null + } + lock.unlock() + + if (newEOptP ne null) { + val theDependers = dependers + dependers = dependers.filter(d ⇒ suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) + dependersLock.unlock() + notifyDependers(newEOptP, theEOptP, theDependers) + } + + if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) } def addDependerOrScheduleContinuation( @@ -777,10 +817,8 @@ class YAPPS( // 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). if ((theEOptP ne dependee) && - // Note that the dependee might be a different reference of an equivalent EPK. - theEOptP.isEPS && (theEOptP.isFinal || !suppressedPKs(dependee.pk.id))) { - scheduleTask(new YappsContinuationTask(depender, theEOptP.asEPS, dependee)) + scheduleTask(new YappsContinuationTask(depender, dependee)) false } else { dependers += depender @@ -797,29 +835,6 @@ class YAPPS( dependersLock.unlock() } - def update(update: UpdateComputation[Entity, Property]): Unit = { - lock.lockInterruptibly() - val theEOptP = eOptP - val newEOptP = update(theEOptP) match { - case Some(interimEP) ⇒ - eOptP = interimEP - interimEP - case _ ⇒ - null - } - lock.unlock() - - if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) - - dependersLock.lockInterruptibly() - val theDependers = dependers - dependers = dependers.filter(d ⇒ suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) - dependersLock.unlock() - - if (newEOptP ne null) - notifyDependers(newEOptP, theEOptP, theDependers) - } - def notifyDependers( theEOptP: SomeEPS, oldEOptP: SomeEOptionP, @@ -830,25 +845,24 @@ class YAPPS( theDependers.foreach { depender ⇒ if (!unnotifiedPKs.contains(depender.pk) && (theEOptP.isFinal || !suppressInterimUpdates(depender.pk.id)(theEOptP.pk.id))) { - scheduleTask(new YappsContinuationTask(depender, theEOptP, oldEOptP)) + scheduleTask(new YappsContinuationTask(depender, oldEOptP)) } } } } - def applyContinuation(dependee: SomeEPS, oldDependee: SomeEOptionP): Unit = { + def applyContinuation(oldDependee: SomeEOptionP): Unit = { // IMPROVE: Use tryLock() instead - val isSuppressed = suppressInterimUpdates(eOptP.pk.id)(dependee.pk.id) - val epk = dependee.toEPK + val isSuppressed = suppressInterimUpdates(eOptP.pk.id)(oldDependee.pk.id) + val epk = oldDependee.toEPK lock.lockInterruptibly() val theDependees = dependees // We are still interessted in that dependee? - if (theDependees != null && theDependees.exists { - // TODO: adjust aplly() method in order to guarantee eq for EPKs - d ⇒ (oldDependee.isEPK && epk == d) || (d eq oldDependee) || (isSuppressed && epk == d.toEPK) + if (theDependees != null && theDependees.exists { d ⇒ + (d eq oldDependee) || (isSuppressed && epk == d.toEPK) }) { // We always retrieve the most up-to-date state of the dependee. - val currentDependee = ps(dependee.pk.id).get(dependee.e).eOptP.asEPS + val currentDependee = 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". handleResult(c(currentDependee)) @@ -878,8 +892,4 @@ object YAPPS extends PropertyStoreFactory[YAPPS] { val ps = new YAPPS(contextMap) ps } -} - -class MyRRLock extends ReentrantLock { - override def getOwner: Thread = super.getOwner } \ No newline at end of file From c9ab696725585c80bb505000b44246c56fb6c885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Mon, 17 Feb 2020 16:44:10 +0100 Subject: [PATCH 083/327] improved performance by using monitors instead of ReentrantLocks --- .../main/scala/org/opalj/fpcf/par/YAPPS.scala | 150 +++++++++--------- 1 file changed, 78 insertions(+), 72 deletions(-) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala index 5f6aba3fc6..d1000b6041 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala @@ -6,7 +6,6 @@ package par import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.PriorityBlockingQueue import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.locks.ReentrantLock import scala.collection.mutable.ArrayBuffer import scala.util.control.ControlThrowable @@ -695,10 +694,10 @@ class YAPPS( override def apply(): Unit = { val state = ps(pkId).get(e) - state.lock.lock() - if (state.eOptP.isEPK) - handleResult(pc(e)) - state.lock.unlock() + state.lock.synchronized { + if (state.eOptP.isEPK) + handleResult(pc(e)) + } } } @@ -718,25 +717,27 @@ class YAPPS( @volatile var dependees: Traversable[SomeEOptionP], @volatile var dependers: Set[SomeEPK] = Set.empty ) { - val lock = new ReentrantLock() - val dependersLock = new ReentrantLock() + val lock = new Object() + val dependersLock = new Object() def setFinal(finalEP: FinalEP[Entity, Property], unnotifiedPKs: Set[PropertyKind]): Unit = { - lock.lockInterruptibly() - val theEOptP = eOptP - if (theEOptP.isFinal) { - throw new IllegalStateException(s"${theEOptP.e} already had the property $theEOptP") - } else { - if (debug) eOptP.checkIsValidPropertiesUpdate(finalEP, Nil) - dependersLock.lock() - eOptP = finalEP - } - dependees = null - lock.unlock() + var theEOptP: SomeEOptionP = null + var theDependers: Set[SomeEPK] = null + lock.synchronized { + theEOptP = eOptP + if (theEOptP.isFinal) { + throw new IllegalStateException(s"${theEOptP.e} already had the property $theEOptP") + } else { + if (debug) eOptP.checkIsValidPropertiesUpdate(finalEP, Nil) + dependersLock.synchronized { + eOptP = finalEP - val theDependers = dependers - dependers = null - dependersLock.unlock() + theDependers = dependers + dependers = null + } + } + dependees = null + } notifyDependers(finalEP, theEOptP, theDependers, unnotifiedPKs) @@ -750,27 +751,30 @@ class YAPPS( ): Unit = { var requiresNotification = false - lock.lockInterruptibly() - val theEOptP = eOptP - if (theEOptP.isFinal) { - throw new IllegalStateException(s"${theEOptP.e} already had the property $theEOptP") - } else { - if (debug) theEOptP.checkIsValidPropertiesUpdate(interimEP, newDependees) - if (interimEP.isUpdatedComparedTo(theEOptP)) { - requiresNotification = true - dependersLock.lockInterruptibly() - eOptP = interimEP + var theEOptP: SomeEOptionP = null + var theDependers: Set[SomeEPK] = null + lock.synchronized { + theEOptP = eOptP + if (theEOptP.isFinal) { + throw new IllegalStateException(s"${theEOptP.e} already had the property $theEOptP") + } else { + if (debug) theEOptP.checkIsValidPropertiesUpdate(interimEP, newDependees) + if (interimEP.isUpdatedComparedTo(theEOptP)) { + requiresNotification = true + dependersLock.synchronized { + eOptP = interimEP + + theDependers = dependers + // Clear all dependers that will be notified, they will re-register if required + dependers = dependers.filter(d ⇒ suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) + } + } + c = newC + dependees = newDependees } - c = newC - dependees = newDependees } - lock.unlock() if (requiresNotification) { - val theDependers = dependers - // Clear all dependers that will be notified, they will re-register if required - dependers = dependers.filter(d ⇒ suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) - dependersLock.unlock() notifyDependers(interimEP, theEOptP, theDependers) } @@ -780,24 +784,29 @@ class YAPPS( } def partialUpdate(updateComputation: UpdateComputation[Entity, Property]): Unit = { - lock.lockInterruptibly() - val theEOptP = eOptP - val u = updateComputation(theEOptP) - val newEOptP = u match { - case Some(interimEP) ⇒ - if (debug) assert(eOptP != interimEP) - dependersLock.lockInterruptibly() - eOptP = interimEP - interimEP - case _ ⇒ - null + var theEOptP: SomeEOptionP = null + var newEOptP: SomeEPS = null + var theDependers: Set[SomeEPK] = null + + lock.synchronized { + theEOptP = eOptP + val u = updateComputation(theEOptP) + newEOptP = u match { + case Some(interimEP) ⇒ + if (debug) assert(eOptP != interimEP) + dependersLock.synchronized { + eOptP = interimEP + + theDependers = dependers + dependers = dependers.filter(d ⇒ suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) + } + interimEP + case _ ⇒ + null + } } - lock.unlock() if (newEOptP ne null) { - val theDependers = dependers - dependers = dependers.filter(d ⇒ suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) - dependersLock.unlock() notifyDependers(newEOptP, theEOptP, theDependers) } @@ -809,8 +818,7 @@ class YAPPS( dependee: SomeEOptionP, suppressedPKs: Array[Boolean] ): Boolean = { - dependersLock.lockInterruptibly() - try { + dependersLock.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). @@ -822,15 +830,13 @@ class YAPPS( dependers += depender true } - } finally { - dependersLock.unlock() } } def removeDepender(epk: SomeEPK): Unit = { - dependersLock.lockInterruptibly() - if (dependers != null) dependers -= epk - dependersLock.unlock() + dependersLock.synchronized { + if (dependers != null) dependers -= epk + } } def notifyDependers( @@ -853,19 +859,19 @@ class YAPPS( // IMPROVE: Use tryLock() instead val isSuppressed = suppressInterimUpdates(eOptP.pk.id)(oldDependee.pk.id) val epk = oldDependee.toEPK - lock.lockInterruptibly() - val theDependees = dependees - // We are still interessted in that dependee? - if (theDependees != null && theDependees.exists { d ⇒ - (d eq oldDependee) || (isSuppressed && epk == d.toEPK) - }) { - // We always retrieve the most up-to-date state of the dependee. - val currentDependee = 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". - handleResult(c(currentDependee)) + lock.synchronized { + val theDependees = dependees + // We are still interessted in that dependee? + if (theDependees != null && theDependees.exists { d ⇒ + (d eq oldDependee) || (isSuppressed && epk == d.toEPK) + }) { + // We always retrieve the most up-to-date state of the dependee. + val currentDependee = 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". + handleResult(c(currentDependee)) + } } - lock.unlock() } } From 31f9f4146e73be6f8db00fcce41a9f41ed673b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Tue, 18 Feb 2020 10:57:17 +0100 Subject: [PATCH 084/327] dependers are now a java HashSet. split dependers and suppressed dependers --- .../main/scala/org/opalj/fpcf/par/YAPPS.scala | 90 ++++++++----------- 1 file changed, 38 insertions(+), 52 deletions(-) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala index d1000b6041..a5a5c74363 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala @@ -526,7 +526,9 @@ class YAPPS( val epk = interimEPKState.eOptP.toEPK dependees.foreach { dependee ⇒ // during execution, no other thread accesses the dependers of the EPKState - ps(dependee.pk.id).get(dependee.e).dependers -= epk + val dependeeState = ps(dependee.pk.id).get(dependee.e) + dependeeState.dependers.remove(epk) + dependeeState.suppressedDependers.remove(epk) } scheduleTask(new YappsSetTask(interimEPKState.eOptP.toFinalEP)) } @@ -694,7 +696,7 @@ class YAPPS( override def apply(): Unit = { val state = ps(pkId).get(e) - state.lock.synchronized { + state.synchronized { if (state.eOptP.isEPK) handleResult(pc(e)) } @@ -712,35 +714,31 @@ class YAPPS( } case class YappsEPKState( - @volatile var eOptP: SomeEOptionP, - @volatile var c: OnUpdateContinuation, - @volatile var dependees: Traversable[SomeEOptionP], - @volatile var dependers: Set[SomeEPK] = Set.empty + @volatile var eOptP: SomeEOptionP, + @volatile var c: OnUpdateContinuation, + @volatile var dependees: Traversable[SomeEOptionP], + dependers: java.util.HashSet[SomeEPK] = new java.util.HashSet(), + suppressedDependers: java.util.HashSet[SomeEPK] = new java.util.HashSet() ) { - val lock = new Object() - val dependersLock = new Object() def setFinal(finalEP: FinalEP[Entity, Property], unnotifiedPKs: Set[PropertyKind]): Unit = { var theEOptP: SomeEOptionP = null - var theDependers: Set[SomeEPK] = null - lock.synchronized { + this.synchronized { theEOptP = eOptP if (theEOptP.isFinal) { throw new IllegalStateException(s"${theEOptP.e} already had the property $theEOptP") } else { if (debug) eOptP.checkIsValidPropertiesUpdate(finalEP, Nil) - dependersLock.synchronized { + dependers.synchronized { eOptP = finalEP - theDependers = dependers - dependers = null + notifyAndClearDependers(finalEP, theEOptP, dependers, unnotifiedPKs) + notifyAndClearDependers(finalEP, theEOptP, suppressedDependers, unnotifiedPKs) } } dependees = null } - notifyDependers(finalEP, theEOptP, theDependers, unnotifiedPKs) - if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) } @@ -749,24 +747,19 @@ class YAPPS( newC: OnUpdateContinuation, newDependees: Traversable[SomeEOptionP] ): Unit = { - var requiresNotification = false var theEOptP: SomeEOptionP = null - var theDependers: Set[SomeEPK] = null - lock.synchronized { + this.synchronized { theEOptP = eOptP if (theEOptP.isFinal) { throw new IllegalStateException(s"${theEOptP.e} already had the property $theEOptP") } else { if (debug) theEOptP.checkIsValidPropertiesUpdate(interimEP, newDependees) if (interimEP.isUpdatedComparedTo(theEOptP)) { - requiresNotification = true - dependersLock.synchronized { + dependers.synchronized { eOptP = interimEP - theDependers = dependers - // Clear all dependers that will be notified, they will re-register if required - dependers = dependers.filter(d ⇒ suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) + notifyAndClearDependers(interimEP, theEOptP, dependers) } } c = newC @@ -774,10 +767,6 @@ class YAPPS( } } - if (requiresNotification) { - notifyDependers(interimEP, theEOptP, theDependers) - } - updateDependees(this, newDependees) if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) @@ -785,20 +774,16 @@ class YAPPS( def partialUpdate(updateComputation: UpdateComputation[Entity, Property]): Unit = { var theEOptP: SomeEOptionP = null - var newEOptP: SomeEPS = null - var theDependers: Set[SomeEPK] = null - lock.synchronized { + this.synchronized { theEOptP = eOptP - val u = updateComputation(theEOptP) - newEOptP = u match { + updateComputation(theEOptP) match { case Some(interimEP) ⇒ if (debug) assert(eOptP != interimEP) - dependersLock.synchronized { + dependers.synchronized { eOptP = interimEP - theDependers = dependers - dependers = dependers.filter(d ⇒ suppressInterimUpdates(d.pk.id)(theEOptP.pk.id)) + notifyAndClearDependers(interimEP, theEOptP, dependers) } interimEP case _ ⇒ @@ -806,10 +791,6 @@ class YAPPS( } } - if (newEOptP ne null) { - notifyDependers(newEOptP, theEOptP, theDependers) - } - if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) } @@ -818,7 +799,7 @@ class YAPPS( dependee: SomeEOptionP, suppressedPKs: Array[Boolean] ): Boolean = { - dependersLock.synchronized { + 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). @@ -827,39 +808,44 @@ class YAPPS( scheduleTask(new YappsContinuationTask(depender, dependee)) false } else { - dependers += depender + if (suppressedPKs(theEOptP.pk.id)) { + suppressedDependers.add(depender) + } else { + dependers.add(depender) + } true } } } def removeDepender(epk: SomeEPK): Unit = { - dependersLock.synchronized { - if (dependers != null) dependers -= epk + dependers.synchronized { + dependers.remove(epk) + suppressedDependers.remove(epk) } } - def notifyDependers( + def notifyAndClearDependers( theEOptP: SomeEPS, oldEOptP: SomeEOptionP, - theDependers: Set[SomeEPK], - unnotifiedPKs: Set[PropertyKind] = Set.empty + theDependers: java.util.HashSet[SomeEPK], + unnotifiedPKs: Set[PropertyKind] = Set.empty ): Unit = { - if (theDependers ne null) { - theDependers.foreach { depender ⇒ - if (!unnotifiedPKs.contains(depender.pk) && - (theEOptP.isFinal || !suppressInterimUpdates(depender.pk.id)(theEOptP.pk.id))) { + theDependers.forEach { depender ⇒ + if (!unnotifiedPKs.contains(depender.pk)) { scheduleTask(new YappsContinuationTask(depender, oldEOptP)) } } - } + + // Clear all dependers that will be notified, they will re-register if required + theDependers.clear() } def applyContinuation(oldDependee: SomeEOptionP): Unit = { // IMPROVE: Use tryLock() instead val isSuppressed = suppressInterimUpdates(eOptP.pk.id)(oldDependee.pk.id) val epk = oldDependee.toEPK - lock.synchronized { + this.synchronized { val theDependees = dependees // We are still interessted in that dependee? if (theDependees != null && theDependees.exists { d ⇒ From 6ded353ea7b97b6169d80cf721e2ac675706c0fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Tue, 18 Feb 2020 12:43:56 +0100 Subject: [PATCH 085/327] added todos --- .../main/scala/org/opalj/fpcf/PropertyComputationResult.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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..f939879118 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: Traversable[SomeEOptionP],//IMPROVE: require sets or EOptionPSets val c: ProperOnUpdateContinuation ) extends ProperPropertyComputationResult { result ⇒ @@ -388,7 +388,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: Traversable[SomeEOptionP], //IMPROVE: require sets or EOptionPSets c: OnUpdateContinuation ) extends ProperPropertyComputationResult { From a0bfeedc7c57eb976a59b6f2d8370df62fe5ac1b Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 19 Feb 2020 09:49:35 +0100 Subject: [PATCH 086/327] Dependent Immutability improvements --- .../ClassImmutabilityAnalysisDemo.scala | 2 +- ...AnalysisDemo_performanceMeasurements.scala | 2 +- .../FieldImmutabilityAnalysisDemo.scala | 9 +- ...AnalysisDemo_performanceMeasurements.scala | 2 +- ...AnalysisDemo_performanceMeasurements.scala | 2 +- ...AnalysisDemo_performanceMeasurements.scala | 154 +- ...yAnalysisDemo_performanceMeasurement.scala | 133 +- .../ClassImmutabilityMatcher.scala | 6 +- .../FieldImmutabilityMatcher.scala | 6 +- .../properties/ClassImmutability_new.scala | 83 +- .../fpcf/properties/FieldImmutability.scala | 88 +- .../properties/ReferenceImmutability.scala | 60 +- .../properties/TypeImmutability_new.scala | 6 +- .../L0FieldImmutabilityAnalysis.scala | 369 +-- .../L0ReferenceImmutabilityAnalysis.scala | 14 +- ...mutabilityLazyInitializationAnalysis.scala | 2169 +++++++++-------- .../LxClassImmutabilityAnalysis_new.scala | 64 +- .../LxTypeImmutabilityAnalysis_new.scala | 4 +- 18 files changed, 1658 insertions(+), 1515 deletions(-) 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 index 2918e919df..c4d1503537 100644 --- 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 @@ -99,7 +99,7 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { propertyStore .entities(ClassImmutability_new.key) .toList - .collect({ case x @ FinalEP(_, DependentImmutableClass(_)) => x }) + .collect({ case x @ FinalEP(_, DependentImmutableClass) => x }) .map(x => x.toString + "\n") ) sb.append("\nShallow Immutable Class: \n") diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala index 39924dbd93..c965db7431 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -47,7 +47,7 @@ object ClassImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnal def analyze(theProject: Project[URL]): String = { var times: List[Seconds] = Nil: List[Seconds] - for (i <- 0 until 19) { + for (i <- 0 until 10) { val project = Project.recreate(theProject) val analysesManager = project.get(FPCFAnalysesManagerKey) analysesManager.project.get(RTACallGraphKey) 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 index 7ca9c569cc..7331dfc42e 100644 --- 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 @@ -2,8 +2,8 @@ package org.opalj.fpcf.analyses import java.io.BufferedWriter -import java.io.File import java.io.FileWriter +import java.io.File import java.net.URL import java.util.Calendar @@ -17,7 +17,6 @@ import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis 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.FieldImmutability import org.opalj.br.fpcf.properties.MutableField import org.opalj.br.fpcf.properties.ShallowImmutableField import org.opalj.fpcf.PropertyStore @@ -77,6 +76,7 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } { t => analysisTime = t.toSeconds } + val sb: StringBuilder = new StringBuilder sb.append("Mutable Fields: \n") sb.append( @@ -97,9 +97,8 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { sb.append("\nDependet Immutable Fields: \n") sb.append( propertyStore - .entities(FieldImmutability.key) + .finalEntities(DependentImmutableField) .toList - .collect({ case x: DependentImmutableField => x }) .map(x => x.toString + "\n") .toString() ) @@ -113,7 +112,7 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { ) val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/refImm" + dateString + ".txt") + val file = new File("C:/MA/results/fieldImm" + dateString + ".txt") val bw = new BufferedWriter(new FileWriter(file)) bw.write(sb.toString()) bw.close() diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala index be2e23b644..b0b5ce4bcf 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -44,7 +44,7 @@ object FieldImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnal def analyze(theProject: Project[URL]): String = { var times: List[Seconds] = Nil: List[Seconds] - for (i <- 0 until 19) { + for (i <- 0 until 10) { val project = Project.recreate(theProject) val analysesManager = project.get(FPCFAnalysesManagerKey) analysesManager.project.get(RTACallGraphKey) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala index 24ff667072..c7ebda8175 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -46,7 +46,7 @@ object ReferenceImmutabilityAnalysisDemo_performanceMeasurements def analyze(theProject: Project[URL]): String = { var times: List[Seconds] = Nil: List[Seconds] - for (i <- 0 until 19) { + for (i <- 0 until 10) { val project = Project.recreate(theProject) val analysesManager = project.get(FPCFAnalysesManagerKey) analysesManager.project.get(RTACallGraphKey) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala index 4c6f2c79d9..54bc1e8ae3 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala @@ -31,87 +31,87 @@ import org.opalj.util.Seconds object ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(theProject: Project[URL]): String = { - var times: List[Seconds] = Nil: List[Seconds] - for (i ← 0 until 19) { - val project = Project.recreate(theProject) - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityLazyInitializationAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyStaticDataUsageAnalysis, - LazyTypeImmutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyFieldLocalityAnalysis, - LazyReturnValueFreshnessAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - times = analysisTime :: times - } - /*def printResult(string: String, property: Property)( + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i <- 0 until 10) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityLazyInitializationAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyTypeImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyFieldLocalityAnalysis, + LazyReturnValueFreshnessAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + /*def printResult(string: String, property: Property)( implicit propertyStore: PropertyStore ): Unit = {} **/ - /** - * val notInitializedReferencesString = propertyStore - * .finalEntities(NoLazyInitialization) - * .toList - * .toString() - * val notThreadSafeLazyInitializationString = propertyStore - * .finalEntities(NotThreadSafeLazyInitialization) - * .toList - * .toString() - * val lazyInitializationString = propertyStore - * .finalEntities(LazyInitialization) - * .toList - * .toString() - * println(s"Not initialized References $notInitializedReferencesString") - * println(s"Not threadsafe Lazy Initialization: $notThreadSafeLazyInitializationString") - * println(s"Lazy Initialization String: $lazyInitializationString") - */ - /** - * "Not lazy initialized References: " + propertyStore - * .finalEntities(NoLazyInitialization) - * .toList - * .toString() + "\n" + - * "Not thread safe lazy initialization: " + propertyStore - * .finalEntities(NotThreadSafeLazyInitialization) - * .toList - * .toString() + "\n" + - * "Lazy Initialization: " + propertyStore - * .finalEntities(LazyInitialization) - * .toList - * .toString()* - */ - times.foreach(s ⇒ println(s+" seconds")) - val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size - f"took: $aver seconds on average" - } + /** + * val notInitializedReferencesString = propertyStore + * .finalEntities(NoLazyInitialization) + * .toList + * .toString() + * val notThreadSafeLazyInitializationString = propertyStore + * .finalEntities(NotThreadSafeLazyInitialization) + * .toList + * .toString() + * val lazyInitializationString = propertyStore + * .finalEntities(LazyInitialization) + * .toList + * .toString() + * println(s"Not initialized References $notInitializedReferencesString") + * println(s"Not threadsafe Lazy Initialization: $notThreadSafeLazyInitializationString") + * println(s"Lazy Initialization String: $lazyInitializationString") + */ + /** + * "Not lazy initialized References: " + propertyStore + * .finalEntities(NoLazyInitialization) + * .toList + * .toString() + "\n" + + * "Not thread safe lazy initialization: " + propertyStore + * .finalEntities(NotThreadSafeLazyInitialization) + * .toList + * .toString() + "\n" + + * "Lazy Initialization: " + propertyStore + * .finalEntities(LazyInitialization) + * .toList + * .toString()* + */ + times.foreach(s => println(s + " seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size + f"took: $aver seconds on average" + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala index 996a182686..32228d931a 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala @@ -16,15 +16,14 @@ import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis 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.ShallowImmutableType -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -33,73 +32,73 @@ import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis */ object TypeImmutabilityAnalysisDemo_performanceMeasurement extends ProjectAnalysisApplication { - override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" + override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" - override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" + override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(theProject: Project[URL]): String = { + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - var times: List[Seconds] = Nil: List[Seconds] - for (i ← 0 until 19) { - val project = Project.recreate(theProject) - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - EagerLxTypeImmutabilityAnalysis_new, - LazyLxClassImmutabilityAnalysis_new, - LazyTypeImmutabilityAnalysis, - LazyFieldLocalityAnalysis, - LazySimpleEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - times = analysisTime :: times - } + def analyze(theProject: Project[URL]): String = { - /** - * "Mutable Type: "+propertyStore - * .finalEntities(MutableType_new) - * .toList - * .toString()+"\n"+ - * "Shallow Immutable Type: "+propertyStore - * .finalEntities(ShallowImmutableType) - * .toList - * .toString()+"\n"+ - * "Dependent Immutable Type: "+propertyStore - * .finalEntities(DependentImmutableType) - * .toList - * .toString()+"\n"+ - * "Deep Immutable Type: "+propertyStore - * .finalEntities(DeepImmutableType) - * .toList - * .toString()+"\n"* - */ - times.foreach(s ⇒ println(s+" seconds")) - val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size - f"took: $aver seconds on average" + var times: List[Seconds] = Nil: List[Seconds] + for (i <- 0 until 10) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new, + LazyTypeImmutabilityAnalysis, + LazyFieldLocalityAnalysis, + LazySimpleEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t => + analysisTime = t.toSeconds + } + times = analysisTime :: times } + + /** + * "Mutable Type: "+propertyStore + * .finalEntities(MutableType_new) + * .toList + * .toString()+"\n"+ + * "Shallow Immutable Type: "+propertyStore + * .finalEntities(ShallowImmutableType) + * .toList + * .toString()+"\n"+ + * "Dependent Immutable Type: "+propertyStore + * .finalEntities(DependentImmutableType) + * .toList + * .toString()+"\n"+ + * "Deep Immutable Type: "+propertyStore + * .finalEntities(DeepImmutableType) + * .toList + * .toString()+"\n"* + */ + times.foreach(s => println(s + " seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size + f"took: $aver seconds on average" + } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala index 4a15bb6dcd..b128add15c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala @@ -46,8 +46,8 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) ): Option[String] = { if (!properties.exists(p ⇒ { p match { - case DependentImmutableClass(_) ⇒ property == DependentImmutableClass() - case _ ⇒ p == property + /// case DependentImmutableClass(_) => property == DependentImmutableClass() + case _ ⇒ p == property } })) { //p == property)) { // ... when we reach this point the expected property was not found. @@ -62,7 +62,7 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) class MutableClassMatcher extends ClassImmutabilityMatcher(MutableClass) -class DependentImmutableClassMatcher extends ClassImmutabilityMatcher(DependentImmutableClass()) +class DependentImmutableClassMatcher extends ClassImmutabilityMatcher(DependentImmutableClass) class ShallowImmutableClassMatcher extends ClassImmutabilityMatcher(ShallowImmutableClass) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala index 1871e2971d..3afe3e8370 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala @@ -44,8 +44,8 @@ class FieldImmutabilityMatcher(val property: FieldImmutability) extends Abstract ): Option[String] = { if (!properties.exists(p ⇒ { p match { - case DependentImmutableField(_) ⇒ property == DependentImmutableField() //TODO optimize - case _ ⇒ p == property + case DependentImmutableField ⇒ property == DependentImmutableField //TODO optimize + case _ ⇒ p == property } })) { //p == property)) { @@ -62,6 +62,6 @@ class MutableFieldMatcher extends FieldImmutabilityMatcher(MutableField) class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(ShallowImmutableField) -class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(DependentImmutableField()) +class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(DependentImmutableField) class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(DeepImmutableField) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index b4977729a9..bb8fcf0032 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -1,12 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.br.fpcf.properties -import org.opalj.fpcf.Property +import org.opalj.fpcf.Entity +import org.opalj.fpcf.OrderedProperty import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = ClassImmutability_new + final type Self = ClassImmutability_new } /** @@ -25,37 +26,77 @@ sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaIn * @author Tobias Peter Roth */ sealed trait ClassImmutability_new - extends Property + extends OrderedProperty with ClassImmutabilityPropertyMetaInformation_new { - final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key - def correspondingTypeImmutability: TypeImmutability_new + final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key + def correspondingTypeImmutability: TypeImmutability_new } object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[ClassImmutability_new]] property. - */ - final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( - "opalj.ClassImmutability_new", - MutableClass - ) + /** + * The key associated with every [[ClassImmutability_new]] property. + */ + final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( + "opalj.ClassImmutability_new", + MutableClass + ) } -case object MutableClass extends ClassImmutability_new { - def correspondingTypeImmutability = MutableType_new +case object DeepImmutableClass extends ClassImmutability_new { + override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + + def meet(that: ClassImmutability_new): ClassImmutability_new = + if (this == that) + this + else + that } -case class DependentImmutableClass(typeBounds: Set[(String, String)] = Set.empty) - extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = - DependentImmutableType //TODO check +case object DependentImmutableClass extends ClassImmutability_new { + override def correspondingTypeImmutability: TypeImmutability_new = + DependentImmutableType //TODO check + + def meet(that: ClassImmutability_new): ClassImmutability_new = + if (that == MutableClass || that == ShallowImmutableClass) + that + else + this + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + + if (other == DeepImmutableClass) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + } + } + } case object ShallowImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = ShallowImmutableType + override def correspondingTypeImmutability: TypeImmutability_new = ShallowImmutableType + def meet(that: ClassImmutability_new): ClassImmutability_new = + if (that == MutableClass) + that + else + this + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + + if (other == DeepImmutableClass || other == DependentImmutableClass) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + } + } + } -case object DeepImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType +case object MutableClass extends ClassImmutability_new { + def correspondingTypeImmutability = MutableType_new + def meet(other: ClassImmutability_new): ClassImmutability_new = this + + 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/FieldImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldImmutability.scala index 23b7699fad..56b914fb38 100644 --- 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 @@ -1,13 +1,14 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.br.fpcf.properties -import org.opalj.fpcf.Property +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 + type Self = FieldImmutability } @@ -23,40 +24,73 @@ sealed trait FieldImmutabilityPropertyMetaInformation extends PropertyMetaInform * * @author Tobias Peter Roth */ -sealed trait FieldImmutability extends Property with FieldImmutabilityPropertyMetaInformation { +sealed trait FieldImmutability + extends OrderedProperty + with FieldImmutabilityPropertyMetaInformation { - final def key: PropertyKey[FieldImmutability] = FieldImmutability.key + final def key: PropertyKey[FieldImmutability] = FieldImmutability.key } object FieldImmutability extends FieldImmutabilityPropertyMetaInformation { - final val PropertyKeyName = "opalj.FieldImmutability" + final val PropertyKeyName = "opalj.FieldImmutability" - final val key: PropertyKey[FieldImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableField - ) + 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 = + if (this == that) + this + else + 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 +case object MutableField extends FieldImmutability { -sealed trait ImmutableField extends FieldImmutability + def meet(other: FieldImmutability): this.type = this -case object ShallowImmutableField extends ImmutableField + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + if (other != MutableField) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + } + } -case class DependentImmutableField(var genericString: Option[String] = None) - extends ImmutableField - with FieldImmutability //{ -// def genericString: Option[String] = None -//} -/** - * case object DependentImmutableField extends DependentImmutableField_ { - * def setGenericString(s: Option[String]) = { - * genericString = s - * this - * } - * }* - */ -case object DeepImmutableField extends ImmutableField +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala index d4c57da5a7..489c8ee92a 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -1,13 +1,15 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.br.fpcf.properties -import org.opalj.fpcf.Property +import org.opalj.br.fpcf.properties +import org.opalj.fpcf.Entity +import org.opalj.fpcf.OrderedProperty import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - type Self = ReferenceImmutability + type Self = ReferenceImmutability } @@ -26,25 +28,55 @@ sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaIn * @author Tobias Peter Roth */ sealed trait ReferenceImmutability - extends Property + extends OrderedProperty with ReferenceImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key + final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key } object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { - final val PropertyKeyName = "opalj.ReferenceImmutability" + final val PropertyKeyName = "opalj.ReferenceImmutability" - final val key: PropertyKey[ReferenceImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableReference - ) - } + final val key: PropertyKey[ReferenceImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableReference + ) + } +} + +case object ImmutableReference extends ReferenceImmutability { + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + def meet(that: ReferenceImmutability): ReferenceImmutability = + if (this == that) + this + else + that } -case object MutableReference extends ReferenceImmutability +case object LazyInitializedReference extends ReferenceImmutability { + def meet(other: ReferenceImmutability): properties.ReferenceImmutability = { + if (other == MutableReference) { + other + } else { + this + } + } + + override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { + if (other == ImmutableReference) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } + } + +} -case object LazyInitializedReference extends ReferenceImmutability +case object MutableReference extends ReferenceImmutability { + def meet(other: ReferenceImmutability): this.type = this -case object ImmutableReference extends ReferenceImmutability + override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { + if (other != MutableReference) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other => $this") + } + } +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala index 68d4a303b6..4a2b9e9d7a 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -94,7 +94,7 @@ case object DependentImmutableType extends TypeImmutability_new { override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { if (other == DeepImmutableType) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } } @@ -116,7 +116,7 @@ case object ShallowImmutableType extends TypeImmutability_new { override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { if (other == DeepImmutableType || other == DependentImmutableType) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } } } @@ -133,7 +133,7 @@ case object MutableType_new extends TypeImmutability_new { override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { if (other != MutableType_new) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index d115616740..c13fe39947 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -1,12 +1,15 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.br.fpcf.analyses +import org.opalj.br.ClassSignature import org.opalj.br.ClassTypeSignature import org.opalj.br.Field -import org.opalj.br.FieldType +import org.opalj.br.FormalTypeParameter 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.analyses.FieldAccessInformationKey import org.opalj.br.analyses.SomeProject @@ -16,6 +19,7 @@ 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.analyses.DependentImmutabilityKind.DependentImmutabilityKind import org.opalj.br.fpcf.properties.DeepImmutableField import org.opalj.br.fpcf.properties.DeepImmutableType import org.opalj.br.fpcf.properties.DependentImmutableField @@ -46,11 +50,14 @@ import org.opalj.fpcf.SomeEPS case class State(f: Field) { var field: Field = f - var typeImmutability: Option[Boolean] = Some(true) + var typeImmutability: Option[Boolean] = None var referenceImmutability: Option[Boolean] = None - var depencenciesTypes: scala.collection.mutable.Set[ObjectType] = - scala.collection.mutable.Set[ObjectType]() - var dependentTypeImmutability: Option[Boolean] = None + var dependentImmutability: Option[DependentImmutabilityKind] = None +} + +object DependentImmutabilityKind extends Enumeration { + type DependentImmutabilityKind = Value + val dependent, nowShallowOrMutable, onlyDeepImmutable = Value } /** @@ -71,7 +78,8 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { entity match { - case field: Field => determineFieldImmutability(field) + case field: Field => + determineFieldImmutability(field) case _ => val m = entity.getClass.getName + "is not an org.opalj.br.Field" throw new IllegalArgumentException(m) @@ -81,181 +89,173 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) private[analyses] def determineFieldImmutability( field: Field ): ProperPropertyComputationResult = { - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - - def determineGenericFieldImmutability(state: State): Option[Boolean] = { - var genericFields: Set[ObjectType] = Set.empty - state.field.fieldTypeSignature match { - case Some(fts) => - fts match { - case ClassTypeSignature(_, SimpleClassTypeSignature(name, typeArguments), _) => { - typeArguments.foreach( - ta => { - ta match { - case ProperTypeArgument( - varianceIndicator, - ClassTypeSignature( - packageIdentifier1, - SimpleClassTypeSignature( - packageIdentifier2, - typeArguments2 - ), - _ - ) - ) => { - packageIdentifier1 match { - case Some(pid1) => genericFields += ObjectType(pid1 + packageIdentifier2) - case _ => genericFields += ObjectType(packageIdentifier2) - } - - } + var classFormalTypeParameters: Option[Set[String]] = None + def loadFormalTypeparameters() = { + var result: Set[String] = Set.empty + field.classFile.attributes.foreach( + x => + x match { + case SourceFile(_) => + case ClassSignature(typeParameters, _, _) => + typeParameters.foreach( + y => + y match { + case FormalTypeParameter(identifier, _, _) => result += identifier case _ => } - } ) - } case _ => } - case _ => - } - genericFields.toList.foreach(objectType => { + ) + if (result.size > 0) + classFormalTypeParameters = Some(result) + } + def isInClassesGenericTypeParameters(string: String): Boolean = { + if (classFormalTypeParameters == None) + false + else + classFormalTypeParameters.head.contains(string) + } + + def handleTypeImmutability(state: State) = { + val objectType = field.fieldType.asFieldType + if (objectType.isArrayType) { + state.typeImmutability = Some(true) + } else if (objectType.isBaseType) { + state.typeImmutability = Some(true); + } else { val result = propertyStore(objectType, TypeImmutability_new.key) + dependencies = dependencies.filter(_.e ne result.e) result match { - case FinalP(DeepImmutableType) => - case FinalP(ShallowImmutableType | MutableType_new) => { - return Some(false); //state.typeImmutability = Some(false) + case FinalEP(e, DeepImmutableType) => { + state.typeImmutability = Some(true); } - case ep @ _ => { - dependencies += ep + case FinalEP(f, DependentImmutableType) => { + if (dependencies.size > 0) + state.typeImmutability = None + else + state.typeImmutability = Some(false) } - } - }) - if (dependencies.size > 0) None; - else Some(true); - } - def handleTypeImmutability(objectType: FieldType)(state: State): Option[Boolean] = { - if (objectType.isArrayType) return Some(true); //TODO - if (objectType.isBaseType) return Some(true); - val result = propertyStore(objectType, TypeImmutability_new.key) - result match { - case FinalEP(e, DeepImmutableType) => { - return Some(true); - } - case FinalEP(f, DependentImmutableType) => { - //TODO under construction - //--------------------------------------------------------------------------------------- - state.typeImmutability = determineGenericFieldImmutability(state) - //--------------------------------------------------------------------------------------- - //statfielde.dependentTypeImmutability = Some(true) - return state.typeImmutability; - } - - case FinalEP(e, ShallowImmutableType) => { - return Some(false); //TODO mindstorm if this approch is appropriate - } - case FinalEP(e, t) if (t == MutableType_new) => { //MutableType) ⇒ { - return Some(false); - } - case x @ _ => { - dependencies += x - return None; //TODO check!!!!! None; + case FinalEP(e, ShallowImmutableType | MutableType_new) => { + state.typeImmutability = Some(false) + } + case ep: InterimEP[e, p] => dependencies += ep + case epk @ _ => dependencies += epk } } } - def hasImmutableType(field: Field)(state: State): Option[Boolean] = { - handleTypeImmutability(field.fieldType.asFieldType)(state) - } - def hasImmutableReference(field: Field): Option[Boolean] = { + def hasGenericType(state: State): Unit = { + var flag_noShallow = true + var flag_onlyDeep = true + var genericFields: List[ObjectType] = List() + state.field.asField.attributes.foreach( + _ match { + case RuntimeInvisibleAnnotationTable(_) => + case TypeVariableSignature(t) => + flag_onlyDeep = false + if (!isInClassesGenericTypeParameters(t)) { + flag_noShallow = false + } - propertyStore(field, ReferenceImmutability.key) match { - case FinalEP(_, MutableReference) => { - Some(false) - } - case FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO - Some(true) + case ClassTypeSignature( + packageIdentifier, + SimpleClassTypeSignature(simpleName, typeArguments), + _ + ) => + typeArguments + .foreach({ + _ match { + case ProperTypeArgument( + variance, + TypeVariableSignature(identifier: String) + ) => + flag_onlyDeep = false + if (!isInClassesGenericTypeParameters(identifier)) { + flag_noShallow = false + } + case ProperTypeArgument( + varianceIndicator, + ClassTypeSignature( + packageIdentifier1, + SimpleClassTypeSignature( + packageIdentifier2, + typeArguments2 + ), + _ + ) + ) => { + val oPath = + packageIdentifier1 match { + case Some(pid1) => pid1 + packageIdentifier2 + case _ => packageIdentifier2 + } + genericFields = ObjectType(oPath) :: genericFields + } + case _ => + flag_noShallow = false + flag_onlyDeep = false + } + }) + + case _ => + flag_noShallow = false + flag_onlyDeep = false } - case x @ _ => { - dependencies += x - None + ) + genericFields.foreach(objectType => { + + val result = propertyStore(objectType, TypeImmutability_new.key) + dependencies = dependencies.filter(_.e ne result.e) + result match { + case FinalP(DeepImmutableType) => + case FinalP(ShallowImmutableType | MutableType_new) => { + flag_noShallow = false + flag_onlyDeep = false + } + case ep: InterimEP[e, p] => dependencies += ep + case ep @ _ => + dependencies += ep } + + }) + + if (state.field.asField.attributes.size == state.field.asField.attributes + .collect({ case x @ RuntimeInvisibleAnnotationTable(_) => x }) + .size) { + flag_noShallow = false + flag_onlyDeep = false } - } - val state: State = new State(field) + if (flag_onlyDeep) + state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) + else if (flag_noShallow) + state.dependentImmutability = Some(DependentImmutabilityKind.nowShallowOrMutable) + } def createResult(state: State): ProperPropertyComputationResult = { state.referenceImmutability match { - case Some(false) => Result(field, MutableField) + case Some(false) | None => + Result(field, MutableField) case Some(true) => { - //If the field type is object. It is a generic field - //Test Dependent... //TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - //var genericString: Option[String] = None - //if (field.fieldType == ObjectType("java/lang/Object")) { - //++ println("field attributes: "+field.asField.attributes) - val genericString = field.asField.attributes.toList.collectFirst({ - case TypeVariableSignature(t) => t - case ClassTypeSignature( - packageIdentifier, - SimpleClassTypeSignature(simpleName, typeArguments), - classTypeSignatureSuffix - ) => { - val tA = typeArguments - .find(typeArgument => { - typeArgument match { - case ProperTypeArgument(variance, signature) => { - signature match { - case TypeVariableSignature(identifier) => true - case _ => false - } - } - case _ => false - } - }) - if (tA.size > 0) - tA.head match { - case ProperTypeArgument(variance, TypeVariableSignature(identifier)) => - identifier - case _ => "" - } else "" - } - }) - if (!field.attributes.isEmpty && genericString != None && genericString != Some("")) { - Result(field, DependentImmutableField(genericString)) - } else - state.typeImmutability match { - case Some(true) => Result(field, DeepImmutableField) - case Some(false) => { - state.dependentTypeImmutability match { - case Some(true) => Result(field, DependentImmutableField()) - case _ => Result(field, ShallowImmutableField) - } - } - case None if (dependencies.isEmpty) => Result(field, ShallowImmutableField) - case None => { - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) + state.typeImmutability match { + case Some(true) => + Result(field, DeepImmutableField) + case Some(false) | None => { + state.dependentImmutability match { + case Some(DependentImmutabilityKind.nowShallowOrMutable) => + Result(field, DependentImmutableField) + case Some(DependentImmutabilityKind.onlyDeepImmutable) => + Result(field, DeepImmutableField) + case _ => Result(field, ShallowImmutableField) } } - } - case None if (dependencies.isEmpty) => Result(field, MutableField) - case None => { - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) + } } } } + def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { dependencies = dependencies.filter(_.e ne eps.e) (eps: @unchecked) match { @@ -263,40 +263,59 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) dependencies += eps InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) } - - case x @ FinalEP(_, t) - if (t == DeepImmutableType) => { // || t == ShallowImmutableType) ⇒ { // ImmutableContainerType || t == ImmutableType) ⇒ { - state.typeImmutability = Some(true) - } - - case x @ FinalEP(_, MutableType_new | ShallowImmutableType) => { //MutableType) ⇒ { + case FinalEP(_, DeepImmutableType) => state.typeImmutability = Some(true) + case FinalEP(_, MutableType_new | ShallowImmutableType) => state.typeImmutability = Some(false) + case FinalEP(f, DependentImmutableType) => { + state.typeImmutability = Some(false) + if (state.dependentImmutability == None) + state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } - case x @ FinalEP(f, DependentImmutableType) => { - //------------------------------------------------------------------------------------- - state.typeImmutability = determineGenericFieldImmutability(state) - //------------------------------------------------------------------------------------- - //state.typeImmutability = Some(false) - //state.dependentTypeImmutability = Some(true) - } - case x @ FinalEP(_, MutableReference) => { state.referenceImmutability = Some(false) } - case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO state.referenceImmutability = Some(true) } + case x @ _ => + dependencies = dependencies + x + } + if (dependencies.isEmpty) createResult(state) + else + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } + val state: State = new State(field) - case x @ _ => dependencies = dependencies + x + val result = propertyStore(state.field, ReferenceImmutability.key) + result match { + case FinalEP(_, ImmutableReference | LazyInitializedReference) => + state.referenceImmutability = Some(true) + case FinalP(MutableReference) => return Result(field, MutableField) + case x @ _ => { + dependencies += x } - createResult(state) } - state.depencenciesTypes = scala.collection.mutable.Set[ObjectType]() - state.referenceImmutability = hasImmutableReference(field) - state.typeImmutability = hasImmutableType(field)(state); - createResult(state) + loadFormalTypeparameters() + hasGenericType(state) + handleTypeImmutability(state) + if (dependencies.isEmpty) + createResult(state) + else + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index 03bfaecc4b..c4bedef66b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -143,7 +143,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val thisType = field.classFile.thisType - if (field.isPublic) + if (field.isPublic || field.isPackagePrivate || field.isProtected) return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) // Collect all classes that have access to the field, i.e. the declaring class and possibly @@ -433,7 +433,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val (guardIndex, guardedIndex, readIndex) = findGuard(writeIndex, defaultValue, code, cfg) match { case Some((guard, guarded, read)) => (guard, guarded, read) - case None => return false; + case None => return false; } // Detect only simple patterns where the lazily initialized value is returned immediately @@ -564,13 +564,11 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec // outside an initializer, so we can ignore synchronization if (state.referenceImmutability == LazyInitializedReference) //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; - val defaultValue = getDefaultValue() if (defaultValue.isEmpty) return true; @@ -990,7 +988,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (objRefDefinition != SelfReferenceParameter) None else expr.asGetField.resolveField(project) case GetStatic.ASTID => expr.asGetStatic.resolveField(project) - case _ => None + case _ => None } field.contains(state.field) } @@ -1062,7 +1060,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec def isNonDeterministic( eop: EOptionP[DeclaredMethod, Purity] )(implicit state: State): Boolean = eop match { - case LBP(p: Purity) if p.isDeterministic => false + case LBP(p: Purity) if p.isDeterministic => false case UBP(p: Purity) if !p.isDeterministic => true case _ => state.purityDependees += eop @@ -1077,10 +1075,10 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec eop: EOptionP[Field, ReferenceImmutability] )(implicit state: State): Boolean = eop match { case FinalEP(e, ImmutableReference) => true - case FinalEP(e, MutableReference) => false + case FinalEP(e, MutableReference) => false // case LBP(ImmutableReference) => true - case UBP(MutableReference) => false + case UBP(MutableReference) => false /** * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala index 3e33cbeb0f..6afceeed3e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala @@ -105,206 +105,206 @@ import scala.collection.mutable class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - case class State( - field: Field, - var referenceImmutability: ReferenceImmutabilityLazyInitialization = NoLazyInitialization, - 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, - var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, - var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty, - var isThreadSafeType: Boolean = true - ) { - def hasDependees: Boolean = { - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || - escapeDependees.nonEmpty || tacDependees.nonEmpty - } - - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) - } - } - - 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) - - def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field ⇒ determineReferenceImmutabilityLazyInitialization(field) - case _ ⇒ - val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) + case class State( + field: Field, + var referenceImmutability: ReferenceImmutabilityLazyInitialization = NoLazyInitialization, + 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, + var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty, + var isThreadSafeType: Boolean = true + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty } - /** - * Analyzes the mutability of private non-final fields. - * - * 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 determineReferenceImmutabilityLazyInitialization( - field: Field - ): ProperPropertyComputationResult = { - implicit val state: State = State(field) - // Fields are not final if they are read prematurely! - //if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) - // return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); - //if (field.isFinal) - // return createResult(); - - //state.referenceImmutability = ImmutableReference //EffectivelyFinalField - - //val thisType = field.classFile.thisType - - //if (field.isPublic) - // return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) - - // 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) { - * if (!closedPackages.isClosed(thisType.packageName)) { - * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - * } - * project.classesPerPackage(thisType.packageName) - * } else { - * Set(field.classFile) - * } - * - * val classesHavingAccess: Iterator[ClassFile] = - * if (field.isProtected) { - * if (typeExtensibility(thisType).isYesOrUnknown) { - * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - * } - * 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))) - /// return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - - // 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 - // of the class after the reference to the class and (an indirect) reference to the - // field has become available. Consider the following example: - // class X implements Y{ - // - // private Object o; - // - // public Object getO() { return o; } - // - // private static X instance; - // static { - // instance = new X(); - // Z.register(instance); - // // when we reach this point o is now (via getO) publically accessible and - // // X is properly initialized! - // o = new Object(); // o is mutated... - // } - // } - - /** - * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) - None - } - } - - for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode ← getTACAI(method, pcs) - } { - //if ( - checkMethod(method, taCode, pcs) //) { - // return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); - //} - - } - - ///if (state.lazyInitInvocation.isDefined) { - /// val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - /// handleCalls(calleesEOP) - ///} - - createResult() + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) } - + } + + 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) + + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field => determineReferenceImmutabilityLazyInitialization(field) + case _ => + val m = entity.getClass.getSimpleName + " is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + + /** + * Analyzes the mutability of private non-final fields. + * + * 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 determineReferenceImmutabilityLazyInitialization( + field: Field + ): ProperPropertyComputationResult = { + implicit val state: State = State(field) + // Fields are not final if they are read prematurely! + //if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) + // return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); + //if (field.isFinal) + // return createResult(); + + //state.referenceImmutability = ImmutableReference //EffectivelyFinalField + + //val thisType = field.classFile.thisType + + //if (field.isPublic) + // return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) + + // 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 /** - * def handleCalls( - * calleesEOP: EOptionP[DeclaredMethod, Callees] - * )( - * implicit - * state: State - * ): Boolean = { - * calleesEOP match { - * case FinalP(callees) => - * state.calleesDependee = None - * handleCallees(callees) - * case InterimUBP(callees) => - * state.calleesDependee = Some(calleesEOP) - * handleCallees(callees) - * case _ => - * state.calleesDependee = Some(calleesEOP) - * false + * val initialClasses = + * if (field.isProtected || field.isPackagePrivate) { + * if (!closedPackages.isClosed(thisType.packageName)) { + * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + * } + * project.classesPerPackage(thisType.packageName) + * } else { + * Set(field.classFile) * } - * }* - */ - /** * - * def handleCallees(callees: Callees)(implicit state: State): Boolean = { - * val pc = state.lazyInitInvocation.get._2 - * if (callees.isIncompleteCallSite(pc)) { - * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - * true + * val classesHavingAccess: Iterator[ClassFile] = + * if (field.isProtected) { + * if (typeExtensibility(thisType).isYesOrUnknown) { + * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + * } + * val subclassesIterator: Iterator[ClassFile] = + * classHierarchy.allSubclassTypes(thisType, reflexive = false).flatMap { ot => + * project.classFile(ot).filter(cf => !initialClasses.contains(cf)) + * } + * initialClasses.iterator ++ subclassesIterator * } else { - * val targets = callees.callees(pc).toTraversable - * if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { - * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - * true - * } else false + * initialClasses.iterator * } - * }* */ + // If there are native methods, we give up + ///if (classesHavingAccess.exists(_.methods.exists(_.isNative))) + /// return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } + /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. + * Returns the TACode for a method if available, registering dependencies as necessary. */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + 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 eps: InterimEP[Method, TACAI] => + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk => + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + for { + (method, pcs) <- fieldAccessInformation.writeAccesses(field) + taCode <- getTACAI(method, pcs) + } { + //if ( + checkMethod(method, taCode, pcs) //) { + // return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); + //} - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + } + + ///if (state.lazyInitInvocation.isDefined) { + /// val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + /// handleCalls(calleesEOP) + ///} + + createResult() + } + + /** + * def handleCalls( + * calleesEOP: EOptionP[DeclaredMethod, Callees] + * )( + * implicit + * state: State + * ): Boolean = { + * calleesEOP match { + * case FinalP(callees) => + * state.calleesDependee = None + * handleCallees(callees) + * case InterimUBP(callees) => + * state.calleesDependee = Some(calleesEOP) + * handleCallees(callees) + * case _ => + * state.calleesDependee = Some(calleesEOP) + * false + * } + * }* + */ + /** + * + * def handleCallees(callees: Callees)(implicit state: State): Boolean = { + * val pc = state.lazyInitInvocation.get._2 + * if (callees.isIncompleteCallSite(pc)) { + * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + * true + * } else { + * val targets = callees.callees(pc).toTraversable + * if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { + * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + * true + * } else false + * } + * }* + */ + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field * The code below can be used to identify such value, but is not yet adapted to using the * TACAI property */ - /* + /* var constantVal: Option[Any] = None var allInitializeConstant = true @@ -362,942 +362,943 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p } constantVal */ + } + + /** + * 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 NoLazyInitialization)) //NonFinalFieldByAnalysis)) + InterimResult( + state.field, + NoLazyInitialization, //MutableReference, //NonFinalFieldByAnalysis, + state.referenceImmutability, + state.dependees, + c + ) + else { + if (state.referenceImmutability == LazyInitialization && !state.isThreadSafeType) + Result(state.field, NotThreadSafeLazyInitialization) + else + Result(state.field, state.referenceImmutability) } - - /** - * 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 NoLazyInitialization)) //NonFinalFieldByAnalysis)) - InterimResult( - state.field, - NoLazyInitialization, //MutableReference, //NonFinalFieldByAnalysis, - state.referenceImmutability, - state.dependees, - c - ) - else { - if (state.referenceImmutability == LazyInitialization && !state.isThreadSafeType) - Result(state.field, NotThreadSafeLazyInitialization) - 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 = { + val isNotFinal: Boolean = eps.pk match { + case EscapeProperty.key => + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + checkMethod(method, newEP.ub.tac.get, pcs) + //case Callees.key => + // handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key => + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key => + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + isNonDeterministic(newEP) + case ReferenceImmutabilityLazyInitialization.key => + val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + !isImmutableReference(newEP) } - /** - * 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 = { - val isNotFinal: Boolean = eps.pk match { - case EscapeProperty.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - 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)) - checkMethod(method, newEP.ub.tac.get, pcs) - //case Callees.key => - // handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key ⇒ - isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] - state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) - isNonDeterministic(newEP) - case ReferenceImmutabilityLazyInitialization.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] - state.referenceImmutabilityDependees = - state.referenceImmutabilityDependees.filter(_.e ne newEP.e) - !isImmutableReference(newEP) - } - - if (isNotFinal) { - Result(state.field, NoLazyInitialization) //Result(state.field, NonFinalFieldByAnalysis) - } else - createResult() + if (isNotFinal) { + Result(state.field, NoLazyInitialization) //Result(state.field, NonFinalFieldByAnalysis) + } else + createResult() + } + + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int], + pcs: PCs + )(implicit state: State): Boolean = { + //xx println("PS: "+propertyStore(declaredMethods(method), Purity.key)) + val write = code(writeIndex).asFieldWriteAccessStmt + //xx println("1") + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + ////return false; + state.isThreadSafeType = false } - - /** - * Checks whether a field write may be a lazy initialization. - * - * We only consider simple cases of lazy initialization where the single field write is guarded - * so it is executed only if the field still has its default value and where the written value - * is guaranteed to be the same even if the write is executed multiple times (may happen because - * of the initializing method being executed more than once on concurrent threads). - * - * Also, all field reads must be used only for a guard or their uses have to be guarded - * themselves. - */ - def isLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int], - pcs: PCs - )(implicit state: State): Boolean = { - //xx println("PS: "+propertyStore(declaredMethods(method), Purity.key)) - val write = code(writeIndex).asFieldWriteAccessStmt - //xx println("1") - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - // Only handle lazy initialization of ints and floats as they are guaranteed to be - // written atomically - ////return false; - state.isThreadSafeType = false - } - //xx println("2") - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return false; // Reads outside the (single) lazy initialization method - } - //xx println("3") - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val (guardIndex, guardedIndex, readIndex) = - findGuard(writeIndex, defaultValue, code, cfg) match { - case Some((guard, guarded, read)) ⇒ (guard, guarded, read) - case None ⇒ return false; - } - //xx println("4") - // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) //return false; - // possibly double checked locking - { - //TODO - if (isDoubleCheckedLocking(method, pcs)) { - state.isThreadSafeType = true; - return true; - } else - return false; - } - //xx println("5") - // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - return false; - //xx println("6") - // Field reads (except for the guard) may only be executed if the field's value is not the - // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - return false; - //xx println("7") - true - } - - 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 eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) - None - } + //xx println("2") + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs => (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method } - - def isDoubleCheckedLocking(method: Method, pcs: PCs)(implicit state: State): Boolean = { - val fieldAccessInformation = project.get(FieldAccessInformationKey) - var guards: List[(Int, Int)] = Nil - var assignments: List[Int] = Nil - var monitorEnter: Option[(Int, Option[ObjectType])] = None - var monitorExit: Option[(Int, Option[ObjectType])] = None - var result = true - val accessingPcs = fieldAccessInformation.allWriteAccesses - .filter(p ⇒ p._1 == state.field) - .head - ._2 - .filter(p ⇒ p._1 == method) - .head - ._2 - - if (state.field.fieldType.isObjectType && method.asMethod.isStatic) { - val tacCode = getTACAI(method, pcs) - tacCode match { - case Some(tac) ⇒ { - val hm = new mutable.HashMap[Int, Assignment[V]]() - //index for tac-code - var i: Int = -1 - if (tac != null && tac.instructions != null) - tac.instructions.foreach(instr ⇒ { - i = i + 1 - if (instr.isIfStmt) { - var currentFieldsClassType: ObjectType = null - if (state.field.fieldType.isObjectType) - currentFieldsClassType = state.field.fieldType.asObjectType // method.classFile.thisType - var ifLeftType: ReferenceType = null - if (instr.asIf.left.isVar && instr.asIf.left.asVar.value.isReferenceValue) // && instr.asIf.left.asVar.value.asReferenceValue) - try { - ifLeftType = instr.asIf.left.asVar.value.asReferenceValue.asReferenceType - } catch { - case _: Throwable ⇒ - } - - if ( //is non null check - instr.asIf.condition == NE && - // has same type as field - currentFieldsClassType != null && - ifLeftType != null && - ifLeftType.equals(currentFieldsClassType) //guards field? - ) { - // => guards the value - guards = (i, instr.asIf.target) :: guards - } - } - if (instr.isAssignment) { - hm += (i -> instr.asAssignment) - if (accessingPcs.contains(instr.pc)) - assignments = instr.pc.toInt :: assignments - } - - if (instr.isPutStatic && accessingPcs.contains(instr.pc)) { - assignments = i :: assignments - } - - if (instr.isMonitorEnter) { - val defB = instr.asMonitorEnter.objRef.asVar.definedBy.head - var objTypeOfMonitorEnter: Option[ObjectType] = None - try { - objTypeOfMonitorEnter = - Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) - } catch { - case _: Throwable ⇒ - } - monitorEnter = Some((i, objTypeOfMonitorEnter)) - //hm.foreach(x ⇒ println(x)) - } - - if (instr.isMonitorExit) { - val defB = instr.asMonitorExit.objRef.asVar.definedBy.head - var objTypeOfMonitorExit: Option[ObjectType] = None - try { - objTypeOfMonitorExit = - Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) - } catch { - case _: Throwable ⇒ - } - monitorExit = Some((i, objTypeOfMonitorExit)) - } - }) - } - case _ ⇒ result = false - } - assignments.foreach(a ⇒ guards.foreach(g ⇒ result = result && a > g._1 && a < g._2)) - result = result && assignments.size >= 1 - result = result && monitorEnter != None && monitorExit != None - monitorEnter match { - case Some((n, Some(ot: ObjectType))) ⇒ - result = result && - ot == state.field.fieldType.asObjectType && - //outer guard(s) - guards.filter(x ⇒ n > x._1).size >= 1 && - //inner guard(s) - guards.filter(x ⇒ n < x._1).size >= 1 - case None ⇒ result = false - case _ ⇒ result = false - } - monitorExit match { - case Some((n, Some(ot: ObjectType))) if (ot == state.field.fieldType.asObjectType) ⇒ - case None ⇒ result = false - case _ ⇒ result = false - } - result + //xx println("3") + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) => (guard, guarded, read) + case None => return false; + } + //xx println("4") + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) //return false; + // possibly double checked locking + { + //TODO + if (isDoubleCheckedLocking(method, pcs)) { + state.isThreadSafeType = true; + return true; } else - false + return false; + } + //xx println("5") + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + //xx println("6") + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + //xx println("7") + true + } + + 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 eps: InterimEP[Method, TACAI] => + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk => + state.tacDependees += method -> ((epk, pcs)) + None } + } + + def isDoubleCheckedLocking(method: Method, pcs: PCs)(implicit state: State): Boolean = { + val fieldAccessInformation = project.get(FieldAccessInformationKey) + var guards: List[(Int, Int)] = Nil + var assignments: List[Int] = Nil + var monitorEnter: Option[(Int, Option[ObjectType])] = None + var monitorExit: Option[(Int, Option[ObjectType])] = None + var result = true + val accessingPcs = fieldAccessInformation.allWriteAccesses + .filter(p => p._1 == state.field) + .head + ._2 + .filter(p => p._1 == method) + .head + ._2 + + if (state.field.fieldType.isObjectType && method.asMethod.isStatic) { + val tacCode = getTACAI(method, pcs) + tacCode match { + case Some(tac) => { + val hm = new mutable.HashMap[Int, Assignment[V]]() + //index for tac-code + var i: Int = -1 + if (tac != null && tac.instructions != null) + if (tac != null && tac.instructions != null) + tac.instructions.foreach(instr => { + i = i + 1 + if (instr.isIfStmt) { + var currentFieldsClassType: ObjectType = null + if (state.field.fieldType.isObjectType) + currentFieldsClassType = state.field.fieldType.asObjectType // method.classFile.thisType + var ifLeftType: ReferenceType = null + if (instr.asIf.left.isVar && instr.asIf.left.asVar.value.isReferenceValue) // && instr.asIf.left.asVar.value.asReferenceValue) + try { + ifLeftType = instr.asIf.left.asVar.value.asReferenceValue.asReferenceType + } catch { + case _: Throwable => + } - def checkWrites( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - //xx println("51") - val definitions = write.value.asVar.definedBy - //xx println("52") - val isDeterministic = - if (definitions.size == 1) { - // The value written must be computed deterministically - //xx println("53") - checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) - } else { - // More than one definition site for the value might lead to differences between - // invocations, but not if this method has no parameters and is deterministic - // (in this case, the definition reaching the write will always be the same) - //xx println("54") - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - //xx println("55") - // The field write must be guarded correctly - val r1 = isDeterministic - val r2 = checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - //xx println("isDeterministic: "+r1) - //xx println("checkWriteIsGuarded: "+r2) - //r1 && r2 //TODO check - if (!(r1 && r2) && !state.isThreadSafeType) { - //state.isThreadSafeType = false - true - } else - r1 && r2 - } + if (//is non null check + instr.asIf.condition == NE && + // has same type as field + currentFieldsClassType != null && + ifLeftType != null && + ifLeftType.equals(currentFieldsClassType) //guards field? + ) { + // => guards the value + guards = (i, instr.asIf.target) :: guards + } + } + if (instr.isAssignment) { + hm += (i -> instr.asAssignment) + if (accessingPcs.contains(instr.pc)) + assignments = instr.pc.toInt :: assignments + } - /** - * Checks if the field write for a lazy initialization is immediately followed by a return of - * the written value (possibly loaded by another field read). - */ - def checkImmediateReturn( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 + if (instr.isPutStatic && accessingPcs.contains(instr.pc)) { + assignments = i :: assignments + } - var load = -1 + if (instr.isMonitorEnter) { + val defB = instr.asMonitorEnter.objRef.asVar.definedBy.head + var objTypeOfMonitorEnter: Option[ObjectType] = None + try { + objTypeOfMonitorEnter = + Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) + } catch { + case _: Throwable => + } + monitorEnter = Some((i, objTypeOfMonitorEnter)) + //hm.foreach(x ⇒ println(x)) + } - while (index < code.length) { - val stmt = code(index) - stmt.astID match { - case Assignment.ASTID ⇒ - if (isReadOfCurrentField(stmt.asAssignment.expr)) - load = index - else - return false; // No field read or a field read of a different field - case ReturnValue.ASTID ⇒ - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) - return true; // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) - return true; // return of field value loaded by field read - else - return false; // return of different value - case _ ⇒ return false; // neither a field read nor a return - } - index += 1 + if (instr.isMonitorExit) { + val defB = instr.asMonitorExit.objRef.asVar.definedBy.head + var objTypeOfMonitorExit: Option[ObjectType] = None + try { + objTypeOfMonitorExit = + Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) + } catch { + case _: Throwable => + } + monitorExit = Some((i, objTypeOfMonitorExit)) + } + }) } - false + case _ => result = false + } + assignments.foreach(a => guards.foreach(g => result = result && a > g._1 && a < g._2)) + result = result && assignments.size >= 1 + result = result && monitorEnter != None && monitorExit != None + monitorEnter match { + case Some((n, Some(ot: ObjectType))) => + result = result && + ot == state.field.fieldType.asObjectType && + //outer guard(s) + guards.filter(x => n > x._1).size >= 1 && + //inner guard(s) + guards.filter(x => n < x._1).size >= 1 + case None => result = false + case _ => result = false + } + monitorExit match { + case Some((n, Some(ot: ObjectType))) if (ot == state.field.fieldType.asObjectType) => + case None => result = false + case _ => result = false + } + result + } else + false + } + + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + //xx println("51") + val definitions = write.value.asVar.definedBy + //xx println("52") + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + //xx println("53") + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) + //xx println("54") + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + //xx println("55") + // The field write must be guarded correctly + val r1 = isDeterministic + val r2 = checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + //xx println("isDeterministic: "+r1) + //xx println("checkWriteIsGuarded: "+r2) + //r1 && r2 //TODO check + if (!(r1 && r2) && !state.isThreadSafeType) { + //state.isThreadSafeType = false + true + } else + r1 && r2 + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID => + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID => + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ => return false; // neither a field read nor a return + } + index += 1 } - - def lazyInitializerIsDeterministic( - method: Method, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( - propertyStore(declaredMethods(method), Purity.key) - )) - result - } - - /** - * Analyzes field writes for a single method, returning false if the field may still be - * effectively final and true otherwise. - */ - def checkMethod( - method: Method, - taCode: TACode[TACMethodParameter, V], - pcs: PCs - )(implicit state: State): Boolean = { - val field = state.field - val stmts = taCode.stmts - for (pc ← pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID ⇒ - if (method.isInitializer) { - - if (field.isStatic) { - if (method.isConstructor) - return true; - } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - 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.referenceImmutability == NotThreadSafeLazyInitialization || state.referenceImmutability == LazyInitialization) //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; - - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - // A field written outside an initializer must be lazily - // initialized or it is non-final - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex, - pcs - )) - return true; - state.referenceImmutability = LazyInitialization //LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - // 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. - return true; - } - } - case _ ⇒ throw new RuntimeException("unexpected field access"); - } + false + } + + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + )) + result + } + + /** + * Analyzes field writes for a single method, returning false if the field may still be + * effectively final and true otherwise. + */ + def checkMethod( + method: Method, + taCode: TACode[TACMethodParameter, V], + pcs: PCs + )(implicit state: State): Boolean = { + val field = state.field + val stmts = taCode.stmts + for (pc <- pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID => + if (method.isInitializer) { + + if (field.isStatic) { + if (method.isConstructor) + return true; } else { - // nothing to do as the put field is dead + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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.referenceImmutability == NotThreadSafeLazyInitialization || state.referenceImmutability == LazyInitialization) //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; + + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + // A field written outside an initializer must be lazily + // initialized or it is non-final + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex, + pcs + )) + return true; + state.referenceImmutability = LazyInitialization //LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ => throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead } - false + } } - - /** - * 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 eps: InterimEP[Method, TACAI] ⇒ - * state.tacDependees += method → ((eps, pcs)) - * eps.ub.tac - * case epk ⇒ - * state.tacDependees += method → ((epk, pcs)) - * None - * } - * } - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - /** - * 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 // Must be locally created - else { - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - handleEscapeProperty(escape) - } - } + false + } + + /** + * 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 eps: InterimEP[Method, TACAI] ⇒ + * state.tacDependees += method → ((eps, pcs)) + * eps.ub.tac + * case epk ⇒ + * state.tacDependees += method → ((epk, pcs)) + * None + * } + * } + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) } + } } - - /** - * 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 = 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 + } + + /** + * 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 = 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 + } + + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt => + stmt.astID == CaughtException.ASTID + } flatMap { exception => + exception.asCaughtException.origins.map { origin: Int => + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator } - /** - * Checks if the field write is only executed if the field's value was still the default value. - * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization - * and the field write. - */ - def checkWriteIsGuarded( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val startBB = cfg.bb(writeIndex).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt ⇒ - stmt.astID == CaughtException.ASTID - } flatMap { exception ⇒ - exception.asCaughtException.origins.map { origin: Int ⇒ - if (isImmediateVMException(origin)) - pcOfImmediateVMException(origin) - else - pcOfMethodExternalException(origin) - }.iterator - } + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail + val startPC = curBB.startPC + val endPC = curBB.endPC - val startPC = curBB.startPC - val endPC = curBB.endPC + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement - if (startPC == 0 || startPC == guardedIndex) - return false; // Reached method start or wrong branch of guarding if-Statement + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors + true + } + + /** + * 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]] + )(implicit state: State): Boolean = { + //xx println("begin checkwriteisdeterministic") + def isConstant(uvar: Expr[V]): Boolean = { + val defSites = uvar.asVar.definedBy + + def isConstantDef(index: Int) = { + //xx println("index: "+index) + if (index < 0) false + else if (code(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + //xx println("expr: "+expr) + //println("resolveField(p): " + expr.asFieldRead.resolveField(p)) + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) => + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ => // Unknown field + false + }) } + } - true + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) } - /** - * 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]] - )(implicit state: State): Boolean = { - //xx println("begin checkwriteisdeterministic") - def isConstant(uvar: Expr[V]): Boolean = { - val defSites = uvar.asVar.definedBy - - def isConstantDef(index: Int) = { - //xx println("index: "+index) - if (index < 0) false - else if (code(defSites.head).asAssignment.expr.isConst) true - else { - val expr = code(index).asAssignment.expr - //xx println("expr: "+expr) - //println("resolveField(p): " + expr.asFieldRead.resolveField(p)) - expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ ⇒ // Unknown field - false - }) - } - } - - defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) - } - - val value = origin.expr + val value = origin.expr - //xx println("value.astID: "+value.astID) + //xx println("value.astID: "+value.astID) - val isNonConstDeterministic = value.astID match { - case GetStatic.ASTID | GetField.ASTID ⇒ - //xx println("a") - value.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - //xx println("b") - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ ⇒ // Unknown field - //xx println("c") - false - } - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ - //xx println("d") - // 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.exists(!isConstant(_))) { - //xx println("e") - false - } else { - //xx println("f") - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true - } - case _ ⇒ - //xx println("g") - // 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, code) + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID => + //xx println("a") + value.asFieldRead.resolveField(p) match { + case Some(field) => + //xx println("b") + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ => // Unknown field + //xx println("c") + false } - - value.isConst || isNonConstDeterministic - } - - /** - * Checks that all non-dead field reads that are not used for the guarding if-Statement of a - * lazy initialization are only executed if the field did not have its default value or after - * the (single) field write. - */ - def checkReads( - reads: Seq[(Method, PCs)], - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - ): Boolean = { - // There is only a single method with reads aside from initializers (checked by - // isLazilyInitialized), so we have to check only reads from that one method. - reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ - val index = pcToIndex(readPC) - index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => + //xx println("d") + // 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.exists(!isConstant(_))) { + //xx println("e") + false + } else { + //xx println("f") + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true } + case _ => + //xx println("g") + // 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, code) } - /** - * Checks that a field read is only executed if the field did not have its default value or - * after the (single) field write. - */ - def checkRead( - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Boolean = { - val startBB = cfg.bb(readIndex).asBasicBlock - val writeBB = cfg.bb(writeIndex) - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - - if (startPC == 0) - return false; // Reached the start of the method but not the guard or field write - - if ((curBB eq writeBB) && writeIndex > readIndex) - return false; // In the basic block of the write, but before the write - - if (startPC != guardedIndex && // Did not reach the guard - (curBB ne writeBB) /* Not the basic block of the write */ ) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - true + value.isConst || isNonConstDeterministic + } + + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC => + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) } - - /** - * Gets all predecessor BasicBlocks of a CFGNode. - */ - def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode ⇒ - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) - } - result.toList + } + + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } } - /** - * 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. - */ - def findGuard( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Option[(Int, Int, Int)] = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int)] = None - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID ⇒ - val ifStmt = cfStmt.asIf - ifStmt.condition match { - case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1)) - } - - case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None; - } else { - result = Some((endPC, ifStmt.targetStmt)) - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - var fieldReadUses: IntTrieSet = IntTrieSet.empty - if (definitions.head >= 0 && code(definitions.head).isAssignment) - fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard - } + true + } + + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode => + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) + } + result.toList + } + + /** + * 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. + */ + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID => + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) => + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) => + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ => + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ => + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } - None + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + var fieldReadUses: IntTrieSet = IntTrieSet.empty + if (definitions.head >= 0 && code(definitions.head).isAssignment) + fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use => + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard } - /** - * 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])(implicit state: State): Boolean = { - val field = expr.astID match { - case GetField.ASTID ⇒ - val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) None - else expr.asGetField.resolveField(project) - case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) - case _ ⇒ None - } - field.contains(state.field) + None + } + + /** + * 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])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID => + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID => expr.asGetStatic.resolveField(project) + case _ => None } + field.contains(state.field) + } + + /** + * 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]] + )(implicit state: State): Boolean = { /** - * Determines if an if-Statement is actually a guard for the current field, i.e. it compares - * the current field to the default value. + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. */ - def isGuard( - ifStmt: If[V], - defaultValue: Any, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - - /** - * Checks if an expression is an IntConst or FloatConst with the corresponding default value. - */ - def isDefaultConst(expr: Expr[V]): Boolean = { - //xx println("Expression: "+expr) - if (expr.isNullExpr) - true //-- - else 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 || - expr.isFloatConst && defaultValue == expr.asFloatConst.value - } - } - - /** - * Checks whether the non-constant expression of the if-Statement is a read of the current - * field. - */ - def isGuardInternal(expr: V): Boolean = { - expr.definedBy forall { index ⇒ - if (index < 0) false // If the value is from a parameter, this can not be the guard - else isReadOfCurrentField(code(index).asAssignment.expr) - } - } - - if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { - isGuardInternal(ifStmt.rightExpr.asVar) - } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { - isGuardInternal(ifStmt.leftExpr.asVar) - } else //TODO check - //false - { - - //TODO if expression = Nullexpr - //xx println("----------------------------------------<<<<") - state.isThreadSafeType = false - true //TODO check - } + def isDefaultConst(expr: Expr[V]): Boolean = { + //xx println("Expression: "+expr) + if (expr.isNullExpr) + true //-- + else 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } } /** - * Checks if the field is prematurely read, i.e. read before it is initialized in the - * constructor, using the corresponding property. + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. */ - def isPrematurelyRead( - eop: EOptionP[Field, FieldPrematurelyRead] - )(implicit state: State): Boolean = - eop match { - case LBP(NotPrematurelyReadField) ⇒ - state.prematurelyReadDependee = None - false - case UBP(PrematurelyReadField) ⇒ true - case eps ⇒ - state.prematurelyReadDependee = Some(eps) - false - } + def isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index => + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } + } - /** - * Checkes 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. - */ - 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 + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else //TODO check + //false + { + + //TODO if expression = Nullexpr + //xx println("----------------------------------------<<<<") + state.isThreadSafeType = false + true //TODO check + } + } + + /** + * Checks if 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) => true + case eps => + state.prematurelyReadDependee = Some(eps) + false } + /** + * Checkes 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. + */ + 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 + } + + /** + * Checkes if 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, ReferenceImmutability] + )(implicit state: State): Boolean = eop match { + case FinalEP(e, ImmutableReference) => true + case FinalEP(e, MutableReference) => false + // + case LBP(ImmutableReference) => true + case UBP(MutableReference) => false + /** - * Checkes if the field the value assigned to a (potentially) lazily initialized field is final, - * ensuring that the same value is written even for concurrent executions. + * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ + * true + * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * */ - def isImmutableReference( - eop: EOptionP[Field, ReferenceImmutability] - )(implicit state: State): Boolean = eop match { - case FinalEP(e, ImmutableReference) ⇒ true - case FinalEP(e, MutableReference) ⇒ false - // - case LBP(ImmutableReference) ⇒ true - case UBP(MutableReference) ⇒ false - - /** - * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ - * true - * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * - */ - case _ ⇒ - state.referenceImmutabilityDependees += eop - true - } + case _ => + state.referenceImmutabilityDependees += eop + true + } } trait L0ReferenceImmutabilityLazyInitializationAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(ReferenceImmutabilityLazyInitialization), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(ReferenceImmutabilityLazyInitialization), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.ub(EscapeProperty) + ) - final def derivedProperty: PropertyBounds = - PropertyBounds.lub(ReferenceImmutabilityLazyInitialization) + final def derivedProperty: PropertyBounds = + PropertyBounds.lub(ReferenceImmutabilityLazyInitialization) } @@ -1308,18 +1309,18 @@ object EagerL0ReferenceImmutabilityLazyInitializationAnalysis extends L0ReferenceImmutabilityLazyInitializationAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)( - analysis.determineReferenceImmutabilityLazyInitialization - ) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)( + analysis.determineReferenceImmutabilityLazyInitialization + ) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -1329,18 +1330,18 @@ object LazyL0ReferenceImmutabilityLazyInitializationAnalysis extends L0ReferenceImmutabilityLazyInitializationAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) - ps.registerLazyPropertyComputation( - ReferenceImmutabilityLazyInitialization.key, - analysis.determineReferenceImmutabilityLazyInitialization - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) + ps.registerLazyPropertyComputation( + ReferenceImmutabilityLazyInitialization.key, + analysis.determineReferenceImmutabilityLazyInitialization + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 715084a6e6..6cbfa831e2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -90,6 +90,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal t: ObjectType, immutability: ClassImmutability_new //MutableObject ): MultiResult = { + //println("I") val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) val r = allSubtypes.map { st => new FinalEP(st, immutability) @@ -102,6 +103,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal cfMutabilityIsFinal: Boolean, result: ProperPropertyComputationResult ): IncrementalResult[ClassFile] = { + // println("J") var results: List[ProperPropertyComputationResult] = List(result) var nextComputations: List[(PropertyComputation[ClassFile], ClassFile)] = Nil val directSubtypes = classHierarchy.directSubtypesOf(t) @@ -126,6 +128,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } def determineGenericTypeBounds(classFile: ClassFile): Set[(String, String)] = { + // println("K") var genericTypeBounds: Set[(String, String)] = Set.empty classFile.attributes.toList.collectFirst({ case ClassSignature(typeParameters, _, _) => @@ -154,6 +157,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { + //println("L") e match { case t: ObjectType => //this is safe @@ -211,7 +215,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal )( cf: ClassFile ): ProperPropertyComputationResult = { - + //println("M") val t = cf.thisType var dependees = Map.empty[Entity, EOptionP[Entity, Property]] @@ -227,13 +231,16 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal !f.isStatic } var hasShallowImmutableFields = false + var hasDependentImmutableFields = false val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) - var dependentImmutableFields: Set[String] = Set.empty - + //println("start") + //println(superClassType.simpleName) fieldsPropertyStoreInformation.foreach( f => { + //println("A") + //println(f) f match { case FinalP(MutableField) => { if (lazyComputation) @@ -242,11 +249,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal return createResultForAllSubtypes(t, MutableClass); } case FinalP(ShallowImmutableField) => hasShallowImmutableFields = true - case FinalP(DependentImmutableField(x)) => { - x match { - case Some(v) => dependentImmutableFields = dependentImmutableFields + v - case _ => - } + case FinalEP(fi, DependentImmutableField) => { + hasDependentImmutableFields = true } case FinalP(DeepImmutableField) => @@ -265,7 +269,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } } ) - + //println("B") + //println(superClassMutabilityIsFinal) var minLocalImmutability: ClassImmutability_new = if (!superClassMutabilityIsFinal) { MutableClass //MutableObjectByAnalysis @@ -274,24 +279,30 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal ShallowImmutableClass //ImmutableContainer } + //println("C") + //println(superClassInformation) // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability_new = superClassInformation match { case UBP(ShallowImmutableClass) => ShallowImmutableClass //ImmutableContainer case _ => DeepImmutableClass // ImmutableObject } - - if (!dependentImmutableFields.isEmpty && maxLocalImmutability != ShallowImmutableClass) { - maxLocalImmutability = DependentImmutableClass() - } - + //println("D") + //println(hasShallowImmutableFields) if (hasShallowImmutableFields) { maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } + //if (!dependentImmutableFields.isEmpty && maxLocalImmutability != ShallowImmutableClass) { + if (hasDependentImmutableFields && maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { + //println("E") + 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. + //println("F") maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } @@ -301,6 +312,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal // <=> all fields are (effectively) final // <=> the type mutability of all fields is final // (i.e., ImmutableType or ImmutableContainerType) + //println("G") if (lazyComputation) return Result(t, maxLocalImmutability); @@ -313,7 +325,9 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } def c(someEPS: SomeEPS): ProperPropertyComputationResult = { - //[DEBUG] val oldDependees = dependees + //println("H") + //[DEBUG] + val oldDependees = dependees dependees = dependees.filter(_._1 ne someEPS.e) someEPS match { // Superclass related dependencies: @@ -336,12 +350,11 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case LUBP(MutableClass, DeepImmutableClass) => //_: MutableObject, ImmutableObject) ⇒ // No information about superclass - case FinalEP(f, DependentImmutableField(x)) => { - //determineGenericFieldImmutability(f.asInstanceOf[Field]) - if (hasShallowImmutableFields) { + case FinalEP(f, DependentImmutableField) => { + if (hasShallowImmutableFields && maxLocalImmutability != MutableClass) { maxLocalImmutability = ShallowImmutableClass - } else { - maxLocalImmutability = DependentImmutableClass() + } else if (maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { + maxLocalImmutability = DependentImmutableClass } //} else @@ -350,7 +363,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal // Field Mutability related dependencies: // - + case FinalP(DeepImmutableField) => case FinalP(ShallowImmutableField) => { maxLocalImmutability = ShallowImmutableClass } @@ -410,7 +423,14 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal Result(t, maxLocalImmutability) } else { - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + //println("c interim") + //println("min: "+minLocalImmutability) + //println("max: "+maxLocalImmutability) + //println(dependees.values) + if (oldDependees == dependees) { + Result(t, minLocalImmutability) + } else + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index 09fef956d2..6cf59d57a8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -123,7 +123,7 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC case FinalP(ShallowImmutableClass) => //ImmutableContainer) => joinedImmutability = ShallowImmutableType // ImmutableContainerType maxImmutability = ShallowImmutableType //ImmutableContainerType - case FinalP(DependentImmutableClass(_)) => + case FinalP(DependentImmutableClass) => joinedImmutability = DependentImmutableType maxImmutability = DependentImmutableType @@ -226,7 +226,7 @@ class LxTypeImmutabilityAnalysis_new(final val project: SomeProject) extends FPC maxImmutability = ShallowImmutableType //ImmutableContainerType dependencies = dependencies - e nextResult() - case FinalEP(e, DependentImmutableClass(_) | DependentImmutableType) => { + case FinalEP(e, DependentImmutableClass | DependentImmutableType) => { //if (x == DependentImmutableClass() || x == DependentImmutableType) => { maxImmutability = DependentImmutableType dependencies = dependencies - e From 1105873d9728e942e56acf7646a2484840c7e6d0 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 19 Feb 2020 12:14:36 +0100 Subject: [PATCH 087/327] updating PropertiesTest to the new status --- .../scala/org/opalj/fpcf/PropertiesTest.scala | 71 ++++++++++++++++--- 1 file changed, 60 insertions(+), 11 deletions(-) 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 85e57f6ec3..7e953ddc60 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 @@ -7,9 +7,9 @@ import java.net.URL import com.typesafe.config.Config import com.typesafe.config.ConfigValueFactory +import org.opalj.bi.reader.ClassFileReader import org.scalatest.FunSpec import org.scalatest.Matchers - import org.opalj.log.LogContext import org.opalj.util.ScalaMajorVersion import org.opalj.fpcf.properties.PropertyMatcher @@ -51,6 +51,13 @@ 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 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" + ) + def withRT = false /** @@ -59,10 +66,9 @@ abstract class PropertiesTest extends FunSpec with Matchers { final val FixtureProject: Project[URL] = { val classFileReader = Project.JavaClassFileReader() import classFileReader.ClassFiles - val sourceFolder = s"DEVELOPING_OPAL/validate/target/scala-$ScalaMajorVersion/test-classes" - val fixtureFiles = new File(sourceFolder) - val fixtureClassFiles = ClassFiles(fixtureFiles) - if (fixtureClassFiles.isEmpty) fail(s"no class files at $fixtureFiles") + + val fixtureClassFiles = getFixtureClassFiles(classFileReader) //AllClassFiles(List(annotationFiles, fixtureFiles)) + if (fixtureClassFiles.isEmpty) fail(s"no class files at $testFilePath") val projectClassFiles = fixtureClassFiles.filter { cfSrc => val (cf, _) = cfSrc @@ -87,6 +93,29 @@ abstract class PropertiesTest extends FunSpec with Matchers { ) } + /** + * Override this method to limit the fixture project to certain subpackages only. + * To do so specify the list of packages that shall be included. A specified package always + * includes all its subpackages. + * + * All package path must be given in '/' notation. + * + * Examples: + * All files related to the escape tests. + * ``` + * List("org/opalj/fpcf/fixtures/escape") + * ``` + * + * All files related to specific escape tests, i.e., cycles and virtual calls + * ``` + * List( + * "org/opalj/fpcf/fixtures/escape/cycles", + * "org/opalj/fpcf/fixtures/escape/virtual_calls", + * ) + * ``` + */ + def fixtureProjectPackage: List[String] = List.empty + def createConfig(): Config = { val configForEntryPoints = BaseConfig .withValue( @@ -197,7 +226,7 @@ abstract class PropertiesTest extends FunSpec with Matchers { "\nexpectation: " + error ) fail(m) - case None => /* OK */ + case None => /* OK */ case result => fail("matcher returned unexpected result: " + result) } } @@ -305,7 +334,7 @@ abstract class PropertiesTest extends FunSpec with Matchers { annotations = code.runtimeInvisibleTypeAnnotations filter { ta => ta.target match { case TAOfNew(`pc`) => true - case _ => false + case _ => false } } if annotations.nonEmpty @@ -344,10 +373,10 @@ abstract class PropertiesTest extends FunSpec with Matchers { PropertyStoreKey, (context: List[PropertyStoreContext[AnyRef]]) => { /* - val ps = PKEParallelTasksPropertyStore.create( - new RecordAllPropertyStoreTracer, - context.iterator.map(_.asTuple).toMap - ) + val ps = PKEParallelTasksPropertyStore.create( + new RecordAllPropertyStoreTracer, + context.iterator.map(_.asTuple).toMap + ) */ val ps = PKESequentialPropertyStore(context: _*) ps @@ -365,6 +394,26 @@ abstract class PropertiesTest extends FunSpec with Matchers { throw t; } } + + private[this] def getFixtureClassFiles( + classFileReader: ClassFileReader + ): Traversable[(classFileReader.ClassFile, URL)] = { + import classFileReader.AllClassFiles + + var classFilePaths: List[File] = List.empty + + val relevantPackages = fixtureProjectPackage + if (fixtureProjectPackage.nonEmpty) { + classFilePaths = classFilePaths ++ propertyPaths.map(new File(_)) + classFilePaths = classFilePaths ++ relevantPackages.map { path => + new File({ s"$testFilePath$path" }) + } + } else { + classFilePaths = new File(testFilePath) :: classFilePaths + } + + AllClassFiles(classFilePaths) + } } case class TestContext( From 9ec9506171355b66aceacc6217dc3f0e95f47668 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 19 Feb 2020 12:17:38 +0100 Subject: [PATCH 088/327] updating PropertiesTest to the new status 2 --- .../src/test/scala/org/opalj/fpcf/PropertiesTest.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 7e953ddc60..2341e93e9a 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 @@ -226,7 +226,7 @@ abstract class PropertiesTest extends FunSpec with Matchers { "\nexpectation: " + error ) fail(m) - case None => /* OK */ + case None => /* OK */ case result => fail("matcher returned unexpected result: " + result) } } @@ -334,7 +334,7 @@ abstract class PropertiesTest extends FunSpec with Matchers { annotations = code.runtimeInvisibleTypeAnnotations filter { ta => ta.target match { case TAOfNew(`pc`) => true - case _ => false + case _ => false } } if annotations.nonEmpty From 329546315150b278b91ce9e291ffe1d44ba1e0f0 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 19 Feb 2020 12:50:52 +0100 Subject: [PATCH 089/327] updating PropertyStoreKey to the new status --- .../src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala index 95537737ac..a936318e17 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala @@ -9,8 +9,6 @@ import org.opalj.log.OPALLogger import org.opalj.concurrent.NumberOfThreadsForCPUBoundTasks import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.PropertyStoreContext -//import org.opalj.fpcf.par.PKECPropertyStore -import org.opalj.fpcf.seq.PKESequentialPropertyStore import org.opalj.br.analyses.ProjectInformationKey import org.opalj.br.analyses.SomeProject @@ -59,8 +57,8 @@ object PropertyStoreKey ) psFactory(context) case None ⇒ - val ps = PKESequentialPropertyStore(context: _*) - //val ps = PKECPropertyStore(context: _*) + val ps = org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) + //val ps = org.opalj.fpcf.par.PKECPropertyStore(context: _*) ps } } From ed84ec765271426850b21b0c0b8de282d917e53c Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 19 Feb 2020 15:50:31 +0100 Subject: [PATCH 090/327] updating PropertiesTest to the new status --- .../scala/org/opalj/fpcf/PropertiesTest.scala | 676 +++++++++--------- 1 file changed, 335 insertions(+), 341 deletions(-) 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 2341e93e9a..060bb5b658 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,373 +51,367 @@ 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 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" - ) - - def withRT = false - - /** - * The representation of the fixture project. - */ - final val FixtureProject: Project[URL] = { - val classFileReader = Project.JavaClassFileReader() - import classFileReader.ClassFiles - - val fixtureClassFiles = getFixtureClassFiles(classFileReader) //AllClassFiles(List(annotationFiles, fixtureFiles)) - if (fixtureClassFiles.isEmpty) fail(s"no class files at $testFilePath") - - val projectClassFiles = fixtureClassFiles.filter { cfSrc => - val (cf, _) = cfSrc - cf.thisType.packageName.startsWith("org/opalj/fpcf/fixtures") - } + 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" + ) - val propertiesClassFiles = fixtureClassFiles.filter { cfSrc => - val (cf, _) = cfSrc - cf.thisType.packageName.startsWith("org/opalj/fpcf/properties") - } + def withRT = false - val libraryClassFiles = (if (withRT) ClassFiles(RTJar) else List()) ++ propertiesClassFiles + /** + * The representation of the fixture project. + */ + final val FixtureProject: Project[URL] = { + val classFileReader = Project.JavaClassFileReader() + import classFileReader.ClassFiles - implicit val config: Config = createConfig() + val fixtureClassFiles = getFixtureClassFiles(classFileReader) //AllClassFiles(List(annotationFiles, fixtureFiles)) + if (fixtureClassFiles.isEmpty) fail(s"no class files at $testFilePath") - info(s"the test fixture project consists of ${projectClassFiles.size} class files") - Project( - projectClassFiles, - libraryClassFiles, - libraryClassFilesAreInterfacesOnly = false, - virtualClassFiles = Traversable.empty - ) - } - - /** - * Override this method to limit the fixture project to certain subpackages only. - * To do so specify the list of packages that shall be included. A specified package always - * includes all its subpackages. - * - * All package path must be given in '/' notation. - * - * Examples: - * All files related to the escape tests. - * ``` - * List("org/opalj/fpcf/fixtures/escape") - * ``` - * - * All files related to specific escape tests, i.e., cycles and virtual calls - * ``` - * List( - * "org/opalj/fpcf/fixtures/escape/cycles", - * "org/opalj/fpcf/fixtures/escape/virtual_calls", - * ) - * ``` - */ - 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( - InitialEntryPointsKey.ConfigKeyPrefix + "AllEntryPointsFinder.projectMethodsOnly", - ConfigValueFactory.fromAnyRef(true) - ) + val projectClassFiles = fixtureClassFiles.filter { cfSrc ⇒ + val (cf, _) = cfSrc + cf.thisType.packageName.startsWith("org/opalj/fpcf/fixtures") + } - configForEntryPoints - .withValue( - InitialInstantiatedTypesKey.ConfigKeyPrefix + "analysis", - ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllInstantiatedTypesFinder") - ) - .withValue( - InitialInstantiatedTypesKey.ConfigKeyPrefix + - "AllInstantiatedTypesFinder.projectClassesOnly", - ConfigValueFactory.fromAnyRef(true) - ) - } - - final val PropertyValidatorType = ObjectType("org/opalj/fpcf/properties/PropertyValidator") - - /** - * Returns the [[org.opalj.fpcf.properties.PropertyMatcher]] associated with the annotation - - * if the annotation specifies an expected property currently relevant; i.e, if the - * property kind specified by the annotation is in the set `propertyKinds`. - * - * @param p The current project. - * @param propertyKinds The property kinds which should be analyzed. - * @param annotation The annotation found in the project. - */ - def getPropertyMatcher( - p: Project[URL], - propertyKinds: Set[String] - )( - annotation: AnnotationLike - ): Option[(AnnotationLike, String, Type /* type of the matcher */ )] = { - if (!annotation.annotationType.isObjectType) - return None; - - // Get the PropertyValidator meta-annotation of the given entity's annotation: - val annotationClassFile = p.classFile(annotation.annotationType.asObjectType).get - annotationClassFile.runtimeInvisibleAnnotations.collectFirst { - case Annotation( - PropertyValidatorType, - Seq( - ElementValuePair("key", StringValue(propertyKind)), - ElementValuePair("validator", ClassValue(propertyMatcherType)) - ) - ) if propertyKinds.contains(propertyKind) => - (annotation, propertyKind, propertyMatcherType) - } - } - - /** - * Called by tests to trigger the validation of the derived properties against the - * specified ones. - * - * @param context The validation context; typically the return value of [[executeAnalyses]]. - * @param eas An iterator over the relevant entities along with the found annotations. - * @param propertyKinds The kinds of properties (as specified by the annotations) that are - * to be tested. - */ - def validateProperties( - context: TestContext, - eas: TraversableOnce[ - ( - Entity, - /*the processed annotation*/ String => String /* a String identifying the entity */, - Traversable[AnnotationLike] + val propertiesClassFiles = fixtureClassFiles.filter { cfSrc ⇒ + val (cf, _) = cfSrc + cf.thisType.packageName.startsWith("org/opalj/fpcf/properties") + } + + val libraryClassFiles = (if (withRT) ClassFiles(RTJar) else List()) ++ propertiesClassFiles + + implicit val config: Config = createConfig() + + info(s"the test fixture project consists of ${projectClassFiles.size} class files") + Project( + projectClassFiles, + libraryClassFiles, + libraryClassFilesAreInterfacesOnly = false, + virtualClassFiles = Traversable.empty ) - ], - propertyKinds: Set[String] - ): Unit = { - val TestContext(p: Project[URL], ps: PropertyStore, as: List[FPCFAnalysis]) = context - val ats = - as.map(a => ObjectType(a.getClass.getName.replace('.', '/'))).toSet - - for { - (e, entityIdentifier, annotations) <- eas - augmentedAnnotations = annotations.flatMap(getPropertyMatcher(p, propertyKinds)) - (annotation, propertyKind, matcherType) <- augmentedAnnotations - } { - val annotationTypeName = annotation.annotationType.asObjectType.simpleName - val matcherClass = Class.forName(matcherType.toJava) - val matcherClassConstructor = matcherClass.getDeclaredConstructor() - val matcher = matcherClassConstructor.newInstance().asInstanceOf[PropertyMatcher] - if (matcher.isRelevant(p, ats, e, annotation)) { - - it(entityIdentifier(s"$annotationTypeName")) { - info(s"validator: " + matcherClass.toString.substring(32)) - val epss = ps.properties(e).toIndexedSeq - val nonFinalPSs = epss.filter(_.isRefinable) - assert( - nonFinalPSs.isEmpty, - nonFinalPSs.mkString("some epss are not final:\n\t", "\n\t", "\n") - ) - val properties = epss.map(_.toFinalEP.p) - matcher.validateProperty(p, ats, e, annotation, properties) match { - case Some(error: String) => - val propertiesAsStrings = properties.map(_.toString) - val m = propertiesAsStrings.mkString( - "actual: ", - ", ", - "\nexpectation: " + error - ) - fail(m) - case None => /* OK */ - case result => fail("matcher returned unexpected result: " + result) - } + } + + /** + * Override this method to limit the fixture project to certain subpackages only. + * To do so specify the list of packages that shall be included. A specified package always + * includes all its subpackages. + * + * All package path must be given in '/' notation. + * + * Examples: + * All files related to the escape tests. + * ``` + * List("org/opalj/fpcf/fixtures/escape") + * ``` + * + * All files related to specific escape tests, i.e., cycles and virtual calls + * ``` + * List( + * "org/opalj/fpcf/fixtures/escape/cycles", + * "org/opalj/fpcf/fixtures/escape/virtual_calls", + * ) + * ``` + */ + 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( + 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) + ) + } + + final val PropertyValidatorType = ObjectType("org/opalj/fpcf/properties/PropertyValidator") + + /** + * Returns the [[org.opalj.fpcf.properties.PropertyMatcher]] associated with the annotation - + * if the annotation specifies an expected property currently relevant; i.e, if the + * property kind specified by the annotation is in the set `propertyKinds`. + * + * @param p The current project. + * @param propertyKinds The property kinds which should be analyzed. + * @param annotation The annotation found in the project. + */ + def getPropertyMatcher( + p: Project[URL], + propertyKinds: Set[String] + )( + annotation: AnnotationLike + ): Option[(AnnotationLike, String, Type /* type of the matcher */ )] = { + if (!annotation.annotationType.isObjectType) + return None; + + // Get the PropertyValidator meta-annotation of the given entity's annotation: + val annotationClassFile = p.classFile(annotation.annotationType.asObjectType).get + annotationClassFile.runtimeInvisibleAnnotations.collectFirst { + case Annotation( + PropertyValidatorType, + Seq( + ElementValuePair("key", StringValue(propertyKind)), + ElementValuePair("validator", ClassValue(propertyMatcherType)) + ) + ) if propertyKinds.contains(propertyKind) ⇒ + (annotation, propertyKind, propertyMatcherType) } + } - } + /** + * Called by tests to trigger the validation of the derived properties against the + * specified ones. + * + * @param context The validation context; typically the return value of [[executeAnalyses]]. + * @param eas An iterator over the relevant entities along with the found annotations. + * @param propertyKinds The kinds of properties (as specified by the annotations) that are + * to be tested. + */ + def validateProperties( + context: TestContext, + eas: TraversableOnce[(Entity, /*the processed annotation*/ String ⇒ String /* a String identifying the entity */ , Traversable[AnnotationLike])], + propertyKinds: Set[String] + ): Unit = { + val TestContext(p: Project[URL], ps: PropertyStore, as: List[FPCFAnalysis]) = context + val ats = + as.map(a ⇒ ObjectType(a.getClass.getName.replace('.', '/'))).toSet + + for { + (e, entityIdentifier, annotations) ← eas + augmentedAnnotations = annotations.flatMap(getPropertyMatcher(p, propertyKinds)) + (annotation, propertyKind, matcherType) ← augmentedAnnotations + } { + val annotationTypeName = annotation.annotationType.asObjectType.simpleName + val matcherClass = Class.forName(matcherType.toJava) + val matcherClassConstructor = matcherClass.getDeclaredConstructor() + val matcher = matcherClassConstructor.newInstance().asInstanceOf[PropertyMatcher] + if (matcher.isRelevant(p, ats, e, annotation)) { + + it(entityIdentifier(s"$annotationTypeName")) { + info(s"validator: "+matcherClass.toString.substring(32)) + val epss = ps.properties(e).toIndexedSeq + val nonFinalPSs = epss.filter(_.isRefinable) + assert( + nonFinalPSs.isEmpty, + nonFinalPSs.mkString("some epss are not final:\n\t", "\n\t", "\n") + ) + val properties = epss.map(_.toFinalEP.p) + matcher.validateProperty(p, ats, e, annotation, properties) match { + case Some(error: String) ⇒ + val propertiesAsStrings = properties.map(_.toString) + val m = propertiesAsStrings.mkString( + "actual: ", + ", ", + "\nexpectation: "+error + ) + fail(m) + case None ⇒ /* OK */ + case result ⇒ fail("matcher returned unexpected result: "+result) + } + } + + } + } } - } - - // - // CONVENIENCE METHODS - // - - def fieldsWithAnnotations( - recreatedFixtureProject: SomeProject - ): Traversable[(Field, String => String, Annotations)] = { - for { - f <- recreatedFixtureProject.allFields // cannot be parallelized; "it" is not thread safe - annotations = f.runtimeInvisibleAnnotations - if annotations.nonEmpty - } yield { - (f, (a: String) => f.toJava(s"@$a").substring(24), annotations) + + // + // CONVENIENCE METHODS + // + + def fieldsWithAnnotations( + recreatedFixtureProject: SomeProject + ): Traversable[(Field, String ⇒ String, Annotations)] = { + for { + f ← recreatedFixtureProject.allFields // cannot be parallelized; "it" is not thread safe + annotations = f.runtimeInvisibleAnnotations + if annotations.nonEmpty + } yield { + (f, (a: String) ⇒ f.toJava(s"@$a").substring(24), annotations) + } } - } - - def methodsWithAnnotations( - recreatedFixtureProject: SomeProject - ): Traversable[(Method, String => String, Annotations)] = { - for { - // cannot be parallelized; "it" is not thread safe - m <- recreatedFixtureProject.allMethods - annotations = m.runtimeInvisibleAnnotations - if annotations.nonEmpty - } yield { - (m, (a: String) => m.toJava(s"@$a").substring(24), annotations) + + def methodsWithAnnotations( + recreatedFixtureProject: SomeProject + ): Traversable[(Method, String ⇒ String, Annotations)] = { + for { + // cannot be parallelized; "it" is not thread safe + m ← recreatedFixtureProject.allMethods + annotations = m.runtimeInvisibleAnnotations + if annotations.nonEmpty + } yield { + (m, (a: String) ⇒ m.toJava(s"@$a").substring(24), annotations) + } } - } - - def declaredMethodsWithAnnotations( - recreatedFixtureProject: SomeProject - ): Traversable[(DefinedMethod, String => String, Annotations)] = { - val declaredMethods = recreatedFixtureProject.get(DeclaredMethodsKey) - for { - // cannot be parallelized; "it" is not thread safe - m <- recreatedFixtureProject.allMethods - dm = declaredMethods(m) - annotations = m.runtimeInvisibleAnnotations - if annotations.nonEmpty - } yield { - ( - dm, - (a: String) => m.toJava(s"@$a").substring(24), - annotations - ) + + def declaredMethodsWithAnnotations( + recreatedFixtureProject: SomeProject + ): Traversable[(DefinedMethod, String ⇒ String, Annotations)] = { + val declaredMethods = recreatedFixtureProject.get(DeclaredMethodsKey) + for { + // cannot be parallelized; "it" is not thread safe + m ← recreatedFixtureProject.allMethods + dm = declaredMethods(m) + annotations = m.runtimeInvisibleAnnotations + if annotations.nonEmpty + } yield { + ( + dm, + (a: String) ⇒ m.toJava(s"@$a").substring(24), + annotations + ) + } } - } - - def classFilesWithAnnotations( - recreatedFixtureProject: SomeProject - ): Traversable[(ClassFile, String => String, Annotations)] = { - for { - // cannot be parallelized; "it" is not thread safe - cf <- recreatedFixtureProject.allClassFiles - annotations = cf.runtimeInvisibleAnnotations - if annotations.nonEmpty - } yield { - (cf, (a: String) => cf.thisType.toJava.substring(24) + s"@$a", annotations) + + def classFilesWithAnnotations( + recreatedFixtureProject: SomeProject + ): Traversable[(ClassFile, String ⇒ String, Annotations)] = { + for { + // cannot be parallelized; "it" is not thread safe + cf ← recreatedFixtureProject.allClassFiles + annotations = cf.runtimeInvisibleAnnotations + if annotations.nonEmpty + } yield { + (cf, (a: String) ⇒ cf.thisType.toJava.substring(24) + s"@$a", annotations) + } } - } - - // there can't be any annotations of the implicit "this" parameter... - def explicitFormalParametersWithAnnotations( - recreatedFixtureProject: SomeProject - ): Traversable[(VirtualFormalParameter, String => String, Annotations)] = { - val formalParameters = recreatedFixtureProject.get(VirtualFormalParametersKey) - val declaredMethods = recreatedFixtureProject.get(DeclaredMethodsKey) - for { - // cannot be parallelized; "it" is not thread safe - m <- recreatedFixtureProject.allMethods - parameterAnnotations = m.runtimeInvisibleParameterAnnotations - i <- parameterAnnotations.indices - annotations = parameterAnnotations(i) - if annotations.nonEmpty - dm = declaredMethods(m) - } yield { - val fp = formalParameters(dm)(i + 1) - ( - fp, - (a: String) => - s"VirtualFormalParameter: (origin ${fp.origin} in " + - s"${dm.declaringClassType}#${m.toJava(s"@$a")}", - annotations - ) + + // there can't be any annotations of the implicit "this" parameter... + def explicitFormalParametersWithAnnotations( + recreatedFixtureProject: SomeProject + ): Traversable[(VirtualFormalParameter, String ⇒ String, Annotations)] = { + val formalParameters = recreatedFixtureProject.get(VirtualFormalParametersKey) + val declaredMethods = recreatedFixtureProject.get(DeclaredMethodsKey) + for { + // cannot be parallelized; "it" is not thread safe + m ← recreatedFixtureProject.allMethods + parameterAnnotations = m.runtimeInvisibleParameterAnnotations + i ← parameterAnnotations.indices + annotations = parameterAnnotations(i) + if annotations.nonEmpty + dm = declaredMethods(m) + } yield { + val fp = formalParameters(dm)(i + 1) + ( + fp, + (a: String) ⇒ + s"VirtualFormalParameter: (origin ${fp.origin} in "+ + s"${dm.declaringClassType}#${m.toJava(s"@$a")}", + annotations + ) + } } - } - - def allocationSitesWithAnnotations( - recreatedFixtureProject: SomeProject - ): Traversable[(DefinitionSite, String => String, Traversable[AnnotationLike])] = { - val allocationSites = recreatedFixtureProject.get(DefinitionSitesKey).getAllocationSites - for { - as <- allocationSites - m = as.method - pc = as.pc - code = m.body.get - annotations = code.runtimeInvisibleTypeAnnotations filter { ta => - ta.target match { - case TAOfNew(`pc`) => true - case _ => false + + def allocationSitesWithAnnotations( + recreatedFixtureProject: SomeProject + ): Traversable[(DefinitionSite, String ⇒ String, Traversable[AnnotationLike])] = { + val allocationSites = recreatedFixtureProject.get(DefinitionSitesKey).getAllocationSites + for { + as ← allocationSites + m = as.method + pc = as.pc + code = m.body.get + annotations = code.runtimeInvisibleTypeAnnotations filter { ta ⇒ + ta.target match { + case TAOfNew(`pc`) ⇒ true + case _ ⇒ false + } + } + if annotations.nonEmpty + } yield { + ( + as, + (a: String) ⇒ + s"AllocationSite: (pc ${as.pc} in "+ + s"${m.toJava(s"@$a").substring(24)})", + annotations + ) } - } - if annotations.nonEmpty - } yield { - ( - as, - (a: String) => - s"AllocationSite: (pc ${as.pc} in " + - s"${m.toJava(s"@$a").substring(24)})", - annotations - ) } - } - - def init(p: Project[URL]): Unit = {} - - def executeAnalyses( - analysisRunners: ComputationSpecification[FPCFAnalysis]* - ): TestContext = { - executeAnalyses(analysisRunners.toIterable) - } - - def executeAnalyses( - analysisRunners: Iterable[ComputationSpecification[FPCFAnalysis]] - ): TestContext = { - try { - val p = FixtureProject.recreate { piKeyUnidueId => - piKeyUnidueId != PropertyStoreKey.uniqueId - } // to ensure that this project is not "polluted" - implicit val logContext: LogContext = p.logContext - init(p) - - PropertyStore.updateDebug(true) - - p.getOrCreateProjectInformationKeyInitializationData( - PropertyStoreKey, - (context: List[PropertyStoreContext[AnyRef]]) => { - /* + + def init(p: Project[URL]): Unit = {} + + def executeAnalyses( + analysisRunners: ComputationSpecification[FPCFAnalysis]* + ): TestContext = { + executeAnalyses(analysisRunners.toIterable) + } + + def executeAnalyses( + analysisRunners: Iterable[ComputationSpecification[FPCFAnalysis]] + ): TestContext = { + try { + val p = FixtureProject.recreate { piKeyUnidueId ⇒ + piKeyUnidueId != PropertyStoreKey.uniqueId + } // to ensure that this project is not "polluted" + implicit val logContext: LogContext = p.logContext + init(p) + + PropertyStore.updateDebug(true) + + p.getOrCreateProjectInformationKeyInitializationData( + PropertyStoreKey, + (context: List[PropertyStoreContext[AnyRef]]) ⇒ { + /* val ps = PKEParallelTasksPropertyStore.create( new RecordAllPropertyStoreTracer, context.iterator.map(_.asTuple).toMap ) */ - val ps = PKESequentialPropertyStore(context: _*) - ps + val ps = PKESequentialPropertyStore(context: _*) + ps + } + ) + + val ps = p.get(PropertyStoreKey) + + val (_, csas) = p.get(FPCFAnalysesManagerKey).runAll(analysisRunners) + TestContext(p, ps, csas.collect { case (_, as) ⇒ as }) + } catch { + case t: Throwable ⇒ + t.printStackTrace() + t.getSuppressed.foreach(e ⇒ e.printStackTrace()) + throw t; } - ) + } - val ps = p.get(PropertyStoreKey) + private[this] def getFixtureClassFiles( + classFileReader: ClassFileReader + ): Traversable[(classFileReader.ClassFile, URL)] = { + import classFileReader.AllClassFiles + + var classFilePaths: List[File] = List.empty + + val relevantPackages = fixtureProjectPackage + if (fixtureProjectPackage.nonEmpty) { + classFilePaths = classFilePaths ++ propertyPaths.map(new File(_)) + classFilePaths = classFilePaths ++ relevantPackages.map { path ⇒ + new File({ s"$testFilePath$path" }) + } + } else { + classFilePaths = new File(testFilePath) :: classFilePaths + } - val (_, csas) = p.get(FPCFAnalysesManagerKey).runAll(analysisRunners) - TestContext(p, ps, csas.collect { case (_, as) => as }) - } catch { - case t: Throwable => - t.printStackTrace() - t.getSuppressed.foreach(e => e.printStackTrace()) - throw t; + AllClassFiles(classFilePaths) } - } - - private[this] def getFixtureClassFiles( - classFileReader: ClassFileReader - ): Traversable[(classFileReader.ClassFile, URL)] = { - import classFileReader.AllClassFiles - - var classFilePaths: List[File] = List.empty - - val relevantPackages = fixtureProjectPackage - if (fixtureProjectPackage.nonEmpty) { - classFilePaths = classFilePaths ++ propertyPaths.map(new File(_)) - classFilePaths = classFilePaths ++ relevantPackages.map { path => - new File({ s"$testFilePath$path" }) - } - } else { - classFilePaths = new File(testFilePath) :: classFilePaths - } - - AllClassFiles(classFilePaths) - } } case class TestContext( - project: Project[URL], - propertyStore: PropertyStore, - analyses: List[FPCFAnalysis] + project: Project[URL], + propertyStore: PropertyStore, + analyses: List[FPCFAnalysis] ) From 5de4582edb83b4dba62e28ed9ee525b8c0167fc3 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 19 Feb 2020 16:14:56 +0100 Subject: [PATCH 091/327] formatting PropertiesTest --- .../src/test/scala/org/opalj/fpcf/PropertiesTest.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 060bb5b658..7919cbb919 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,8 +51,7 @@ 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" @@ -399,8 +398,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 From 13600b84ab7735e24fee16a2002c9e237623fdf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Wed, 19 Feb 2020 16:25:10 +0100 Subject: [PATCH 092/327] replaced broken pkecpropertystore impl. with the new one --- .../org/opalj/support/info/CallGraph.scala | 2 +- .../scala/org/opalj/support/info/Purity.scala | 2 +- OPAL/br/src/main/resources/reference.conf | 58 +- .../fpcf/PropertyComputationResult.scala | 2 +- .../scala/org/opalj/fpcf/par/EPKState.scala | 522 ------ .../opalj/fpcf/par/PKECPropertyStore.scala | 1580 ++++++++--------- .../org/opalj/fpcf/par/ParTasksManager.scala | 254 --- .../opalj/fpcf/par/QualifiedTask.scala.temp | 138 -- .../org/opalj/fpcf/par/SeqTasksManager.scala | 111 -- .../org/opalj/fpcf/par/TasksManager.scala | 163 -- .../opalj/fpcf/par/UpdateAndNotifyState.scala | 21 - .../main/scala/org/opalj/fpcf/par/YAPPS.scala | 885 --------- .../fpcf/par/PKECPropertyStoreTest.scala | 125 +- .../scala/org/opalj/fpcf/par/YAPPSTest.scala | 41 - .../FPCFAnalysesIntegrationTest.config | 16 +- 15 files changed, 816 insertions(+), 3104 deletions(-) delete mode 100644 OPAL/si/src/main/scala/org/opalj/fpcf/par/EPKState.scala delete mode 100644 OPAL/si/src/main/scala/org/opalj/fpcf/par/ParTasksManager.scala delete mode 100644 OPAL/si/src/main/scala/org/opalj/fpcf/par/QualifiedTask.scala.temp delete mode 100644 OPAL/si/src/main/scala/org/opalj/fpcf/par/SeqTasksManager.scala delete mode 100644 OPAL/si/src/main/scala/org/opalj/fpcf/par/TasksManager.scala delete mode 100644 OPAL/si/src/main/scala/org/opalj/fpcf/par/UpdateAndNotifyState.scala delete mode 100644 OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala delete mode 100644 OPAL/si/src/test/scala/org/opalj/fpcf/par/YAPPSTest.scala 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 d5dd8c3969..a24a95773e 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 @@ -147,7 +147,7 @@ object CallGraph extends ProjectAnalysisApplication { // FIXME: The PKECPropertyStore is broken org.opalj.fpcf.par.PKECPropertyStore(context: _*) }*/ - org.opalj.fpcf.par.YAPPS(context: _*) + org.opalj.fpcf.par.PKECPropertyStore(context: _*) //org.opalj.fpcf.par.DHTPropertyStore(context: _*) //org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) //org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = 4 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 c3a0bffa94..36903beb7c 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 @@ -201,7 +201,7 @@ object Purity { // FIXME: this property store is broken org.opalj.fpcf.par.PKECPropertyStore(context: _*) }*/ - org.opalj.fpcf.par.YAPPS(context: _*) + org.opalj.fpcf.par.PKECPropertyStore(context: _*) //org.opalj.fpcf.par.DHTPropertyStore(context: _*) //org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) //org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = 4 diff --git a/OPAL/br/src/main/resources/reference.conf b/OPAL/br/src/main/resources/reference.conf index 24d814f92c..dc1679fe6d 100644 --- a/OPAL/br/src/main/resources/reference.conf +++ b/OPAL/br/src/main/resources/reference.conf @@ -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/si/src/main/scala/org/opalj/fpcf/PropertyComputationResult.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyComputationResult.scala index f939879118..f9dc5e8925 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],//IMPROVE: require sets or EOptionPSets + val dependees: Traversable[SomeEOptionP], //IMPROVE: require sets or EOptionPSets val c: ProperOnUpdateContinuation ) extends ProperPropertyComputationResult { result ⇒ 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..8a31253514 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,70 @@ package fpcf package par import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.PriorityBlockingQueue import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.ConcurrentLinkedQueue -import java.util.{Arrays ⇒ JArrays} -import com.typesafe.config.Config -import scala.collection.JavaConverters._ import scala.collection.mutable.ArrayBuffer +import scala.util.control.ControlThrowable -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. + * Yet another parallel property store. * - * The number of threads that is used for parallelizing the computation is determined by: - * `NumberOfThreadsForProcessingPropertyComputations` - * - * @author Michael Eichberg + * @author Dominik Helm */ -final class PKECPropertyStore( - final val ctx: Map[Class[_], AnyRef], - implicit final val tasksManager: TasksManager, - final val tracer: Option[PropertyStoreTracer] +class PKECPropertyStore( + final val ctx: Map[Class[_], AnyRef] )( 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 THREAD_COUNT = 4 - // - // - // PARALLELIZATION RELATED FUNCTIONALITY - // - // + val taskManager: PKECTaskManager = PKECFIFOTaskManager - import tasksManager.phaseSetupCompleted - import tasksManager.awaitPoolQuiescence - import tasksManager.parallelize - import tasksManager.forkResultHandler - import tasksManager.forkOnUpdateContinuation - import tasksManager.forkLazyPropertyComputation - import tasksManager.schedulePropertyComputation + override def MaxEvaluationDepth: Int = 0 - // -------------------------------------------------------------------------------------------- - // - // STATISTICS - // - // -------------------------------------------------------------------------------------------- + val ps: Array[ConcurrentHashMap[Entity, EPKState]] = + Array.fill(PropertyKind.SupportedPropertyKinds) { new ConcurrentHashMap() } - private[this] var quiescenceCounter = 0 - override def quiescenceCount: Int = quiescenceCounter + val tasks: PriorityBlockingQueue[QualifiedTask] = new PriorityBlockingQueue() - override def scheduledTasksCount: Int = tasksManager.scheduledTasksCount - override def scheduledOnUpdateComputationsCount: Int = { - tasksManager.scheduledOnUpdateComputationsCount - } + private[this] val triggeredComputations: Array[Array[SomePropertyComputation]] = + new Array(PropertyKind.SupportedPropertyKinds) - 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 +75,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 +154,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 +176,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 +206,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 +219,166 @@ final class PKECPropertyStore( // // -------------------------------------------------------------------------------------------- - private[this] def triggerComputations(e: Entity, pkId: Int): Unit = { - val triggeredComputations = this.triggeredComputations(pkId) - if (triggeredComputations == null) - return ; + private[par] def scheduleTask(task: QualifiedTask): Unit = { + activeTasks.incrementAndGet() + scheduledTasks.incrementAndGet() + tasks.offer(task) + } - triggeredComputations foreach { pc ⇒ - if (tracer.isDefined) tracer.get.triggeredComputation(e, pkId, pc) - schedulePropertyComputation(e, pc.asInstanceOf[PropertyComputation[Entity]]) - } + 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 = handleExceptions { parallelize(f) } + 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(r, u, e, pk) + + case InterimPartialResult.id ⇒ + val InterimPartialResult(prs, dependees, c) = r + + prs foreach { pr ⇒ + handlePartialResult( + pr, + pr.u.asInstanceOf[SomeEOptionP ⇒ Option[SomeInterimEP]], + pr.e, + pr.pk + ) + } + + val e = new FakeEntity() + val epk = EPK(e, AnalysisKey) + + val epkState = EPKState( + epk, + { dependee: SomeEPS ⇒ + val result = c(dependee) + + ps(AnalysisKeyId).remove(e) + + //println(s"interim partial result remove dependees (${dependees} from depender: $epk") + dependees.foreach { dependee ⇒ + ps(dependee.pk.id).get(dependee.e).removeDepender(epk) + } + + result + }, + dependees + ) - 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() + ps(AnalysisKeyId).put(e, epkState) + + updateDependees(epkState, dependees) } - hadForcedEPKs } - override def handleResult(r: PropertyComputationResult): Unit = handleExceptions { - if (tracer.isDefined) tracer.get.scheduledResultProcessing(r) - forkResultHandler(r) + 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) + + //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]]) + } + } + } + + private[this] def handleInterimResult( + interimEP: InterimEP[Entity, _ >: Null <: Property], + c: ProperOnUpdateContinuation, + dependees: Traversable[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( + pr: PropertyComputationResult, + 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: Traversable[SomeEOptionP]): Unit = { + val dependerEpk = depender.eOptP.toEPK + val suppressedPKs = suppressInterimUpdates(dependerEpk.pk.id) + newDependees.forall { dependee ⇒ + val dependeeState = ps(dependee.pk.id).get(dependee.e) + dependeeState.addDependerOrScheduleContinuation(dependerEpk, dependee, suppressedPKs) + } } override protected[this] def doApply[E <: Entity, P <: Property]( @@ -304,715 +386,621 @@ 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) + if (lazyComputation ne null) { + val previous = ps(pkId).putIfAbsent(e, EPKState(epk, null, null)) + if (previous eq null) { + scheduleTask( + new LazyComputationTask( + e, + lazyComputation.asInstanceOf[E ⇒ PropertyComputationResult], + 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]] + } } 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 + val newState = EPKState(epk, d ⇒ new Result(transformer._2(e, d.asFinal.p)), Some(dependee)) + val previous = ps(pkId).putIfAbsent(e, newState) + if (previous eq null) { + updateDependees(newState, Some(dependee)) + epk + } else { + previous.eOptP.asInstanceOf[EOptionP[E, P]] } - epkState.dependees = Set(sourceEOptionP) } - } while (!sourceEPKState.addDepender(sourceEOptionP, epk, alwaysExceptIfFinal = true)) - if (tracer.isDefined) tracer.get.registeredTransformer(sourceEPKState, epkState) + } else { + val previous = ps(pkId).putIfAbsent(e, EPKState(epk, null, null)) + if (previous eq null) { + epk + } else { + previous.eOptP.asInstanceOf[EOptionP[E, P]] + } + } + } 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) + private[this] val activeTasks = new AtomicInteger(0) + private[this] val threads: Array[PKECThread] = Array.fill(THREAD_COUNT) { null } + + private[this] def startThreads(thread: (Int) ⇒ PKECThread): Unit = { + var tId = 0 + while (tId < THREAD_COUNT) { + val t = thread(tId) + threads(tId) = t + tId += 1 + } + threads.foreach { _.start() } + threads.foreach { _.join } + if (doTerminate) { + if (exception ne null) throw exception + else throw new InterruptedException } - dependerEPKState.clearDependees() } - 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) - } - if (dependerEPK.pk == AnalysisKey) { - properties(AnalysisKey.id).remove(dependerEPK.e) + override def waitOnPhaseCompletion(): Unit = { + idle = false + + // 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 + + while (subPhaseId < subPhaseFinalizationOrder.length) { + var continueCycles = false + do { + var continueFallbacks = false + do { + startThreads(new WorkerThread(_)) + + quiescenceCounter += 1 + + startThreads(new FallbackThread(_)) + + continueFallbacks = !tasks.isEmpty + } while (continueFallbacks) + + startThreads(new CycleResolutionThread(_)) + + resolveCycles() + + continueCycles = !tasks.isEmpty + } while (continueCycles) + + startThreads(new PartialPropertiesFinalizerThread(_)) + + subPhaseId += 1 } + + idle = true } - 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)" - ) - } + 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) - 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)) + // 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 } - if (invokeTriggeredComputations) { - triggerComputations(e, pkId) + + val theSuccessors = (interimEPKState: EPKState) ⇒ { + successors(getResponsibleTId(interimEPKState.eOptP.e))(interimEPKState) } - } - // 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 + val cSCCs = graphs.closedSCCs(theInterimStates, theSuccessors) + + for (cSCC ← cSCCs) { + for (interimEPKState ← cSCC) { + val dependees = interimEPKState.dependees + val epk = interimEPKState.eOptP.toEPK + dependees.foreach { dependee ⇒ + // during execution, no other thread accesses the dependers of the EPKState + val dependeeState = ps(dependee.pk.id).get(dependee.e) + dependeeState.dependers.remove(epk) + dependeeState.suppressedDependers.remove(epk) } - } else { - epkState + scheduleTask(new SetTask(interimEPKState.eOptP.toFinalEP)) } } + } - // 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 - } + class PKECThread(name: String) extends Thread(name) - // 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 - } else { - false + class WorkerThread(ownTId: Int) extends PKECThread(s"PropertyStoreThread-#$ownTId") { + + override def run(): Unit = { + try { + while (!doTerminate) { + val curTask = tasks.poll() + if (curTask eq null) { + val active = activeTasks.get() + if (active == 0) { + threads.foreach { t ⇒ + if (t ne this) + t.interrupt() + } + return ; + } else { + val nextTask = tasks.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 + if (!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 (getResponsibleTId(eOptP.e) == ownTId && + epkState.eOptP.isRefinable) { + localInterimStates.append(epkState) + } + }) + } + pkId += 1 + } + interimStates(ownTId) = localInterimStates + + successors(ownTId) = (interimEPKState: EPKState) ⇒ { + val dependees = interimEPKState.dependees + if (dependees != null) { + interimEPKState.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 = + Integer.compare(this.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: SomeEPK, oldDependee: SomeEOptionP) extends QualifiedTask { + scheduledOnUpdateComputations.incrementAndGet() - case Results.id ⇒ r.asResults.foreach { processResult } + val priority = { + val dependerState = ps(depender.pk.id).get(depender.e) + val dependerDependees = if (dependerState == null) null else dependerState.dependees + val dependerDependeesSize = if (dependerDependees == null) 0 else dependerDependees.size - case IncrementalResult.id ⇒ - val IncrementalResult(ir, npcs) = r - processResult(ir) - npcs /*: Iterator[(PropertyComputation[e],e)]*/ foreach { npc ⇒ - val (pc, e) = npc - schedulePropertyComputation(e, pc) - } + val dependeeState = ps(oldDependee.pk.id).get(oldDependee.e) + val dependeeDependersSize = dependeeState.dependers.size() + dependeeState.suppressedDependers.size() + taskManager.weight(depender, oldDependee, dependerDependeesSize, dependeeDependersSize) + } - // - // Methods which actually store results... - // + override def apply(): Unit = { + val epkState = ps(depender.pk.id).get(depender.e) + if (epkState ne null) { + val isSuppressed = suppressInterimUpdates(depender.pk.id)(oldDependee.pk.id) + epkState.applyContinuation(oldDependee, isSuppressed) + } + } + } - case Result.id ⇒ - val Result(finalEP) = r - handleFinalResult(finalEP) + private[this] def getResponsibleTId(e: Entity): Int = { + Math.abs(e.hashCode() >> 5) % THREAD_COUNT + } +} - case MultiResult.id ⇒ - val MultiResult(results) = r - results foreach { finalEP ⇒ handleFinalResult(finalEP) } +case class EPKState( + @volatile var eOptP: SomeEOptionP, + @volatile var c: OnUpdateContinuation, + @volatile var dependees: Traversable[SomeEOptionP], + dependers: java.util.HashSet[SomeEPK] = new java.util.HashSet(), + suppressedDependers: java.util.HashSet[SomeEPK] = new java.util.HashSet() +) { + + 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 - case InterimResult.id ⇒ - val interimR = r.asInterimResult - handleInterimResult( - interimR.eps, - interimR.c, - interimR.dependees - ) + notifyAndClearDependers(finalEP, theEOptP, dependers, unnotifiedPKs) + notifyAndClearDependers(finalEP, theEOptP, suppressedDependers, unnotifiedPKs) + } + } + dependees = null + } - case PartialResult.id ⇒ - val PartialResult(e, pk, u) = r - handlePartialResult(e, pk, u) + if (theEOptP.isEPK) ps.triggerComputations(theEOptP.e, theEOptP.pk.id) + } - case InterimPartialResult.id ⇒ - val InterimPartialResult(prs, dependees, c) = r + def interimUpdate( + interimEP: InterimEP[Entity, Property], + newC: OnUpdateContinuation, + newDependees: Traversable[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 - if (debug && dependees.isEmpty) { - throw new IllegalArgumentException(s"interim partial result $r without dependees") + notifyAndClearDependers(interimEP, theEOptP, dependers) + } } + c = newC + dependees = newDependees + } + } - // 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 - } + ps.updateDependees(this, newDependees) + + if (theEOptP.isEPK) ps.triggerComputations(theEOptP.e, theEOptP.pk.id) + } + + def partialUpdate(updateComputation: UpdateComputation[Entity, Property])(implicit ps: PKECPropertyStore): Unit = { + var theEOptP: SomeEOptionP = null + + this.synchronized { + theEOptP = eOptP + updateComputation(theEOptP) match { + case Some(interimEP) ⇒ + if (ps.debug) assert(eOptP != interimEP) + dependers.synchronized { + eOptP = interimEP + + notifyAndClearDependers(interimEP, theEOptP, dependers) } + interimEP + case _ ⇒ + null + } + } + + if (theEOptP.isEPK) ps.triggerComputations(theEOptP.e, theEOptP.pk.id) + } + + def addDependerOrScheduleContinuation( + depender: SomeEPK, + dependee: SomeEOptionP, + 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). + if ((theEOptP ne dependee) && + (theEOptP.isFinal || !suppressedPKs(dependee.pk.id))) { + ps.scheduleTask(new ps.ContinuationTask(depender, dependee)) + false + } else { + if (suppressedPKs(theEOptP.pk.id)) { + suppressedDependers.add(depender) + } else { + dependers.add(depender) } + true + } + } + } + def removeDepender(epk: SomeEPK): Unit = { + dependers.synchronized { + dependers.remove(epk) + suppressedDependers.remove(epk) } } - override def waitOnPhaseCompletion(): Unit = { - phaseSetupCompleted() + def notifyAndClearDependers( + theEOptP: SomeEPS, + oldEOptP: SomeEOptionP, + theDependers: java.util.HashSet[SomeEPK], + unnotifiedPKs: Set[PropertyKind] = Set.empty + )(implicit ps: PKECPropertyStore): Unit = { + theDependers.forEach { depender ⇒ + if (!unnotifiedPKs.contains(depender.pk)) { + ps.scheduleTask(new ps.ContinuationTask(depender, oldEOptP)) + } + } - handleExceptions { - val maxPKIndex = PropertyKey.maxId + // Clear all dependers that will be notified, they will re-register if required + theDependers.clear() + } + + def applyContinuation(oldDependee: SomeEOptionP, isSuppressed: Boolean)(implicit ps: PKECPropertyStore): Unit = { + val epk = oldDependee.toEPK + this.synchronized { + val theDependees = dependees + // We are still interessted in that dependee? + if (theDependees != null && theDependees.exists { d ⇒ + (d eq oldDependee) || (isSuppressed && epk == d.toEPK) + }) { + // 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 some values were explicitly set, we have to trigger corresponding triggered - // computations. - setAndPreinitializedValues.foreach { epk ⇒ triggerComputations(epk.e, epk.pk.id) } - setAndPreinitializedValues = List.empty +trait PKECTaskManager { + def weight( + taskEPK: SomeEPK, + updatedEOptionP: SomeEOptionP, // the current eOptionP to which the task is related + updatedEOptionPDependees: Int, // the dependees of the eOptionP + currentDependersOfUpdatedEOptionP: Int + ): Int +} +case object PKECFIFOTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(0) - 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) - } - } - } + override def weight( + taskEPK: SomeEPK, + updatedEOptionP: SomeEOptionP, + updatedEOptionPDependees: Int, + currentDependersOfUpdatedEOptionP: Int + ): Int = { + counter.getAndIncrement() + } +} - if (triggerAndClearForcedEPKs() /* forces the evaluation as a side-effect */ ) { - awaitPoolQuiescence() - continueComputation.set(true) - } +case object PKECLIFOTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) - // 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() + override def weight( + taskEPK: SomeEPK, + updatedEOptionP: SomeEOptionP, + updatedEOptionPDependees: Int, + currentDependersOfUpdatedEOptionP: Int + ): Int = { + counter.getAndDecrement() + } +} - if (triggerAndClearForcedEPKs()) { - awaitPoolQuiescence() - continueComputation.set(true) - } +case object PKECManyDependeesFirstTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) - subPhaseId += 1 - } - } while (continueComputation.get()) + override def weight( + taskEPK: SomeEPK, + updatedEOptionP: SomeEOptionP, + dependeesCount: Int, + deoendersCount: Int + ): Int = { + -dependeesCount + } +} - // TODO assert that we don't have any more InterimEPKStates - } - if (exception != null) throw exception; +case object PKECManyDependeesLastTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) + + override def weight( + taskEPK: SomeEPK, + updatedEOptionP: SomeEOptionP, + dependeesCount: Int, + deoendersCount: Int + ): Int = { + dependeesCount + } +} + +case object PKECManyDependersFirstTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) + + override def weight( + taskEPK: SomeEPK, + updatedEOptionP: SomeEOptionP, + dependeesCount: Int, + deoendersCount: Int + ): Int = { + -deoendersCount } +} + +case object PKECManyDependersLastTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) + + override def weight( + taskEPK: SomeEPK, + updatedEOptionP: SomeEOptionP, + dependeesCount: Int, + deoendersCount: Int + ): Int = { + deoendersCount + } +} - override protected[this] def onFirstException(t: Throwable): Unit = { - super.onFirstException(t) - if (tracer.isDefined) tracer.get.firstException(t) +case object PKECManyDependenciesFirstTaskManager extends PKECTaskManager { + val counter = new AtomicInteger(Int.MaxValue) + + override def weight( + taskEPK: SomeEPK, + updatedEOptionP: SomeEOptionP, + dependeesCount: Int, + deoendersCount: Int + ): Int = { + -(Math.max(1, deoendersCount) * Math.max(dependeesCount, 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( + taskEPK: SomeEPK, + updatedEOptionP: SomeEOptionP, + dependeesCount: Int, + deoendersCount: Int + ): Int = { + Math.max(1, deoendersCount) * Math.max(dependeesCount, 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" - ) - def apply( context: PropertyStoreContext[_ <: AnyRef]* )( @@ -1020,48 +1008,8 @@ 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") - } - 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) ps } - -} - -private[par] class FakeEntity() { - - override def toString: String = "FakeEntity" -} +} \ No newline at end of file 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/main/scala/org/opalj/fpcf/par/YAPPS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala deleted file mode 100644 index a5a5c74363..0000000000 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/YAPPS.scala +++ /dev/null @@ -1,885 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package par - -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.PriorityBlockingQueue -import java.util.concurrent.atomic.AtomicInteger - -import scala.collection.mutable.ArrayBuffer -import scala.util.control.ControlThrowable - -import org.opalj.log.LogContext -import org.opalj.fpcf.PropertyKey.fallbackPropertyBasedOnPKId - -/** - * Yet another parallel property store. - * - * @author Dominik Helm - */ -class YAPPS( - final val ctx: Map[Class[_], AnyRef] -)( - implicit - val logContext: LogContext -) extends ParallelPropertyStore { - propertyStore ⇒ - - val THREAD_COUNT = 4 - - override def MaxEvaluationDepth: Int = 0 - - val ps: Array[ConcurrentHashMap[Entity, YappsEPKState]] = - Array.fill(PropertyKind.SupportedPropertyKinds) { new ConcurrentHashMap() } - - val tasks: PriorityBlockingQueue[YappsTask] = new PriorityBlockingQueue() - - private[this] val triggeredComputations: Array[Array[SomePropertyComputation]] = - new Array(PropertyKind.SupportedPropertyKinds) - - private[this] var setAndPreinitializedValues: List[SomeEPK] = List.empty - - override def shutdown(): Unit = {} - - var idle = true - override def isIdle: Boolean = idle - - // -------------------------------------------------------------------------------------------- - // - // STATISTICS - // - // -------------------------------------------------------------------------------------------- - - private[this] var quiescenceCounter = 0 - override def quiescenceCount: Int = quiescenceCounter - - private[this] val scheduledTasks: Array[Int] = Array.fill(THREAD_COUNT) { 0 } - override def scheduledTasksCount: Int = scheduledTasks.sum - - private[this] val scheduledOnUpdateComputations: Array[Int] = Array.fill(THREAD_COUNT) { 0 } - override def scheduledOnUpdateComputationsCount: Int = scheduledOnUpdateComputations.sum - - override def fallbacksUsedForComputedPropertiesCount: Int = 0 //TODO - - override private[fpcf] def incrementFallbacksUsedForComputedPropertiesCounter(): Unit = { - // ??? - } - - // -------------------------------------------------------------------------------------------- - // - // BASIC QUERY METHODS (ONLY TO BE CALLED WHEN THE STORE IS QUIESCENT) - // - // -------------------------------------------------------------------------------------------- - - override def toString(printProperties: Boolean): String = - if (printProperties) { - val properties = for (pkId ← 0 to PropertyKey.maxId) yield { - var entities: List[String] = List.empty - ps(pkId).forEachValue(Long.MaxValue, { state: YappsEPKState ⇒ - entities ::= state.eOptP.toString.replace("\n", "\n\t") - }) - entities.sorted.mkString(s"Entities for property key $pkId:\n\t", "\n\t", "\n") - } - properties.mkString("PropertyStore(\n\t", "\n\t", "\n)") - } else { - s"PropertyStore(properties=${ps.iterator.map(_.size).sum})" - } - - override def entities(propertyFilter: SomeEPS ⇒ Boolean): Iterator[Entity] = { - ps.iterator.flatMap { propertiesPerKind ⇒ - var result: List[Entity] = List.empty - propertiesPerKind.forEachValue(Long.MaxValue, { state: YappsEPKState ⇒ if (propertyFilter(state.eOptP.asEPS)) result ::= state.eOptP.e }) - result - } - } - - override def entities[P <: Property](pk: PropertyKey[P]): Iterator[EPS[Entity, P]] = { - var result: List[EPS[Entity, P]] = List.empty - ps(pk.id).forEachValue(Long.MaxValue, { state: YappsEPKState ⇒ result ::= state.eOptP.asInstanceOf[EPS[Entity, P]] }) - result.iterator - } - - override def entities[P <: Property](lb: P, ub: P): Iterator[Entity] = { - entities { eps ⇒ eps.lb == lb && eps.ub == ub } - } - - override def entitiesWithLB[P <: Property](lb: P): Iterator[Entity] = { - entities { eps ⇒ eps.lb == lb } - } - - override def entitiesWithUB[P <: Property](ub: P): Iterator[Entity] = { - entities { eps ⇒ eps.ub == ub } - } - - override def properties[E <: Entity](e: E): Iterator[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 ePKState = ps(pk.id).get(e) - (ePKState ne null) && (ePKState.eOptP.hasUBP || ePKState.eOptP.hasLBP) - } - - override def isKnown(e: Entity): Boolean = { - ps.exists { propertiesPerKind ⇒ - propertiesPerKind.containsKey(e) - } - } - - override def get[E <: Entity, P <: Property](e: E, pk: PropertyKey[P]): Option[EOptionP[E, P]] = { - val ePKState = ps(pk.id).get(e) - if (ePKState eq null) - None - else - Some(ePKState.eOptP.asInstanceOf[EOptionP[E, P]]) - } - - override def get[E <: Entity, P <: Property](epk: EPK[E, P]): Option[EOptionP[E, P]] = { - get(epk.e, epk.pk) - } - - // -------------------------------------------------------------------------------------------- - // - // CORE IMPLEMENTATION - NOT THREAD SAFE PART - // - // -------------------------------------------------------------------------------------------- - - override protected[this] def doScheduleEagerComputationForEntity[E <: Entity]( - e: E - )(pc: PropertyComputation[E]): Unit = { - schedulePropertyComputation(e, pc) - } - - override protected[this] def doRegisterTriggeredComputation[E <: Entity, P <: Property]( - pk: PropertyKey[P], - pc: PropertyComputation[E] - ): Unit = { - - // Recall that the scheduler has to take care of registering a triggered computation - // before the first analysis derives a respective value! - // Hence, there is no need to immediately check that we have to trigger a computation. - - val pkId = pk.id - val oldComputations: Array[SomePropertyComputation] = triggeredComputations(pkId) - var newComputations: Array[SomePropertyComputation] = null - - if (oldComputations == null) { - newComputations = Array[SomePropertyComputation](pc) - } else { - newComputations = java.util.Arrays.copyOf(oldComputations, oldComputations.length + 1) - newComputations(oldComputations.length) = pc - } - triggeredComputations(pkId) = newComputations - } - - override protected[this] def doSet(e: Entity, p: Property): Unit = { - val epkState = YappsEPKState(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 protected[this] def doPreInitialize[E <: Entity, P <: Property]( - e: E, - pk: PropertyKey[P] - )(pc: EOptionP[E, P] ⇒ InterimEP[E, P]): Unit = { - val pkId = pk.id - val propertiesOfKind = ps(pkId) - val oldEPKState = propertiesOfKind.get(e) - val newInterimEP: SomeInterimEP = - oldEPKState match { - case null ⇒ - val epk = EPK(e, pk) - setAndPreinitializedValues ::= epk - pc(epk) - case epkState ⇒ - pc(epkState.eOptP.asInstanceOf[EOptionP[E, P]]) - } - assert(newInterimEP.isRefinable) - val newEPKState = YappsEPKState(newInterimEP, null, null) - propertiesOfKind.put(e, newEPKState) - } - - // -------------------------------------------------------------------------------------------- - // - // CORE IMPLEMENTATION - THREAD SAFE PART - // - // -------------------------------------------------------------------------------------------- - - private[this] def scheduleTask(task: YappsTask): Unit = { - activeTasks.incrementAndGet() - tasks.offer(task) - } - - private[this] def schedulePropertyComputation[E <: Entity]( - e: E, - pc: PropertyComputation[E] - ): Unit = { - scheduleTask(new YappsPropertyComputationTask(e, pc)) - } - - override def force[E <: Entity, P <: Property](e: E, pk: PropertyKey[P]): Unit = { - doApply(EPK(e, pk), e, pk.id) - } - - override def execute(f: ⇒ Unit): Unit = { - scheduleTask(new YappsExecuteTask(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(r, u, e, pk) - - case InterimPartialResult.id ⇒ - val InterimPartialResult(prs, dependees, c) = r - - prs foreach { pr ⇒ - handlePartialResult( - pr, - pr.u.asInstanceOf[SomeEOptionP ⇒ Option[SomeInterimEP]], - pr.e, - pr.pk - ) - } - - val e = new FakeEntity() - val epk = EPK(e, AnalysisKey) - - val epkState = YappsEPKState( - epk, - { dependee: SomeEPS ⇒ - val result = c(dependee) - - ps(AnalysisKeyId).remove(e) - - //println(s"interim partial result remove dependees (${dependees} from depender: $epk") - dependees.foreach { dependee ⇒ - ps(dependee.pk.id).get(dependee.e).removeDepender(epk) - } - - result - }, - dependees - ) - - ps(AnalysisKeyId).put(e, epkState) - - updateDependees(epkState, dependees) - } - } - - 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; YappsEPKState(finalEP, null, null) }) - if (isFresh) triggerComputations(e, pk.id) - else ePKState.setFinal(finalEP, unnotifiedPKs) - - //TODO remove depender status - } - - private[this] def triggerComputations(e: Entity, pkId: Int): Unit = { - val computations = triggeredComputations(pkId) - if (computations ne null) { - computations foreach { pc ⇒ - schedulePropertyComputation(e, pc.asInstanceOf[PropertyComputation[Entity]]) - } - } - } - - private[this] def handleInterimResult( - interimEP: InterimEP[Entity, _ >: Null <: Property], - c: ProperOnUpdateContinuation, - dependees: Traversable[SomeEOptionP] - ): Unit = { - val SomeEPS(e, pk) = interimEP - var isFresh = false - val ePKState = - ps(pk.id).computeIfAbsent(e, { _ ⇒ isFresh = true; YappsEPKState(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( - pr: PropertyComputationResult, - update: UpdateComputation[Entity, Property], - e: Entity, - pk: PropertyKey[Property] - ): Unit = { - val ePKState = ps(pk.id).computeIfAbsent(e, _ ⇒ YappsEPKState(EPK(e, pk), null, null)) - ePKState.partialUpdate(update) - } - - def updateDependees(depender: YappsEPKState, newDependees: Traversable[SomeEOptionP]): Unit = { - val dependerEpk = depender.eOptP.toEPK - val suppressedPKs = suppressInterimUpdates(dependerEpk.pk.id) - newDependees.forall { dependee ⇒ - val dependeeState = ps(dependee.pk.id).get(dependee.e) - dependeeState.addDependerOrScheduleContinuation(dependerEpk, dependee, suppressedPKs) - } - } - - override protected[this] def doApply[E <: Entity, P <: Property]( - epk: EPK[E, P], - e: E, - pkId: Int - ): EOptionP[E, P] = { - val current = ps(pkId).get(e) - if (current eq null) { - val lazyComputation = lazyComputations(pkId) - if (lazyComputation ne null) { - val previous = ps(pkId).putIfAbsent(e, YappsEPKState(epk, null, null)) - if (previous eq null) { - scheduleTask( - new YappsLazyComputationTask( - e, - lazyComputation.asInstanceOf[E ⇒ PropertyComputationResult], - 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, YappsEPKState(result, null, null)) - if (previous eq null) { - triggerComputations(e, pkId) - result.asInstanceOf[FinalEP[E, P]] - } else { - previous.eOptP.asInstanceOf[EOptionP[E, P]] - } - } else { - val newState = YappsEPKState(epk, d ⇒ new Result(transformer._2(e, d.asFinal.p)), Some(dependee)) - val previous = ps(pkId).putIfAbsent(e, newState) - if (previous eq null) { - updateDependees(newState, Some(dependee)) - epk - } else { - previous.eOptP.asInstanceOf[EOptionP[E, P]] - } - } - } else { - val previous = ps(pkId).putIfAbsent(e, YappsEPKState(epk, null, null)) - if (previous eq null) { - epk - } else { - previous.eOptP.asInstanceOf[EOptionP[E, P]] - } - } - } else { - val finalEP = computeFallback[E, P](e, pkId) - val previous = ps(pkId).putIfAbsent(e, YappsEPKState(finalEP, null, null)) - if (previous eq null) { - triggerComputations(e, pkId) - finalEP - } else { - previous.eOptP.asInstanceOf[EOptionP[E, P]] - } - } - } else { - current.eOptP.asInstanceOf[EOptionP[E, P]] - } - } - - private[this] val activeTasks = new AtomicInteger(0) - private[this] val threads: Array[YappsThread] = Array.fill(THREAD_COUNT) { null } - - private[this] def startThreads(thread: (Int) ⇒ YappsThread): Unit = { - var tId = 0 - while (tId < THREAD_COUNT) { - val t = thread(tId) - threads(tId) = t - tId += 1 - } - threads.foreach { _.start() } - threads.foreach { _.join } - if (doTerminate) { - if (exception ne null) throw exception - else throw new InterruptedException - } - } - - override def waitOnPhaseCompletion(): Unit = { - idle = false - - // 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 - - while (subPhaseId < subPhaseFinalizationOrder.length) { - var continueCycles = false - do { - var continueFallbacks = false - do { - startThreads(new YappsWorkerThread(_)) - - quiescenceCounter += 1 - - startThreads(new YappsFallbackThread(_)) - - continueFallbacks = !tasks.isEmpty - } while (continueFallbacks) - - startThreads(new YappsCycleResolutionThread(_)) - - resolveCycles() - - continueCycles = !tasks.isEmpty - } while (continueCycles) - - startThreads(new YappsPartialPropertiesFinalizerThread(_)) - - subPhaseId += 1 - } - - idle = true - } - - private[this] val interimStates: Array[ArrayBuffer[YappsEPKState]] = - Array.fill(THREAD_COUNT)(null) - private[this] val successors: Array[YappsEPKState ⇒ Traversable[YappsEPKState]] = - Array.fill(THREAD_COUNT)(null) - - // executed on the main thread only - private[this] def resolveCycles(): Unit = { - val theInterimStates = new ArrayBuffer[YappsEPKState](interimStates.iterator.map(_.size).sum) - var tId = 0 - while (tId < THREAD_COUNT) { - theInterimStates ++= interimStates(tId) - tId += 1 - } - - val theSuccessors = (interimEPKState: YappsEPKState) ⇒ { - successors(getResponsibleTId(interimEPKState.eOptP.e))(interimEPKState) - } - - val cSCCs = graphs.closedSCCs(theInterimStates, theSuccessors) - - for (cSCC ← cSCCs) { - for (interimEPKState ← cSCC) { - val dependees = interimEPKState.dependees - val epk = interimEPKState.eOptP.toEPK - dependees.foreach { dependee ⇒ - // during execution, no other thread accesses the dependers of the EPKState - val dependeeState = ps(dependee.pk.id).get(dependee.e) - dependeeState.dependers.remove(epk) - dependeeState.suppressedDependers.remove(epk) - } - scheduleTask(new YappsSetTask(interimEPKState.eOptP.toFinalEP)) - } - } - } - - class YappsThread(name: String) extends Thread(name) - - class YappsWorkerThread(ownTId: Int) extends YappsThread(s"PropertyStoreThread-#$ownTId") { - - override def run(): Unit = { - try { - while (!doTerminate) { - val curTask = tasks.poll() - if (curTask eq null) { - val active = activeTasks.get() - if (active == 0) { - threads.foreach { t ⇒ - if (t ne this) - t.interrupt() - } - return ; - } else { - val nextTask = tasks.take() - if (!doTerminate) { - nextTask.apply() - activeTasks.decrementAndGet() - } - } - } else { - if (!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 YappsFallbackThread(ownTId: Int) extends YappsThread(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: YappsEPKState ⇒ - 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 YappsCycleResolutionThread(ownTId: Int) extends YappsThread(s"PropertyStoreCycleResolutionThread-#$ownTId") { - - override def run(): Unit = { - val localInterimStates = ArrayBuffer.empty[YappsEPKState] - var pkId = 0 - while (pkId <= PropertyKey.maxId) { - if (propertyKindsComputedInThisPhase(pkId)) { - ps(pkId).forEachValue(Long.MaxValue, { epkState: YappsEPKState ⇒ - val eOptP = epkState.eOptP - if (getResponsibleTId(eOptP.e) == ownTId && - epkState.eOptP.isRefinable) { - localInterimStates.append(epkState) - } - }) - } - pkId += 1 - } - interimStates(ownTId) = localInterimStates - - successors(ownTId) = (interimEPKState: YappsEPKState) ⇒ { - val dependees = interimEPKState.dependees - if (dependees != null) { - interimEPKState.dependees.map { eOptionP ⇒ - ps(eOptionP.pk.id).get(eOptionP.e) - } - } else { - Traversable.empty - } - } - } - } - - class YappsPartialPropertiesFinalizerThread(ownTId: Int) extends YappsThread(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: YappsEPKState ⇒ - 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) - }) - } - } - } - - trait YappsTask extends (() ⇒ Unit) with Comparable[YappsTask] { - val priority: Int - - override def compareTo(other: YappsTask): Int = this.priority - other.priority - } - - class YappsExecuteTask(f: ⇒ Unit) extends YappsTask { - val priority = 0 - - override def apply(): Unit = { - f - } - } - - class YappsSetTask[E <: Entity, P <: Property]( - finalEP: FinalEP[E, P] - ) extends YappsTask { - val priority = 0 - - override def apply(): Unit = { - handleFinalResult(finalEP) - } - } - - class YappsPropertyComputationTask[E <: Entity]( - e: E, - pc: PropertyComputation[E] - ) extends YappsTask { - val priority = 0 - - override def apply(): Unit = { - handleResult(pc(e)) - } - } - - class YappsLazyComputationTask[E <: Entity]( - e: E, - pc: PropertyComputation[E], - pkId: Int - ) extends YappsTask { - val priority = 0 - - override def apply(): Unit = { - val state = ps(pkId).get(e) - state.synchronized { - if (state.eOptP.isEPK) - handleResult(pc(e)) - } - } - } - - class YappsContinuationTask(depender: SomeEPK, oldDependee: SomeEOptionP) extends YappsTask { - val priority = 0 - - override def apply(): Unit = { - val epkState = ps(depender.pk.id).get(depender.e) - if (epkState ne null) - epkState.applyContinuation(oldDependee) - } - } - - case class YappsEPKState( - @volatile var eOptP: SomeEOptionP, - @volatile var c: OnUpdateContinuation, - @volatile var dependees: Traversable[SomeEOptionP], - dependers: java.util.HashSet[SomeEPK] = new java.util.HashSet(), - suppressedDependers: java.util.HashSet[SomeEPK] = new java.util.HashSet() - ) { - - def setFinal(finalEP: FinalEP[Entity, Property], unnotifiedPKs: Set[PropertyKind]): 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 (debug) eOptP.checkIsValidPropertiesUpdate(finalEP, Nil) - dependers.synchronized { - eOptP = finalEP - - notifyAndClearDependers(finalEP, theEOptP, dependers, unnotifiedPKs) - notifyAndClearDependers(finalEP, theEOptP, suppressedDependers, unnotifiedPKs) - } - } - dependees = null - } - - if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) - } - - def interimUpdate( - interimEP: InterimEP[Entity, Property], - newC: OnUpdateContinuation, - newDependees: Traversable[SomeEOptionP] - ): 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 (debug) theEOptP.checkIsValidPropertiesUpdate(interimEP, newDependees) - if (interimEP.isUpdatedComparedTo(theEOptP)) { - dependers.synchronized { - eOptP = interimEP - - notifyAndClearDependers(interimEP, theEOptP, dependers) - } - } - c = newC - dependees = newDependees - } - } - - updateDependees(this, newDependees) - - if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) - } - - def partialUpdate(updateComputation: UpdateComputation[Entity, Property]): Unit = { - var theEOptP: SomeEOptionP = null - - this.synchronized { - theEOptP = eOptP - updateComputation(theEOptP) match { - case Some(interimEP) ⇒ - if (debug) assert(eOptP != interimEP) - dependers.synchronized { - eOptP = interimEP - - notifyAndClearDependers(interimEP, theEOptP, dependers) - } - interimEP - case _ ⇒ - null - } - } - - if (theEOptP.isEPK) triggerComputations(theEOptP.e, theEOptP.pk.id) - } - - def addDependerOrScheduleContinuation( - depender: SomeEPK, - dependee: SomeEOptionP, - suppressedPKs: Array[Boolean] - ): 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). - if ((theEOptP ne dependee) && - (theEOptP.isFinal || !suppressedPKs(dependee.pk.id))) { - scheduleTask(new YappsContinuationTask(depender, dependee)) - false - } else { - if (suppressedPKs(theEOptP.pk.id)) { - suppressedDependers.add(depender) - } else { - dependers.add(depender) - } - true - } - } - } - - def removeDepender(epk: SomeEPK): Unit = { - dependers.synchronized { - dependers.remove(epk) - suppressedDependers.remove(epk) - } - } - - def notifyAndClearDependers( - theEOptP: SomeEPS, - oldEOptP: SomeEOptionP, - theDependers: java.util.HashSet[SomeEPK], - unnotifiedPKs: Set[PropertyKind] = Set.empty - ): Unit = { - theDependers.forEach { depender ⇒ - if (!unnotifiedPKs.contains(depender.pk)) { - scheduleTask(new YappsContinuationTask(depender, oldEOptP)) - } - } - - // Clear all dependers that will be notified, they will re-register if required - theDependers.clear() - } - - def applyContinuation(oldDependee: SomeEOptionP): Unit = { - // IMPROVE: Use tryLock() instead - val isSuppressed = suppressInterimUpdates(eOptP.pk.id)(oldDependee.pk.id) - val epk = oldDependee.toEPK - this.synchronized { - val theDependees = dependees - // We are still interessted in that dependee? - if (theDependees != null && theDependees.exists { d ⇒ - (d eq oldDependee) || (isSuppressed && epk == d.toEPK) - }) { - // We always retrieve the most up-to-date state of the dependee. - val currentDependee = 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". - handleResult(c(currentDependee)) - } - } - } - - } - - private[this] def getResponsibleTId(e: Entity): Int = { - Math.abs(e.hashCode() >> 5) % THREAD_COUNT - } -} - -object YAPPS extends PropertyStoreFactory[YAPPS] { - - final val MaxEvaluationDepthKey = "org.opalj.fpcf.par.PKECPropertyStore.MaxEvaluationDepth" - - def apply( - context: PropertyStoreContext[_ <: AnyRef]* - )( - implicit - logContext: LogContext - ): YAPPS = { - val contextMap: Map[Class[_], AnyRef] = context.map(_.asTuple).toMap - - val ps = new YAPPS(contextMap) - ps - } -} \ No newline at end of file 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..5cc61083eb 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,41 @@ /* 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 - } -} - -class PKECPropertyStoreTestWithDebuggingMaxEvalDepthDefault - extends PKECPropertyStoreTestWithDebugging { - - def createPropertyStore(): PKECPropertyStore = { - val ps = PKECPropertyStore(classOf[PropertyStoreTracer], new RecordAllPropertyStoreEvents()) - ps.suppressError = true - ps - } - -} - -class PKECPropertyStoreTestWithDebuggingMaxEvalDepth32AndSeqTaskManager - extends PKECPropertyStoreTestWithDebugging { - - 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) - ) - ps.suppressError = true - ps - } - -} - -class PKECPropertyStoreTestWithDebuggingMaxEvalDepth0AndSeqTaskManager - extends PKECPropertyStoreTestWithDebugging { - - 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) - ) - ps.suppressError = true - ps + // TODO Basic smoke test? } - } -class PKECPropertyStoreTestWithDebuggingMaxEvalDepth32AndParTaskManager - extends PKECPropertyStoreTestWithDebugging { +class PKECPropertyStoreTestWithDebugging + 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) ps.suppressError = true ps } } -// FIXME: PKECPropertyStore seems to be broken -/*class PKECPropertyStoreTestWithDebuggingMaxEvalDepth1AndParTaskManager - extends PKECPropertyStoreTestWithDebugging { - - 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) - ) - ps.suppressError = true - ps - } - -}*/ - // ************************************************************************************************* // ************************************* NO DEBUGGING ********************************************** // ************************************************************************************************* -abstract class PKECPropertyStoreTestWithoutDebugging +abstract class AbstractPKECPropertyStoreTestWithoutDebugging extends PropertyStoreTestWithoutDebugging[PKECPropertyStore] -class PKECPropertyStoreTestWithoutDebuggingMaxEvalDepth128AndSeqTaskManager - extends PKECPropertyStoreTestWithoutDebugging { - - def createPropertyStore(): PKECPropertyStore = { - val ps = PKECPropertyStore("Seq", 128)() - ps.suppressError = true - ps - } - -} - -class PKECPropertyStoreTestWithoutDebuggingMaxEvalDepth0AndSeqTaskManager - extends PKECPropertyStoreTestWithoutDebugging { - - def createPropertyStore(): PKECPropertyStore = { - val ps = PKECPropertyStore("Seq", 0)() - ps.suppressError = true - ps - } - -} - -/*class PKECPropertyStoreTestWithoutDebuggingMaxEvalDepth1AndParTaskManager - extends PKECPropertyStoreTestWithoutDebugging { +class PKECPropertyStoreTestWithoutDebugging + extends AbstractPKECPropertyStoreTestWithoutDebugging { def createPropertyStore(): PKECPropertyStore = { - val ps = PKECPropertyStore("Par", 1)() + val ps = new PKECPropertyStore(Map.empty) ps.suppressError = true ps } -}*/ +} \ No newline at end of file diff --git a/OPAL/si/src/test/scala/org/opalj/fpcf/par/YAPPSTest.scala b/OPAL/si/src/test/scala/org/opalj/fpcf/par/YAPPSTest.scala deleted file mode 100644 index 13ffe51022..0000000000 --- a/OPAL/si/src/test/scala/org/opalj/fpcf/par/YAPPSTest.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package par - -abstract class YAPPSTestWithDebugging - extends PropertyStoreTestWithDebugging[YAPPS] { - - override def afterAll(ps: YAPPS): Unit = { - // TODO Basic smoke test? - } -} - -class YAPPSTestWithDebuggingMaxEvalDepthDefault - extends YAPPSTestWithDebugging { - - def createPropertyStore(): YAPPS = { - val ps = new YAPPS(Map.empty) - ps.suppressError = true - ps - } - -} - -// ************************************************************************************************* -// ************************************* NO DEBUGGING ********************************************** -// ************************************************************************************************* - -abstract class YAPPSTestWithoutDebugging - extends PropertyStoreTestWithoutDebugging[YAPPS] - -class YAPPSTestWithoutDebuggingMaxEvalDepth128AndSeqTaskManager - extends YAPPSTestWithoutDebugging { - - def createPropertyStore(): YAPPS = { - val ps = new YAPPS(Map.empty) - 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 From ca212470066a81f52c3dbb846c6fe243f355c8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Wed, 19 Feb 2020 16:28:33 +0100 Subject: [PATCH 093/327] removed dht store --- .../org/opalj/fpcf/par/DHTPropertyStore.scala | 1099 ----------------- .../opalj/fpcf/par/DHTPropertyStoreTest.scala | 42 - 2 files changed, 1141 deletions(-) delete mode 100644 OPAL/si/src/main/scala/org/opalj/fpcf/par/DHTPropertyStore.scala delete mode 100644 OPAL/si/src/test/scala/org/opalj/fpcf/par/DHTPropertyStoreTest.scala diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/par/DHTPropertyStore.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/par/DHTPropertyStore.scala deleted file mode 100644 index 5454dbd0a4..0000000000 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/par/DHTPropertyStore.scala +++ /dev/null @@ -1,1099 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org -package opalj -package fpcf -package par - -import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.ConcurrentLinkedQueue - -import scala.collection.mutable.AnyRefMap -import scala.collection.mutable.{Set ⇒ MutableSet} -import scala.collection.mutable.ArrayBuffer -import scala.collection.mutable.PriorityQueue -import scala.collection.mutable.Queue -import scala.util.control.ControlThrowable - -import org.opalj.log.LogContext -import org.opalj.fpcf.PropertyKey.fallbackPropertyBasedOnPKId - -/** - * Yet another parallel property store. - * Based on the ideas of a distributed hash table, computations for an entity are bound to a - * specific thread. - * - * @author Dominik Helm - */ -class DHTPropertyStore( - final val ctx: Map[Class[_], AnyRef] -)( - implicit - val logContext: LogContext -) extends ParallelPropertyStore { - propertyStore ⇒ - - val THREAD_COUNT = 1 - - private[this] val ps: Array[Array[AnyRefMap[Entity, DhtEpkState]]] = - Array.fill(THREAD_COUNT) { - Array.fill(PropertyKind.SupportedPropertyKinds) { - AnyRefMap.empty - } - } - - private[this] val dependers: Array[AnyRefMap[SomeEPK, MutableSet[SomeEPK]]] = - Array.fill(THREAD_COUNT) { AnyRefMap.empty } - - private[this] val tasks: Array[Array[ConcurrentLinkedQueue[DhtTask]]] = - Array.fill(THREAD_COUNT) { - Array.fill(THREAD_COUNT) { - new ConcurrentLinkedQueue - } - } - - //TODO Need Priority Queue here - private[this] val localTasks: Array[PriorityQueue[DhtContinuationTask]] = - Array.fill(THREAD_COUNT) { - new PriorityQueue()({ - (x: DhtContinuationTask, y: DhtContinuationTask) ⇒ - x.priority - y.priority - - }) - } - - private[this] val fastQueues: Array[Queue[DhtTask]] = - Array.fill(THREAD_COUNT) { new Queue() } - - private[this] val activeThreads: AtomicInteger = new AtomicInteger(THREAD_COUNT) - - private[this] val threads: Array[Thread] = new Array(THREAD_COUNT) - - private[this] val triggeredComputations: Array[Array[SomePropertyComputation]] = - new Array(PropertyKind.SupportedPropertyKinds) - - private[this] var setAndPreinitializedValues: List[SomeEPK] = List.empty - - override def MaxEvaluationDepth: Int = 0 //TODO - override def shutdown(): Unit = {} - var idle: Boolean = true - override def isIdle: Boolean = idle - - // -------------------------------------------------------------------------------------------- - // - // STATISTICS - // - // -------------------------------------------------------------------------------------------- - - private[this] var quiescenceCounter = 0 - override def quiescenceCount: Int = quiescenceCounter - - private[this] val scheduledTasks: Array[Int] = Array.fill(THREAD_COUNT) { 0 } - override def scheduledTasksCount: Int = scheduledTasks.sum - - private[this] val scheduledOnUpdateComputations: Array[Int] = Array.fill(THREAD_COUNT) { 0 } - override def scheduledOnUpdateComputationsCount: Int = scheduledOnUpdateComputations.sum - - override def fallbacksUsedForComputedPropertiesCount: Int = 0 //TODO - - override private[fpcf] def incrementFallbacksUsedForComputedPropertiesCounter(): Unit = { - // ??? - } - - // -------------------------------------------------------------------------------------------- - // - // BASIC QUERY METHODS (ONLY TO BE CALLED WHEN THE STORE IS QUIESCENT) - // - // -------------------------------------------------------------------------------------------- - - override def toString(printProperties: Boolean): String = - if (printProperties) { - var pkId = 0 - while (pkId <= PropertyKey.maxId) { - ps.iterator.flatMap(_(pkId).valuesIterator.map { - _.eOptP.toString.replace("\n", "\n\t") - }).toList.sorted.mkString(s"Entities for property key $pkId:\n\t", "\n\t", "\n") - pkId += 1 - } - ps.mkString("PropertyStore(\n\t", "\n\t", "\n)") - } else { - s"PropertyStore(properties=${ps.iterator.flatMap(_.map(_.size)).sum})" - } - - override def entities(propertyFilter: SomeEPS ⇒ Boolean): Iterator[Entity] = { - ps.iterator.flatMap { propertiesPerThread ⇒ - propertiesPerThread.iterator.flatMap { propertiesPerKind ⇒ - propertiesPerKind.valuesIterator.filter { epkState ⇒ - propertyFilter(epkState.eOptP.asEPS) - } - } - }.map(_.eOptP.e) - } - - override def entities[P <: Property](pk: PropertyKey[P]): Iterator[EPS[Entity, P]] = { - ps.iterator.flatMap { propertiesPerThread ⇒ - propertiesPerThread(pk.id).valuesIterator.map(_.eOptP.asInstanceOf[EPS[Entity, P]]) - } - } - - override def entities[P <: Property](lb: P, ub: P): Iterator[Entity] = { - entities { eps ⇒ eps.lb == lb && eps.ub == ub } - } - - override def entitiesWithLB[P <: Property](lb: P): Iterator[Entity] = { - entities { eps ⇒ eps.lb == lb } - } - - override def entitiesWithUB[P <: Property](ub: P): Iterator[Entity] = { - entities { eps ⇒ eps.ub == ub } - } - - override def properties[E <: Entity](e: E): Iterator[EPS[E, Property]] = { - ps(getResponsibleTId(e)).iterator.flatMap { propertiesPerKind ⇒ - val ePKState = propertiesPerKind.get(e) - if (ePKState.exists(_.eOptP.isEPS)) - Iterator.single(ePKState.get.eOptP.asInstanceOf[EPS[E, Property]]) - else - Iterator.empty - } - } - - override def hasProperty(e: Entity, pk: PropertyKind): Boolean = { - val ePKState = ps(getResponsibleTId(e))(pk.id).get(e) - ePKState.exists { state ⇒ - state.eOptP.hasUBP || state.eOptP.hasLBP - } - } - - override def isKnown(e: Entity): Boolean = { - ps(getResponsibleTId(e)).exists { propertiesPerKind ⇒ - propertiesPerKind.contains(e) - } - } - - override def get[E <: Entity, P <: Property](e: E, pk: PropertyKey[P]): Option[EOptionP[E, P]] = { - ps(getResponsibleTId(e))(pk.id).get(e).map(_.eOptP.asInstanceOf[EOptionP[E, P]]) - } - - override def get[E <: Entity, P <: Property](epk: EPK[E, P]): Option[EOptionP[E, P]] = { - get(epk.e, epk.pk) - } - - // -------------------------------------------------------------------------------------------- - // - // CORE IMPLEMENTATION - NOT THREAD SAFE PART - // - // -------------------------------------------------------------------------------------------- - - override protected[this] def doScheduleEagerComputationForEntity[E <: Entity]( - e: E - )(pc: PropertyComputation[E]): Unit = { - schedulePropertyComputation(e, pc, 0) - } - - override protected[this] def doRegisterTriggeredComputation[E <: Entity, P <: Property]( - pk: PropertyKey[P], - pc: PropertyComputation[E] - ): Unit = { - - // Recall that the scheduler has to take care of registering a triggered computation - // before the first analysis derives a respective value! - // Hence, there is no need to immediately check that we have to trigger a computation. - - val pkId = pk.id - val oldComputations: Array[SomePropertyComputation] = triggeredComputations(pkId) - var newComputations: Array[SomePropertyComputation] = null - - if (oldComputations == null) { - newComputations = Array[SomePropertyComputation](pc) - } else { - newComputations = java.util.Arrays.copyOf(oldComputations, oldComputations.length + 1) - newComputations(oldComputations.length) = pc - } - triggeredComputations(pkId) = newComputations - } - - override protected[this] def doSet(e: Entity, p: Property): Unit = { - val epkState = DhtEpkState(FinalEP(e, p), null, null) - - val oldP = ps(getResponsibleTId(e))(p.id).put(e, epkState) - if (oldP.isDefined) { - throw new IllegalStateException(s"$e already had the property $oldP") - } - setAndPreinitializedValues ::= EPK(e, p.key) - } - - override protected[this] def doPreInitialize[E <: Entity, P <: Property]( - e: E, - pk: PropertyKey[P] - )(pc: EOptionP[E, P] ⇒ InterimEP[E, P]): Unit = { - val pkId = pk.id - val propertiesOfKind = ps(getResponsibleTId(e))(pkId) - val oldEPKState = propertiesOfKind.get(e) - val newInterimEP: SomeInterimEP = - oldEPKState match { - case None ⇒ - val epk = EPK(e, pk) - setAndPreinitializedValues ::= epk - pc(epk) - case Some(epkState) ⇒ - pc(epkState.eOptP.asInstanceOf[EOptionP[E, P]]) - } - assert(newInterimEP.isRefinable) - val newEPKState = DhtEpkState(newInterimEP, null, null) - propertiesOfKind.put(e, newEPKState) - } - - // -------------------------------------------------------------------------------------------- - // - // CORE IMPLEMENTATION - THREAD SAFE PART - // - // -------------------------------------------------------------------------------------------- - - override def force[E <: Entity, P <: Property](e: E, pk: PropertyKey[P]): Unit = { - doApply(EPK(e, pk), e, pk.id) - } - - override def execute(f: ⇒ Unit): Unit = { - submitTask(new DhtExecuteTask(f), 0 /* TODO: Use some fair distribution */ , currentTId()) - } - - private[this] def schedulePropertyComputation[E <: Entity]( - e: E, - pc: PropertyComputation[E], - tId: Int - ): Unit = { - submitTask(new DhtPropertyComputationTask(e, pc), getResponsibleTId(e), tId) - } - - private[this] def currentTId(): Int = { - Thread.currentThread() match { - case DhtThread(id) ⇒ id - case _ ⇒ 0 - } - } - - private[this] val fakeEntities: Array[Int] = Array.fill(THREAD_COUNT) { 0 } - - override def handleResult(r: PropertyComputationResult): Unit = { - doHandleResult(r) - } - - private[this] def doHandleResult(r: PropertyComputationResult, origin: DhtEpkState = null): 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 { doHandleResult(_, origin) } - - case IncrementalResult.id ⇒ - val IncrementalResult(ir, npcs) = r - doHandleResult(ir, origin) - npcs /*: Iterator[(PropertyComputation[e],e)]*/ foreach { npc ⇒ - val (pc, e) = npc - val tId = currentTId() - schedulePropertyComputation(e, pc, tId) - } - - // - // 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(r, u, e, pk) - - case InterimPartialResult.id ⇒ - val InterimPartialResult(prs, dependees, c) = r - - prs foreach { pr ⇒ - handlePartialResult( - pr, - pr.u.asInstanceOf[SomeEOptionP ⇒ Option[SomeInterimEP]], - pr.e, - pr.pk - ) - } - - val ownTId = currentTId() - - /*if((origin ne null) && origin.eOptP.e.isInstanceOf[DhtFakeEntity]){ - val epk = origin.eOptP.asEPK - val e = epk.e - - val epkState = DhtEpkState( - epk, - { dependee: SomeEPS ⇒ - val result = c(dependee) - - ps(ownTId)(AnalysisKeyId).remove(e) - - val ds = dependers(ownTId) - dependees.foreach { dependee ⇒ - ds(dependee.toEPK) -= epk - } - - result - }, - dependees - ) - - ps(ownTId)(AnalysisKeyId).put(epk, epkState) - - updateDependees(epk, dependees, Some(origin), ownTId) - } else {*/ - val e = new DhtFakeEntity(ownTId + THREAD_COUNT * fakeEntities(ownTId)) - fakeEntities(ownTId) += 1 - val epk = EPK(e, AnalysisKey) - - val epkState = DhtEpkState( - epk, - { dependee: SomeEPS ⇒ - val result = c(dependee) - - ps(ownTId)(AnalysisKeyId).remove(e) - - val ds = dependers(ownTId) - dependees.foreach { dependee ⇒ - ds(dependee.toEPK) -= epk - } - - result - }, - dependees - ) - - ps(ownTId)(AnalysisKeyId).put(e, epkState) - - updateDependees(epk, dependees, None, ownTId) - //} - } - } - - private[this] def notifyDependers( - dependee: SomeEPS, - ownTId: Int, - unnotifiedPKs: Set[PropertyKind] = Set.empty - ): Unit = { - var tId = 0 - while (tId < THREAD_COUNT) { - submitTask(new DhtNotifyDependersTask(dependee, unnotifiedPKs), tId, ownTId) - tId += 1 - } - } - - private[this] def triggerComputations(e: Entity, pkId: Int, tId: Int): Unit = { - val computations = triggeredComputations(pkId) - if (computations ne null) { - computations foreach { pc ⇒ - schedulePropertyComputation(e, pc.asInstanceOf[PropertyComputation[Entity]], tId) - } - } - } - - private[this] def handleFinalResult( - finalEP: FinalEP[Entity, Property], - unnotifiedPKs: Set[PropertyKind] = Set.empty - ): Unit = { - val epkState = DhtEpkState(finalEP, null, null) - - val FinalEP(e, p) = finalEP - val ownTId = currentTId() - val targetTId = getResponsibleTId(e) - - if (targetTId != ownTId) { - submitTask(new DhtSetTask(e, finalEP, p.id), targetTId, ownTId) - return ; - } - - val oldPO = ps(ownTId)(p.id).put(e, epkState) - - if (oldPO.isDefined) { - val oldP = oldPO.get - - if (debug) { - if (oldP.eOptP.isFinal) { - throw new IllegalStateException(s"$e already had the property $oldP") - } else { - oldP.eOptP.checkIsValidPropertiesUpdate(finalEP, Nil) - } - } - - val dependees = oldP.dependees - if (dependees ne null) { - val ds = dependers(ownTId) - dependees.foreach { dependee ⇒ - val theDependers = ds.get(dependee.toEPK) - if (theDependers.isDefined) theDependers.get -= finalEP.toEPK - } - } - - if (oldP.eOptP.isEPK) - triggerComputations(e, p.id, ownTId) - } else { - triggerComputations(e, p.id, ownTId) - } - - notifyDependers(finalEP, ownTId, unnotifiedPKs) - } - - private[this] def handleInterimResult( - interimEP: InterimEP[Entity, _ >: Null <: Property], - c: ProperOnUpdateContinuation, - dependees: Traversable[SomeEOptionP] - ): Unit = { - val epkState = DhtEpkState(interimEP, c, dependees) - - val SomeEPS(e, p) = interimEP - val ownTId = currentTId() - val targetTId = getResponsibleTId(e) - - if (targetTId != ownTId) { - submitTask( - new DhtPropertyComputationTask( - e, - { _: Entity ⇒ InterimResult(interimEP, dependees, c) } - ), - targetTId, - ownTId - ) - return ; - } - - val oldPO = ps(ownTId)(p.id).put(e, epkState) - - if (oldPO.isDefined) { - val oldP = oldPO.get - - if (debug) { - oldPO.get.eOptP.checkIsValidPropertiesUpdate(interimEP, dependees) - } - - if (oldP.eOptP.isEPK) - triggerComputations(e, p.id, ownTId) - } else { - triggerComputations(e, p.id, ownTId) - } - - updateDependees(interimEP.toEPK, dependees, oldPO, ownTId) - - if (oldPO.isEmpty || interimEP.isUpdatedComparedTo(oldPO.get.eOptP)) - notifyDependers(interimEP, ownTId) - } - - private[this] def updateDependees(depender: SomeEPK, dependees: Traversable[SomeEOptionP], oldPO: Option[DhtEpkState], ownTId: Int): Unit = { - val oldDependees: Set[SomeEOptionP] = if (oldPO.isDefined) oldPO.get.dependees.toSet else Set.empty - var newDependees: Set[SomeEOptionP] = Set.empty - - val ds = dependers(ownTId) - dependees.foreach { dependee ⇒ - if (oldDependees.contains(dependee)) { - //oldDependees -= dependee - } else { - ds.getOrElseUpdate(dependee.toEPK, MutableSet.empty) += depender - newDependees += dependee - } - } - - //println(s"${dependees.size} vs ${newDependees.size} vs ${oldDependees.size}") - - //TODO Drop old status as depender for removed dependees? - - val suppressedPKs = suppressInterimUpdates(depender.pk.id) - var updateTasks: List[DhtTask] = List.empty - val hasKnownUpdate = newDependees.exists { dependee ⇒ - val dependeeState = ps(ownTId)(dependee.pk.id).get(dependee.e) - dependeeState match { - case Some(DhtEpkState(eOptP, _, _)) if (eOptP ne dependee) && eOptP.isEPS && (eOptP.isFinal || !suppressedPKs(eOptP.pk.id)) ⇒ - updateTasks = List(new DhtContinuationTask(depender.e, depender.pk.id, dependees.size, eOptP.asEPS)) - true - case _ ⇒ - updateTasks ::= new DhtNotifyIfUpdatedTask(depender, dependees.size, dependee) - false - } - } - - if (hasKnownUpdate) { - submitTask(updateTasks.head, ownTId, ownTId) - } else { - updateTasks.foreach { task ⇒ - val targetTId = getResponsibleTId(task.asInstanceOf[DhtNotifyIfUpdatedTask].dependee.e) - if (targetTId != ownTId) - submitTask(task, targetTId, ownTId) - } - } - } - - private[this] def handlePartialResult( - pr: PropertyComputationResult, - getResult: (SomeEOptionP ⇒ Option[SomeInterimEP]), - e: Entity, - pk: SomePropertyKey - ): Unit = { - val ownTId = currentTId() - val targetTId = getResponsibleTId(e) - - if (targetTId != ownTId) { - submitTask( - new DhtPropertyComputationTask( - e, - { _: Entity ⇒ pr } - ), - targetTId, - ownTId - ) - return ; - } - - val oldPO = ps(ownTId)(pk.id).get(e) - - val oldEOptP = if (oldPO.isDefined) { - oldPO.get.eOptP - } else { - EPK(e, pk) - } - - if (oldEOptP.isFinal) { - throw new IllegalStateException(s"$e already had the property ${oldEOptP.asFinal.p}") - } - - val result = getResult(oldEOptP) - - if (result.isDefined) { - ps(ownTId)(pk.id).put(e, DhtEpkState(result.get, null, null)) - if (oldEOptP.isEPK) - triggerComputations(e, pk.id, ownTId) - - notifyDependers(result.get, ownTId) - } - } - - override protected[this] def doApply[E <: Entity, P <: Property]( - epk: EPK[E, P], - e: E, - pkId: Int - ): EOptionP[E, P] = { - //TODO always return current state? - val targetTId = getResponsibleTId(e) - val ownTId = currentTId() - if (targetTId == ownTId || isIdle) { - val ePKState = ps(targetTId)(pkId).get(e) - if (ePKState.isDefined) - ePKState.get.eOptP.asInstanceOf[EOptionP[E, P]] - else - handleUnknownEPK(epk, e, pkId, targetTId, ownTId) - } else { - handleUnknownEPK(epk, e, pkId, targetTId, ownTId) - } - } - - private[this] def handleUnknownEPK[E <: Entity, P <: Property]( - epk: EPK[E, P], - e: E, - pkId: Int, - targetTId: Int, - sourceTId: Int - ): EOptionP[E, P] = { - val lazyComputation = lazyComputations(pkId) - if (lazyComputation ne null) { - submitTask( - new DhtLazyComputationTask( - e, - pkId, - lazyComputation.asInstanceOf[E ⇒ PropertyComputationResult] - ), - targetTId, - sourceTId - ) - epk - } 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) - ps(targetTId)(pkId).put(e, DhtEpkState(result, null, null)) - triggerComputations(e, pkId, sourceTId) - result.asInstanceOf[FinalEP[E, P]] - } else { - ps(targetTId)(pkId).put(e, DhtEpkState(epk, d ⇒ new Result(transformer._2(e, d.asFinal.p)), Some(dependee))) - updateDependees(epk, Some(dependee), None, targetTId) - epk - } - } else { - submitTask(new DhtSetInitialTask(e, epk, pkId), targetTId, sourceTId) - epk - } - } else { - val finalEP = computeFallback[E, P](e, pkId) - submitTask(new DhtSetInitialTask(e, finalEP, pkId), targetTId, sourceTId) - finalEP - } - } - - private[this] def startThreads(thread: Int ⇒ DhtThread): Unit = { - var tId = 0 - while (tId < THREAD_COUNT) { - val t = thread(tId) - threads(tId) = t - tId += 1 - } - threads.foreach { _.start() } - threads.foreach { _.join } - if (doTerminate) { - if (exception ne null) throw exception - else throw new InterruptedException - } - } - - private[this] def hasActiveThreads: Boolean = { - var tId = 0 - while (tId < THREAD_COUNT) { - if (isActive(tId).get()) { - return true; - } - tId += 1 - } - false - } - - override def waitOnPhaseCompletion(): Unit = { - idle = false - - // If some values were explicitly set, we have to trigger corresponding triggered - // computations. - setAndPreinitializedValues.foreach { epk ⇒ triggerComputations(epk.e, epk.pk.id, 0) } - setAndPreinitializedValues = List.empty - - while (subPhaseId < subPhaseFinalizationOrder.length) { - var continueCycles = false - do { - var continueFallbacks = false - do { - activeThreads.set(THREAD_COUNT) - startThreads(new DhtWorkerThread(_)) - - quiescenceCounter += 1 - - startThreads(new DhtFallbackThread(_)) - - continueFallbacks = hasActiveThreads - } while (continueFallbacks) - - startThreads(new DhtCycleResolutionThread(_)) - - resolveCycles() - - continueCycles = hasActiveThreads - } while (continueCycles) - - startThreads(new DhtPartialPropertiesFinalizerThread(_)) - - subPhaseId += 1 - } - - idle = true - } - - private[this] def resolveCycles(): Unit = { - val theInterimStates = new ArrayBuffer[DhtEpkState](interimStates.iterator.map(_.size).sum) - var tId = 0 - while (tId < THREAD_COUNT) { - theInterimStates ++= interimStates(tId) - tId += 1 - } - - val theSuccessors = (interimEPKState: DhtEpkState) ⇒ { - successors(getResponsibleTId(interimEPKState.eOptP.e))(interimEPKState) - } - - val cSCCs = graphs.closedSCCs(theInterimStates, theSuccessors) - - for (cSCC ← cSCCs) { - for (interimEPKState ← cSCC) { - val dependees = interimEPKState.dependees - val epk = interimEPKState.eOptP.toEPK - val e = epk.e - val targetTId = getResponsibleTId(e) - dependees.foreach { dependee ⇒ - dependers(targetTId)(dependee.toEPK) -= epk - } - submitTask( - new DhtSetTask( - e, - interimEPKState.eOptP.toFinalEP, - epk.pk.id - ), - targetTId, - 0 - ) - } - } - } - - private[this] val isActive: Array[AtomicBoolean] = Array.fill(THREAD_COUNT)(new AtomicBoolean(true)) - - private[this] def getResponsibleTId(e: Entity): Int = { - Math.abs(e.hashCode() >> 5) % THREAD_COUNT - } - - private[this] val interimStates: Array[ArrayBuffer[DhtEpkState]] = - Array.fill(THREAD_COUNT)(null) - private[this] val successors: Array[DhtEpkState ⇒ Traversable[DhtEpkState]] = - Array.fill(THREAD_COUNT)(null) - - private[this] def submitTask(task: DhtTask, targetTId: Int, sourceTId: Int): Unit = { - if (targetTId == sourceTId) { - if (task.isInstanceOf[DhtContinuationTask]) { - val ct = task.asInstanceOf[DhtContinuationTask] - localTasks(sourceTId).enqueue(ct) - val lastUpdate = nextUpdate(sourceTId).put((ct.dependee.toEPK, ct.dependerE, ct.dependerPKId), ct) - lastUpdate.foreach(_.perform = false) - } else { - fastQueues(sourceTId).enqueue(task) - } - isActive(targetTId).set(true) - } else { - tasks(targetTId)(sourceTId).offer(task) - val targetThread = threads(targetTId) - val wasActive = isActive(targetTId).getAndSet(true) - if (!wasActive) { - targetThread synchronized { - targetThread.notifyAll() - } - } - } - } - - //TODO this is a slow hack to prevent reordered continuation task messing up the state - private[this] val nextUpdate: Array[AnyRefMap[(SomeEPK, Entity, Int), DhtContinuationTask]] = Array.fill(THREAD_COUNT)(AnyRefMap.empty) - - class DhtThread(val ownTId: Int, name: String) extends Thread(name) - - object DhtThread { - def unapply(t: DhtThread): Some[Int] = { - Some(t.ownTId) - } - } - - class DhtWorkerThread(ownTId: Int) extends DhtThread(ownTId, s"PropertyStoreThread-#$ownTId") { - - private[this] val localQueue: PriorityQueue[DhtContinuationTask] = localTasks(ownTId) - private[this] val localQueues: Array[ConcurrentLinkedQueue[DhtTask]] = tasks(ownTId) - private[this] val fastQueue = fastQueues(ownTId) - - override def run(): Unit = { - try { - while (!doTerminate) { - consolidateQueues() - if (localQueue.isEmpty && fastQueue.isEmpty) { - val active = activeThreads.decrementAndGet() - if (active == 0) { - var continue = false - var tId = 0 - while (tId < THREAD_COUNT) { - if (tId != ownTId && isActive(tId).get()) { - continue = true - } - tId += 1 - } - consolidateQueues() - if (!continue && localQueue.isEmpty && fastQueue.isEmpty) { - threads.foreach { t ⇒ - if (t ne this) - t.interrupt() - } - isActive(ownTId).set(false) - return ; - } - activeThreads.incrementAndGet() - } else { - val t = Thread.currentThread() - t synchronized { - isActive(ownTId).set(false) //TODO does this have to be later? - consolidateQueues() - if (localQueue.isEmpty && fastQueue.isEmpty) { - try { - t.wait() - } catch { - case _: InterruptedException ⇒ return ; - } - } else { - isActive(ownTId).set(true) - } - } - activeThreads.incrementAndGet() - } - } else { - while (fastQueue.nonEmpty && !doTerminate) - fastQueue.dequeue().apply() - if (localQueue.nonEmpty) - localQueue.dequeue().apply() - } - } - } 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() - } - } - } - - private[this] def consolidateQueues(): Unit = { - var tId = 0 - val ownNextUpdates = nextUpdate(ownTId) - while (tId < THREAD_COUNT) { - val queue = localQueues(tId) - var t: DhtTask = null - while ({ t = queue.poll(); t ne null }) { - if (t.isInstanceOf[DhtContinuationTask]) { - val ct = t.asInstanceOf[DhtContinuationTask] - localQueue += ct - val lastUpdate = ownNextUpdates.put((ct.dependee.toEPK, ct.dependerE, ct.dependerPKId), ct) - lastUpdate.foreach(_.perform = false) - } else { - fastQueue.enqueue(t) - } - } - - tId += 1 - } - } - } - - class DhtFallbackThread(ownTId: Int) extends DhtThread(ownTId, s"PropertyStoreFallbackThread-#$ownTId") { - - override def run(): Unit = { - val localStore = ps(ownTId) - - var pkId = 0 - while (pkId <= PropertyKey.maxId) { - if (propertyKindsComputedInThisPhase(pkId)) { - localStore(pkId).valuesIterator.foreach { oldState ⇒ - if (oldState.eOptP.isEPK && ((oldState.dependees eq null) || oldState.dependees.isEmpty)) { - val e = oldState.eOptP.e - val reason = PropertyIsNotDerivedByPreviouslyExecutedAnalysis - val p = fallbackPropertyBasedOnPKId(propertyStore, reason, e, pkId) - val finalEP = FinalEP(e, p) - incrementFallbacksUsedForComputedPropertiesCounter() - handleFinalResult(finalEP) - } - } - } - pkId += 1 - } - } - } - - class DhtCycleResolutionThread(ownTId: Int) extends DhtThread(ownTId, s"PropertyStoreCycleResolutionThread-#$ownTId") { - - override def run(): Unit = { - val localInterimStates = ArrayBuffer.empty[DhtEpkState] - var pkId = 0 - while (pkId <= PropertyKey.maxId) { - if (propertyKindsComputedInThisPhase(pkId)) { - ps(ownTId)(pkId).valuesIterator.foreach { epkState ⇒ - if (epkState.eOptP.isRefinable) localInterimStates += epkState - } - } - pkId += 1 - } - interimStates(ownTId) = localInterimStates - - successors(ownTId) = (interimEPKState: DhtEpkState) ⇒ { - val dependees = interimEPKState.dependees - if (dependees != null) { - interimEPKState.dependees.map { eOptionP ⇒ - val tId = getResponsibleTId(eOptionP.e) - ps(tId)(eOptionP.pk.id)(eOptionP.e) - } - } else { - Traversable.empty - } - } - } - } - - class DhtPartialPropertiesFinalizerThread(ownTId: Int) extends DhtThread(ownTId, s"PropertyStorePartialPropertiesFinalizerThread-#$ownTId") { - - override def run(): Unit = { - val pksToFinalize = subPhaseFinalizationOrder(subPhaseId).toSet - - pksToFinalize foreach { pk ⇒ - val pkId = pk.id - ps(ownTId)(pkId).valuesIterator.foreach { epkState ⇒ - val eOptP = epkState.eOptP - if (eOptP.isRefinable && !eOptP.isEPK) //TODO Won't be required once subPhaseFinalizationOrder is reliably only the partial properties - handleFinalResult(eOptP.toFinalEP, pksToFinalize) - } - } - } - } - - trait DhtTask extends (() ⇒ Unit) { - val priority = 1000000 - } - - class DhtExecuteTask(f: ⇒ Unit) extends DhtTask { - override def apply(): Unit = { - f - } - } - - class DhtSetTask[E <: Entity, P <: Property](e: E, finalEP: FinalEP[E, P], pkId: Int) extends DhtTask { - override def apply(): Unit = { - val ownTId = currentTId() - val oldStateO = ps(ownTId)(pkId).put(e, DhtEpkState(finalEP, null, null)) - notifyDependers(finalEP, ownTId) - if (oldStateO.isEmpty) triggerComputations(e, pkId, ownTId) - } - } - - class DhtSetInitialTask[E <: Entity, P <: Property](e: E, eOptP: EOptionP[E, P], pkId: Int) extends DhtTask { - override def apply(): Unit = { - val ownTId = currentTId() - val newState = DhtEpkState(eOptP, null, null) - val epkState = ps(ownTId)(pkId).getOrElseUpdate(e, newState) - if (!eOptP.isEPK && (epkState eq newState)) { - notifyDependers(eOptP.asEPS, ownTId) - triggerComputations(e, pkId, ownTId) - } - } - } - - class DhtPropertyComputationTask[E <: Entity](e: E, pc: PropertyComputation[E]) extends DhtTask { - scheduledTasks(currentTId()) += 1 - - override def apply(): Unit = { - handleResult(pc(e)) - } - } - - class DhtLazyComputationTask[E <: Entity](e: E, pkId: Int, pc: PropertyComputation[E]) extends DhtTask { - scheduledTasks(currentTId()) += 1 - - override def apply(): Unit = { - val tId = currentTId() - val epkState = ps(tId)(pkId).get(e) - if (epkState.isEmpty) - handleResult(pc(e)) - } - } - - class DhtContinuationTask(val dependerE: Entity, val dependerPKId: Int, val dependeeCount: Int, val dependee: SomeEPS) extends DhtTask { - override val priority = dependeeCount - - var perform = true - - scheduledOnUpdateComputations(currentTId()) += 1 - - override def apply(): Unit = { - if (perform) { - val ownTId = currentTId() - /*if (ownTId != getResponsibleTId(dependee.e)) - ps(ownTId)(dependee.pk.id)(dependee.e) = DhtEpkState(dependee, null, null)*/ - val eSO = ps(ownTId)(dependerPKId).get(dependerE) - if (eSO.isDefined) { - val epkState = eSO.get - val dependeeEPK = dependee.toEPK - if (epkState.dependees != null && epkState.dependees.exists(_.toEPK == dependeeEPK)) { - //val ds = dependers(ownTId) - //ds(dependeeEPK) -= epkState.eOptP.toEPK - // TODO maybe get current state of dependee here? - doHandleResult(epkState.c(dependee), epkState) - } - } - } - } - } - - class DhtNotifyIfUpdatedTask(depender: SomeEPK, val dependeeCount: Int, val dependee: SomeEOptionP) extends DhtTask { - override def apply(): Unit = { - val dependeeTId = currentTId() - val dependerTId = getResponsibleTId(depender.e) - val e = dependee.e - val pk = dependee.pk - val pkId = pk.id - val ePKState = ps(dependeeTId)(pkId).get(e) - if (ePKState.isEmpty) { - handleUnknownEPK(EPK(e, pk), e, pkId, dependeeTId, dependerTId) - } else { - val eOptP = ePKState.get.eOptP - if ((eOptP ne dependee) && eOptP.isEPS) { - val continuationTask = new DhtContinuationTask(depender.e, depender.pk.id, dependeeCount, eOptP.asEPS) - assert(dependeeTId != dependerTId) - submitTask( - continuationTask, - dependerTId, - dependeeTId - ) - } - } - } - } - - class DhtNotifyDependersTask(val dependee: SomeEPS, val unnotifiedPKs: Set[PropertyKind]) extends DhtTask { - override def apply(): Unit = { - val ownTId = currentTId() - val ds = dependers(ownTId) - val theDependers = ds.get(dependee.toEPK) - if (theDependers.isDefined) { - theDependers.get.foreach { depender ⇒ - val EOptionP(e, pk) = depender - if (!unnotifiedPKs.contains(pk) && (dependee.isFinal || !suppressInterimUpdates(depender.pk.id)(dependee.pk.id))) { - submitTask(new DhtContinuationTask(e, pk.id, 0, dependee), ownTId, ownTId) - } - } - } - } - } -} - -object DHTPropertyStore extends PropertyStoreFactory[DHTPropertyStore] { - - final val MaxEvaluationDepthKey = "org.opalj.fpcf.par.PKECPropertyStore.MaxEvaluationDepth" - - def apply( - context: PropertyStoreContext[_ <: AnyRef]* - )( - implicit - logContext: LogContext - ): DHTPropertyStore = { - val contextMap: Map[Class[_], AnyRef] = context.map(_.asTuple).toMap - - val ps = new DHTPropertyStore(contextMap) - ps - } -} - -case class DhtEpkState(eOptP: SomeEOptionP, c: OnUpdateContinuation, dependees: Traversable[SomeEOptionP]) - -class DhtFakeEntity(override val hashCode: Int) \ No newline at end of file diff --git a/OPAL/si/src/test/scala/org/opalj/fpcf/par/DHTPropertyStoreTest.scala b/OPAL/si/src/test/scala/org/opalj/fpcf/par/DHTPropertyStoreTest.scala deleted file mode 100644 index b8163f6599..0000000000 --- a/OPAL/si/src/test/scala/org/opalj/fpcf/par/DHTPropertyStoreTest.scala +++ /dev/null @@ -1,42 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org -package opalj -package fpcf -package par - -abstract class DHTPropertyStoreTestWithDebugging - extends PropertyStoreTestWithDebugging[DHTPropertyStore] { - - override def afterAll(ps: DHTPropertyStore): Unit = { - // TODO Basic smoke test? - } -} - -class DHTPropertyStoreTestWithDebuggingMaxEvalDepthDefault - extends DHTPropertyStoreTestWithDebugging { - - def createPropertyStore(): DHTPropertyStore = { - val ps = new DHTPropertyStore(Map.empty) - ps.suppressError = true - ps - } - -} - -// ************************************************************************************************* -// ************************************* NO DEBUGGING ********************************************** -// ************************************************************************************************* - -abstract class DHTPropertyStoreTestWithoutDebugging - extends PropertyStoreTestWithoutDebugging[DHTPropertyStore] - -class DHTPropertyStoreTestWithoutDebuggingMaxEvalDepth128AndSeqTaskManager - extends DHTPropertyStoreTestWithoutDebugging { - - def createPropertyStore(): DHTPropertyStore = { - val ps = new DHTPropertyStore(Map.empty) - ps.suppressError = true - ps - } - -} \ No newline at end of file From 8de798370c07e72d3164e7c2cb93070bdf07a35c Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Fri, 21 Feb 2020 10:23:48 +0100 Subject: [PATCH 094/327] Removed @volatile, the locks suffice --- .../main/scala/org/opalj/fpcf/par/PKECPropertyStore.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 8a31253514..3715ef8633 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 @@ -736,9 +736,9 @@ class PKECPropertyStore( } case class EPKState( - @volatile var eOptP: SomeEOptionP, - @volatile var c: OnUpdateContinuation, - @volatile var dependees: Traversable[SomeEOptionP], + var eOptP: SomeEOptionP, + var c: OnUpdateContinuation, + var dependees: Traversable[SomeEOptionP], dependers: java.util.HashSet[SomeEPK] = new java.util.HashSet(), suppressedDependers: java.util.HashSet[SomeEPK] = new java.util.HashSet() ) { From 87879aab6e84126e553a28688301a8dfa84aca79 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 21 Feb 2020 14:20:08 +0100 Subject: [PATCH 095/327] handling extending generic types --- .../L0FieldImmutabilityAnalysis.scala | 531 +++++++++--------- 1 file changed, 273 insertions(+), 258 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index c13fe39947..b1707e1083 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -49,15 +49,16 @@ import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS case class State(f: Field) { - var field: Field = f - var typeImmutability: Option[Boolean] = None - var referenceImmutability: Option[Boolean] = None - var dependentImmutability: Option[DependentImmutabilityKind] = None + var field: Field = f + var typeImmutability: Option[Boolean] = None + var referenceImmutability: Option[Boolean] = None + var dependentImmutability: Option[DependentImmutabilityKind] = None + var genericTypeSetNotDeepImmutable = false } object DependentImmutabilityKind extends Enumeration { - type DependentImmutabilityKind = Value - val dependent, nowShallowOrMutable, onlyDeepImmutable = Value + type DependentImmutabilityKind = Value + val dependent, nowShallowOrMutable, onlyDeepImmutable = Value } /** @@ -72,263 +73,277 @@ object DependentImmutabilityKind extends Enumeration { class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - final val typeExtensibility = project.get(TypeExtensibilityKey) - final val closedPackages = project.get(ClosedPackagesKey) - final val fieldAccessInformation = project.get(FieldAccessInformationKey) - - def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { - entity match { - case field: Field => - determineFieldImmutability(field) - case _ => - val m = entity.getClass.getName + "is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } - } - - private[analyses] def determineFieldImmutability( - field: Field - ): ProperPropertyComputationResult = { - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - var classFormalTypeParameters: Option[Set[String]] = None - def loadFormalTypeparameters() = { - var result: Set[String] = Set.empty - field.classFile.attributes.foreach( - x => - x match { - case SourceFile(_) => - case ClassSignature(typeParameters, _, _) => - typeParameters.foreach( - y => - y match { - case FormalTypeParameter(identifier, _, _) => result += identifier - case _ => - } - ) - case _ => - } - ) - if (result.size > 0) - classFormalTypeParameters = Some(result) - } - def isInClassesGenericTypeParameters(string: String): Boolean = { - if (classFormalTypeParameters == None) - false - else - classFormalTypeParameters.head.contains(string) + final val typeExtensibility = project.get(TypeExtensibilityKey) + final val closedPackages = project.get(ClosedPackagesKey) + final val fieldAccessInformation = project.get(FieldAccessInformationKey) + + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { + entity match { + case field: Field ⇒ + determineFieldImmutability(field) + case _ ⇒ + val m = entity.getClass.getName+"is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } } - def handleTypeImmutability(state: State) = { - val objectType = field.fieldType.asFieldType - if (objectType.isArrayType) { - state.typeImmutability = Some(true) - } else if (objectType.isBaseType) { - state.typeImmutability = Some(true); - } else { - val result = propertyStore(objectType, TypeImmutability_new.key) - dependencies = dependencies.filter(_.e ne result.e) - result match { - case FinalEP(e, DeepImmutableType) => { - state.typeImmutability = Some(true); - } - case FinalEP(f, DependentImmutableType) => { - if (dependencies.size > 0) - state.typeImmutability = None + private[analyses] def determineFieldImmutability( + field: Field + ): ProperPropertyComputationResult = { + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + var classFormalTypeParameters: Option[Set[String]] = None + def loadFormalTypeparameters() = { + var result: Set[String] = Set.empty + field.classFile.attributes.foreach( + x ⇒ + x match { + case SourceFile(_) ⇒ + case ClassSignature(typeParameters, _, _) ⇒ + typeParameters.foreach( + y ⇒ + y match { + case FormalTypeParameter(identifier, _, _) ⇒ result += identifier + case _ ⇒ + } + ) + case _ ⇒ + } + ) + if (result.size > 0) + classFormalTypeParameters = Some(result) + } + def isInClassesGenericTypeParameters(string: String): Boolean = { + if (classFormalTypeParameters == None) + false else - state.typeImmutability = Some(false) - } - case FinalEP(e, ShallowImmutableType | MutableType_new) => { - state.typeImmutability = Some(false) - } - case ep: InterimEP[e, p] => dependencies += ep - case epk @ _ => dependencies += epk + classFormalTypeParameters.head.contains(string) } - } - } - def hasGenericType(state: State): Unit = { - var flag_noShallow = true - var flag_onlyDeep = true - var genericFields: List[ObjectType] = List() - state.field.asField.attributes.foreach( - _ match { - case RuntimeInvisibleAnnotationTable(_) => - case TypeVariableSignature(t) => - flag_onlyDeep = false - if (!isInClassesGenericTypeParameters(t)) { - flag_noShallow = false + def handleTypeImmutability(state: State) = { + val objectType = field.fieldType.asFieldType + if (objectType.isArrayType) { + state.typeImmutability = Some(true) + } else if (objectType.isBaseType) { + state.typeImmutability = Some(true); + } else { + val result = propertyStore(objectType, TypeImmutability_new.key) + dependencies = dependencies.filter(_.e ne result.e) + result match { + case FinalEP(e, DeepImmutableType) ⇒ { + state.typeImmutability = Some(true); + } + case FinalEP(f, DependentImmutableType) ⇒ { + if (dependencies.size > 0) + state.typeImmutability = None + else + state.typeImmutability = Some(false) + } + case FinalEP(e, ShallowImmutableType | MutableType_new) ⇒ { + state.typeImmutability = Some(false) + if (state.field.fieldType.isObjectType && + state.field.fieldType.asObjectType != ObjectType("java/lang/Object")) { + state.dependentImmutability = None //when the generic type is still final + state.genericTypeSetNotDeepImmutable = true + } + } + case ep: InterimEP[e, p] ⇒ dependencies += ep + case epk @ _ ⇒ dependencies += epk + } } + } - case ClassTypeSignature( - packageIdentifier, - SimpleClassTypeSignature(simpleName, typeArguments), - _ - ) => - typeArguments - .foreach({ + def hasGenericType(state: State): Unit = { + var flag_noShallow = true + var flag_onlyDeep = true + var genericFields: List[ObjectType] = List() + state.field.asField.attributes.foreach( _ match { - case ProperTypeArgument( - variance, - TypeVariableSignature(identifier: String) - ) => - flag_onlyDeep = false - if (!isInClassesGenericTypeParameters(identifier)) { - flag_noShallow = false - } - case ProperTypeArgument( - varianceIndicator, - ClassTypeSignature( - packageIdentifier1, - SimpleClassTypeSignature( - packageIdentifier2, - typeArguments2 - ), + case RuntimeInvisibleAnnotationTable(_) ⇒ + case TypeVariableSignature(t) ⇒ + flag_onlyDeep = false + if (!isInClassesGenericTypeParameters(t)) { + flag_noShallow = false + } + + case ClassTypeSignature( + packageIdentifier, + SimpleClassTypeSignature(simpleName, typeArguments), _ - ) - ) => { - val oPath = - packageIdentifier1 match { - case Some(pid1) => pid1 + packageIdentifier2 - case _ => packageIdentifier2 - } - genericFields = ObjectType(oPath) :: genericFields - } - case _ => - flag_noShallow = false - flag_onlyDeep = false + ) ⇒ + typeArguments + .foreach({ + _ match { + case ProperTypeArgument( + variance, + TypeVariableSignature(identifier: String) + ) ⇒ + flag_onlyDeep = false + if (!isInClassesGenericTypeParameters(identifier)) { + flag_noShallow = false + } + case ProperTypeArgument( + varianceIndicator, + ClassTypeSignature( + packageIdentifier1, + SimpleClassTypeSignature( + packageIdentifier2, + typeArguments2 + ), + _ + ) + ) ⇒ { + val oPath = + packageIdentifier1 match { + case Some(pid1) ⇒ pid1 + packageIdentifier2 + case _ ⇒ packageIdentifier2 + } + genericFields = ObjectType(oPath) :: genericFields + } + case _ ⇒ + flag_noShallow = false + flag_onlyDeep = false + } + }) + + case _ ⇒ + flag_noShallow = false + flag_onlyDeep = false + } + ) + genericFields.foreach(objectType ⇒ { + + val result = propertyStore(objectType, TypeImmutability_new.key) + dependencies = dependencies.filter(_.e ne result.e) + result match { + case FinalP(DeepImmutableType) ⇒ + case FinalP(ShallowImmutableType | MutableType_new) ⇒ { + flag_noShallow = false + flag_onlyDeep = false + } + case ep: InterimEP[e, p] ⇒ dependencies += ep + case ep @ _ ⇒ + dependencies += ep } - }) - - case _ => - flag_noShallow = false - flag_onlyDeep = false - } - ) - genericFields.foreach(objectType => { - - val result = propertyStore(objectType, TypeImmutability_new.key) - dependencies = dependencies.filter(_.e ne result.e) - result match { - case FinalP(DeepImmutableType) => - case FinalP(ShallowImmutableType | MutableType_new) => { - flag_noShallow = false - flag_onlyDeep = false - } - case ep: InterimEP[e, p] => dependencies += ep - case ep @ _ => - dependencies += ep - } - }) + }) - if (state.field.asField.attributes.size == state.field.asField.attributes - .collect({ case x @ RuntimeInvisibleAnnotationTable(_) => x }) - .size) { - flag_noShallow = false - flag_onlyDeep = false - } + if (state.field.asField.attributes.size == state.field.asField.attributes + .collect({ case x @ RuntimeInvisibleAnnotationTable(_) ⇒ x }) + .size) { + flag_noShallow = false + flag_onlyDeep = false + } - if (flag_onlyDeep) - state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) - else if (flag_noShallow) - state.dependentImmutability = Some(DependentImmutabilityKind.nowShallowOrMutable) - } + if (flag_onlyDeep) + state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) + else if (flag_noShallow) + state.dependentImmutability = Some(DependentImmutabilityKind.nowShallowOrMutable) + } - def createResult(state: State): ProperPropertyComputationResult = { - state.referenceImmutability match { - case Some(false) | None => - Result(field, MutableField) - case Some(true) => { - state.typeImmutability match { - case Some(true) => - Result(field, DeepImmutableField) - case Some(false) | None => { - state.dependentImmutability match { - case Some(DependentImmutabilityKind.nowShallowOrMutable) => - Result(field, DependentImmutableField) - case Some(DependentImmutabilityKind.onlyDeepImmutable) => - Result(field, DeepImmutableField) - case _ => Result(field, ShallowImmutableField) - } + def createResult(state: State): ProperPropertyComputationResult = { + state.referenceImmutability match { + case Some(false) | None ⇒ + Result(field, MutableField) + case Some(true) ⇒ { + state.typeImmutability match { + case Some(true) ⇒ + Result(field, DeepImmutableField) + case Some(false) | None ⇒ { + if (state.genericTypeSetNotDeepImmutable) + Result(field, ShallowImmutableField) + else + state.dependentImmutability match { + case Some(DependentImmutabilityKind.nowShallowOrMutable) ⇒ + Result(field, DependentImmutableField) + case Some(DependentImmutabilityKind.onlyDeepImmutable) ⇒ + Result(field, DeepImmutableField) + case _ ⇒ Result(field, ShallowImmutableField) + } + } + } + } } - } } - } - } - def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - dependencies = dependencies.filter(_.e ne eps.e) - (eps: @unchecked) match { - case x: InterimEP[_, _] => { - dependencies += eps - InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) - } - case FinalEP(_, DeepImmutableType) => state.typeImmutability = Some(true) - case FinalEP(_, MutableType_new | ShallowImmutableType) => - state.typeImmutability = Some(false) - case FinalEP(f, DependentImmutableType) => { - state.typeImmutability = Some(false) - if (state.dependentImmutability == None) - state.dependentImmutability = Some(DependentImmutabilityKind.dependent) - } - case x @ FinalEP(_, MutableReference) => { - state.referenceImmutability = Some(false) + def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { + dependencies = dependencies.filter(_.e ne eps.e) + (eps: @unchecked) match { + case x: InterimEP[_, _] ⇒ { + dependencies += eps + InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + } + case FinalEP(_, DeepImmutableType) ⇒ state.typeImmutability = Some(true) + case FinalEP(t, MutableType_new | ShallowImmutableType) ⇒ + state.typeImmutability = Some(false) + if (t == state.field.fieldType && + state.field.fieldType.isObjectType && + state.field.fieldType.asObjectType != ObjectType("java/lang/Object")) { + state.dependentImmutability = None + state.genericTypeSetNotDeepImmutable = true + } + case FinalEP(f, DependentImmutableType) ⇒ { + state.typeImmutability = Some(false) + if (state.dependentImmutability == None) + state.dependentImmutability = Some(DependentImmutabilityKind.dependent) + } + case x @ FinalEP(_, MutableReference) ⇒ { + state.referenceImmutability = Some(false) + } + case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ { //TODO + state.referenceImmutability = Some(true) + } + case x @ _ ⇒ + dependencies = dependencies + x + } + if (dependencies.isEmpty) createResult(state) + else + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) } - case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) => { //TODO - state.referenceImmutability = Some(true) + val state: State = new State(field) + + val result = propertyStore(state.field, ReferenceImmutability.key) + result match { + case FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ + state.referenceImmutability = Some(true) + case FinalP(MutableReference) ⇒ return Result(field, MutableField) + case x @ _ ⇒ { + dependencies += x + } } - case x @ _ => - dependencies = dependencies + x - } - if (dependencies.isEmpty) createResult(state) - else - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - } - val state: State = new State(field) - - val result = propertyStore(state.field, ReferenceImmutability.key) - result match { - case FinalEP(_, ImmutableReference | LazyInitializedReference) => - state.referenceImmutability = Some(true) - case FinalP(MutableReference) => return Result(field, MutableField) - case x @ _ => { - dependencies += x - } - } - loadFormalTypeparameters() - hasGenericType(state) - handleTypeImmutability(state) - if (dependencies.isEmpty) - createResult(state) - else - InterimResult( - field, - MutableField, - DeepImmutableField, - dependencies, - c(state) - ) - - } + loadFormalTypeparameters() + hasGenericType(state) + handleTypeImmutability(state) + if (dependencies.isEmpty) + createResult(state) + else + InterimResult( + field, + MutableField, + DeepImmutableField, + dependencies, + c(state) + ) + + } } trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.lub(TypeImmutability_new), - PropertyBounds.lub(FieldImmutability) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.lub(TypeImmutability_new), + PropertyBounds.lub(FieldImmutability) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) } @@ -339,16 +354,16 @@ object EagerL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -358,19 +373,19 @@ object LazyL0FieldImmutabilityAnalysis extends L0FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - FieldImmutability.key, - analysis.determineFieldImmutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0FieldImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + FieldImmutability.key, + analysis.determineFieldImmutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } From b444e8764bc3185a5e9acdc36aaa6c08261a9b3e Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 21 Feb 2020 14:20:58 +0100 Subject: [PATCH 096/327] removing workaround for running on jdk --- .../LxClassImmutabilityAnalysis_new.scala | 853 +++++++++--------- 1 file changed, 428 insertions(+), 425 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 6cbfa831e2..80d92aff90 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -73,7 +73,7 @@ import org.opalj.br.fpcf.properties.ShallowImmutableField * */ class LxClassImmutabilityAnalysis_new(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. @@ -82,326 +82,327 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal * 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_new //MutableObject - ): MultiResult = { - //println("I") - 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] = { - // println("J") - 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 ::= ( - ( - determineClassImmutability_new(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, MutableClass) //MutableObjectDueToUnknownSupertypes) - } + /** + * 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_new //MutableObject + ): MultiResult = { + //println("I") + val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) + val r = allSubtypes.map { st ⇒ + new FinalEP(st, immutability) + }.toSeq + MultiResult(r) } - IncrementalResult(Results(results), nextComputations.iterator) - } - - def determineGenericTypeBounds(classFile: ClassFile): Set[(String, String)] = { - // println("K") - var genericTypeBounds: Set[(String, String)] = Set.empty - classFile.attributes.toList.collectFirst({ - case ClassSignature(typeParameters, _, _) => - typeParameters - .collect({ - case ftp @ FormalTypeParameter(identifier, classBound, interfaceBound) => ftp - }) - .foreach({ - case FormalTypeParameter(identifier, classBound, interfaceBound) => - classBound match { - case Some( - ClassTypeSignature( - packageIdentifier, - SimpleClassTypeSignature(simpleName, typeArguments), - classTypeSignatureSuffix + @inline private[this] def createIncrementalResult( + t: ObjectType, + cfMutability: EOptionP[Entity, Property], + cfMutabilityIsFinal: Boolean, + result: ProperPropertyComputationResult + ): IncrementalResult[ClassFile] = { + // println("J") + 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 ::= ( + ( + determineClassImmutability_new(t, cfMutability, cfMutabilityIsFinal, false) _, + scf + ) ) - ) => { - genericTypeBounds += ((identifier, simpleName)) //+ typeBounds - } - case _ => - } - - }) - }) - genericTypeBounds - } - - def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { - //println("L") - e match { - case t: ObjectType => - //this is safe - val a = classHierarchy.superclassType(t) - a match { - case None => Result(t, MutableClass); //MutableObjectDueToUnknownSupertypes) - case Some(superClassType) => - val cf = project.classFile(t) match { - case None => - return Result(t, MutableClass); // MutableObjectByAnalysis) //TODO consider other lattice element - case Some(cf) => cf - } - - propertyStore(superClassType, ClassImmutability_new.key) match { - case UBP(MutableClass) => //MutableObject) ⇒ - Result(t, MutableClass) - case eps: EPS[ObjectType, ClassImmutability_new] => - determineClassImmutability_new( - superClassType, - eps, - eps.isFinal, - lazyComputation = true - )(cf) - case epk => - determineClassImmutability_new( - superClassType, - epk, - superClassMutabilityIsFinal = false, - lazyComputation = true - )(cf) + 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) //MutableObjectDueToUnknownSupertypes) } } - case _ => - val m = e.getClass.getSimpleName + " is not an org.opalj.br.ObjectType" - throw new IllegalArgumentException(m) + IncrementalResult(Results(results), nextComputations.iterator) } - } - 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 determineClassImmutability_new( - superClassType: ObjectType, - superClassInformation: EOptionP[Entity, Property], - superClassMutabilityIsFinal: Boolean, - lazyComputation: Boolean - )( - cf: ClassFile - ): ProperPropertyComputationResult = { - //println("M") - val t = cf.thisType - - var dependees = Map.empty[Entity, EOptionP[Entity, Property]] - - if (!superClassMutabilityIsFinal) { - dependees += (SuperClassKey -> superClassInformation) + def determineGenericTypeBounds(classFile: ClassFile): Set[(String, String)] = { + // println("K") + var genericTypeBounds: Set[(String, String)] = Set.empty + classFile.attributes.toList.collectFirst({ + case ClassSignature(typeParameters, _, _) ⇒ + typeParameters + .collect({ + case ftp @ FormalTypeParameter(identifier, classBound, interfaceBound) ⇒ ftp + }) + .foreach({ + case FormalTypeParameter(identifier, classBound, interfaceBound) ⇒ + classBound match { + case Some( + ClassTypeSignature( + packageIdentifier, + SimpleClassTypeSignature(simpleName, typeArguments), + classTypeSignatureSuffix + ) + ) ⇒ { + genericTypeBounds += ((identifier, simpleName)) //+ typeBounds + } + case _ ⇒ + } + + }) + }) + genericTypeBounds } - // Collect all fields for which we need to determine the effective mutability! - var hasFieldsWithUnknownMutability = false - - val instanceFields = cf.fields.filter { f => - !f.isStatic - } - var hasShallowImmutableFields = false - var hasDependentImmutableFields = false - - val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) - - //println("start") - //println(superClassType.simpleName) - fieldsPropertyStoreInformation.foreach( - f => { - //println("A") - //println(f) - f match { - case FinalP(MutableField) => { - if (lazyComputation) - return Result(t, MutableClass); - else - return createResultForAllSubtypes(t, MutableClass); - } - case FinalP(ShallowImmutableField) => hasShallowImmutableFields = true - case FinalEP(fi, 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); + def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { + //println("L") + e match { + case t: ObjectType ⇒ + //this is safe + val a = classHierarchy.superclassType(t) + a match { + case None ⇒ Result(t, MutableClass); //MutableObjectDueToUnknownSupertypes) + case Some(superClassType) ⇒ + val cf = project.classFile(t) match { + case None ⇒ + return Result(t, MutableClass); // MutableObjectByAnalysis) //TODO consider other lattice element + case Some(cf) ⇒ cf + } + + propertyStore(superClassType, ClassImmutability_new.key) match { + case UBP(MutableClass) ⇒ //MutableObject) ⇒ + Result(t, MutableClass) + case eps: EPS[ObjectType, ClassImmutability_new] ⇒ + determineClassImmutability_new( + superClassType, + eps, + eps.isFinal, + lazyComputation = true + )(cf) + case epk ⇒ + determineClassImmutability_new( + superClassType, + epk, + superClassMutabilityIsFinal = false, + lazyComputation = true + )(cf) + } + } + case _ ⇒ + val m = e.getClass.getSimpleName+" is not an org.opalj.br.ObjectType" + throw new IllegalArgumentException(m) } - } - ) - //println("B") - //println(superClassMutabilityIsFinal) - var minLocalImmutability: ClassImmutability_new = - if (!superClassMutabilityIsFinal) { - MutableClass //MutableObjectByAnalysis - - } else { - ShallowImmutableClass //ImmutableContainer - } - - //println("C") - //println(superClassInformation) - // NOTE: maxLocalImmutability does not take the super classes' mutability into account! - var maxLocalImmutability: ClassImmutability_new = superClassInformation match { - case UBP(ShallowImmutableClass) => ShallowImmutableClass //ImmutableContainer - case _ => DeepImmutableClass // ImmutableObject - } - //println("D") - //println(hasShallowImmutableFields) - if (hasShallowImmutableFields) { - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } - //if (!dependentImmutableFields.isEmpty && maxLocalImmutability != ShallowImmutableClass) { - if (hasDependentImmutableFields && maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { - //println("E") - maxLocalImmutability = DependentImmutableClass - } + 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 determineClassImmutability_new( + superClassType: ObjectType, + superClassInformation: EOptionP[Entity, Property], + superClassMutabilityIsFinal: Boolean, + lazyComputation: Boolean + )( + cf: ClassFile + ): ProperPropertyComputationResult = { + //println("M") + val t = cf.thisType + + var dependees = Map.empty[Entity, EOptionP[Entity, Property]] + + if (!superClassMutabilityIsFinal) { + dependees += (SuperClassKey -> superClassInformation) + } - 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. - //println("F") - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - } + // Collect all fields for which we need to determine the effective mutability! + var hasFieldsWithUnknownMutability = false - 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) - //println("G") - if (lazyComputation) - return Result(t, maxLocalImmutability); - - return createIncrementalResult( - t, - FinalEP(t, maxLocalImmutability), - cfMutabilityIsFinal = true, - Result(t, maxLocalImmutability) - ); - } + val instanceFields = cf.fields.filter { f ⇒ + !f.isStatic + } + var hasShallowImmutableFields = false + var hasDependentImmutableFields = false + + val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) + + //println("start") + //println(superClassType.simpleName) + fieldsPropertyStoreInformation.foreach( + f ⇒ { + //println("A") + //println(f) + f match { + case FinalP(MutableField) ⇒ { + if (lazyComputation) + return Result(t, MutableClass); + else + return createResultForAllSubtypes(t, MutableClass); + } + case FinalP(ShallowImmutableField) ⇒ hasShallowImmutableFields = true + case FinalEP(fi, 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); + } + } + ) + //println("B") + //println(superClassMutabilityIsFinal) + ///_var minLocalImmutability: ClassImmutability_new = + ///_ if (!superClassMutabilityIsFinal) { + ///_ MutableClass //MutableObjectByAnalysis + + ///_ } else { + ///_ ShallowImmutableClass //ImmutableContainer + ///_ } + var minLocalImmutability: ClassImmutability_new = MutableClass + + //println("C") + //println(superClassInformation) + // NOTE: maxLocalImmutability does not take the super classes' mutability into account! + var maxLocalImmutability: ClassImmutability_new = superClassInformation match { + case UBP(ShallowImmutableClass) ⇒ ShallowImmutableClass //ImmutableContainer + case _ ⇒ DeepImmutableClass // ImmutableObject + } + //println("D") + //println(hasShallowImmutableFields) + if (hasShallowImmutableFields) { + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + } - def c(someEPS: SomeEPS): ProperPropertyComputationResult = { - //println("H") - //[DEBUG] - val oldDependees = dependees - dependees = dependees.filter(_._1 ne someEPS.e) - someEPS match { - // Superclass related dependencies: - // - case UBP(MutableClass) => // MutableObject) ⇒ - return Result(t, MutableClass); //MutableObjectByAnalysis); - - case LBP(DeepImmutableClass) => //_:ImmutableObject) ⇒ // the super class - dependees -= SuperClassKey - - case UBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is at most immutable container - if (someEPS.isFinal) dependees -= SuperClassKey - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - //dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) //TypeImmutability.key) - - case LBP(ShallowImmutableClass) => //ImmutableContainer) ⇒ // super class is a least immutable container - if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && - !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - - case LUBP(MutableClass, DeepImmutableClass) => //_: MutableObject, ImmutableObject) ⇒ // No information about superclass - - case FinalEP(f, DependentImmutableField) => { - if (hasShallowImmutableFields && maxLocalImmutability != MutableClass) { - maxLocalImmutability = ShallowImmutableClass - } else if (maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { + //if (!dependentImmutableFields.isEmpty && maxLocalImmutability != ShallowImmutableClass) { + if (hasDependentImmutableFields && maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { + //println("E") maxLocalImmutability = DependentImmutableClass - } + } - //} else - // maxLocalImmutability = DependentImmutableClass // DependentImmutableClass //TODO possibly here? + 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. + //println("F") + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } - // Field Mutability related dependencies: - // - case FinalP(DeepImmutableField) => - case FinalP(ShallowImmutableField) => { - 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) + //println("G") + if (lazyComputation) + return Result(t, maxLocalImmutability); + + return createIncrementalResult( + t, + FinalEP(t, maxLocalImmutability), + cfMutabilityIsFinal = true, + Result(t, maxLocalImmutability) + ); } - case FinalP(MutableField) => return Result(t, MutableClass); - case UBP(MutableField) => //_: NonFinalField) ⇒ - return Result(t, MutableClass); //MutableObjectByAnalysis); + def c(someEPS: SomeEPS): ProperPropertyComputationResult = { + //println("H") + //[DEBUG] + //val oldDependees = dependees + dependees = dependees.filter(_._1 ne someEPS.e) + someEPS match { + // Superclass related dependencies: + // + case UBP(MutableClass) ⇒ // MutableObject) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); + + case LBP(DeepImmutableClass) ⇒ //_:ImmutableObject) ⇒ // the super class + dependees -= SuperClassKey + + case UBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is at most immutable container + if (someEPS.isFinal) dependees -= SuperClassKey + maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + //dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) //TypeImmutability.key) + + case LBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is a least immutable container + if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && + !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + + case LUBP(MutableClass, DeepImmutableClass) ⇒ //_: MutableObject, ImmutableObject) ⇒ // No information about superclass + + case FinalEP(f, DependentImmutableField) ⇒ { + if (hasShallowImmutableFields && maxLocalImmutability != MutableClass) { + maxLocalImmutability = ShallowImmutableClass + } else if (maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { + maxLocalImmutability = DependentImmutableClass + } + + //} else + // maxLocalImmutability = DependentImmutableClass // DependentImmutableClass //TODO possibly here? + } + + // Field Mutability related dependencies: + // + case FinalP(DeepImmutableField) ⇒ + case FinalP(ShallowImmutableField) ⇒ { + maxLocalImmutability = ShallowImmutableClass + } + case FinalP(MutableField) ⇒ return Result(t, MutableClass); - case ELBP(e, ShallowImmutableField | DeepImmutableField) => //FinalField) => - dependees -= e - if (minLocalImmutability != ShallowImmutableClass) // && // ImmutableContainer && - //!dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + case UBP(MutableField) ⇒ //_: NonFinalField) ⇒ + return Result(t, MutableClass); //MutableObjectByAnalysis); - case UBP(ShallowImmutableField | DeepImmutableField) => //_: FinalField) ⇒ // no information about field mutability + case ELBP(e, ShallowImmutableField | DeepImmutableField) ⇒ //FinalField) => + dependees -= e + if (minLocalImmutability != ShallowImmutableClass) // && // ImmutableContainer && + //!dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - case _ => Result(t, MutableClass) //TODO check + case UBP(ShallowImmutableField | DeepImmutableField) ⇒ //_: FinalField) ⇒ // no information about field mutability - } + case _ ⇒ Result(t, MutableClass) //TODO check - if (someEPS.isRefinable) { - val entity = if (someEPS.pk == ClassImmutability_new.key) SuperClassKey else someEPS.e - dependees += (entity -> someEPS) - } + } - /*[DEBUG] + if (someEPS.isRefinable) { + val entity = if (someEPS.pk == ClassImmutability_new.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" ) */ - // Lift lower bound once no dependencies other than field type mutabilities are left - if (minLocalImmutability != ShallowImmutableClass) // && //ImmutableContainer && - //dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer - if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { - /*[DEBUG] + // Lift lower bound once no dependencies other than field type mutabilities are left + if (minLocalImmutability != ShallowImmutableClass) // && //ImmutableContainer && + //dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) + minLocalImmutability = ShallowImmutableClass //ImmutableContainer + if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { + /*[DEBUG] assert( maxLocalImmutability == ConditionallyImmutableObject || maxLocalImmutability == ImmutableObject @@ -420,121 +421,123 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal ) */ - Result(t, maxLocalImmutability) - - } else { - //println("c interim") - //println("min: "+minLocalImmutability) - //println("max: "+maxLocalImmutability) - //println(dependees.values) - if (oldDependees == dependees) { - Result(t, minLocalImmutability) - } else - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - } - } + Result(t, maxLocalImmutability) + + } else { + //println("c interim") + //println("min: "+minLocalImmutability) + //println("max: "+maxLocalImmutability) + //println(dependees.values) + /** + * if (oldDependees == dependees) { + * Result(t, minLocalImmutability) + * } else* + */ + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + } + } - //[DEBUG] assert(initialImmutability.isRefinable) - - val result = - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - if (lazyComputation) - result - else { - val isFinal = dependees.isEmpty - createIncrementalResult( - t, - EPS(t, minLocalImmutability, maxLocalImmutability), - isFinal, - result - ) + //[DEBUG] assert(initialImmutability.isRefinable) + + val result = + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + if (lazyComputation) + result + else { + val isFinal = dependees.isEmpty + createIncrementalResult( + t, + EPS(t, minLocalImmutability, maxLocalImmutability), + isFinal, + result + ) + } } - } } trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) - - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, FieldImmutability) //TypeImmutability, FieldImmutability) - - 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 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.map(cf => set(cf.thisType, DeepImmutableClass)) //ImmutableObject)) - - // 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) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, FieldImmutability) //TypeImmutability, FieldImmutability) + + 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 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.map(cf ⇒ set(cf.thisType, DeepImmutableClass)) //ImmutableObject)) + + // 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 } - // 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 = {} + 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 = {} } /** @@ -546,22 +549,22 @@ object EagerLxClassImmutabilityAnalysis_new extends ClassImmutabilityAnalysisScheduler_new 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 LxClassImmutabilityAnalysis_new(p) - ps.scheduleEagerComputationsForEntities(cfs)( - analysis.determineClassImmutability_new( - superClassType = null, - FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), - superClassMutabilityIsFinal = true, - lazyComputation = false - ) - ) - analysis - } + 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 LxClassImmutabilityAnalysis_new(p) + ps.scheduleEagerComputationsForEntities(cfs)( + analysis.determineClassImmutability_new( + superClassType = null, + FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), + superClassMutabilityIsFinal = true, + lazyComputation = false + ) + ) + analysis + } } /** @@ -573,18 +576,18 @@ object LazyLxClassImmutabilityAnalysis_new extends ClassImmutabilityAnalysisScheduler_new with FPCFLazyAnalysisScheduler { - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - - override def register( - p: SomeProject, - ps: PropertyStore, - unused: InitializationData - ): FPCFAnalysis = { - val analysis = new LxClassImmutabilityAnalysis_new(p) - ps.registerLazyPropertyComputation( - ClassImmutability_new.key, - analysis.doDetermineClassImmutability_new - ) - analysis - } + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + override def register( + p: SomeProject, + ps: PropertyStore, + unused: InitializationData + ): FPCFAnalysis = { + val analysis = new LxClassImmutabilityAnalysis_new(p) + ps.registerLazyPropertyComputation( + ClassImmutability_new.key, + analysis.doDetermineClassImmutability_new + ) + analysis + } } From eff812957d67c97a08e34b8ef7b580532283d1fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Thu, 27 Feb 2020 13:00:32 +0100 Subject: [PATCH 097/327] also clear dependees for interim partial results --- .../org/opalj/fpcf/par/PKECPropertyStore.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 3715ef8633..36bcde5d06 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 @@ -305,9 +305,9 @@ class PKECPropertyStore( { dependee: SomeEPS ⇒ val result = c(dependee) - ps(AnalysisKeyId).remove(e) + val state = ps(AnalysisKeyId).remove(e) + state.dependees = null - //println(s"interim partial result remove dependees (${dependees} from depender: $epk") dependees.foreach { dependee ⇒ ps(dependee.pk.id).get(dependee.e).removeDepender(epk) } @@ -736,11 +736,11 @@ class PKECPropertyStore( } case class EPKState( - var eOptP: SomeEOptionP, - var c: OnUpdateContinuation, - var dependees: Traversable[SomeEOptionP], - dependers: java.util.HashSet[SomeEPK] = new java.util.HashSet(), - suppressedDependers: java.util.HashSet[SomeEPK] = new java.util.HashSet() + var eOptP: SomeEOptionP, + var c: OnUpdateContinuation, + var dependees: Traversable[SomeEOptionP], + dependers: java.util.HashSet[SomeEPK] = new java.util.HashSet(), + suppressedDependers: java.util.HashSet[SomeEPK] = new java.util.HashSet() ) { def setFinal(finalEP: FinalEP[Entity, Property], unnotifiedPKs: Set[PropertyKind])(implicit ps: PKECPropertyStore): Unit = { From 168bb523725fe3e872d04ef01c14e3084e928e99 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Fri, 28 Feb 2020 14:29:52 +0100 Subject: [PATCH 098/327] Use multiple queues to avoid contention --- .../org/opalj/fpcf/par/PKECPropertyStore.scala | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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 36bcde5d06..de9caaf4e6 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 @@ -36,11 +36,12 @@ class PKECPropertyStore( val ps: Array[ConcurrentHashMap[Entity, EPKState]] = Array.fill(PropertyKind.SupportedPropertyKinds) { new ConcurrentHashMap() } - val tasks: PriorityBlockingQueue[QualifiedTask] = new PriorityBlockingQueue() - private[this] val triggeredComputations: Array[Array[SomePropertyComputation]] = new Array(PropertyKind.SupportedPropertyKinds) + private[this] val queues: Array[PriorityBlockingQueue[QualifiedTask]] = + Array.fill(THREAD_COUNT) { new PriorityBlockingQueue() } + private[this] var setAndPreinitializedValues: List[SomeEPK] = List.empty override def shutdown(): Unit = {} @@ -222,7 +223,7 @@ class PKECPropertyStore( private[par] def scheduleTask(task: QualifiedTask): Unit = { activeTasks.incrementAndGet() scheduledTasks.incrementAndGet() - tasks.offer(task) + queues(getResponsibleTId(task)).offer(task) } private[this] def schedulePropertyComputation[E <: Entity]( @@ -486,14 +487,14 @@ class PKECPropertyStore( startThreads(new FallbackThread(_)) - continueFallbacks = !tasks.isEmpty + continueFallbacks = !queues.forall(_.isEmpty) } while (continueFallbacks) startThreads(new CycleResolutionThread(_)) resolveCycles() - continueCycles = !tasks.isEmpty + continueCycles = !queues.forall(_.isEmpty) } while (continueCycles) startThreads(new PartialPropertiesFinalizerThread(_)) @@ -543,6 +544,8 @@ class PKECPropertyStore( class WorkerThread(ownTId: Int) extends PKECThread(s"PropertyStoreThread-#$ownTId") { + val tasks = queues(ownTId) + override def run(): Unit = { try { while (!doTerminate) { From a59629b29880ebe5eb4b9020125fb66de0c76755 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 5 Mar 2020 19:49:11 +0100 Subject: [PATCH 099/327] class immutability analysis optimization --- .../LxClassImmutabilityAnalysis_new.scala | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 80d92aff90..883af1e199 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -248,7 +248,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal else return createResultForAllSubtypes(t, MutableClass); } - case FinalP(ShallowImmutableField) ⇒ hasShallowImmutableFields = true + case FinalP(ShallowImmutableField) ⇒ + hasShallowImmutableFields = true case FinalEP(fi, DependentImmutableField) ⇒ { hasDependentImmutableFields = true } @@ -271,21 +272,25 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal ) //println("B") //println(superClassMutabilityIsFinal) - ///_var minLocalImmutability: ClassImmutability_new = - ///_ if (!superClassMutabilityIsFinal) { - ///_ MutableClass //MutableObjectByAnalysis - - ///_ } else { - ///_ ShallowImmutableClass //ImmutableContainer - ///_ } + /** + * var minLocalImmutability: ClassImmutability_new = + * if (!superClassMutabilityIsFinal) { + * MutableClass //MutableObjectByAnalysis + * + * } else { + * ShallowImmutableClass //ImmutableContainer + * } * + */ var minLocalImmutability: ClassImmutability_new = MutableClass //println("C") //println(superClassInformation) // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability_new = superClassInformation match { - case UBP(ShallowImmutableClass) ⇒ ShallowImmutableClass //ImmutableContainer - case _ ⇒ DeepImmutableClass // ImmutableObject + case UBP(MutableClass) ⇒ MutableClass + case UBP(ShallowImmutableClass) ⇒ ShallowImmutableClass //ImmutableContainer + case UBP(DependentImmutableClass) ⇒ DependentImmutableClass + case _ ⇒ DeepImmutableClass // ImmutableObject } //println("D") //println(hasShallowImmutableFields) @@ -352,11 +357,20 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case LUBP(MutableClass, DeepImmutableClass) ⇒ //_: MutableObject, ImmutableObject) ⇒ // No information about superclass case FinalEP(f, DependentImmutableField) ⇒ { - if (hasShallowImmutableFields && maxLocalImmutability != MutableClass) { + if (hasShallowImmutableFields) { maxLocalImmutability = ShallowImmutableClass - } else if (maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { + } else if (maxLocalImmutability != MutableClass && maxLocalImmutability != ShallowImmutableClass) { maxLocalImmutability = DependentImmutableClass } + /** + * if (hasShallowImmutableFields && maxLocalImmutability != MutableClass) { + * maxLocalImmutability = ShallowImmutableClass + * } else if (maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { + * maxLocalImmutability = DependentImmutableClass + * } else + * maxLocalImmutability = ShallowImmutableClass + * }* + */ //} else // maxLocalImmutability = DependentImmutableClass // DependentImmutableClass //TODO possibly here? @@ -379,9 +393,12 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal //!dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - case UBP(ShallowImmutableField | DeepImmutableField) ⇒ //_: FinalField) ⇒ // no information about field mutability + case UBP(DeepImmutableField) ⇒ //_: FinalField) ⇒ // no information about field mutability + case UBP(ShallowImmutableField) ⇒ maxLocalImmutability = ShallowImmutableClass + case UBP(DependentImmutableField) if (maxLocalImmutability != ShallowImmutableClass) ⇒ + maxLocalImmutability = DependentImmutableClass - case _ ⇒ Result(t, MutableClass) //TODO check + case _ ⇒ Result(t, MutableClass) //TODO check } @@ -428,11 +445,10 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal //println("min: "+minLocalImmutability) //println("max: "+maxLocalImmutability) //println(dependees.values) - /** - * if (oldDependees == dependees) { - * Result(t, minLocalImmutability) - * } else* - */ + + // if (oldDependees == dependees) { + // Result(t, minLocalImmutability) + //} else InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) } } From 98ad480e56c1688371b5c6aeec6a0968dea44b75 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 6 Mar 2020 11:47:46 +0100 Subject: [PATCH 100/327] optimization in Field Immutability analysis. The best immutability property is now default. --- .../L0FieldImmutabilityAnalysis.scala | 107 ++++++++---------- 1 file changed, 48 insertions(+), 59 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index b1707e1083..d35cc5797d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -50,15 +50,15 @@ import org.opalj.fpcf.SomeEPS case class State(f: Field) { var field: Field = f - var typeImmutability: Option[Boolean] = None + var typeImmutability: Option[Boolean] = Some(true) var referenceImmutability: Option[Boolean] = None - var dependentImmutability: Option[DependentImmutabilityKind] = None + var dependentImmutability: Option[DependentImmutabilityKind] = Some(DependentImmutabilityKind.dependent) var genericTypeSetNotDeepImmutable = false } object DependentImmutabilityKind extends Enumeration { type DependentImmutabilityKind = Value - val dependent, nowShallowOrMutable, onlyDeepImmutable = Value + val dependent, notShallowOrMutable, onlyDeepImmutable = Value } /** @@ -122,28 +122,25 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def handleTypeImmutability(state: State) = { val objectType = field.fieldType.asFieldType if (objectType.isArrayType) { - state.typeImmutability = Some(true) + //state.typeImmutability = Some(true) // true is default } else if (objectType.isBaseType) { - state.typeImmutability = Some(true); - } else { + //state.typeImmutability = Some(true) // true is default + } else if (objectType.asObjectType == ObjectType.Object) + state.typeImmutability = Some(false) + else { val result = propertyStore(objectType, TypeImmutability_new.key) dependencies = dependencies.filter(_.e ne result.e) result match { - case FinalEP(e, DeepImmutableType) ⇒ { - state.typeImmutability = Some(true); - } + case FinalEP(e, DeepImmutableType) ⇒ case FinalEP(f, DependentImmutableType) ⇒ { - if (dependencies.size > 0) - state.typeImmutability = None - else - state.typeImmutability = Some(false) + state.typeImmutability = Some(false) } case FinalEP(e, ShallowImmutableType | MutableType_new) ⇒ { state.typeImmutability = Some(false) + state.dependentImmutability = None if (state.field.fieldType.isObjectType && - state.field.fieldType.asObjectType != ObjectType("java/lang/Object")) { + state.field.fieldType.asObjectType != ObjectType.Object) { state.dependentImmutability = None //when the generic type is still final - state.genericTypeSetNotDeepImmutable = true } } case ep: InterimEP[e, p] ⇒ dependencies += ep @@ -153,16 +150,17 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } def hasGenericType(state: State): Unit = { - var flag_noShallow = true + var flag_notShallow = true var flag_onlyDeep = true var genericFields: List[ObjectType] = List() state.field.asField.attributes.foreach( _ match { case RuntimeInvisibleAnnotationTable(_) ⇒ case TypeVariableSignature(t) ⇒ + //state.typeImmutability = Some(false) // respects the cas flag_onlyDeep = false if (!isInClassesGenericTypeParameters(t)) { - flag_noShallow = false + flag_notShallow = false } case ClassTypeSignature( @@ -179,7 +177,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) ⇒ flag_onlyDeep = false if (!isInClassesGenericTypeParameters(identifier)) { - flag_noShallow = false + flag_notShallow = false } case ProperTypeArgument( varianceIndicator, @@ -200,44 +198,45 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) genericFields = ObjectType(oPath) :: genericFields } case _ ⇒ - flag_noShallow = false + flag_notShallow = false flag_onlyDeep = false + state.typeImmutability = Some(false) } }) case _ ⇒ - flag_noShallow = false + state.typeImmutability = Some(false) + flag_notShallow = false flag_onlyDeep = false } ) genericFields.foreach(objectType ⇒ { - val result = propertyStore(objectType, TypeImmutability_new.key) dependencies = dependencies.filter(_.e ne result.e) result match { - case FinalP(DeepImmutableType) ⇒ - case FinalP(ShallowImmutableType | MutableType_new) ⇒ { - flag_noShallow = false + case FinalP(DeepImmutableType) ⇒ //nothing to to here: default value is deep imm + case FinalP(ShallowImmutableType | DependentImmutableType | MutableType_new) ⇒ { + flag_notShallow = false flag_onlyDeep = false + state.typeImmutability = Some(false) } - case ep: InterimEP[e, p] ⇒ dependencies += ep case ep @ _ ⇒ dependencies += ep } - }) if (state.field.asField.attributes.size == state.field.asField.attributes .collect({ case x @ RuntimeInvisibleAnnotationTable(_) ⇒ x }) .size) { - flag_noShallow = false + flag_notShallow = false flag_onlyDeep = false } - - if (flag_onlyDeep) - state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) - else if (flag_noShallow) - state.dependentImmutability = Some(DependentImmutabilityKind.nowShallowOrMutable) + if (state.dependentImmutability != None) { + if (flag_onlyDeep) + state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) + else if (flag_notShallow) + state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) + } } def createResult(state: State): ProperPropertyComputationResult = { @@ -249,16 +248,13 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(true) ⇒ Result(field, DeepImmutableField) case Some(false) | None ⇒ { - if (state.genericTypeSetNotDeepImmutable) - Result(field, ShallowImmutableField) - else - state.dependentImmutability match { - case Some(DependentImmutabilityKind.nowShallowOrMutable) ⇒ - Result(field, DependentImmutableField) - case Some(DependentImmutabilityKind.onlyDeepImmutable) ⇒ - Result(field, DeepImmutableField) - case _ ⇒ Result(field, ShallowImmutableField) - } + state.dependentImmutability match { + case Some(DependentImmutabilityKind.notShallowOrMutable) ⇒ + Result(field, DependentImmutableField) + case Some(DependentImmutabilityKind.onlyDeepImmutable) ⇒ + Result(field, DeepImmutableField) + case _ ⇒ Result(field, ShallowImmutableField) + } } } } @@ -272,24 +268,22 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) dependencies += eps InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) } - case FinalEP(_, DeepImmutableType) ⇒ state.typeImmutability = Some(true) + case FinalP(DeepImmutableType) ⇒ //state.typeImmutability = Some(true) case FinalEP(t, MutableType_new | ShallowImmutableType) ⇒ state.typeImmutability = Some(false) - if (t == state.field.fieldType && - state.field.fieldType.isObjectType && - state.field.fieldType.asObjectType != ObjectType("java/lang/Object")) { - state.dependentImmutability = None - state.genericTypeSetNotDeepImmutable = true + if (t != ObjectType.Object) { + state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } case FinalEP(f, DependentImmutableType) ⇒ { state.typeImmutability = Some(false) if (state.dependentImmutability == None) state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } - case x @ FinalEP(_, MutableReference) ⇒ { - state.referenceImmutability = Some(false) + case x @ FinalP(MutableReference) ⇒ { + state.typeImmutability = Some(false) + return Result(field, MutableField); } - case x @ FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ { //TODO + case x @ FinalP(ImmutableReference | LazyInitializedReference) ⇒ { //TODO state.referenceImmutability = Some(true) } case x @ _ ⇒ @@ -306,20 +300,19 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) } val state: State = new State(field) - val result = propertyStore(state.field, ReferenceImmutability.key) result match { case FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ state.referenceImmutability = Some(true) - case FinalP(MutableReference) ⇒ return Result(field, MutableField) + case FinalP(MutableReference) ⇒ return Result(field, MutableField); case x @ _ ⇒ { dependencies += x } } - loadFormalTypeparameters() - hasGenericType(state) handleTypeImmutability(state) + hasGenericType(state) + if (dependencies.isEmpty) createResult(state) else @@ -330,9 +323,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) dependencies, c(state) ) - } - } trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { @@ -385,7 +376,5 @@ object LazyL0FieldImmutabilityAnalysis ) analysis } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - } From e9fe41ccb6ca957c0d530f97a091475ae412fb10 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 6 Mar 2020 11:50:17 +0100 Subject: [PATCH 101/327] New Purity Analysis depending on new immutability analyses. --- ...mutabilityAnalysisDemo_withNewPurity.scala | 144 +++ ...mutabilityAnalysisDemo_withNewPurity.scala | 142 +++ ...mutabilityAnalysisDemo_withNewPurity.scala | 122 ++ ...mutabilityAnalysisDemo_withNewPurity.scala | 128 +++ ...ClassImmutabilityTests_withNewPurity.scala | 96 ++ ...FieldImmutabilityTests_withNewPurity.scala | 92 ++ ...renceImmutabilityTests_withNewPurity.scala | 57 + .../TypeImmutabilityTests_withNewPurity.scala | 72 ++ .../purity/AbstractPurityAnalysis_new.scala | 723 ++++++++++++ .../purity/L2PurityAnalysis_new.scala | 1019 +++++++++++++++++ 10 files changed, 2595 insertions(+) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala new file mode 100644 index 0000000000..159557d53b --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala @@ -0,0 +1,144 @@ +/* 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.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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +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.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds + +/** + * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result + * + * @author Tobias Peter Roth + */ +object ClassImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplication { + + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + + val analysesManager = project.get(FPCFAnalysesManagerKey) + + analysesManager.project.get(RTACallGraphKey) + + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyL1FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + + val sb = new StringBuilder + sb.append("Mutable Class: \n") + val mutableClasses = propertyStore + .finalEntities(MutableClass) + .toList + sb.append( + mutableClasses + .map(x ⇒ x.toString+" |Mutable Class\n") + ) + sb.append("\nShallow Immutable Class:\n") + val shallowImmutableClasses = propertyStore.finalEntities(ShallowImmutableClass) + .toList + sb.append( + shallowImmutableClasses + .map(x ⇒ x.toString+" |Shallow Immutable Class\n") + ) + sb.append("\nDependent Immutable Class: \n") + val dependentImmutableClasses = propertyStore + .finalEntities(DependentImmutableClass).toList + sb.append( + dependentImmutableClasses + .map(x ⇒ x.toString+" |Dependent Immutable Class\n") + ) + + sb.append("\nDeep Immutable Class Classes:\n") + val deepImmutableClasses = propertyStore + .finalEntities(DeepImmutableClass) + sb.append( + deepImmutableClasses + .toList + .map(x ⇒ x.toString+" |Deep Immutable Class\n") + ) + sb.append("\nDeep Immutable Class Classes: Interface\n") + val deepImmutableClassesInterfaces = propertyStore + .finalEntities(DeepImmutableClass) + .toList + sb.append( + deepImmutableClassesInterfaces + .map(x ⇒ x.toString+" |Deep Immutable Class Interface\n") + ) + sb.append("\n\n") + sb.append("mutable Classes: "+mutableClasses.size+"\n") + sb.append("shallow immutable classes: "+shallowImmutableClasses.size+"\n") + sb.append("dependent immutable classes: "+dependentImmutableClasses.size+"\n") + sb.append("deep immutable classes: "+deepImmutableClasses.size+"\n") + sb.append("deep immutable classes interfaces: "+deepImmutableClassesInterfaces.size+"\n") + + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/classImm"+dateString+".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + + " took : "+analysisTime+" seconds" + } +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala new file mode 100644 index 0000000000..08523bf559 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala @@ -0,0 +1,142 @@ +/* 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.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.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +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.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.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds + +/** + * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object FieldImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0FieldImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0FieldImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + + analysesManager.project.get(RTACallGraphKey) + + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyLxClassImmutabilityAnalysis_new, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyL1FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + + val sb: StringBuilder = new StringBuilder + sb.append("Mutable Fields: \n") + val mutableFields = propertyStore + .finalEntities(MutableField) + .toList + sb.append( + mutableFields + .map(x ⇒ x.toString+" |Mutable Field\n") + .toString() + ) + sb.append("\nShallow Immutable Fields: \n") + val shallowImmutableFields = propertyStore + .finalEntities(ShallowImmutableField) + .toList + sb.append( + shallowImmutableFields + .map(x ⇒ x.toString+" |Shallow Immutable Field\n") + .toString() + ) + sb.append("\nDependet Immutable Fields: \n") + val dependentImmutableFields = propertyStore + .finalEntities(DependentImmutableField) + .toList + sb.append( + dependentImmutableFields + .map(x ⇒ x.toString+" |Dependent Immutable Field\n") + .toString() + ) + sb.append("Deep Immutable Fields: ") + val deepImmutableFields = propertyStore + .finalEntities(DeepImmutableField) + .toList + sb.append( + deepImmutableFields + .map(x ⇒ x.toString+" |Deep Immutable Field\n") + .toString + ) + sb.append("\n\n") + sb.append( + s""" mutable fields: ${mutableFields.size} + | shallow immutable fields: ${shallowImmutableFields.size} + | dependent immutable fields: ${dependentImmutableFields.size} + | deep immutable fields: ${deepImmutableFields.size} + |""".stripMargin + ) + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/fieldImm_"+dateString+".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + + " took : "+analysisTime+" seconds" + } +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala new file mode 100644 index 0000000000..28e3094898 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala @@ -0,0 +1,122 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.analyses + +import java.io._ +import java.net.URL +import java.util.Calendar + +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.LazyClassImmutabilityAnalysis +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.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +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.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds + +/** + * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyFieldLocalityAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyL1FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + var sb: StringBuilder = new StringBuilder() + sb = sb.append("Mutable References: \n") + val mutableReferences = propertyStore.finalEntities(MutableReference).toList + sb = sb.append( + mutableReferences.map(x ⇒ x.toString+"\n").toString() + ) + + sb = sb.append("\n Lazy Initialized Reference: \n") + val lazyInitializedReferences = propertyStore + .finalEntities(LazyInitializedReference) + .toList + sb = sb.append( + lazyInitializedReferences + .map(x ⇒ x.toString+"\n") + .toString() + ) + + /** + * .toList + * .toString() + "\n" +* + */ + sb = sb.append("\nImmutable References: \n") + val immutableReferences = propertyStore.finalEntities(ImmutableReference).toList + sb = sb.append( + immutableReferences.map(x ⇒ x.toString+"\n").toString() + ) + sb.append("\n\n") + sb.append(" took : "+analysisTime+" seconds\n") + sb.append( + s""" mutable References: ${mutableReferences.size} + | lazy initialized References: ${lazyInitializedReferences.size} + | immutable References: ${immutableReferences.size} + |""".stripMargin + + ) + + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/refImm"+dateString+".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + + " took : "+analysisTime+" seconds" + } +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala new file mode 100644 index 0000000000..d8ec91a4f6 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala @@ -0,0 +1,128 @@ +/* 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.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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis +import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis +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_new +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.util.Seconds + +/** + * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result + * + * @author Tobias Peter Roth + */ +object TypeImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplication { + + override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" + + override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new, + LazyFieldLocalityAnalysis, + LazySimpleEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyL1FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + val sb: StringBuilder = new StringBuilder + sb.append("\nMutableTypes: \n") + val mutableTypes = propertyStore + .finalEntities(MutableType_new) + .toList + sb.append( + mutableTypes + .map(x ⇒ x.toString+" |Mutable Type\n") + .toString + ) + sb.append("\nShallow Immutable Types:\n") + val shallowImmutableTypes = propertyStore.finalEntities(ShallowImmutableType).toList + sb.append(shallowImmutableTypes.map(x ⇒ x.toString+" |Shallow Immutable Type\n")) + sb.append("\nDependent Immutable Types: \n") + val dependentImmutableTypes = propertyStore.finalEntities(DependentImmutableType).toList + sb.append( + dependentImmutableTypes.map(x ⇒ x.toString+" |Dependent Immutable Type\n") + ) + sb.append("\nDeep Immutable Types:\n") + val deepImmutableTypes = propertyStore.finalEntities(DeepImmutableType).toList + sb.append(deepImmutableTypes.map(x ⇒ x.toString+" |Deep Immutable Type\n")) + sb.append(s"\nType immutability analysis took: $analysisTime on average") + + sb.append("\n\n") + sb.append( + s""" + | mutable types: ${mutableTypes.size} + | shallow immutable types: ${shallowImmutableTypes.size} + | dependent immutable types: ${dependentImmutableTypes.size} + | deep immutable types: ${deepImmutableTypes.size} + |""".stripMargin + ) + + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/typeImm"+dateString+".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + + " took : "+analysisTime+" seconds" + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala new file mode 100644 index 0000000000..bf1f442902 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala @@ -0,0 +1,96 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.LazyL0FieldImmutabilityAnalysis +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.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new + +/** + * @author Tobias Peter Roth + */ +class ClassImmutabilityTests_withNewPurity extends PropertiesTest { + + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures") + } + + 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("ClassImmutability_new")) + * }* + */ + describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { + println(1) + val as = executeAnalyses( + Set( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties( + as, + classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), + Set("ClassImmutability_new") + ) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala new file mode 100644 index 0000000000..1a91c935eb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala @@ -0,0 +1,92 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.EagerL0FieldImmutabilityAnalysis +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.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new + +/** + * @author Tobias Peter Roth + */ +class FieldImmutabilityTests_withNewPurity extends PropertiesTest { + + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures") + } + + 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")) + } + + describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + val as = executeAnalyses( + Set( + LazyL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + EagerL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala new file mode 100644 index 0000000000..2bcf6c549c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala @@ -0,0 +1,57 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +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_new + +/** + * @author Tobias Peter Roth + */ +class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { + + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures") + } + + 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("ReferenceImmutability")) + } + + describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { + val as = executeAnalyses( + Set( + EagerL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala new file mode 100644 index 0000000000..45f423a143 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala @@ -0,0 +1,72 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.LazyL0FieldImmutabilityAnalysis +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.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new + +/** + * Tests the Type Immutability Analysis with the new lattice + * + * @author Tobias Peter Roth + */ +class TypeImmutabilityTests_withNewPurity extends PropertiesTest { + + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures") + } + + 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("TypeImmutability_new")) + * } + */ + + describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { + println(1) + val as = executeAnalyses( + Set( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties( + as, + // fieldsWithAnnotations(as.project), + classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), + Set("TypeImmutability_new") + ) //TODO class files ... with annotation + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala new file mode 100644 index 0000000000..ccdea6f9cd --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala @@ -0,0 +1,723 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.purity + +import org.opalj.ai +import org.opalj.ai.ValueOrigin +import org.opalj.ai.isImmediateVMException +import org.opalj.br.ComputationalTypeReference +import org.opalj.br.DeclaredMethod +import org.opalj.br.DefinedMethod +import org.opalj.br.Field +import org.opalj.br.Method +import org.opalj.br.ObjectType +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.analyses.ConfiguredPurity +import org.opalj.br.fpcf.analyses.ConfiguredPurityKey +import org.opalj.br.fpcf.properties.ClassImmutability_new +import org.opalj.br.fpcf.properties.CompileTimePure +import org.opalj.br.fpcf.properties.DeepImmutableClass +import org.opalj.br.fpcf.properties.DeepImmutableType +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.ImpureByAnalysis +import org.opalj.br.fpcf.properties.ImpureByLackOfInformation +import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.Pure +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.properties.ReferenceImmutability +import org.opalj.br.fpcf.properties.SideEffectFree +import org.opalj.br.fpcf.properties.TypeImmutability_new +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.collection.immutable.EmptyIntTrieSet +import org.opalj.collection.immutable.IntTrieSet +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.Entity +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.InterimLUBP +import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.InterimUBP +import org.opalj.fpcf.LBP +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Property +import org.opalj.fpcf.Result +import org.opalj.fpcf.SomeEOptionP +import org.opalj.fpcf.UBPS +import org.opalj.log.GlobalLogContext +import org.opalj.log.OPALLogger +import org.opalj.tac.ArrayLength +import org.opalj.tac.ArrayLoad +import org.opalj.tac.ArrayStore +import org.opalj.tac.Assignment +import org.opalj.tac.BinaryExpr +import org.opalj.tac.Call +import org.opalj.tac.CaughtException +import org.opalj.tac.Checkcast +import org.opalj.tac.ClassConst +import org.opalj.tac.Compare +import org.opalj.tac.DUVar +import org.opalj.tac.DoubleConst +import org.opalj.tac.Expr +import org.opalj.tac.ExprStmt +import org.opalj.tac.FieldRead +import org.opalj.tac.FloatConst +import org.opalj.tac.GetField +import org.opalj.tac.GetStatic +import org.opalj.tac.Goto +import org.opalj.tac.If +import org.opalj.tac.InstanceOf +import org.opalj.tac.IntConst +import org.opalj.tac.InvokedynamicFunctionCall +import org.opalj.tac.InvokedynamicMethodCall +import org.opalj.tac.JSR +import org.opalj.tac.LongConst +import org.opalj.tac.MethodHandleConst +import org.opalj.tac.MethodTypeConst +import org.opalj.tac.MonitorEnter +import org.opalj.tac.MonitorExit +import org.opalj.tac.New +import org.opalj.tac.NewArray +import org.opalj.tac.NonVirtualFunctionCall +import org.opalj.tac.NonVirtualMethodCall +import org.opalj.tac.Nop +import org.opalj.tac.NullExpr +import org.opalj.tac.Param +import org.opalj.tac.PrefixExpr +import org.opalj.tac.PrimitiveTypecastExpr +import org.opalj.tac.PutField +import org.opalj.tac.PutStatic +import org.opalj.tac.Ret +import org.opalj.tac.Return +import org.opalj.tac.ReturnValue +import org.opalj.tac.StaticFunctionCall +import org.opalj.tac.StaticMethodCall +import org.opalj.tac.Stmt +import org.opalj.tac.StringConst +import org.opalj.tac.Switch +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACode +import org.opalj.tac.Throw +import org.opalj.tac.Var +import org.opalj.tac.VirtualFunctionCall +import org.opalj.tac.VirtualMethodCall +import org.opalj.tac.fpcf.analyses.cg.uVarForDefSites +import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.value.ValueInformation + +import scala.annotation.switch + +/** + * Base trait for analyses that analyze the purity of methods. + * + * Provides types and methods needed for purity analyses. + */ +trait AbstractPurityAnalysis_new extends FPCFAnalysis { + + /** The type of the TAC domain. */ + type V = DUVar[ValueInformation] + + /** + * The state of the analysis. + * Analyses are expected to extend this trait with the information they need. + * + * lbPurity - The current minimum possible purity level for the method + * ubPurity - The current maximum purity level for the method + * method - The currently analyzed method + * declClass - The declaring class of the currently analyzed method + * code - The code of the currently analyzed method + */ + trait AnalysisState { + var lbPurity: Purity + var ubPurity: Purity + val method: Method + val definedMethod: DeclaredMethod + val declClass: ObjectType + var pcToIndex: Array[Int] + var code: Array[Stmt[V]] + } + + type StateType <: AnalysisState + + protected[this] def raterFqn: String + + val rater: DomainSpecificRater + + protected[this] implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + + val configuredPurity: ConfiguredPurity = project.get(ConfiguredPurityKey) + + /** + * Reduces the maxPurity of the current method to at most the given purity level. + */ + def reducePurityLB(newLevel: Purity)(implicit state: StateType): Unit = { + state.lbPurity = state.lbPurity meet newLevel + } + + /** + * Reduces the minPurity and maxPurity of the current method to at most the given purity level. + */ + def atMost(newLevel: Purity)(implicit state: StateType): Unit = { + state.lbPurity = state.lbPurity meet newLevel + state.ubPurity = state.ubPurity meet newLevel + } + + /** + * Examines whether the given expression denotes an object/array that is local to the current + * method, i.e. the method has control over the object/array and actions on it might not + * influence purity. + * + * @param otherwise The maxPurity will be reduced to at most this level if the expression is not + * local. + */ + def isLocal( + expr: Expr[V], + otherwise: Purity, + excludedDefSites: IntTrieSet = EmptyIntTrieSet + )(implicit state: StateType): Boolean + + /** + * Checks whether the statement, which is the origin of an exception, directly created the + * exception or if the VM instantiated the exception. Here, we are only concerned about the + * exceptions thrown by the instructions not about exceptions that are transitively thrown; + * e.g. if a method is called. + * TODO We need this method because currently, for exceptions that terminate the method, no + * definitions are recorded. Once this is done, use that information instead to determine + * whether it may be an immediate exception or not. + */ + def isSourceOfImmediateException(origin: ValueOrigin)(implicit state: StateType): Boolean = { + + def evaluationMayCauseVMLevelException(expr: Expr[V]): Boolean = { + (expr.astID: @switch) match { + + case NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + val rcvr = expr.asInstanceFunctionCall.receiver + !rcvr.isVar || rcvr.asVar.value.asReferenceValue.isNull.isYesOrUnknown + + case StaticFunctionCall.ASTID ⇒ false + + case _ ⇒ true + } + } + + val stmt = state.code(origin) + (stmt.astID: @switch) match { + case StaticMethodCall.ASTID ⇒ false // We are looking for implicit exceptions only + + case Throw.ASTID ⇒ + stmt.asThrow.exception.asVar.value.asReferenceValue.isNull.isNotNo + + case NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + val rcvr = stmt.asInstanceMethodCall.receiver + !rcvr.isVar || rcvr.asVar.value.asReferenceValue.isNull.isNotNo + + case Assignment.ASTID ⇒ evaluationMayCauseVMLevelException(stmt.asAssignment.expr) + + case ExprStmt.ASTID ⇒ evaluationMayCauseVMLevelException(stmt.asExprStmt.expr) + + case _ ⇒ true + } + } + + /** + * Examines whether a call constitutes a domain-specific action using the domain-specific rater. + * If it is, the maxPurity will be reduced to at most the domain-specific purity returned by the + * domain-specific rater. + */ + def isDomainSpecificCall( + call: Call[V], + receiver: Option[Expr[V]] + )(implicit state: StateType): Boolean = { + implicit val code: Array[Stmt[V]] = state.code + val ratedResult = rater.handleCall(call, receiver) + if (ratedResult.isDefined) + atMost(ratedResult.get) + ratedResult.isDefined + } + + /** + * Examines a statement for its influence on the method's purity. + * This method will return false for impure statements, so evaluation can be terminated early. + */ + def checkPurityOfStmt(stmt: Stmt[V])(implicit state: StateType): Boolean = { + val isStmtNotImpure = (stmt.astID: @switch) match { + // For method calls, purity will be checked later + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + true + + // We don't handle unresolved Invokedynamics + // - either OPAL removes it or we forget about it + case InvokedynamicMethodCall.ASTID ⇒ + atMost(ImpureByAnalysis) + false + + // Returning objects/arrays is pure, if the returned object/array is locally initialized + // and non-escaping or the object is immutable + case ReturnValue.ASTID ⇒ + checkPurityOfReturn(stmt.asReturnValue.expr) + true + case Throw.ASTID ⇒ + checkPurityOfReturn(stmt.asThrow.exception) + true + + // Synchronization on non-escaping locally initialized objects/arrays is pure (and + // useless...) + case MonitorEnter.ASTID | MonitorExit.ASTID ⇒ + isLocal(stmt.asSynchronizationStmt.objRef, ImpureByAnalysis) + + // Storing into non-escaping locally initialized objects/arrays is pure + case ArrayStore.ASTID ⇒ isLocal(stmt.asArrayStore.arrayRef, ImpureByAnalysis) + case PutField.ASTID ⇒ isLocal(stmt.asPutField.objRef, ImpureByAnalysis) + + case PutStatic.ASTID ⇒ + // Note that a putstatic is not necessarily pure/sideeffect free, even if it + // is executed within a static initializer to initialize a field of + // `the` class; it is possible that the initialization triggers the + // initialization of another class which reads the value of this static field. + // See + // https://stackoverflow.com/questions/6416408/static-circular-dependency-in-java + // for an in-depth discussion. + // (Howevever, if we would check for cycles, we could determine that it is pure, + // but this is not considered to be too useful...) + atMost(ImpureByAnalysis) + false + + // Creating implicit exceptions is side-effect free (because of fillInStackTrace) + // but it may be ignored as domain-specific + case CaughtException.ASTID ⇒ + for { + origin ← stmt.asCaughtException.origins + if isImmediateVMException(origin) + } { + val baseOrigin = state.code(ai.underlyingPC(origin)) + val ratedResult = rater.handleException(baseOrigin) + if (ratedResult.isDefined) atMost(ratedResult.get) + else atMost(SideEffectFree) + } + true + + // Reference comparisons may have different results for structurally equal values + case If.ASTID ⇒ + val If(_, left, _, right, _) = stmt + if (left.cTpe eq ComputationalTypeReference) + if (!(isLocal(left, CompileTimePure) || isLocal(right, CompileTimePure))) + atMost(SideEffectFree) + true + + // The following statements do not further influence purity + case Goto.ASTID | JSR.ASTID | Ret.ASTID | Switch.ASTID | Assignment.ASTID | + Return.ASTID | Nop.ASTID | ExprStmt.ASTID | Checkcast.ASTID ⇒ + true + } + + isStmtNotImpure && stmt.forallSubExpressions(checkPurityOfExpr) + } + + /** + * Examines an expression for its influence on the method's purity. + * This method will return false for impure expressions, so evaluation can be terminated early. + */ + def checkPurityOfExpr(expr: Expr[V])(implicit state: StateType): Boolean = { + val isExprNotImpure = (expr.astID: @switch) match { + // For function calls, purity will be checked later + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | + VirtualFunctionCall.ASTID ⇒ + true + + // Field/array loads are pure if the field is (effectively) final or the object/array is + // local and non-escaping + case GetStatic.ASTID ⇒ + implicit val code: Array[Stmt[V]] = state.code + val ratedResult = rater.handleGetStatic(expr.asGetStatic) + if (ratedResult.isDefined) atMost(ratedResult.get) + else checkPurityOfFieldRef(expr.asGetStatic) + true + case GetField.ASTID ⇒ + checkPurityOfFieldRef(expr.asGetField) + true + case ArrayLoad.ASTID ⇒ + if (state.ubPurity.isDeterministic) + isLocal(expr.asArrayLoad.arrayRef, SideEffectFree) + true + + // We don't handle unresolved Invokedynamic + // - either OPAL removes it or we forget about it + case InvokedynamicFunctionCall.ASTID ⇒ + atMost(ImpureByAnalysis) + false + + // The following expressions do not further influence purity, potential exceptions are + // handled explicitly + case New.ASTID | NewArray.ASTID | InstanceOf.ASTID | Compare.ASTID | Param.ASTID | + MethodTypeConst.ASTID | MethodHandleConst.ASTID | IntConst.ASTID | LongConst.ASTID | + FloatConst.ASTID | DoubleConst.ASTID | StringConst.ASTID | ClassConst.ASTID | + NullExpr.ASTID | BinaryExpr.ASTID | PrefixExpr.ASTID | PrimitiveTypecastExpr.ASTID | + ArrayLength.ASTID | Var.ASTID ⇒ + true + + } + + isExprNotImpure && expr.forallSubExpressions(checkPurityOfExpr) + } + + def checkPurityOfMethod( + callee: DeclaredMethod, + params: Seq[Expr[V]] + )(implicit state: StateType): Boolean = { + if (callee.hasSingleDefinedMethod && (callee.definedMethod eq state.method)) { + true + } else { + val calleePurity = propertyStore(callee, Purity.key) + checkMethodPurity(calleePurity, params) + } + } + + def getCall(stmt: Stmt[V])(implicit state: StateType): Call[V] = stmt.astID match { + case StaticMethodCall.ASTID ⇒ stmt.asStaticMethodCall + case NonVirtualMethodCall.ASTID ⇒ stmt.asNonVirtualMethodCall + case VirtualMethodCall.ASTID ⇒ stmt.asVirtualMethodCall + case Assignment.ASTID ⇒ stmt.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID ⇒ stmt.asExprStmt.expr.asFunctionCall + case CaughtException.ASTID ⇒ + /* + * There is no caught exception instruction in bytecode, so it might be the case, that + * in the three-address code, there is a CaughtException stmt right before the call + * with the same pc. Therefore, we have to get the call stmt after the current stmt. + * + * Example: + * void foo() { + * try { + * ... + * } catch (Exception e) { + * e.printStackTrace(); + * } + * } + * + * In TAC: + * 12: pc=52 caught java.lang.Exception ... + * 13: pc=52 java.lang.Exception.printStackTrace() + */ + getCall(state.code(state.pcToIndex(stmt.pc) + 1)) + case _ ⇒ + throw new IllegalStateException(s"unexpected stmt $stmt") + } + + /** + * Examines the influence of the purity property of a method on the examined method's purity. + * + * @note Adds dependendies when necessary. + */ + def checkMethodPurity( + ep: EOptionP[DeclaredMethod, Purity], + params: Seq[Expr[V]] = Seq.empty + )(implicit state: StateType): Boolean + + /** + * Examines whether a field read influences a method's purity. + * Reading values from fields that are not (effectively) final may cause nondeterministic + * behavior, so the method can only be side-effect free. + */ + def checkPurityOfFieldRef( + fieldRef: FieldRead[V] + )(implicit state: StateType): Unit = { + // Don't do dependee checks if already non-deterministic + if (state.ubPurity.isDeterministic) { + fieldRef.asFieldRead.resolveField match { + case Some(field) if field.isStatic ⇒ + checkFieldMutability(propertyStore(field, ReferenceImmutability.key), None) //FieldMutability.key), None) + case Some(field) ⇒ + checkFieldMutability( + propertyStore(field, ReferenceImmutability.key), Some(fieldRef.asGetField.objRef) //FieldMutability.key), Some(fieldRef.asGetField.objRef) + ) + case _ ⇒ // Unknown field + if (fieldRef.isGetField) isLocal(fieldRef.asGetField.objRef, SideEffectFree) + else atMost(SideEffectFree) + } + } + } + + /** + * Examines the influence that a given field mutability has on the method's purity. + */ + def checkFieldMutability( + ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], + objRef: Option[Expr[V]] + )(implicit state: StateType): Unit = ep match { + case LBP(ImmutableReference | LazyInitializedReference) ⇒ //_: FinalField) ⇒ // Final fields don't impede purity + case _: FinalEP[Field, ReferenceImmutability] ⇒ //FieldMutability] ⇒ // Mutable field + if (objRef.isDefined) { + if (state.ubPurity.isDeterministic) + isLocal(objRef.get, SideEffectFree) + } else atMost(SideEffectFree) + case _ ⇒ + reducePurityLB(SideEffectFree) + if (state.ubPurity.isDeterministic) + handleUnknownFieldMutability(ep, objRef) + } + + /** + * Handles what to do when the mutability of a field is not yet known. + * Analyses must implement this method with the behavior they need, e.g. registering dependees. + */ + def handleUnknownFieldMutability( + ep: EOptionP[Field, ReferenceImmutability], //FieldMutability], + objRef: Option[Expr[V]] + )(implicit state: StateType): Unit + + /** + * Examines the effect of returning a value on the method's purity. + * Returning a reference to a mutable object or array may cause nondeterministic behavior + * as the object/array may be modified between invocations of the method, so the method can + * only be side-effect free. E.g., a given parameter which references a mutable object is + * returned (and not otherwise accessed). + */ + def checkPurityOfReturn(returnValue: Expr[V])(implicit state: StateType): Unit = { + if (returnValue.cTpe != ComputationalTypeReference) + return ; // Only non-primitive return values influence purity. + + if (!state.ubPurity.isDeterministic) + return ; // If the method can't be pure, the return value is not important. + + if (!returnValue.isVar) { + // The expression could refer to further expressions in a non-flat representation. To + // avoid special handling, we just fallback to SideEffectFreeWithoutAllocations here if + // the return value is not local as the analysis is intended to be used on flat + // representations anyway. + isLocal(returnValue, SideEffectFree) + return ; + } + + val value = returnValue.asVar.value.asReferenceValue + if (value.isNull.isYes) + return ; // Null is immutable + + if (value.upperTypeBound.exists(_.isArrayType)) { + // Arrays are always mutable + isLocal(returnValue, SideEffectFree) + return ; + } + + if (value.isPrecise) { // Precise class known, use ClassImmutability + val returnType = value.upperTypeBound.head + + val classImmutability = + propertyStore( + returnType, + ClassImmutability_new.key // ClassImmutability.key + ).asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]] // ClassImmutability]] + checkTypeMutability(classImmutability, returnValue) + + } else { // Precise class unknown, use TypeImmutability + // IMPROVE Use ObjectType once we attach the respective information to ObjectTypes + val returnTypes = value.upperTypeBound + + returnTypes.forall { returnType ⇒ + val typeImmutability = + propertyStore( + returnType, + TypeImmutability_new.key //.key + ).asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] //TypeImmutability]] + checkTypeMutability(typeImmutability, returnValue) + } + } + } + + /** + * Examines the effect that the mutability of a returned value's type has on the method's + * purity. + */ + def checkTypeMutability( + ep: EOptionP[ObjectType, Property], + returnValue: Expr[V] + )(implicit state: StateType): Boolean = ep match { + // Returning immutable object is pure + case LBP(DeepImmutableType | DeepImmutableClass) ⇒ true // ImmutableType | ImmutableObject) ⇒ true7 + case _: FinalEP[ObjectType, Property] ⇒ + atMost(Pure) // Can not be compile time pure if mutable object is returned + if (state.ubPurity.isDeterministic) + isLocal(returnValue, SideEffectFree) + false // Return early if we are already side-effect free + case _ ⇒ + reducePurityLB(SideEffectFree) + if (state.ubPurity.isDeterministic) + handleUnknownTypeMutability(ep, returnValue) + true + } + + /** + * Handles what to do when the mutability of a type is not yet known. + * Analyses must implement this method with the behavior they need, e.g. registering dependees. + */ + def handleUnknownTypeMutability( + ep: EOptionP[ObjectType, Property], + expr: Expr[V] + )(implicit state: StateType): Unit + + /** + * Examines the effect that the purity of all potential callees has on the purity of the method. + */ + def checkPurityOfCallees( + calleesEOptP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: StateType + ): Boolean = { + handleCalleesUpdate(calleesEOptP) + calleesEOptP match { + case UBPS(p: Callees, isFinal) ⇒ + if (!isFinal) reducePurityLB(ImpureByAnalysis) + + val hasIncompleteCallSites = + p.incompleteCallSites.exists { pc ⇒ + val index = state.pcToIndex(pc) + if (index < 0) + false // call will not be executed + else { + val call = getCall(state.code(state.pcToIndex(pc))) + !isDomainSpecificCall(call, call.receiverOption) + } + } + + if (hasIncompleteCallSites) { + atMost(ImpureByAnalysis) + return false; + } + + val noDirectCalleeIsImpure = p.directCallSites().forall { + case (pc, callees) ⇒ + val index = state.pcToIndex(pc) + if (index < 0) + true // call will not be executed + else { + val call = getCall(state.code(index)) + isDomainSpecificCall(call, call.receiverOption) || + callees.forall { callee ⇒ + checkPurityOfMethod( + callee, + call.receiverOption.orNull +: call.params + ) + } + } + } + + if (!noDirectCalleeIsImpure) + return false; + + val noIndirectCalleeIsImpure = p.indirectCallSites().forall { + case (pc, callees) ⇒ + val index = state.pcToIndex(pc) + if (index < 0) + true // call will not be executed + else { + val call = getCall(state.code(index)) + isDomainSpecificCall(call, call.receiverOption) || + callees.forall { callee ⇒ + checkPurityOfMethod( + callee, + p.indirectCallReceiver(pc, callee).map(receiver ⇒ + uVarForDefSites(receiver, state.pcToIndex)).orNull +: + p.indirectCallParameters(pc, callee).map { paramO ⇒ + paramO.map(uVarForDefSites(_, state.pcToIndex)).orNull + } + ) + } + } + } + + noIndirectCalleeIsImpure + + case _ ⇒ + reducePurityLB(ImpureByAnalysis) + true + } + } + + /** + * Handles what to do when the set of potential callees changes. + * Analyses must implement this method with the behavior they need, e.g. registering dependees. + */ + def handleCalleesUpdate( + callees: EOptionP[DeclaredMethod, Callees] + )(implicit state: StateType): Unit + + /** + * Handles what to do if the TACAI is not yet final. + */ + def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: StateType): Unit + + /** + * Retrieves and commits the methods purity as calculated for its declaring class type for the + * current DefinedMethod that represents the non-overwritten method in a subtype. + */ + def baseMethodPurity(dm: DefinedMethod): ProperPropertyComputationResult = { + + 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) + case epk ⇒ + InterimResult(dm, ImpureByAnalysis, CompileTimePure, Seq(epk), c) + } + + c(propertyStore(declaredMethods(dm.definedMethod), Purity.key)) + } + + /** + * Determines the purity of the given method. + * + * @param definedMethod A defined method with a body. + */ + def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult + + /** Called when the analysis is scheduled lazily. */ + def doDeterminePurity(e: Entity): ProperPropertyComputationResult = { + e match { + case dm: DefinedMethod if dm.definedMethod.body.isDefined ⇒ + determinePurity(dm) + case dm: DeclaredMethod ⇒ Result(dm, ImpureByLackOfInformation) + case _ ⇒ + throw new IllegalArgumentException(s"$e is not a declared method") + } + } + + /** + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + def getTACAI( + method: Method + )(implicit state: StateType): Option[TACode[TACMethodParameter, V]] = { + propertyStore(method, TACAI.key) match { + case finalEP: FinalEP[Method, TACAI] ⇒ + handleTACAI(finalEP) + finalEP.ub.tac + case eps @ InterimUBP(ub: TACAI) ⇒ + reducePurityLB(ImpureByAnalysis) + handleTACAI(eps) + ub.tac + case epk ⇒ + reducePurityLB(ImpureByAnalysis) + handleTACAI(epk) + None + } + } + + def resolveDomainSpecificRater(fqn: String): DomainSpecificRater = { + import scala.reflect.runtime.universe.runtimeMirror + val mirror = runtimeMirror(getClass.getClassLoader) + try { + val module = mirror.staticModule(fqn) + mirror.reflectModule(module).instance.asInstanceOf[DomainSpecificRater] + } catch { + case ex @ (_: ScalaReflectionException | _: ClassCastException) ⇒ + OPALLogger.error( + "analysis configuration", + "resolve of domain specific rater failed, change "+ + s"org.opalj.fpcf.${this.getClass.getName}.domainSpecificRater in "+ + "ai/reference.conf to an existing DomainSpecificRater implementation", + ex + )(GlobalLogContext) + new BaseDomainSpecificRater // Provide a safe default if resolution failed + } + } + +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala new file mode 100644 index 0000000000..0566b1b90d --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala @@ -0,0 +1,1019 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package tac +package fpcf +package analyses +package purity + +import scala.annotation.switch +import scala.collection.immutable.IntMap +import net.ceedubs.ficus.Ficus._ +import org.opalj.ai +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 +import org.opalj.fpcf.LBP +import org.opalj.fpcf.LUBP +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.SomeEOptionP +import org.opalj.fpcf.SomeEPS +import org.opalj.fpcf.UBP +import org.opalj.br.ComputationalTypeReference +import org.opalj.br.DeclaredMethod +import org.opalj.br.DefinedMethod +import org.opalj.br.Field +import org.opalj.br.Method +import org.opalj.br.ObjectType +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.SomeProject +import org.opalj.br.cfg.CFG +import org.opalj.br.fpcf.properties.ClassifiedImpure +import org.opalj.br.fpcf.properties.CompileTimePure +import org.opalj.br.fpcf.properties.ContextuallyPure +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.FreshReturnValue +import org.opalj.br.fpcf.properties.Getter +import org.opalj.br.fpcf.properties.ImpureByAnalysis +import org.opalj.br.fpcf.properties.LocalField +import org.opalj.br.fpcf.properties.LocalFieldWithGetter +import org.opalj.br.fpcf.properties.NoFreshReturnValue +import org.opalj.br.fpcf.properties.NoLocalField +import org.opalj.br.fpcf.properties.PrimitiveReturnValue +import org.opalj.br.fpcf.properties.Pure +import org.opalj.br.fpcf.properties.ReturnValueFreshness +import org.opalj.br.fpcf.properties.SideEffectFree +import org.opalj.br.fpcf.properties.StaticDataUsage +import org.opalj.br.fpcf.properties.UsesConstantDataOnly +import org.opalj.br.fpcf.properties.UsesNoStaticData +import org.opalj.br.fpcf.properties.UsesVaryingData +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.FPCFEagerAnalysisScheduler +import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.fpcf.analyses.ConfiguredPurityKey +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.br.fpcf.properties.cg.NoCallers +import org.opalj.ai.isImmediateVMException +import org.opalj.br.fpcf.properties.ClassImmutability_new +import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.br.fpcf.properties.ReferenceImmutability +import org.opalj.br.fpcf.properties.TypeImmutability_new +import org.opalj.tac.fpcf.properties.TACAI + +/** + * An inter-procedural analysis to determine a method's purity. + * + * @note This analysis is sound only up to the usual standards, i.e. it does not cope with + * VirtualMachineErrors, LinkageErrors and ReflectiveOperationExceptions and may be unsound in + * the presence of native code, reflection or `sun.misc.Unsafe`. Calls to native methods are + * handled soundly in general as they are considered [[org.opalj.br.fpcf.properties.ImpureByAnalysis]], + * but native methods may break soundness of this analysis by invalidating assumptions such as + * which fields are effectively final. + * @note This analysis is sound even if the three address code hierarchy is not flat, it will + * produce better results for a flat hierarchy, though. This is because it will not assess the + * types of expressions other than [[org.opalj.tac.Var]]s. + * @note This analysis derives all purity level. + * A configurable [[org.opalj.tac.fpcf.analyses.purity.DomainSpecificRater]] is used to + * identify calls, expressions and exceptions that are `LBDPure` instead of `LBImpure` or any + * `SideEffectFree` purity level. Compared to `L1PurityAnaylsis` it identifies objects/arrays + * returned from pure callees that can be considered local. Synchronized methods are treated + * as `ExternallyPure`. + * @author Dominik Helm + */ +class L2PurityAnalysis_new private[analyses] (val project: SomeProject) extends AbstractPurityAnalysis_new { + + /** + * Holds the state of this analysis. + * @param lbPurity The current minimum purity level for the method + * @param ubPurity The current maximum purity level for the method that will be assigned by + * checkPurityOfX methods to aggregrate the purity + * @param method The currently analyzed method + * @param definedMethod The corresponding DefinedMethod we report results for + * @param declClass The declaring class of the currently analyzed method + * @param code The code of the currently analyzed method + */ + class State( + val method: Method, + val definedMethod: DeclaredMethod, + val declClass: ObjectType, + var pcToIndex: Array[Int] = Array.empty, + var code: Array[Stmt[V]] = Array.empty, + var lbPurity: Purity = CompileTimePure, + var ubPurity: Purity = CompileTimePure + ) extends AnalysisState { + var fieldLocalityDependees: Map[Field, (EOptionP[Field, FieldLocality], Set[(Expr[V], Purity)])] = Map.empty + + var fieldMutabilityDependees: Map[Field, (EOptionP[Field, ReferenceImmutability], Set[Option[Expr[V]]])] = Map.empty + //FieldMutability + + var classImmutabilityDependees: Map[ObjectType, (EOptionP[ObjectType, ClassImmutability_new], Set[Expr[V]])] = Map.empty + //ClassImmutability + var typeImmutabilityDependees: Map[ObjectType, (EOptionP[ObjectType, TypeImmutability_new], Set[Expr[V]])] = Map.empty + //TypeImmutability + + 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]] = + (fieldLocalityDependees.valuesIterator.map(_._1) ++ + fieldMutabilityDependees.valuesIterator.map(_._1) ++ + classImmutabilityDependees.valuesIterator.map(_._1) ++ + typeImmutabilityDependees.valuesIterator.map(_._1) ++ + purityDependees.valuesIterator.map(_._1) ++ + calleesDependee ++ + rvfDependees.valuesIterator.map(_._1) ++ + staticDataUsage ++ + tacai).toTraversable + + def addFieldLocalityDependee( + f: Field, + eop: EOptionP[Field, FieldLocality], + data: (Expr[V], Purity) + ): Unit = { + if (fieldLocalityDependees.contains(f)) { + val (_, oldValues) = fieldLocalityDependees(f) + fieldLocalityDependees += ((f, (eop, oldValues + data))) + } else { + fieldLocalityDependees += ((f, (eop, Set(data)))) + } + } + + def addFieldMutabilityDependee( + f: Field, + eop: EOptionP[Field, ReferenceImmutability], // FieldMutability], + owner: Option[Expr[V]] + ): Unit = { + if (fieldMutabilityDependees.contains(f)) { + val (_, oldOwners) = fieldMutabilityDependees(f) + fieldMutabilityDependees += ((f, (eop, oldOwners + owner))) + } else { + fieldMutabilityDependees += ((f, (eop, Set(owner)))) + } + } + + def addClassImmutabilityDependee( + t: ObjectType, + eop: EOptionP[ObjectType, ClassImmutability_new], //ClassImmutability], + value: Expr[V] + ): Unit = { + if (classImmutabilityDependees.contains(t)) { + val (_, oldValues) = classImmutabilityDependees(t) + classImmutabilityDependees += ((t, (eop, oldValues + value))) + } else { + classImmutabilityDependees += ((t, (eop, Set(value)))) + } + } + + def addTypeImmutabilityDependee( + t: ObjectType, + eop: EOptionP[ObjectType, TypeImmutability_new], // TypeImmutability], + value: Expr[V] + ): Unit = { + if (typeImmutabilityDependees.contains(t)) { + val (_, oldValues) = typeImmutabilityDependees(t) + typeImmutabilityDependees += ((t, (eop, oldValues + value))) + } else { + typeImmutabilityDependees += ((t, (eop, Set(value)))) + } + } + + def addPurityDependee( + dm: DeclaredMethod, + eop: EOptionP[DeclaredMethod, Purity], + params: Seq[Expr[V]] + ): Unit = { + if (purityDependees.contains(dm)) { + val (_, oldParams) = purityDependees(dm) + purityDependees += ((dm, (eop, oldParams + params))) + } else { + purityDependees += ((dm, (eop, Set(params)))) + } + } + + def updateCalleesDependee(eps: EOptionP[DeclaredMethod, Callees]): Unit = { + if (eps.isFinal) calleesDependee = None + else calleesDependee = Some(eps) + if (eps.hasUBP) + callees = Some(eps.ub) + } + + def addRVFDependee( + dm: DeclaredMethod, + eop: EOptionP[DeclaredMethod, ReturnValueFreshness], + data: (Option[Expr[V]], Purity) + ): Unit = { + if (rvfDependees.contains(dm)) { + val (_, oldValues) = rvfDependees(dm) + rvfDependees += ((dm, (eop, oldValues + data))) + } else { + rvfDependees += ((dm, (eop, Set(data)))) + } + } + + def removeFieldLocalityDependee(f: Field): Unit = fieldLocalityDependees -= f + def removeFieldMutabilityDependee(f: Field): Unit = fieldMutabilityDependees -= f + def removeClassImmutabilityDependee(t: ObjectType): Unit = classImmutabilityDependees -= t + def removeTypeImmutabilityDependee(t: ObjectType): Unit = typeImmutabilityDependees -= t + def removePurityDependee(dm: DeclaredMethod): Unit = purityDependees -= dm + def removeRVFDependee(dm: DeclaredMethod): Unit = rvfDependees -= dm + + def updateStaticDataUsage(eps: Option[EOptionP[DeclaredMethod, StaticDataUsage]]): Unit = { + staticDataUsage = eps + } + + def updateTacai(eps: EOptionP[Method, TACAI]): Unit = { + if (eps.isFinal) tacai = None + else tacai = Some(eps) + if (eps.hasUBP && eps.ub.tac.isDefined) { + val tac = eps.ub.tac.get + pcToIndex = tac.pcToIndex + code = tac.stmts + } + } + } + + type StateType = State + + val raterFqn: String = project.config.as[String]( + "org.opalj.fpcf.analyses.L2PurityAnalysis.domainSpecificRater" + ) + + val rater: DomainSpecificRater = + L2PurityAnalysis.rater.getOrElse(resolveDomainSpecificRater(raterFqn)) + + /** + * Examines whether the given expression denotes an object/array that is local to the current + * method, i.e. the method has control over the object/array and actions on it might not + * influence purity. + * + * @param otherwise The maxPurity will be reduced to at most this level if the expression is not + * local. + */ + override def isLocal( + expr: Expr[V], + otherwise: Purity, + excludedDefSites: IntTrieSet = EmptyIntTrieSet + )(implicit state: State): Boolean = { + isLocalInternal( + expr, + otherwise, + _ ⇒ CompileTimePure, + treatParamsAsFresh = false + ) + } + + /** + * Examines whether the given expression denotes an object/array that is local to the current + * method, i.e. the method has control over the object/array and actions on it might not + * influence purity. + * + * @note Fresh references can be treated as non-escaping as the analysis result will be impure + * if anything escapes the method via parameters, static field assignments or calls. + * + * @param otherwise The maxPurity will be reduced to at most this level if the expression is not + * local + * @param onParameter The maxPurity will be reduced to at most this level if the expression can + * be a parameter + * @param treatParamsAsFresh The value to be returned if the expression can be a parameter + */ + def isLocalInternal( + expr: Expr[V], + otherwise: Purity, + onParameter: Int ⇒ Purity, + 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.isConst) + return true; + + if (!expr.isVar) { + // The expression could refer to further expressions in a non-flat representation. + // In that case it could be, e.g., a GetStatic. In that case the reference is not + // locally created and/or initialized. To avoid special handling, we just fallback to + // false here as the analysis is intended to be used on flat representations anyway. + atMost(otherwise) + return false; + } + + // Primitive values are always local (required for parameters of contextually pure calls) + // TODO (value is null for the self reference of a throwable constructor...) + if (expr.asVar.value != null && + (expr.asVar.value.computationalType ne ComputationalTypeReference)) + return true; + + val defSites = expr.asVar.definedBy -- excludedDefSites + val isLocal = + defSites.forall( + isLocalDefsite( + _, + otherwise, + onParameter, + treatParamsAsFresh, + defSites, + excludedDefSites + ) + ) + if (!isLocal) atMost(otherwise) + isLocal + } + + /** + * Examines whether the given defsite denotes an object/array that is local to the current + * method, i.e. the method has control over the object/array and actions on it might not + * influence purity. + * + * @param otherwise The maxPurity will be reduced to at most this level if the defsite is not + * local + * @param onParameter The maxPurity will be reduced to at most this level if the defsite is a + * parameter + * @param treatParamsAsFresh The value to be returned if the defsite is a parameter + */ + def isLocalDefsite( + defSite: Int, + otherwise: Purity, + onParameter: Int ⇒ Purity, + treatParamsAsFresh: Boolean, + defSites: IntTrieSet, + excludedDefSites: IntTrieSet + )(implicit state: State): Boolean = { + if (isImmediateVMException(defSite)) + return true; // VMLevelValues are freshly created + + if (ai.isMethodExternalExceptionOrigin(defSite)) + return false; // Method external exceptions are not freshly created + + if (defSite == OriginOfThis) { + if (!state.method.isConstructor) { + atMost(onParameter(0)) + } + return treatParamsAsFresh | state.method.isConstructor; + } + + if (defSite < 0) { + atMost(onParameter(-defSite - 1)) + return treatParamsAsFresh; + } + + val stmt = state.code(defSite) + assert(stmt.astID == Assignment.ASTID, "defSite should be assignment") + + val rhs = stmt.asAssignment.expr + if (rhs.isConst) + return true; + + (rhs.astID: @switch) match { + case New.ASTID | NewArray.ASTID ⇒ true + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | + VirtualFunctionCall.ASTID ⇒ + val oldPurityLevel = + state.rvfCallSites.get(stmt.pc).map(_._2).getOrElse(CompileTimePure) + val data = (rhs.asFunctionCall.receiverOption, otherwise meet oldPurityLevel) + if (state.callees.isDefined) { + checkFreshnessOfReturn(stmt.pc, data, state.callees.get) + } else { + state.rvfCallSites += stmt.pc → data + reducePurityLB(otherwise) + } + true + case GetField.ASTID ⇒ + val GetField(_, declClass, name, fieldType, objRef) = rhs + project.resolveFieldReference(declClass, name, fieldType) match { + case Some(field) ⇒ + val locality = propertyStore(field, FieldLocality.key) + checkLocalityOfField(locality, (objRef, otherwise)) && + isLocalInternal( + objRef, + otherwise, + onParameter, + treatParamsAsFresh, + excludedDefSites ++ defSites + ) + case None ⇒ false + } + case _ ⇒ false + } + } + + def checkLocalityOfField( + ep: EOptionP[Field, FieldLocality], + data: (Expr[V], Purity) + )(implicit state: State): Boolean = { + val isLocal = ep match { + case FinalP(LocalField | LocalFieldWithGetter) ⇒ + true + case FinalP(ExtensibleLocalField | ExtensibleLocalFieldWithGetter) ⇒ + if (data._1.isVar) { + val value = data._1.asVar.value.asReferenceValue + value.isPrecise && + !classHierarchy.isSubtypeOf(value.asReferenceType, ObjectType.Cloneable) + } else + false + case UBP(NoLocalField) ⇒ + false + case _ ⇒ + reducePurityLB(data._2) + if (data._2 meet state.ubPurity ne state.ubPurity) + state.addFieldLocalityDependee(ep.e, ep, data) + true + } + if (!isLocal) + atMost(data._2) + isLocal + } + + def checkLocalityOfReturn( + ep: EOptionP[DeclaredMethod, Property], + data: (Option[Expr[V]], Purity) + )(implicit state: State): Unit = { + import project.classHierarchy.isSubtypeOf + ep match { + case LBP(PrimitiveReturnValue | FreshReturnValue) ⇒ + case FinalP(Getter) ⇒ + if (data._2 meet state.ubPurity ne state.ubPurity) + isLocal(data._1.get, data._2) + case FinalP(ExtensibleGetter) ⇒ + if (data._1.get.isVar) { + val value = data._1.get.asVar.value.asReferenceValue + if (value.isPrecise && !isSubtypeOf(value.asReferenceType, ObjectType.Cloneable)) { + if (data._2 meet state.ubPurity ne state.ubPurity) + isLocal(data._1.get, data._2) + } else { + atMost(data._2) + } + } else { + atMost(data._2) + } + case UBP(NoFreshReturnValue) ⇒ + atMost(data._2) + case _: SomeEOptionP ⇒ + reducePurityLB(data._2) + if (data._2 meet state.ubPurity ne state.ubPurity) { + state.addRVFDependee( + ep.e, + ep.asInstanceOf[EOptionP[DeclaredMethod, ReturnValueFreshness]], + data + ) + } + } + } + + def checkFreshnessOfReturn( + pc: Int, + data: (Option[Expr[V]], Purity), + callees: Callees + )(implicit state: State): Unit = { + callees.callees(pc).foreach { callee ⇒ + if (callee.descriptor.returnType.isReferenceType) + checkLocalityOfReturn(propertyStore(callee, ReturnValueFreshness.key), data) + } + } + + /** + * Examines a statement for its influence on the method's purity. + * This method will return false for impure statements, so evaluation can be terminated early. + */ + override def checkPurityOfStmt(stmt: Stmt[V])(implicit state: State): Boolean = + (stmt.astID: @switch) match { + // Synchronization on non-escaping local objects/arrays is pure (and useless...) + case MonitorEnter.ASTID | MonitorExit.ASTID ⇒ + val objRef = stmt.asSynchronizationStmt.objRef + isLocalInternal( + objRef, + ImpureByAnalysis, + param ⇒ ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + ) && stmt.forallSubExpressions(checkPurityOfExpr) + + // Storing into non-escaping locally initialized arrays/objects is pure + case ArrayStore.ASTID ⇒ + val arrayRef = stmt.asArrayStore.arrayRef + isLocalInternal( + arrayRef, + ImpureByAnalysis, + param ⇒ ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + ) && stmt.forallSubExpressions(checkPurityOfExpr) + case PutField.ASTID ⇒ + val objRef = stmt.asPutField.objRef + isLocalInternal( + objRef, + ImpureByAnalysis, + param ⇒ ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + ) && stmt.forallSubExpressions(checkPurityOfExpr) + + case _ ⇒ super.checkPurityOfStmt(stmt) + } + + /** + * Examines the influence of the purity property of a method on the examined method's purity. + * + * @note Adds dependendees when necessary. + */ + def checkMethodPurity( + ep: EOptionP[DeclaredMethod, Purity], + params: Seq[Expr[V]] + )(implicit state: State): Boolean = ep match { + case UBP(_: ClassifiedImpure) ⇒ + atMost(ImpureByAnalysis) + false + case eps @ LUBP(lb, ub) ⇒ + if (eps.isRefinable && ((lb meet state.ubPurity) ne state.ubPurity)) { + // On conditional, keep dependence + state.addPurityDependee(ep.e, ep, params) + reducePurityLB(lb) + } + // Contextual/external purity is handled below + atMost(ub.withoutContextual) + ub.modifiedParams.forall(param ⇒ + isLocalInternal( + params(param), + ImpureByAnalysis, + param ⇒ ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + )) + case _: SomeEOptionP ⇒ + reducePurityLB(ImpureByAnalysis) + state.addPurityDependee(ep.e, ep, params) + true + } + + /** + * Handles the effect of static data usage on the purity level. + * + * @note Modifies dependees as necessary. + */ + def checkStaticDataUsage(ep: EOptionP[DeclaredMethod, StaticDataUsage])(implicit state: State): Unit = { + ep match { + case LBP(UsesNoStaticData | UsesConstantDataOnly) ⇒ + state.updateStaticDataUsage(None) + case UBP(UsesVaryingData) ⇒ + state.updateStaticDataUsage(None) + atMost(Pure) + case _ ⇒ + reducePurityLB(Pure) + state.updateStaticDataUsage(Some(ep)) + } + } + + /** + * Adds the dependee necessary if a field mutability is not known yet. + */ + override def handleUnknownFieldMutability( + ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], + objRef: Option[Expr[V]] + )(implicit state: State): Unit = { + state.addFieldMutabilityDependee(ep.e, ep, objRef) + } + + /** + * Adds the dependee necessary if a type mutability is not known yet. + */ + override def handleUnknownTypeMutability( + ep: EOptionP[ObjectType, Property], + expr: Expr[V] + )(implicit state: State): Unit = { + if (ep.pk == ClassImmutability_new.key) //ClassImmutability.key) + state.addClassImmutabilityDependee( + ep.e, + ep.asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]], //ClassImmutability]], + expr + ) + else + state.addTypeImmutabilityDependee( + ep.e, + ep.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]], //TypeImmutability]], + expr + ) + } + + /** + * Add or remove the dependee when the callees property changes. + */ + override def handleCalleesUpdate( + callees: EOptionP[DeclaredMethod, Callees] + )(implicit state: State): Unit = { + state.updateCalleesDependee(callees) + if (callees.isRefinable) + reducePurityLB(ImpureByAnalysis) + } + + /* + * Adds the dependee necessary if the TACAI is not yet final. + */ + override def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: State): Unit = { + state.updateTacai(ep) + } + + /** + * Removes dependees that are known to not be needed anymore as they can not reduce the max + * purity level further. + */ + def cleanupDependees()(implicit state: State): Unit = { + if (state.ubPurity ne CompileTimePure) + state.updateStaticDataUsage(None) + + if (!state.ubPurity.isDeterministic) { + state.fieldMutabilityDependees = Map.empty + state.classImmutabilityDependees = Map.empty + state.typeImmutabilityDependees = Map.empty + state.updateStaticDataUsage(None) + } + + 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))) + } + state.fieldLocalityDependees = newFieldLocalityDependees + + var newRVFCallsites: IntMap[(Option[Expr[V]], Purity)] = IntMap.empty + for ((callsite, data) ← state.rvfCallSites) { + if (data._2 meet state.ubPurity ne state.ubPurity) newRVFCallsites += ((callsite, data)) + } + 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))) + } + 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)) + } + state.purityDependees = newPurityDependees + } + + /** + * Raises the lower bound on the purity whenever possible. + */ + def adjustLowerBound()(implicit state: State): Unit = { + if (state.calleesDependee.isDefined) + return ; // Nothing to be done, lower bound is still LBImpure + + var newLowerBound = state.ubPurity + + if (state.tacai.isDefined) return ; // Nothing to be done, lower bound is still LBImpure + + for ((eop, _) ← state.purityDependees.valuesIterator) { + eop match { + case LBP(lb) ⇒ newLowerBound = newLowerBound meet lb + case _ ⇒ + return ; // Nothing to be done, lower bound is still LBImpure + } + } + + if (state.staticDataUsage.isDefined) newLowerBound = newLowerBound meet Pure + + if (state.fieldMutabilityDependees.nonEmpty || + state.classImmutabilityDependees.nonEmpty || + state.typeImmutabilityDependees.nonEmpty) { + newLowerBound = newLowerBound meet SideEffectFree + } + + for { + (_, data) ← state.fieldLocalityDependees.valuesIterator + (_, purity) ← data + } { + newLowerBound = newLowerBound meet purity + } + + for { + (_, purity) ← state.rvfCallSites.valuesIterator + } { + newLowerBound = newLowerBound meet purity + } + + for { + (_, data) ← state.rvfDependees.valuesIterator + (_, purity) ← data + } { + newLowerBound = newLowerBound meet purity + } + + state.lbPurity = newLowerBound + } + + /** + * Continuation to handle updates to properties of dependees. + * Dependees may be + * - methods / virtual methods called (for their purity) + * - fields read (for their mutability) + * - classes files for class types returned (for their mutability) + */ + def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { + val oldPurity = state.ubPurity + eps.ub.key match { + case Purity.key ⇒ + val e = eps.e.asInstanceOf[DeclaredMethod] + val dependees = state.purityDependees(e) + state.removePurityDependee(e) + dependees._2.foreach { e ⇒ + checkMethodPurity(eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]], e) + } + //case FieldMutability.key ⇒ + case ReferenceImmutability.key ⇒ + val e = eps.e.asInstanceOf[Field] + val dependees = state.fieldMutabilityDependees(e) + state.removeFieldMutabilityDependee(e) + dependees._2.foreach { e ⇒ + checkFieldMutability(eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]], e) //FieldMutability]], e) + } + //case ClassImmutability.key ⇒ + case ClassImmutability_new.key ⇒ + val e = eps.e.asInstanceOf[ObjectType] + val dependees = state.classImmutabilityDependees(e) + state.removeClassImmutabilityDependee(e) + dependees._2.foreach { e ⇒ + checkTypeMutability( + eps.asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]], // ClassImmutability]], + e + ) + } + //case TypeImmutability.key ⇒ + case TypeImmutability_new.key ⇒ + val e = eps.e.asInstanceOf[ObjectType] + val dependees = state.typeImmutabilityDependees(e) + state.removeTypeImmutabilityDependee(e) + dependees._2.foreach { e ⇒ + checkTypeMutability(eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]], e) //TypeImmutability]], e) + } + case ReturnValueFreshness.key ⇒ + val e = eps.e.asInstanceOf[DeclaredMethod] + val dependees = state.rvfDependees(e) + state.removeRVFDependee(e) + dependees._2.foreach { e ⇒ + checkLocalityOfReturn( + eps.asInstanceOf[EOptionP[DeclaredMethod, ReturnValueFreshness]], + e + ) + } + case FieldLocality.key ⇒ + val e = eps.e.asInstanceOf[Field] + val dependees = state.fieldLocalityDependees(e) + state.removeFieldLocalityDependee(e) + dependees._2.foreach { e ⇒ + checkLocalityOfField(eps.asInstanceOf[EOptionP[Field, FieldLocality]], e) + } + case Callees.key ⇒ + checkPurityOfCallees(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + state.rvfCallSites.foreach { + case (pc, data) ⇒ checkFreshnessOfReturn(pc, data, eps.ub.asInstanceOf[Callees]) + } + case StaticDataUsage.key ⇒ + checkStaticDataUsage(eps.asInstanceOf[EOptionP[DeclaredMethod, StaticDataUsage]]) + case TACAI.key ⇒ + state.updateTacai(eps.asInstanceOf[EOptionP[Method, TACAI]]) + return determineMethodPurity(eps.ub.asInstanceOf[TACAI].tac.get.cfg); + } + + if (state.ubPurity eq ImpureByAnalysis) + return Result(state.definedMethod, ImpureByAnalysis); + + if (state.ubPurity ne oldPurity) + cleanupDependees() // Remove dependees that we don't need anymore. + adjustLowerBound() + + val dependees = state.dependees + if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { + Result(state.definedMethod, state.ubPurity) + } else { + InterimResult( + state.definedMethod, + state.lbPurity, + state.ubPurity, + dependees, + c + ) + } + } + + /** + * Determines the purity of a method once TACAI is available. + */ + def determineMethodPurity( + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): ProperPropertyComputationResult = { + // Special case: The Throwable constructor is `LBSideEffectFree`, but subtype constructors + // may not be because of overridable fillInStackTrace method + if (state.method.isConstructor && state.declClass.isSubtypeOf(ObjectType.Throwable)) + project.instanceMethods(state.declClass).foreach { mdc ⇒ + if (mdc.name == "fillInStackTrace" && + mdc.method.classFile.thisType != ObjectType.Throwable) { + // "The value" is actually not used at all - hence, we can use "null" + // over here. + val selfReference = UVar(null, SelfReferenceParameter) + val fISTPurity = propertyStore(declaredMethods(mdc.method), Purity.key) + if (!checkMethodPurity(fISTPurity, Seq(selfReference))) { + // Early return for impure fillInStackTrace + return Result(state.definedMethod, state.ubPurity); + } + } + } + + // Synchronized methods have a visible side effect on the receiver + // Static synchronized methods lock the class which is potentially globally visible + if (state.method.isSynchronized) + if (state.method.isStatic) return Result(state.definedMethod, ImpureByAnalysis); + else atMost(ContextuallyPure(IntTrieSet(0))) + + val stmtCount = state.code.length + var s = 0 + while (s < stmtCount) { + if (!checkPurityOfStmt(state.code(s))) { // Early return for impure statements + assert(state.ubPurity.isInstanceOf[ClassifiedImpure]) + return Result(state.definedMethod, state.ubPurity); + } + s += 1 + } + + val callees = propertyStore(state.definedMethod, Callees.key) + if (!checkPurityOfCallees(callees)) + return Result(state.definedMethod, state.ubPurity) + + if (callees.hasUBP) + state.rvfCallSites.foreach { + case (pc, data) ⇒ + checkFreshnessOfReturn(pc, data, callees.ub) + } + + // Creating implicit exceptions is side-effect free (because of fillInStackTrace) + // but it may be ignored as domain-specific + val bbsCausingExceptions = cfg.abnormalReturnNode.predecessors + for { + bb ← bbsCausingExceptions + } { + val pc = bb.asBasicBlock.endPC + if (isSourceOfImmediateException(pc)) { + val throwingStmt = state.code(pc) + val ratedResult = rater.handleException(throwingStmt) + if (ratedResult.isDefined) atMost(ratedResult.get) + else atMost(SideEffectFree) + } + } + + if (state.ubPurity eq CompileTimePure) // Check static data usage only if necessary + checkStaticDataUsage(propertyStore(state.definedMethod, StaticDataUsage.key)) + else + cleanupDependees() // Remove dependees we already know we won't need + + val dependees = state.dependees + if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { + Result(state.definedMethod, state.ubPurity) + } else { + org.opalj.fpcf.InterimResult(state.definedMethod, state.lbPurity, state.ubPurity, dependees, c) + } + } + + /** + * Determines the purity of the given method. + * + * @param definedMethod A defined method with a body. + */ + def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult = { + val method = definedMethod.definedMethod + val declClass = method.classFile.thisType + + // If this is not the method's declaration, but a non-overwritten method in a subtype, + // don't re-analyze the code + if (declClass ne definedMethod.declaringClassType) + return baseMethodPurity(definedMethod.asDefinedMethod); + + implicit val state: State = + new State(method, definedMethod, declClass) + + val tacaiO = getTACAI(method) + + if (tacaiO.isEmpty) + return InterimResult( + definedMethod, + ImpureByAnalysis, + CompileTimePure, + state.dependees, + c + ); + + determineMethodPurity(tacaiO.get.cfg) + } +} + +object L2PurityAnalysis_new { + + /** + * Domain-specific rater used to examine whether certain statements and expressions are + * domain-specific. + * If the Option is None, a rater is created from a config file option. + */ + var rater: Option[DomainSpecificRater] = None + + def setRater(newRater: Option[DomainSpecificRater]): Unit = { + rater = newRater + } +} + +trait L2PurityAnalysisScheduler_new extends FPCFAnalysisScheduler { + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(Purity) + + override def requiredProjectInformation: ProjectInformationKeys = + Seq(DeclaredMethodsKey, ConfiguredPurityKey) + + override def uses: Set[PropertyBounds] = { + Set( + //PropertyBounds.lub(FieldMutability), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.lub(FieldImmutability), + //PropertyBounds.lub(ClassImmutability), + PropertyBounds.lub(ClassImmutability_new), + //PropertyBounds.lub(TypeImmutability), + PropertyBounds.lub(TypeImmutability_new), + PropertyBounds.lub(StaticDataUsage), + PropertyBounds.lub(ReturnValueFreshness), + PropertyBounds.ub(FieldLocality), + PropertyBounds.ub(TACAI), + PropertyBounds.ub(Callees), + PropertyBounds.lub(Purity) + ) + } + + final override type InitializationData = L2PurityAnalysis_new + final override def init(p: SomeProject, ps: PropertyStore): InitializationData = { + new L2PurityAnalysis_new(p) + } + + 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 = {} + +} + +object EagerL2PurityAnalysis_new extends L2PurityAnalysisScheduler_new with FPCFEagerAnalysisScheduler { + + override def start( + p: SomeProject, ps: PropertyStore, analysis: InitializationData + ): FPCFAnalysis = { + 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 ⇒ + dm.asDefinedMethod + } + ps.scheduleEagerComputationsForEntities(methods)( + analysis.determinePurity + ) + analysis + } + + override def uses: Set[PropertyBounds] = super.uses + PropertyBounds.finalP(Callers) + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty +} + +object LazyL2PurityAnalysis_new extends L2PurityAnalysisScheduler_new with FPCFLazyAnalysisScheduler { + + override def register( + p: SomeProject, ps: PropertyStore, analysis: InitializationData + ): FPCFAnalysis = { + ps.registerLazyPropertyComputation(Purity.key, analysis.doDeterminePurity) + analysis + } + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) +} From a90d28a73da90470dc7ab13f01dbfd7d286654e6 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 6 Mar 2020 11:59:25 +0100 Subject: [PATCH 102/327] formatting --- .../ClassImmutabilityAnalysisDemo.scala | 175 +- ...AnalysisDemo_performanceMeasurements.scala | 128 +- .../FieldImmutabilityAnalysisDemo.scala | 173 +- ...AnalysisDemo_performanceMeasurements.scala | 130 +- .../ReferenceImmutabilityAnalysisDemo.scala | 142 +- ...AnalysisDemo_performanceMeasurements.scala | 122 +- ...AnalysisDemo_performanceMeasurements.scala | 154 +- .../TypeImmutabilityAnalysisDemo.scala | 142 +- ...yAnalysisDemo_performanceMeasurement.scala | 128 +- .../org/opalj/br/fpcf/PropertyStoreKey.scala | 3 +- .../analyses/ClassImmutabilityAnalysis.scala | 768 +++--- .../analyses/TypeImmutabilityAnalysis.scala | 403 +-- .../fpcf/properties/ClassImmutability.scala | 66 +- .../properties/ClassImmutability_new.scala | 99 +- .../fpcf/properties/FieldImmutability.scala | 76 +- .../properties/ReferenceImmutability.scala | 60 +- .../properties/TypeImmutability_new.scala | 132 +- .../opalj/fpcf/ComputationSpecification.scala | 290 +-- .../src/main/scala/org/opalj/tac/Stmt.scala | 1734 ++++++------- .../L0ReferenceImmutabilityAnalysis.scala | 1820 +++++++------- ...mutabilityLazyInitializationAnalysis.scala | 2170 ++++++++--------- .../LxTypeImmutabilityAnalysis_new.scala | 434 ++-- 22 files changed, 4704 insertions(+), 4645 deletions(-) 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 index c4d1503537..2b99226af8 100644 --- 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 @@ -7,30 +7,32 @@ import java.io.FileWriter import java.net.URL import java.util.Calendar +import org.opalj.br.ObjectType + +//import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new 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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis -import org.opalj.br.fpcf.properties.ClassImmutability_new 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.fpcf.FinalEP import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -42,87 +44,106 @@ import org.opalj.util.Seconds */ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" - override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + def analyze(project: Project[URL]): String = { - def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) - val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) - analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyL1FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new, - LazyTypeImmutabilityAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyStaticDataUsageAnalysis, - LazySimpleEscapeAnalysis + val sb = new StringBuilder + sb.append("Mutable Class: \n") + val mutableClasses = propertyStore + .finalEntities(MutableClass) + .toList + sb.append( + mutableClasses + .map(x ⇒ x.toString+" |Mutable Class\n") + ) + sb.append("\nShallow Immutable Class:\n") + val shallowImmutableClasses = propertyStore.finalEntities(ShallowImmutableClass) + .toList + sb.append( + shallowImmutableClasses + .map(x ⇒ x.toString+" |Shallow Immutable Class\n") + ) + sb.append("\nDependent Immutable Class: \n") + val dependentImmutableClasses = propertyStore + .finalEntities(DependentImmutableClass).toList + sb.append( + dependentImmutableClasses + .map(x ⇒ x.toString+" |Dependent Immutable Class\n") ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t => - analysisTime = t.toSeconds - } - val sb = new StringBuilder - sb.append("Mutable Class: \n") - sb.append( - propertyStore - .finalEntities(MutableClass) - .toList - .map(x => x.toString + "\n") - ) - sb.append("\nDependent Immutable Class: \n") - sb.append( - propertyStore - .entities(ClassImmutability_new.key) - .toList - .collect({ case x @ FinalEP(_, DependentImmutableClass) => x }) - .map(x => x.toString + "\n") - ) - sb.append("\nShallow Immutable Class: \n") - sb.append( - propertyStore - .finalEntities(ShallowImmutableClass) - .toList - .map(x => x.toString + "\n") - ) - sb.append("\nDeep Immutable Class: \n") - sb.append( - propertyStore - .finalEntities(DeepImmutableClass) - .toList - .map(x => x.toString + "\n") - ) + sb.append("\nDeep Immutable Class Classes:\n") + val allInterfaces = project.allClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet + val deepImmutableClasses = propertyStore + .finalEntities(DeepImmutableClass).toList + .filter(x ⇒ !x.isInstanceOf[ObjectType] || !allInterfaces.contains(x.asInstanceOf[ObjectType])) + sb.append( + deepImmutableClasses + .toList + .map(x ⇒ x.toString+" |Deep Immutable Class\n") + ) + sb.append("\nDeep Immutable Class Classes: Interface\n") + val deepImmutableClassesInterfaces = propertyStore + .finalEntities(DeepImmutableClass).toList + .filter(x ⇒ x.isInstanceOf[ObjectType] && allInterfaces.contains(x.asInstanceOf[ObjectType])) + sb.append( + deepImmutableClassesInterfaces + .map(x ⇒ x.toString+" |Deep Immutable Class Interface\n") + ) + sb.append("\n\n") + sb.append("mutable Classes: "+mutableClasses.size+"\n") + sb.append("shallow immutable classes: "+shallowImmutableClasses.size+"\n") + sb.append("dependent immutable classes: "+dependentImmutableClasses.size+"\n") + sb.append("deep immutable classes: "+deepImmutableClasses.size+"\n") + sb.append("deep immutable classes interfaces: "+deepImmutableClassesInterfaces.size+"\n") - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/classImm" + dateString + ".txt") - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/classImm"+dateString+".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() - " took : " + analysisTime + " seconds" - } + " took : "+analysisTime+" seconds" + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala index c965db7431..8fff676954 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -7,17 +7,14 @@ 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.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis @@ -32,72 +29,69 @@ import org.opalj.util.Seconds; */ object ClassImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { - override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + override def title: String = "run EagerLxClassImmutabilityAnalysis_new" - override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + override def description: String = "run EagerLxClassImmutabilityAnalysis_new" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(theProject: Project[URL]): String = { - var times: List[Seconds] = Nil: List[Seconds] - for (i <- 0 until 10) { - val project = Project.recreate(theProject) - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new, - LazyTypeImmutabilityAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyStaticDataUsageAnalysis, - LazySimpleEscapeAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t => - analysisTime = t.toSeconds - } - times = analysisTime :: times + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) } - /** - * "Mutable Class: "+propertyStore - * .finalEntities(MutableClass) - * .toList - * .toString()+"\n"+ - * "Dependent Immutable Class: "+propertyStore - * .entities(ClassImmutability_new.key) - * .toList - * .toString()+"\n"+ - * "Shallow Immutable Class: "+propertyStore - * .finalEntities(ShallowImmutableClass) - * .toList - * .toString()+"\n"+ - * "Deep Immutable Class: "+propertyStore - * .finalEntities(DeepImmutableClass) - * .toList - * .toString()+"\n" - */ - times.foreach(s => println(s + " seconds")) - val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size - f"took: $aver seconds on average" - } + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i ← 0 until 10) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyStaticDataUsageAnalysis, + LazySimpleEscapeAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + + /** + * "Mutable Class: "+propertyStore + * .finalEntities(MutableClass) + * .toList + * .toString()+"\n"+ + * "Dependent Immutable Class: "+propertyStore + * .entities(ClassImmutability_new.key) + * .toList + * .toString()+"\n"+ + * "Shallow Immutable Class: "+propertyStore + * .finalEntities(ShallowImmutableClass) + * .toList + * .toString()+"\n"+ + * "Deep Immutable Class: "+propertyStore + * .finalEntities(DeepImmutableClass) + * .toList + * .toString()+"\n" + */ + times.foreach(s ⇒ println(s+" seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size + f"took: $aver seconds on average" + } } 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 index 7331dfc42e..62586d6bcd 100644 --- 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 @@ -13,6 +13,8 @@ import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.DeepImmutableField @@ -21,10 +23,13 @@ import org.opalj.br.fpcf.properties.MutableField import org.opalj.br.fpcf.properties.ShallowImmutableField import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +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.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -36,87 +41,103 @@ import org.opalj.util.Seconds */ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0FieldImmutabilityAnalysis" + override def title: String = "runs the EagerL0FieldImmutabilityAnalysis" - override def description: String = - "runs the EagerL0FieldImmutabilityAnalysis" + override def description: String = + "runs the EagerL0FieldImmutabilityAnalysis" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { - propertyStore = analysesManager - .runAll( - LazyLxClassImmutabilityAnalysis_new, - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t => - analysisTime = t.toSeconds - } + propertyStore = analysesManager + .runAll( + LazyLxClassImmutabilityAnalysis_new, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyL1FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } - val sb: StringBuilder = new StringBuilder - sb.append("Mutable Fields: \n") - sb.append( - propertyStore - .finalEntities(MutableField) - .toList - .map(x => x.toString + "\n") - .toString() - ) - sb.append("\nShallow Immutable Fields: \n") - sb.append( - propertyStore - .finalEntities(ShallowImmutableField) - .toList - .map(x => x.toString + "\n") - .toString() - ) - sb.append("\nDependet Immutable Fields: \n") - sb.append( - propertyStore - .finalEntities(DependentImmutableField) - .toList - .map(x => x.toString + "\n") - .toString() - ) - sb.append("Deep Immutable Fields: ") - sb.append( - propertyStore - .finalEntities(DeepImmutableField) - .toList - .map(x => x.toString + "\n") - .toString - ) - - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/fieldImm" + dateString + ".txt") - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() + val sb: StringBuilder = new StringBuilder + sb.append("Mutable Fields: \n") + val mutableFields = propertyStore + .finalEntities(MutableField) + .toList + sb.append( + mutableFields + .map(x ⇒ x.toString+" |Mutable Field\n") + .toString() + ) + sb.append("\nShallow Immutable Fields: \n") + val shallowImmutableFields = propertyStore + .finalEntities(ShallowImmutableField) + .toList + sb.append( + shallowImmutableFields + .map(x ⇒ x.toString+" |Shallow Immutable Field\n") + .toString() + ) + sb.append("\nDependet Immutable Fields: \n") + val dependentImmutableFields = propertyStore + .finalEntities(DependentImmutableField) + .toList + sb.append( + dependentImmutableFields + .map(x ⇒ x.toString+" |Dependent Immutable Field\n") + .toString() + ) + sb.append("Deep Immutable Fields: ") + val deepImmutableFields = propertyStore + .finalEntities(DeepImmutableField) + .toList + sb.append( + deepImmutableFields + .map(x ⇒ x.toString+" |Deep Immutable Field\n") + .toString + ) + sb.append("\n\n") + sb.append( + s""" mutable fields: ${mutableFields.size} + | shallow immutable fields: ${shallowImmutableFields.size} + | dependent immutable fields: ${dependentImmutableFields.size} + | deep immutable fields: ${deepImmutableFields.size} + |""".stripMargin + ) + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/fieldImm_"+dateString+".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() - " took : " + analysisTime + " seconds" - } + " took : "+analysisTime+" seconds" + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala index b0b5ce4bcf..8cbbce302b 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -8,13 +8,10 @@ import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis @@ -28,73 +25,70 @@ import org.opalj.util.Seconds */ object FieldImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0FieldImmutabilityAnalysis" + override def title: String = "runs the EagerL0FieldImmutabilityAnalysis" - override def description: String = - "runs the EagerL0FieldImmutabilityAnalysis" + override def description: String = + "runs the EagerL0FieldImmutabilityAnalysis" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(theProject: Project[URL]): String = { - var times: List[Seconds] = Nil: List[Seconds] - for (i <- 0 until 10) { - val project = Project.recreate(theProject) - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyLxClassImmutabilityAnalysis_new, - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t => - analysisTime = t.toSeconds - } - times = analysisTime :: times + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) } - /** - * "Mutable Fields: "+propertyStore - * .finalEntities(MutableField) - * .toList - * .toString()+"\n"+ - * "Shallow Immutable Fields: "+propertyStore - * .finalEntities(ShallowImmutableField) - * .toList - * .toString()+"\n"+ - * "Dependet Immutable Fields:"+propertyStore - * .finalEntities(DependentImmutableField(None)) - * .toList - * .toString()+"\n"+ - * "Deep Immutable Fields: "+propertyStore - * .finalEntities(DeepImmutableField) - * .toList - * .toString()+"\n"+ - * propertyStore - * .entities(FieldImmutability.key) - * .toList - * .toString* - */ - times.foreach(s => println(s + " seconds")) - val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size - f"took: $aver seconds on average" - } + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i ← 0 until 10) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyLxClassImmutabilityAnalysis_new, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + + /** + * "Mutable Fields: "+propertyStore + * .finalEntities(MutableField) + * .toList + * .toString()+"\n"+ + * "Shallow Immutable Fields: "+propertyStore + * .finalEntities(ShallowImmutableField) + * .toList + * .toString()+"\n"+ + * "Dependet Immutable Fields:"+propertyStore + * .finalEntities(DependentImmutableField(None)) + * .toList + * .toString()+"\n"+ + * "Deep Immutable Fields: "+propertyStore + * .finalEntities(DeepImmutableField) + * .toList + * .toString()+"\n"+ + * propertyStore + * .entities(FieldImmutability.key) + * .toList + * .toString* + */ + times.foreach(s ⇒ println(s+" seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size + f"took: $aver seconds on average" + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index c9f7275191..a896610888 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -14,7 +14,6 @@ import org.opalj.br.fpcf.properties.MutableReference import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.time @@ -23,9 +22,11 @@ import java.io._ import java.util.Calendar import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis /** @@ -35,73 +36,88 @@ import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis */ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyTypeImmutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyFieldLocalityAnalysis + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyFieldLocalityAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyL1FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + var sb: StringBuilder = new StringBuilder() + sb = sb.append("Mutable References: \n") + val mutableReferences = propertyStore.finalEntities(MutableReference).toList + sb = sb.append( + mutableReferences.map(x ⇒ x.toString+"\n").toString() + ) + + sb = sb.append("\n Lazy Initialized Reference: \n") + val lazyInitializedReferences = propertyStore + .finalEntities(LazyInitializedReference) + .toList + sb = sb.append( + lazyInitializedReferences + .map(x ⇒ x.toString+"\n") + .toString() ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t => - analysisTime = t.toSeconds - } - var sb: StringBuilder = new StringBuilder() - sb = sb.append("Mutable References: \n") - sb = sb.append( - propertyStore.finalEntities(MutableReference).toList.map(x => x.toString + "\n").toString() - ) - sb = sb.append("\n Lazy Initialized Reference: \n") - sb = sb.append( - propertyStore - .finalEntities(LazyInitializedReference) - .toList - .map(x => x.toString + "\n") - .toString() - ) + /** + * .toList + * .toString() + "\n" +* + */ + sb = sb.append("\nImmutable References: \n") + val immutableReferences = propertyStore.finalEntities(ImmutableReference).toList + sb = sb.append( + immutableReferences.map(x ⇒ x.toString+"\n").toString() + ) + sb.append("\n\n") + sb.append(" took : "+analysisTime+" seconds\n") + sb.append( + s""" mutable References: ${mutableReferences.size} + | lazy initialized References: ${lazyInitializedReferences.size} + | immutable References: ${immutableReferences.size} + |""".stripMargin + + ) - /** - * .toList - * .toString() + "\n" +* - */ - sb = sb.append("\nImmutable References: \n") - sb = sb.append( - propertyStore.finalEntities(ImmutableReference).toList.map(x => x.toString + "\n").toString() - ) - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/refLazyImm" + dateString + ".txt") - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/refImm"+dateString+".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() - " took : " + analysisTime + " seconds" - } + " took : "+analysisTime+" seconds" + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala index c7ebda8175..b80f3e7a46 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -8,6 +8,7 @@ import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +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.LazyUnsoundPrematurelyReadFieldsAnalysis @@ -15,7 +16,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +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.tac.fpcf.analyses.purity.LazyL2PurityAnalysis @@ -30,68 +31,69 @@ import org.opalj.util.Seconds object ReferenceImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(theProject: Project[URL]): String = { - var times: List[Seconds] = Nil: List[Seconds] - for (i <- 0 until 10) { - val project = Project.recreate(theProject) - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyTypeImmutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyFieldLocalityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t => - analysisTime = t.toSeconds - } - times = analysisTime :: times + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) } - /** - * "Mutable References: "+ - * propertyStore - * .finalEntities(MutableReference) - * .toList - * .toString()+"\n"+ - * "Lazy Initialized Reference: "+propertyStore - * .finalEntities(LazyInitializedReference) - * .toList - * .toString()+"\n"+ - * "Immutable References: "+propertyStore - * .finalEntities(ImmutableReference) - * .toList - * .toString()+"\n"+* - */ - times.foreach(s => println(s + " seconds")) - val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size - f"took: $aver seconds on average" - } + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i ← 0 until 10) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyL1FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + + /** + * "Mutable References: "+ + * propertyStore + * .finalEntities(MutableReference) + * .toList + * .toString()+"\n"+ + * "Lazy Initialized Reference: "+propertyStore + * .finalEntities(LazyInitializedReference) + * .toList + * .toString()+"\n"+ + * "Immutable References: "+propertyStore + * .finalEntities(ImmutableReference) + * .toList + * .toString()+"\n"+* + */ + times.foreach(s ⇒ println(s+" seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size + f"took: $aver seconds on average" + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala index 54bc1e8ae3..f8dc592e0c 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala @@ -31,87 +31,87 @@ import org.opalj.util.Seconds object ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(theProject: Project[URL]): String = { - var times: List[Seconds] = Nil: List[Seconds] - for (i <- 0 until 10) { - val project = Project.recreate(theProject) - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityLazyInitializationAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyStaticDataUsageAnalysis, - LazyTypeImmutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyFieldLocalityAnalysis, - LazyReturnValueFreshnessAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t => - analysisTime = t.toSeconds - } - times = analysisTime :: times + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) } - /*def printResult(string: String, property: Property)( + + def analyze(theProject: Project[URL]): String = { + var times: List[Seconds] = Nil: List[Seconds] + for (i ← 0 until 10) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityLazyInitializationAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyTypeImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyFieldLocalityAnalysis, + LazyReturnValueFreshnessAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + times = analysisTime :: times + } + /*def printResult(string: String, property: Property)( implicit propertyStore: PropertyStore ): Unit = {} **/ - /** - * val notInitializedReferencesString = propertyStore - * .finalEntities(NoLazyInitialization) - * .toList - * .toString() - * val notThreadSafeLazyInitializationString = propertyStore - * .finalEntities(NotThreadSafeLazyInitialization) - * .toList - * .toString() - * val lazyInitializationString = propertyStore - * .finalEntities(LazyInitialization) - * .toList - * .toString() - * println(s"Not initialized References $notInitializedReferencesString") - * println(s"Not threadsafe Lazy Initialization: $notThreadSafeLazyInitializationString") - * println(s"Lazy Initialization String: $lazyInitializationString") - */ - /** - * "Not lazy initialized References: " + propertyStore - * .finalEntities(NoLazyInitialization) - * .toList - * .toString() + "\n" + - * "Not thread safe lazy initialization: " + propertyStore - * .finalEntities(NotThreadSafeLazyInitialization) - * .toList - * .toString() + "\n" + - * "Lazy Initialization: " + propertyStore - * .finalEntities(LazyInitialization) - * .toList - * .toString()* - */ - times.foreach(s => println(s + " seconds")) - val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size - f"took: $aver seconds on average" - } + /** + * val notInitializedReferencesString = propertyStore + * .finalEntities(NoLazyInitialization) + * .toList + * .toString() + * val notThreadSafeLazyInitializationString = propertyStore + * .finalEntities(NotThreadSafeLazyInitialization) + * .toList + * .toString() + * val lazyInitializationString = propertyStore + * .finalEntities(LazyInitialization) + * .toList + * .toString() + * println(s"Not initialized References $notInitializedReferencesString") + * println(s"Not threadsafe Lazy Initialization: $notThreadSafeLazyInitializationString") + * println(s"Lazy Initialization String: $lazyInitializationString") + */ + /** + * "Not lazy initialized References: " + propertyStore + * .finalEntities(NoLazyInitialization) + * .toList + * .toString() + "\n" + + * "Not thread safe lazy initialization: " + propertyStore + * .finalEntities(NotThreadSafeLazyInitialization) + * .toList + * .toString() + "\n" + + * "Lazy Initialization: " + propertyStore + * .finalEntities(LazyInitialization) + * .toList + * .toString()* + */ + times.foreach(s ⇒ println(s+" seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size + f"took: $aver seconds on average" + } } 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 index dcbcb5df3b..e71b5d6e48 100644 --- 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 @@ -12,6 +12,7 @@ import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis @@ -29,9 +30,9 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +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.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -40,71 +41,86 @@ import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis */ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" + override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" - override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" + override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - EagerLxTypeImmutabilityAnalysis_new, - LazyLxClassImmutabilityAnalysis_new, - LazyTypeImmutabilityAnalysis, - LazyFieldLocalityAnalysis, - LazySimpleEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis + def analyze(project: Project[URL]): String = { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new, + LazyFieldLocalityAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyL1FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + val sb: StringBuilder = new StringBuilder + sb.append("\nMutableTypes: \n") + val mutableTypes = propertyStore + .finalEntities(MutableType_new) + .toList + sb.append( + mutableTypes + .map(x ⇒ x.toString+" |Mutable Type\n") + .toString ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t => - analysisTime = t.toSeconds - } - val sb: StringBuilder = new StringBuilder - sb.append("\nMutableTypes: \n") - sb.append( - propertyStore - .finalEntities(MutableType_new) - .toList - .map(x => x.toString + "\n") - .toString - ) - sb.append("\nShallow Immutable Types:\n") - sb.append(propertyStore.finalEntities(ShallowImmutableType).toList.map(x => x.toString + "\n")) - sb.append("\nDependent Immutable Types: \n") - sb.append( - propertyStore.finalEntities(DependentImmutableType).toList.map(x => x.toString + "\n") - ) - sb.append("\nDeep Immutable Types:\n") - sb.append(propertyStore.finalEntities(DeepImmutableType).toList.map(x => x.toString + "\n")) - sb.append(s"\nType immutability analysis took: $analysisTime on average") + sb.append("\nShallow Immutable Types:\n") + val shallowImmutableTypes = propertyStore.finalEntities(ShallowImmutableType).toList + sb.append(shallowImmutableTypes.map(x ⇒ x.toString+" |Shallow Immutable Type\n")) + sb.append("\nDependent Immutable Types: \n") + val dependentImmutableTypes = propertyStore.finalEntities(DependentImmutableType).toList + sb.append( + dependentImmutableTypes.map(x ⇒ x.toString+" |Dependent Immutable Type\n") + ) + sb.append("\nDeep Immutable Types:\n") + val deepImmutableTypes = propertyStore.finalEntities(DeepImmutableType).toList + sb.append(deepImmutableTypes.map(x ⇒ x.toString+" |Deep Immutable Type\n")) + sb.append(s"\nType immutability analysis took: $analysisTime on average") - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/typeImm" + dateString + ".txt") - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() + sb.append("\n\n") + sb.append( + s""" + | mutable types: ${mutableTypes.size} + | shallow immutable types: ${shallowImmutableTypes.size} + | dependent immutable types: ${dependentImmutableTypes.size} + | deep immutable types: ${deepImmutableTypes.size} + |""".stripMargin + ) + + val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString + val file = new File("C:/MA/results/typeImm"+dateString+".txt") + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() - " took : " + analysisTime + " seconds" - } + " took : "+analysisTime+" seconds" + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala index 32228d931a..b3835b40c7 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala @@ -7,17 +7,14 @@ 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.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis @@ -32,73 +29,70 @@ import org.opalj.util.Seconds */ object TypeImmutabilityAnalysisDemo_performanceMeasurement extends ProjectAnalysisApplication { - override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" + override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" - override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" + override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } - def analyze(theProject: Project[URL]): String = { + def analyze(theProject: Project[URL]): String = { - var times: List[Seconds] = Nil: List[Seconds] - for (i <- 0 until 10) { - val project = Project.recreate(theProject) - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - EagerLxTypeImmutabilityAnalysis_new, - LazyLxClassImmutabilityAnalysis_new, - LazyTypeImmutabilityAnalysis, - LazyFieldLocalityAnalysis, - LazySimpleEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t => - analysisTime = t.toSeconds - } - times = analysisTime :: times - } + var times: List[Seconds] = Nil: List[Seconds] + for (i ← 0 until 10) { + val project = Project.recreate(theProject) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new, + LazyFieldLocalityAnalysis, + LazySimpleEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + times = analysisTime :: times + } - /** - * "Mutable Type: "+propertyStore - * .finalEntities(MutableType_new) - * .toList - * .toString()+"\n"+ - * "Shallow Immutable Type: "+propertyStore - * .finalEntities(ShallowImmutableType) - * .toList - * .toString()+"\n"+ - * "Dependent Immutable Type: "+propertyStore - * .finalEntities(DependentImmutableType) - * .toList - * .toString()+"\n"+ - * "Deep Immutable Type: "+propertyStore - * .finalEntities(DeepImmutableType) - * .toList - * .toString()+"\n"* - */ - times.foreach(s => println(s + " seconds")) - val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) => x + y).timeSpan / times.size - f"took: $aver seconds on average" - } + /** + * "Mutable Type: "+propertyStore + * .finalEntities(MutableType_new) + * .toList + * .toString()+"\n"+ + * "Shallow Immutable Type: "+propertyStore + * .finalEntities(ShallowImmutableType) + * .toList + * .toString()+"\n"+ + * "Dependent Immutable Type: "+propertyStore + * .finalEntities(DependentImmutableType) + * .toList + * .toString()+"\n"+ + * "Deep Immutable Type: "+propertyStore + * .finalEntities(DeepImmutableType) + * .toList + * .toString()+"\n"* + */ + times.foreach(s ⇒ println(s+" seconds")) + val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size + f"took: $aver seconds on average" + } } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala index d5984ef9dc..e3b4d99ef9 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala @@ -11,6 +11,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.PropertyStoreContext import org.opalj.br.analyses.ProjectInformationKey import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.seq.PKESequentialPropertyStore /** * The ''key'' object to get the project's [[org.opalj.fpcf.PropertyStore]]. @@ -57,7 +58,7 @@ object PropertyStoreKey ) psFactory(context) case None ⇒ - val ps = org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) + val ps = PKESequentialPropertyStore(context: _*) //val ps = org.opalj.fpcf.par.PKECPropertyStore(context: _*) ps } 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/ClassImmutabilityAnalysis.scala index aba6712bc7..1bab0f0a80 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/ClassImmutabilityAnalysis.scala @@ -67,7 +67,7 @@ import org.opalj.br.fpcf.properties.TypeImmutability * @author Dominik Helm */ class ClassImmutabilityAnalysis(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. @@ -76,298 +76,298 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { * 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: MutableObject - ): 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 ::= ( - ( - determineClassImmutability(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) - } + /** + * 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: MutableObject + ): MultiResult = { + val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) + val r = allSubtypes.map { st ⇒ + new FinalEP(st, immutability) + }.toSeq + MultiResult(r) } - IncrementalResult(Results(results), nextComputations.iterator) - } - - def doDetermineClassImmutability(e: Entity): ProperPropertyComputationResult = { - e match { - case t: ObjectType => - //this is safe - classHierarchy.superclassType(t) match { - case None => Result(t, MutableObjectDueToUnknownSupertypes) - case Some(superClassType) => - val cf = project.classFile(t) match { - case None => - return Result(t, MutableObjectByAnalysis) //TODO consider other lattice element - case Some(cf) => cf - } - propertyStore(superClassType, ClassImmutability.key) match { - case UBP(p: MutableObject) => Result(t, p) - case eps: EPS[ObjectType, ClassImmutability] => - determineClassImmutability( - superClassType, - eps, - eps.isFinal, - lazyComputation = true - )(cf) - case epk => - determineClassImmutability( - superClassType, - epk, - superClassMutabilityIsFinal = false, - lazyComputation = true - )(cf) + @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 ::= ( + ( + determineClassImmutability(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) } + } + IncrementalResult(Results(results), nextComputations.iterator) + } + def doDetermineClassImmutability(e: Entity): ProperPropertyComputationResult = { + e match { + case t: ObjectType ⇒ + //this is safe + classHierarchy.superclassType(t) match { + case None ⇒ Result(t, MutableObjectDueToUnknownSupertypes) + case Some(superClassType) ⇒ + val cf = project.classFile(t) match { + case None ⇒ + return Result(t, MutableObjectByAnalysis) //TODO consider other lattice element + case Some(cf) ⇒ cf + } + + propertyStore(superClassType, ClassImmutability.key) match { + case UBP(p: MutableObject) ⇒ Result(t, p) + case eps: EPS[ObjectType, ClassImmutability] ⇒ + determineClassImmutability( + superClassType, + eps, + eps.isFinal, + lazyComputation = true + )(cf) + case epk ⇒ + determineClassImmutability( + superClassType, + epk, + superClassMutabilityIsFinal = false, + lazyComputation = true + )(cf) + } + + } + case _ ⇒ + val m = e.getClass.getSimpleName+" is not an org.opalj.br.ObjectType" + throw new IllegalArgumentException(m) } - case _ => - val m = e.getClass.getSimpleName + " is not an org.opalj.br.ObjectType" - throw new IllegalArgumentException(m) } - } - private[this] object SuperClassKey + 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 determineClassImmutability( + superClassType: ObjectType, + superClassInformation: EOptionP[Entity, Property], + superClassMutabilityIsFinal: Boolean, + lazyComputation: Boolean + )( + cf: ClassFile + ): ProperPropertyComputationResult = { + // assert(superClassMutability.isMutable.isNoOrUnknown) + val t = cf.thisType + + var dependees = Map.empty[Entity, EOptionP[Entity, Property]] + + if (!superClassMutabilityIsFinal) { + dependees += (SuperClassKey -> superClassInformation) + } - /** - * 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 determineClassImmutability( - superClassType: ObjectType, - superClassInformation: EOptionP[Entity, Property], - superClassMutabilityIsFinal: Boolean, - lazyComputation: Boolean - )( - cf: ClassFile - ): ProperPropertyComputationResult = { - // assert(superClassMutability.isMutable.isNoOrUnknown) - 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 - // Collect all fields for which we need to determine the effective mutability! - var hasFieldsWithUnknownMutability = false + val instanceFields = cf.fields.filter { f ⇒ + !f.isStatic + } + dependees ++= (propertyStore(instanceFields, FieldMutability) collect { + case FinalP(_: NonFinalField) ⇒ + // <=> The class is definitively mutable and therefore also all subclasses. + if (lazyComputation) + return Result(t, MutableObjectByAnalysis); + else + return createResultForAllSubtypes(t, MutableObjectByAnalysis); + case ep @ InterimE(e) ⇒ + hasFieldsWithUnknownMutability = true + (e, ep) + case epk @ EPK(e: Entity, _) ⇒ + // <=> The mutability information is not yet available. + hasFieldsWithUnknownMutability = true + (e, epk) + + // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields + }).toMap + + var minLocalImmutability: ClassImmutability = + if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) + MutableObjectByAnalysis + else + ImmutableContainer + + // NOTE: maxLocalImmutability does not take the super classes' mutability into account! + var maxLocalImmutability: ClassImmutability = superClassInformation match { + case UBP(ImmutableContainer) ⇒ ImmutableContainer + case _ ⇒ ImmutableObject + } - val instanceFields = cf.fields.filter { f => - !f.isStatic - } - dependees ++= (propertyStore(instanceFields, FieldMutability) collect { - case FinalP(_: NonFinalField) => - // <=> The class is definitively mutable and therefore also all subclasses. - if (lazyComputation) - return Result(t, MutableObjectByAnalysis); - else - return createResultForAllSubtypes(t, MutableObjectByAnalysis); - case ep @ InterimE(e) => - hasFieldsWithUnknownMutability = true - (e, ep) - case epk @ EPK(e: Entity, _) => - // <=> The mutability information is not yet available. - hasFieldsWithUnknownMutability = true - (e, epk) - - // case EPS(e, p: EffectivelyFinalField, _) => we can ignore effectively final fields - }).toMap - - var minLocalImmutability: ClassImmutability = - if (!superClassMutabilityIsFinal || hasFieldsWithUnknownMutability) - MutableObjectByAnalysis - else - ImmutableContainer - - // NOTE: maxLocalImmutability does not take the super classes' mutability into account! - var maxLocalImmutability: ClassImmutability = superClassInformation match { - case UBP(ImmutableContainer) => ImmutableContainer - case _ => ImmutableObject - } + 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 + } - 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 - } + var fieldTypes: Set[ObjectType] = Set.empty + if (maxLocalImmutability == ImmutableObject) { + fieldTypes = // IMPROVE Use the precise type of the field (if available)! + cf.fields.collect { + case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType + }.toSet + } - var fieldTypes: Set[ObjectType] = Set.empty - if (maxLocalImmutability == ImmutableObject) { - fieldTypes = // IMPROVE Use the precise type of the field (if available)! - cf.fields.collect { - case f if !f.isStatic && f.fieldType.isObjectType => f.fieldType.asObjectType - }.toSet - } + // For each dependent class file we have to determine the mutability + // of instances of the respective type to determine the immutability + // of instances of this class. + // Basically, we have to distinguish the following cases: + // - A field's type is mutable or conditionally immutable=> + // This class is conditionally immutable. + // - A field's type is immutable => + // The field is ignored. + // (This class is as immutable as its superclass.) + // - A field's type is at least conditionally mutable or + // The immutability of the field's type is not yet known => + // This type is AtLeastConditionallyImmutable + // We have to declare a dependency on the respective type's immutability. + // + // Additional handling is required w.r.t. the supertype: + // If the supertype is Immutable => + // Nothing special to do. + // If the supertype is AtLeastConditionallyImmutable => + // We have to declare a dependency on the supertype. + // If the supertype is ConditionallyImmutable => + // This type is also at most conditionally immutable. + // + // We furthermore have to take the mutability of the fields into consideration: + // If a field is not effectively final => + // This type is mutable. + // If a field is effectively final => + // Nothing special to do. + + val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) + val hasMutableOrConditionallyImmutableField = + // IMPROVE Use the precise type of the field (if available)! + fieldTypesImmutability.exists { eOptP ⇒ + eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) + } - // For each dependent class file we have to determine the mutability - // of instances of the respective type to determine the immutability - // of instances of this class. - // Basically, we have to distinguish the following cases: - // - A field's type is mutable or conditionally immutable=> - // This class is conditionally immutable. - // - A field's type is immutable => - // The field is ignored. - // (This class is as immutable as its superclass.) - // - A field's type is at least conditionally mutable or - // The immutability of the field's type is not yet known => - // This type is AtLeastConditionallyImmutable - // We have to declare a dependency on the respective type's immutability. - // - // Additional handling is required w.r.t. the supertype: - // If the supertype is Immutable => - // Nothing special to do. - // If the supertype is AtLeastConditionallyImmutable => - // We have to declare a dependency on the supertype. - // If the supertype is ConditionallyImmutable => - // This type is also at most conditionally immutable. - // - // We furthermore have to take the mutability of the fields into consideration: - // If a field is not effectively final => - // This type is mutable. - // If a field is effectively final => - // Nothing special to do. - - val fieldTypesImmutability = propertyStore(fieldTypes, TypeImmutability.key) - val hasMutableOrConditionallyImmutableField = - // IMPROVE Use the precise type of the field (if available)! - fieldTypesImmutability.exists { eOptP => - eOptP.hasUBP && (eOptP.ub.isMutable || eOptP.ub.isImmutableContainer) - } - - if (hasMutableOrConditionallyImmutableField) { - maxLocalImmutability = ImmutableContainer - } 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 + if (hasMutableOrConditionallyImmutableField) { + maxLocalImmutability = ImmutableContainer + } 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 + } + fieldTypesWithUndecidedMutability.foreach { eOptP ⇒ + dependees += (eOptP.e -> eOptP) + } } - fieldTypesWithUndecidedMutability.foreach { eOptP => - dependees += (eOptP.e -> eOptP) - } - } - 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) - ); - } + 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 - someEPS match { - // Superclass related dependencies: - // - case UBP(_: MutableObject) => return Result(t, MutableObjectByAnalysis); + def c(someEPS: SomeEPS): ProperPropertyComputationResult = { + //[DEBUG] val oldDependees = dependees + someEPS match { + // Superclass related dependencies: + // + case UBP(_: MutableObject) ⇒ return Result(t, MutableObjectByAnalysis); - case LBP(ImmutableObject) => // the super class - dependees -= SuperClassKey + case LBP(ImmutableObject) ⇒ // the super class + dependees -= SuperClassKey - case UBP(ImmutableContainer) => // super class is at most immutable container - if (someEPS.isFinal) dependees -= SuperClassKey - maxLocalImmutability = ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + case UBP(ImmutableContainer) ⇒ // super class is at most immutable container + if (someEPS.isFinal) dependees -= SuperClassKey + maxLocalImmutability = ImmutableContainer + 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(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 LUBP(_: MutableObject, ImmutableObject) => // No information about superclass + case LUBP(_: MutableObject, ImmutableObject) ⇒ // No information about superclass - // Properties related to the type of the class' fields. - // - case UBP(ImmutableContainerType | MutableType) => - maxLocalImmutability = ImmutableContainer - dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) + // Properties related to the type of the class' fields. + // + case UBP(ImmutableContainerType | MutableType) ⇒ + maxLocalImmutability = ImmutableContainer + dependees = dependees.filterNot(_._2.pk == TypeImmutability.key) - case ELBP(e, ImmutableType) => // Immutable field type, no influence on mutability - dependees -= e + case ELBP(e, ImmutableType) ⇒ // Immutable field type, no influence on mutability + dependees -= e - case UBP(ImmutableType) => // No information about field type + case UBP(ImmutableType) ⇒ // No information about field type - // Field Mutability related dependencies: - // - case UBP(_: NonFinalField) => return Result(t, MutableObjectByAnalysis); + // Field Mutability related dependencies: + // + case UBP(_: NonFinalField) ⇒ return Result(t, MutableObjectByAnalysis); - case ELBP(e, _: FinalField) => - dependees -= e - if (minLocalImmutability != ImmutableContainer && - !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) - minLocalImmutability = ImmutableContainer // Lift lower bound when possible + case ELBP(e, _: FinalField) ⇒ + dependees -= e + if (minLocalImmutability != ImmutableContainer && + !dependees.valuesIterator.exists(_.pk != TypeImmutability.key)) + minLocalImmutability = ImmutableContainer // Lift lower bound when possible - case UBP(_: FinalField) => // no information about field mutability + case UBP(_: FinalField) ⇒ // no information about field mutability - } + } - if (someEPS.isRefinable) { - val entity = if (someEPS.pk == ClassImmutability.key) SuperClassKey else someEPS.e - dependees += (entity -> someEPS) - } + if (someEPS.isRefinable) { + val entity = if (someEPS.pk == ClassImmutability.key) SuperClassKey else someEPS.e + dependees += (entity -> someEPS) + } - /*[DEBUG] + /*[DEBUG] assert( oldDependees != dependees, s"dependees are not correctly updated $e($p)\n:old=$oldDependees\nnew=$dependees" ) */ - // Lift lower bound once no dependencies other than field type mutabilities are left - if (minLocalImmutability != ImmutableContainer && - dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) - minLocalImmutability = ImmutableContainer + // Lift lower bound once no dependencies other than field type mutabilities are left + if (minLocalImmutability != ImmutableContainer && + dependees.valuesIterator.forall(_.pk == TypeImmutability.key)) + minLocalImmutability = ImmutableContainer - if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { - /*[DEBUG] + if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { + /*[DEBUG] assert( maxLocalImmutability == ConditionallyImmutableObject || maxLocalImmutability == ImmutableObject @@ -386,113 +386,113 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { ) */ - Result(t, maxLocalImmutability) + Result(t, maxLocalImmutability) - } else { - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - } - } + } else { + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + } + } - //[DEBUG] assert(initialImmutability.isRefinable) - val result = - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) - if (lazyComputation) - result - else { - val isFinal = dependees.isEmpty - createIncrementalResult( - t, - EPS(t, minLocalImmutability, maxLocalImmutability), - isFinal, - result - ) + //[DEBUG] assert(initialImmutability.isRefinable) + val result = + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + if (lazyComputation) + result + else { + val isFinal = dependees.isEmpty + createIncrementalResult( + t, + EPS(t, minLocalImmutability, maxLocalImmutability), + isFinal, + result + ) + } } - } } trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability) - - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability, TypeImmutability, FieldMutability) - - 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 immutable. - set(ObjectType.Object, ImmutableObject) - - // 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)) - - // 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, MutableObjectDueToUnknownSupertypes) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability) + + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability, TypeImmutability, FieldMutability) + + 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 immutable. + set(ObjectType.Object, ImmutableObject) + + // 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)) + + // 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, 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, MutableObjectDueToUnknownSupertypes) + }) + } + cfs } - // 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, 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 = {} + 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 = {} } /** @@ -504,22 +504,22 @@ object EagerClassImmutabilityAnalysis extends ClassImmutabilityAnalysisScheduler 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 ClassImmutabilityAnalysis(p) - ps.scheduleEagerComputationsForEntities(cfs)( - analysis.determineClassImmutability( - superClassType = null, - FinalEP(ObjectType.Object, ImmutableObject), - superClassMutabilityIsFinal = true, - lazyComputation = false - ) - ) - analysis - } + 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 ClassImmutabilityAnalysis(p) + ps.scheduleEagerComputationsForEntities(cfs)( + analysis.determineClassImmutability( + superClassType = null, + FinalEP(ObjectType.Object, ImmutableObject), + superClassMutabilityIsFinal = true, + lazyComputation = false + ) + ) + analysis + } } /** @@ -531,18 +531,18 @@ object LazyClassImmutabilityAnalysis extends ClassImmutabilityAnalysisScheduler with FPCFLazyAnalysisScheduler { - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - - override def register( - p: SomeProject, - ps: PropertyStore, - unused: InitializationData - ): FPCFAnalysis = { - val analysis = new ClassImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - ClassImmutability.key, - analysis.doDetermineClassImmutability - ) - analysis - } + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + + override def register( + p: SomeProject, + ps: PropertyStore, + unused: InitializationData + ): FPCFAnalysis = { + val analysis = new ClassImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + ClassImmutability.key, + analysis.doDetermineClassImmutability + ) + analysis + } } 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/TypeImmutabilityAnalysis.scala index bcc0458eca..c8330220d8 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/TypeImmutabilityAnalysis.scala @@ -39,194 +39,195 @@ import org.opalj.br.fpcf.properties.TypeImmutability * * @author Michael Eichberg */ -class TypeImmutabilityAnalysis(final val project: SomeProject) extends FPCFAnalysis { - - def doDetermineTypeMutability( - 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 = { - typeExtensibility(t) match { - case Yes | Unknown => Result(t, MutableType) - case No => step2(t) +class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnalysis { + + def doDetermineTypeMutability( + typeExtensibility: ObjectType ⇒ Answer + )( + e: Entity + ): ProperPropertyComputationResult = e match { + case t: ObjectType ⇒ step1(typeExtensibility)(t) + case _ ⇒ throw new IllegalArgumentException(s"$e is not an ObjectType") } - } - - 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, Seq(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, Seq(eps), c) - case epk => - InterimResult(t, MutableType, ImmutableType, Seq(epk), c) - } - } else { - var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] - var joinedImmutability: TypeImmutability = ImmutableType // this may become "Mutable..." - var maxImmutability: TypeImmutability = ImmutableType - - ps(t, ClassImmutability.key) match { - case FinalP(ImmutableObject) => - case FinalP(_: MutableObject) => - return Result(t, MutableType); - - case FinalP(ImmutableContainer) => - joinedImmutability = ImmutableContainerType - maxImmutability = ImmutableContainerType - - 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(ImmutableType) => - case UBP(MutableType) => - return Result(t, MutableType); - - case FinalP(ImmutableContainerType) => - joinedImmutability = joinedImmutability.meet(ImmutableContainerType) - maxImmutability = ImmutableContainerType - - case eps @ InterimLUBP(subtypeLB, subtypeUB) => - joinedImmutability = joinedImmutability.meet(subtypeLB) - maxImmutability = maxImmutability.meet(subtypeUB) - dependencies += ((subtype, eps)) - - case epk => - joinedImmutability = MutableType - dependencies += ((subtype, epk)) + /** + * @param t An object type which is not `java.lang.Object`. + */ + def step1( + typeExtensibility: ObjectType ⇒ Answer + )( + t: ObjectType + ): ProperPropertyComputationResult = { + typeExtensibility(t) match { + case Yes | Unknown ⇒ Result(t, MutableType) + case No ⇒ step2(t) } - } - - if (dependencies.isEmpty) { - Result(t, maxImmutability) - } else if (joinedImmutability == maxImmutability) { - // E.g., as soon as one subtype is an ImmutableContainer, we are at most - // ImmutableContainer, even if all other subtype may even be 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 = { + } + + 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, Seq(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, Seq(eps), c) + case epk ⇒ + InterimResult(t, MutableType, ImmutableType, Seq(epk), c) + } + } else { + var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] + var joinedImmutability: TypeImmutability = ImmutableType // this may become "Mutable..." + var maxImmutability: TypeImmutability = ImmutableType + + ps(t, ClassImmutability.key) match { + case FinalP(ImmutableObject) ⇒ + case FinalP(_: MutableObject) ⇒ + return Result(t, MutableType); + + case FinalP(ImmutableContainer) ⇒ + joinedImmutability = ImmutableContainerType + maxImmutability = ImmutableContainerType + + 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(ImmutableType) ⇒ + case UBP(MutableType) ⇒ + return Result(t, MutableType); + + case FinalP(ImmutableContainerType) ⇒ + joinedImmutability = joinedImmutability.meet(ImmutableContainerType) + maxImmutability = ImmutableContainerType + + 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) + Result(t, maxImmutability) + } else if (joinedImmutability == maxImmutability) { + // E.g., as soon as one subtype is an ImmutableContainer, we are at most + // ImmutableContainer, even if all other subtype may even be immutable! + Result(t, joinedImmutability) } 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 + // 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 == ImmutableContainerType) + Result(t, maxImmutability) + } else { + InterimResult( + t, + joinedImmutability, + maxImmutability, + dependencies.values, + c + ) + } + } + } + + (eps: @unchecked) match { + case FinalEP(e, ImmutableType | ImmutableObject) ⇒ + dependencies = dependencies - e + nextResult() + + case UBP(MutableType | _: MutableObject) ⇒ + Result(t, MutableType) + + case FinalEP(e, ImmutableContainerType | ImmutableContainer) ⇒ + maxImmutability = ImmutableContainerType + 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() + } } - } - if (joinedImmutability == maxImmutability) { - assert(maxImmutability == ImmutableContainerType) - Result(t, maxImmutability) - } else { - InterimResult( - t, - joinedImmutability, - maxImmutability, - dependencies.values, - c - ) - } + + InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) } - } - - (eps: @unchecked) match { - case FinalEP(e, ImmutableType | ImmutableObject) => - dependencies = dependencies - e - nextResult() - - case UBP(MutableType | _: MutableObject) => - Result(t, MutableType) - - case FinalEP(e, ImmutableContainerType | ImmutableContainer) => - maxImmutability = ImmutableContainerType - 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, c) - } } - } } trait TypeImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability) - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability, TypeImmutability) + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability, TypeImmutability) } @@ -239,22 +240,22 @@ object EagerTypeImmutabilityAnalysis extends TypeImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + 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 TypeImmutabilityAnalysis(project) - val allClassFilesIterator = project.allClassFiles.iterator - val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) + override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val typeExtensibility = project.get(TypeExtensibilityKey) + val analysis = new TypeImmutabilityAnalysis(project) + val allClassFilesIterator = project.allClassFiles.iterator + val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) - ps.scheduleEagerComputationsForEntities(types) { - analysis.step1(typeExtensibility) - } + ps.scheduleEagerComputationsForEntities(types) { + analysis.step1(typeExtensibility) + } - analysis - } + analysis + } } @@ -262,19 +263,19 @@ object LazyTypeImmutabilityAnalysis extends TypeImmutabilityAnalysisScheduler 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 TypeImmutabilityAnalysis(p) - val analysisRunner: ProperPropertyComputation[Entity] = - analysis.doDetermineTypeMutability(typeExtensibility) - ps.registerLazyPropertyComputation(TypeImmutability.key, analysisRunner) - analysis - - } + 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 TypeImmutabilityAnalysis(p) + val analysisRunner: ProperPropertyComputation[Entity] = + analysis.doDetermineTypeMutability(typeExtensibility) + ps.registerLazyPropertyComputation(TypeImmutability.key, analysisRunner) + analysis + + } } 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 3c085c8897..b3f5c2ab8e 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 @@ -11,7 +11,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - final type Self = ClassImmutability + final type Self = ClassImmutability } @@ -86,12 +86,12 @@ sealed trait ClassImmutability extends OrderedProperty with ClassImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ClassImmutability] = ClassImmutability.key + final def key: PropertyKey[ClassImmutability] = ClassImmutability.key - def correspondingTypeImmutability: TypeImmutability + def correspondingTypeImmutability: TypeImmutability - /** `true` if instances of the class are mutable. */ - def isMutable: Boolean + /** `true` if instances of the class are mutable. */ + def isMutable: Boolean } /** @@ -99,13 +99,13 @@ sealed trait ClassImmutability */ object ClassImmutability extends ClassImmutabilityPropertyMetaInformation { - /** - * The key associated with every [[ClassImmutability]] property. - */ - final val key: PropertyKey[ClassImmutability] = PropertyKey.create( - "opalj.ClassImmutability", - MutableObjectDueToUnresolvableDependency - ) + /** + * The key associated with every [[ClassImmutability]] property. + */ + final val key: PropertyKey[ClassImmutability] = PropertyKey.create( + "opalj.ClassImmutability", + MutableObjectDueToUnresolvableDependency + ) } /** @@ -118,11 +118,11 @@ object ClassImmutability extends ClassImmutabilityPropertyMetaInformation { */ case object ImmutableObject extends ClassImmutability { - final val correspondingTypeImmutability = ImmutableType + final val correspondingTypeImmutability = ImmutableType - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - final def isMutable: Boolean = false + final def isMutable: Boolean = false } /** @@ -132,45 +132,45 @@ case object ImmutableObject extends ClassImmutability { */ case object ImmutableContainer extends ClassImmutability { - final val correspondingTypeImmutability = ImmutableContainerType + final val correspondingTypeImmutability = ImmutableContainerType - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == ImmutableObject) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + 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 + final def isMutable: Boolean = false } sealed trait MutableObject extends ClassImmutability { - def reason: String - final val correspondingTypeImmutability = MutableType + def reason: String + final val correspondingTypeImmutability = MutableType - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == ImmutableObject || other == ImmutableContainer) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + 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 def isMutable: Boolean = true - final override def toString: String = s"MutableObject(reason=$reason)" + final override def toString: String = s"MutableObject(reason=$reason)" } case object MutableObjectDueToIncompleteAnalysis extends MutableObject { - final def reason = "analysis has not yet completed" + final def reason = "analysis has not yet completed" } case object MutableObjectByAnalysis extends MutableObject { - final def reason = "determined by analysis" + final def reason = "determined by analysis" } case object MutableObjectDueToUnknownSupertypes extends MutableObject { - final def reason = "the type hierarchy is upwards incomplete" + final def reason = "the type hierarchy is upwards incomplete" } case object MutableObjectDueToUnresolvableDependency extends MutableObject { - final def reason = "a dependency cannot be resolved" + final def reason = "a dependency cannot be resolved" } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index bb8fcf0032..ac9b92914a 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -7,7 +7,7 @@ import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyMetaInformation sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = ClassImmutability_new + final type Self = ClassImmutability_new } /** @@ -28,75 +28,76 @@ sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaIn sealed trait ClassImmutability_new extends OrderedProperty with ClassImmutabilityPropertyMetaInformation_new { - final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key - def correspondingTypeImmutability: TypeImmutability_new + final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key + def correspondingTypeImmutability: TypeImmutability_new } object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[ClassImmutability_new]] property. - */ - final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( - "opalj.ClassImmutability_new", - MutableClass - ) + /** + * The key associated with every [[ClassImmutability_new]] property. + */ + final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( + "opalj.ClassImmutability_new", + MutableClass + ) } case object DeepImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - - def meet(that: ClassImmutability_new): ClassImmutability_new = - if (this == that) - this - else - that + override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + + def meet(that: ClassImmutability_new): ClassImmutability_new = + if (this == that) + this + else + that } case object DependentImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = - DependentImmutableType //TODO check - - def meet(that: ClassImmutability_new): ClassImmutability_new = - if (that == MutableClass || that == ShallowImmutableClass) - that - else - this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - - if (other == DeepImmutableClass) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + + override def correspondingTypeImmutability: TypeImmutability_new = + DependentImmutableType //TODO check + + def meet(that: ClassImmutability_new): ClassImmutability_new = + if (that == MutableClass || that == ShallowImmutableClass) + that + else + this + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + + if (other == DeepImmutableClass) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + } } - } } case object ShallowImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = ShallowImmutableType - def meet(that: ClassImmutability_new): ClassImmutability_new = - if (that == MutableClass) - that - else - this - - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - - if (other == DeepImmutableClass || other == DependentImmutableClass) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + override def correspondingTypeImmutability: TypeImmutability_new = ShallowImmutableType + def meet(that: ClassImmutability_new): ClassImmutability_new = + if (that == MutableClass) + that + else + this + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + + if (other == DeepImmutableClass || other == DependentImmutableClass) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + } } - } } case object MutableClass extends ClassImmutability_new { - def correspondingTypeImmutability = MutableType_new - def meet(other: ClassImmutability_new): ClassImmutability_new = this + def correspondingTypeImmutability = MutableType_new + def meet(other: ClassImmutability_new): ClassImmutability_new = this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other != MutableClass) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + 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/FieldImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldImmutability.scala index 56b914fb38..7ad675bdc0 100644 --- 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 @@ -8,7 +8,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait FieldImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - type Self = FieldImmutability + type Self = FieldImmutability } @@ -28,69 +28,69 @@ sealed trait FieldImmutability extends OrderedProperty with FieldImmutabilityPropertyMetaInformation { - final def key: PropertyKey[FieldImmutability] = FieldImmutability.key + final def key: PropertyKey[FieldImmutability] = FieldImmutability.key } object FieldImmutability extends FieldImmutabilityPropertyMetaInformation { - final val PropertyKeyName = "opalj.FieldImmutability" + final val PropertyKeyName = "opalj.FieldImmutability" - final val key: PropertyKey[FieldImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableField - ) - } + 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 = - if (this == that) - this - else - that + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + def meet(that: FieldImmutability): FieldImmutability = + if (this == that) + this + else + that } case object DependentImmutableField extends FieldImmutability { - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == DeepImmutableField) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + 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 + 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 + def meet(that: FieldImmutability): FieldImmutability = + if (that == MutableField) + that + else + this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == DeepImmutableField || other == DependentImmutableField) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + 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 + 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") + 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/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala index 489c8ee92a..c84041e9be 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -9,7 +9,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - type Self = ReferenceImmutability + type Self = ReferenceImmutability } @@ -30,53 +30,53 @@ sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaIn sealed trait ReferenceImmutability extends OrderedProperty with ReferenceImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key + final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key } object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { - final val PropertyKeyName = "opalj.ReferenceImmutability" + final val PropertyKeyName = "opalj.ReferenceImmutability" - final val key: PropertyKey[ReferenceImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableReference - ) - } + final val key: PropertyKey[ReferenceImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableReference + ) + } } case object ImmutableReference extends ReferenceImmutability { - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: ReferenceImmutability): ReferenceImmutability = - if (this == that) - this - else - that + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + def meet(that: ReferenceImmutability): ReferenceImmutability = + if (this == that) + this + else + that } case object LazyInitializedReference extends ReferenceImmutability { - def meet(other: ReferenceImmutability): properties.ReferenceImmutability = { - if (other == MutableReference) { - other - } else { - this + def meet(other: ReferenceImmutability): properties.ReferenceImmutability = { + if (other == MutableReference) { + other + } else { + this + } } - } - override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { - if (other == ImmutableReference) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { + if (other == ImmutableReference) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + } } - } } case object MutableReference extends ReferenceImmutability { - def meet(other: ReferenceImmutability): this.type = this + def meet(other: ReferenceImmutability): this.type = this - override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { - if (other != MutableReference) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other => $this") + override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { + if (other != MutableReference) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other => $this") + } } - } } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala index 4a2b9e9d7a..6f26603d01 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -8,7 +8,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait TypeImmutabilityPropertyMetaInformation_new extends PropertyMetaInformation { - final type Self = TypeImmutability_new + final type Self = TypeImmutability_new } /** @@ -29,19 +29,19 @@ sealed trait TypeImmutability_new extends OrderedProperty with TypeImmutabilityPropertyMetaInformation_new { - /** - * Returns the key used by all `TypeImmutability_new` properties. - */ - final def key = TypeImmutability_new.key + /** + * Returns the key used by all `TypeImmutability_new` properties. + */ + final def key = TypeImmutability_new.key - def isDeepImmutable: Boolean - def isShallowImmutable: Boolean - def isDependentImmutable: Boolean + def isDeepImmutable: Boolean + def isShallowImmutable: Boolean + def isDependentImmutable: Boolean - /** `true` if the immutability is unknown or if the type is mutable.*/ - def isMutable: Boolean + /** `true` if the immutability is unknown or if the type is mutable.*/ + def isMutable: Boolean - def meet(other: TypeImmutability_new): TypeImmutability_new + def meet(other: TypeImmutability_new): TypeImmutability_new } /** @@ -49,13 +49,13 @@ sealed trait TypeImmutability_new */ object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new { - /** - * The key associated with every [[TypeImmutability_new]] property. - */ - final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( - "org.opalj.TypeImmutability_new", - MutableType_new - ) + /** + * The key associated with every [[TypeImmutability_new]] property. + */ + final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( + "org.opalj.TypeImmutability_new", + MutableType_new + ) } /** @@ -64,76 +64,76 @@ object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new */ case object DeepImmutableType extends TypeImmutability_new { - override def isDeepImmutable: Boolean = true - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = false - override def isDependentImmutable: 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 = {} + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (this == that) - this - else - that + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (this == that) + this + else + that } case object DependentImmutableType extends TypeImmutability_new { - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = false - override def isDependentImmutable: Boolean = true - - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (that == MutableType_new || 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") + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = false + override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = true + + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (that == MutableType_new || 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 ShallowImmutableType extends TypeImmutability_new { - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = true - override def isMutable: Boolean = false - override def isDependentImmutable: Boolean = false + override def isDeepImmutable: Boolean = false + override def isShallowImmutable: Boolean = true + override def isMutable: Boolean = false + override def isDependentImmutable: Boolean = false - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (that == MutableType_new) - that - else - this + def meet(that: TypeImmutability_new): TypeImmutability_new = + if (that == MutableType_new) + that + else + this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == DeepImmutableType || other == DependentImmutableType) { - 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_new extends TypeImmutability_new { - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = true - override def isDependentImmutable: 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_new): this.type = this + def meet(other: TypeImmutability_new): this.type = this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other != MutableType_new) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + if (other != MutableType_new) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + } } - } } diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala index cefd90111a..68e34762c6 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala @@ -17,155 +17,155 @@ case class SpecificationViolation(message: String) extends Exception(message) */ trait ComputationSpecification[A] { - // - // PROPERTIES OF COMPUTATION SPECIFICATIONS - // - - /** - * Identifies this computation specification; typically the name of the class - * which implements the underlying analysis. - * - * The default name is the name of `this` class. - * - * '''This method should be overridden.''' - */ - def name: String = { - val nameCandidate = this.getClass.getSimpleName - if (nameCandidate.endsWith("$")) - nameCandidate.substring(0, nameCandidate.length() - 1) - else - nameCandidate - } - - /** - * Returns the kinds of properties which are queried by this analysis. - * - * @note This set consists only of property kinds which are directly used by the analysis. - * - * @note Self usages should also be documented. - * - * @note This method is called after - * [[org.opalj.fpcf.ComputationSpecification#init(ps:org\.opalj\.fpcf\.PropertyStore)*]] - * was called for all analyses belonging to an analysis scenario. - * (E.g., it can be used to collect the set of used property bounds based on the - * configuration choices made in other analyses.) - */ - def uses(ps: PropertyStore): Set[PropertyBounds] - - /** - * Returns the kind of the property that is lazily (on-demand) derived. - */ - def derivesLazily: Option[PropertyBounds] - - /** - * Returns the set of property kinds eagerly derived by the underlying analysis. - */ - def derivesEagerly: Set[PropertyBounds] - - def derivesCollaboratively: Set[PropertyBounds] - - def derives: Iterator[PropertyBounds] = { - derivesEagerly.iterator ++ derivesCollaboratively.iterator ++ derivesLazily.iterator - } - - require( - (derivesCollaboratively intersect derivesEagerly).isEmpty, - "a property either has to be derived eagerly or collaboratively, but not both: " + - (derivesCollaboratively intersect derivesEagerly).mkString(", ") - ) - - require( - derivesLazily.isDefined || derivesEagerly.nonEmpty || derivesCollaboratively.nonEmpty, - "the computation does not derive any information" - ) - - require( - derivesLazily.isEmpty || (derivesEagerly.isEmpty && derivesCollaboratively.isEmpty), - "a lazy analysis cannot also derive information eagerly and/or collaboratively " - ) - - /** - * Specifies the kind of the computation that is performed. The kind restricts in which - * way the analysis is allowed to interact with the property store/other analyses. - */ - def computationType: ComputationType - - def toString(ps: PropertyStore): String = { - val uses = this.uses(ps).iterator.map(_.toSpecification).mkString("uses={", ", ", "}") - - val derivesLazily = - this.derivesLazily.iterator.map(_.toSpecification).mkString("derivesLazily={", ", ", "}") - val derivesEagerly = - this.derivesEagerly.iterator.map(_.toSpecification).mkString("derivesEagerly={", ", ", "}") - val derivesCollaboratively = - this.derivesCollaboratively.iterator - .map(_.toSpecification) - .mkString("derivesCollaboratively={", ", ", "}") - - s"ComputationSpecification(name=$name,type=$computationType," + - s"$uses,$derivesLazily,$derivesEagerly,$derivesCollaboratively)" - } - - override def toString: String = { - s"ComputationSpecification(name=$name,type=$computationType)" - } - - // - // LIFECYCLE RELATED METHODS - // - - /** - * The type of the data used by the analysis at initialization time. - * For analyses without special initialization requirements this type is `Null`. - */ - type InitializationData - - /** - * Called directly after the analysis is registered with an analysis scheduler; in particular - * before any analysis belonging to the same analysis scenario is scheduled – - * independent of the batch in which it will run. - * - * This enables further initialization of the computations that will eventually be executed. - * For example to initialize global configuration information. - * - * A computation specification does not have to call any methods of the property store that - * may trigger or schedule computations; i.e., it must – in particular – not call - * the methods `apply`, `schedule*`, `register*` or `waitOnPhaseCompletion`. - * - * @return The initialization data that is later on passed to schedule. - */ - def init(ps: PropertyStore): InitializationData - - /** - * Called directly before the analyses belonging to a phase are effectively scheduled. I.e., - * after phase setup, but potentially after other analyses' `beforeSchedule` method is called. - */ - def beforeSchedule(ps: PropertyStore): Unit - - /** - * Called by the scheduler to let the analysis register itself or to start execution. - */ - def schedule(ps: PropertyStore, i: InitializationData): A - - /** - * Called back after all analyses of a specific phase have been - * schedule (i.e., before calling waitOnPhaseCompletion). - */ - def afterPhaseScheduling(ps: PropertyStore, analysis: A): Unit - - /** - * Called after phase completion. - */ - def afterPhaseCompletion(ps: PropertyStore, analysis: A): Unit + // + // PROPERTIES OF COMPUTATION SPECIFICATIONS + // + + /** + * Identifies this computation specification; typically the name of the class + * which implements the underlying analysis. + * + * The default name is the name of `this` class. + * + * '''This method should be overridden.''' + */ + def name: String = { + val nameCandidate = this.getClass.getSimpleName + if (nameCandidate.endsWith("$")) + nameCandidate.substring(0, nameCandidate.length() - 1) + else + nameCandidate + } + + /** + * Returns the kinds of properties which are queried by this analysis. + * + * @note This set consists only of property kinds which are directly used by the analysis. + * + * @note Self usages should also be documented. + * + * @note This method is called after + * [[org.opalj.fpcf.ComputationSpecification#init(ps:org\.opalj\.fpcf\.PropertyStore)*]] + * was called for all analyses belonging to an analysis scenario. + * (E.g., it can be used to collect the set of used property bounds based on the + * configuration choices made in other analyses.) + */ + def uses(ps: PropertyStore): Set[PropertyBounds] + + /** + * Returns the kind of the property that is lazily (on-demand) derived. + */ + def derivesLazily: Option[PropertyBounds] + + /** + * Returns the set of property kinds eagerly derived by the underlying analysis. + */ + def derivesEagerly: Set[PropertyBounds] + + def derivesCollaboratively: Set[PropertyBounds] + + def derives: Iterator[PropertyBounds] = { + derivesEagerly.iterator ++ derivesCollaboratively.iterator ++ derivesLazily.iterator + } + + require( + (derivesCollaboratively intersect derivesEagerly).isEmpty, + "a property either has to be derived eagerly or collaboratively, but not both: "+ + (derivesCollaboratively intersect derivesEagerly).mkString(", ") + ) + + require( + derivesLazily.isDefined || derivesEagerly.nonEmpty || derivesCollaboratively.nonEmpty, + "the computation does not derive any information" + ) + + require( + derivesLazily.isEmpty || (derivesEagerly.isEmpty && derivesCollaboratively.isEmpty), + "a lazy analysis cannot also derive information eagerly and/or collaboratively " + ) + + /** + * Specifies the kind of the computation that is performed. The kind restricts in which + * way the analysis is allowed to interact with the property store/other analyses. + */ + def computationType: ComputationType + + def toString(ps: PropertyStore): String = { + val uses = this.uses(ps).iterator.map(_.toSpecification).mkString("uses={", ", ", "}") + + val derivesLazily = + this.derivesLazily.iterator.map(_.toSpecification).mkString("derivesLazily={", ", ", "}") + val derivesEagerly = + this.derivesEagerly.iterator.map(_.toSpecification).mkString("derivesEagerly={", ", ", "}") + val derivesCollaboratively = + this.derivesCollaboratively.iterator + .map(_.toSpecification) + .mkString("derivesCollaboratively={", ", ", "}") + + s"ComputationSpecification(name=$name,type=$computationType,"+ + s"$uses,$derivesLazily,$derivesEagerly,$derivesCollaboratively)" + } + + override def toString: String = { + s"ComputationSpecification(name=$name,type=$computationType)" + } + + // + // LIFECYCLE RELATED METHODS + // + + /** + * The type of the data used by the analysis at initialization time. + * For analyses without special initialization requirements this type is `Null`. + */ + type InitializationData + + /** + * Called directly after the analysis is registered with an analysis scheduler; in particular + * before any analysis belonging to the same analysis scenario is scheduled – + * independent of the batch in which it will run. + * + * This enables further initialization of the computations that will eventually be executed. + * For example to initialize global configuration information. + * + * A computation specification does not have to call any methods of the property store that + * may trigger or schedule computations; i.e., it must – in particular – not call + * the methods `apply`, `schedule*`, `register*` or `waitOnPhaseCompletion`. + * + * @return The initialization data that is later on passed to schedule. + */ + def init(ps: PropertyStore): InitializationData + + /** + * Called directly before the analyses belonging to a phase are effectively scheduled. I.e., + * after phase setup, but potentially after other analyses' `beforeSchedule` method is called. + */ + def beforeSchedule(ps: PropertyStore): Unit + + /** + * Called by the scheduler to let the analysis register itself or to start execution. + */ + def schedule(ps: PropertyStore, i: InitializationData): A + + /** + * Called back after all analyses of a specific phase have been + * schedule (i.e., before calling waitOnPhaseCompletion). + */ + def afterPhaseScheduling(ps: PropertyStore, analysis: A): Unit + + /** + * Called after phase completion. + */ + def afterPhaseCompletion(ps: PropertyStore, analysis: A): Unit } trait SimpleComputationSpecification[A] extends ComputationSpecification[A] { - final override type InitializationData = Null - final override def init(ps: PropertyStore): Null = null - final override def beforeSchedule(ps: PropertyStore): Unit = {} - final override def afterPhaseScheduling(ps: PropertyStore, analysis: A): Unit = {} - final override def afterPhaseCompletion(ps: PropertyStore, analysis: A): Unit = {} + final override type InitializationData = Null + final override def init(ps: PropertyStore): Null = null + final override def beforeSchedule(ps: PropertyStore): Unit = {} + final override def afterPhaseScheduling(ps: PropertyStore, analysis: A): Unit = {} + final override def afterPhaseCompletion(ps: PropertyStore, analysis: A): Unit = {} } 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 f2d0421a8a..6c47b88589 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala @@ -18,76 +18,76 @@ import org.opalj.value.ValueInformation */ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { - /** - * The program counter of the original '''underyling bytecode instruction'''. - * - * This `pc` is independent of the (implicit) `index` of the statement - * in the generated statements array! This pc is, e.g., useful for - * getting line number information. - */ - def pc: UShort - - def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean - - /** - * Called by the framework to enable each statement/expression to re-map the target - * `pc` of a(n unconditional) jump instruction to the index of the respective quadruple - * statement in the statements array. - * - * ==Example== - * The bytecode instruction: `5: goto 10` (where 5 is the original `pc` and `10` is - * the branchoffset) is re-mapped to a `goto pcToIndex(5+10)` quadruples statement. - */ - private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit - - override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] - - // TYPE CONVERSION METHODS - def asIf: If[V] = throw new ClassCastException(); - def asGoto: Goto = throw new ClassCastException(); - def asRet: Ret = throw new ClassCastException(); - def asJSR: JSR = throw new ClassCastException(); - def asSwitch: Switch[V] = throw new ClassCastException(); - def asAssignmentLike: AssignmentLikeStmt[V] = throw new ClassCastException(); - def asAssignment: Assignment[V] = throw new ClassCastException(); - def asReturnValue: ReturnValue[V] = throw new ClassCastException(); - def asReturn: Return = throw new ClassCastException(); - def asNop: Nop = throw new ClassCastException(); - def asSynchronizationStmt: SynchronizationStmt[V] = throw new ClassCastException(); - def asMonitorEnter: MonitorEnter[V] = throw new ClassCastException(); - def asMonitorExit: MonitorExit[V] = throw new ClassCastException(); - def asArrayStore: ArrayStore[V] = throw new ClassCastException(); - def asThrow: Throw[V] = throw new ClassCastException(); - def asFieldWriteAccessStmt: FieldWriteAccessStmt[V] = throw new ClassCastException(); - def asPutStatic: PutStatic[V] = throw new ClassCastException(); - def asPutField: PutField[V] = throw new ClassCastException(); - /*inner type*/ - def asMethodCall: MethodCall[V] = throw new ClassCastException(); - /*inner type*/ - def asInstanceMethodCall: InstanceMethodCall[V] = throw new ClassCastException(); - def asNonVirtualMethodCall: NonVirtualMethodCall[V] = throw new ClassCastException(); - def asVirtualMethodCall: VirtualMethodCall[V] = throw new ClassCastException(); - def asStaticMethodCall: StaticMethodCall[V] = throw new ClassCastException(); - def asInvokedynamicMethodCall: InvokedynamicMethodCall[V] = throw new ClassCastException(); - def asExprStmt: ExprStmt[V] = throw new ClassCastException(); - def asCaughtException: CaughtException[V] = throw new ClassCastException(); - def asCheckcast: Checkcast[V] = throw new ClassCastException(); - - def isAssignment: Boolean = false - def isExprStmt: Boolean = false - def isNonVirtualMethodCall: Boolean = false - def isVirtualMethodCall: Boolean = false - def isStaticMethodCall: Boolean = false - def isIfStmt: Boolean = false - def isMonitorEnter: Boolean = false - def isMonitorExit: Boolean = false - def isPutStatic: Boolean = false + /** + * The program counter of the original '''underyling bytecode instruction'''. + * + * This `pc` is independent of the (implicit) `index` of the statement + * in the generated statements array! This pc is, e.g., useful for + * getting line number information. + */ + def pc: UShort + + def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean + + /** + * Called by the framework to enable each statement/expression to re-map the target + * `pc` of a(n unconditional) jump instruction to the index of the respective quadruple + * statement in the statements array. + * + * ==Example== + * The bytecode instruction: `5: goto 10` (where 5 is the original `pc` and `10` is + * the branchoffset) is re-mapped to a `goto pcToIndex(5+10)` quadruples statement. + */ + private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit + + override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] + + // TYPE CONVERSION METHODS + def asIf: If[V] = throw new ClassCastException(); + def asGoto: Goto = throw new ClassCastException(); + def asRet: Ret = throw new ClassCastException(); + def asJSR: JSR = throw new ClassCastException(); + def asSwitch: Switch[V] = throw new ClassCastException(); + def asAssignmentLike: AssignmentLikeStmt[V] = throw new ClassCastException(); + def asAssignment: Assignment[V] = throw new ClassCastException(); + def asReturnValue: ReturnValue[V] = throw new ClassCastException(); + def asReturn: Return = throw new ClassCastException(); + def asNop: Nop = throw new ClassCastException(); + def asSynchronizationStmt: SynchronizationStmt[V] = throw new ClassCastException(); + def asMonitorEnter: MonitorEnter[V] = throw new ClassCastException(); + def asMonitorExit: MonitorExit[V] = throw new ClassCastException(); + def asArrayStore: ArrayStore[V] = throw new ClassCastException(); + def asThrow: Throw[V] = throw new ClassCastException(); + def asFieldWriteAccessStmt: FieldWriteAccessStmt[V] = throw new ClassCastException(); + def asPutStatic: PutStatic[V] = throw new ClassCastException(); + def asPutField: PutField[V] = throw new ClassCastException(); + /*inner type*/ + def asMethodCall: MethodCall[V] = throw new ClassCastException(); + /*inner type*/ + def asInstanceMethodCall: InstanceMethodCall[V] = throw new ClassCastException(); + def asNonVirtualMethodCall: NonVirtualMethodCall[V] = throw new ClassCastException(); + def asVirtualMethodCall: VirtualMethodCall[V] = throw new ClassCastException(); + def asStaticMethodCall: StaticMethodCall[V] = throw new ClassCastException(); + def asInvokedynamicMethodCall: InvokedynamicMethodCall[V] = throw new ClassCastException(); + def asExprStmt: ExprStmt[V] = throw new ClassCastException(); + def asCaughtException: CaughtException[V] = throw new ClassCastException(); + def asCheckcast: Checkcast[V] = throw new ClassCastException(); + + def isAssignment: Boolean = false + def isExprStmt: Boolean = false + def isNonVirtualMethodCall: Boolean = false + def isVirtualMethodCall: Boolean = false + def isStaticMethodCall: Boolean = false + def isIfStmt: Boolean = false + def isMonitorEnter: Boolean = false + def isMonitorExit: Boolean = false + def isPutStatic: Boolean = false } /** @@ -105,64 +105,64 @@ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { * generating source code. */ case class If[+V <: Var[V]]( - pc: PC, - left: Expr[V], - condition: RelationalOperator, - right: Expr[V], - private[tac] var target: Int + pc: PC, + left: Expr[V], + condition: RelationalOperator, + right: Expr[V], + private[tac] var target: Int ) extends Stmt[V] { - final override def asIf: this.type = this - - final override def isIfStmt: Boolean = true - final override def astID: Int = If.ASTID - final def leftExpr: Expr[V] = left - final def rightExpr: Expr[V] = right - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { - p(left) && p(right) - } - - /** - * The target statement that is executed if the condition evaluates to `true`. - */ - def targetStmt: Int = target - - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - left.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - right.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - target = pcToIndex(target) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - If(pc, left.toCanonicalForm, condition, right.toCanonicalForm, target) - } - - final override def isSideEffectFree: Boolean = { - assert(left.isValueExpression && right.isValueExpression) - true - } - - override def toString: String = s"If(pc=$pc,$left,$condition,$right,target=$target)" + final override def asIf: this.type = this + + final override def isIfStmt: Boolean = true + final override def astID: Int = If.ASTID + final def leftExpr: Expr[V] = left + final def rightExpr: Expr[V] = right + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { + p(left) && p(right) + } + + /** + * The target statement that is executed if the condition evaluates to `true`. + */ + def targetStmt: Int = target + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + left.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + right.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + target = pcToIndex(target) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + If(pc, left.toCanonicalForm, condition, right.toCanonicalForm, target) + } + + final override def isSideEffectFree: Boolean = { + assert(left.isValueExpression && right.isValueExpression) + true + } + + override def toString: String = s"If(pc=$pc,$left,$condition,$right,target=$target)" } object If { - final val ASTID = 0 + final val ASTID = 0 } trait VariableFreeStmt extends Stmt[Nothing] { - final override def toCanonicalForm( - implicit - ev: Nothing <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - this - } + final override def toCanonicalForm( + implicit + ev: Nothing <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + this + } } @@ -173,31 +173,31 @@ trait VariableFreeStmt extends Stmt[Nothing] { */ 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 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 - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - target = pcToIndex(target) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + target = pcToIndex(target) + } - final override def isSideEffectFree: Boolean = true + final override def isSideEffectFree: Boolean = true - /** - * @note Calling this method is only supported after the quadruples representation - * is created and the re-mapping of `pc`s to instruction indexes has happened! - */ - def targetStmt: Int = target + /** + * @note Calling this method is only supported after the quadruples representation + * is created and the re-mapping of `pc`s to instruction indexes has happened! + */ + def targetStmt: Int = target - override def toString: String = s"Goto(pc=$pc,target=$target)" + override def toString: String = s"Goto(pc=$pc,target=$target)" } object Goto { - final val ASTID = 1 + final val ASTID = 1 } /** @@ -212,27 +212,27 @@ object Goto { */ case class Ret(pc: PC, private var returnAddresses: PCs) extends VariableFreeStmt { - 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 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 - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - returnAddresses = returnAddresses map { pcToIndex } - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + returnAddresses = returnAddresses map { pcToIndex } + } - final override def isSideEffectFree: Boolean = true + final override def isSideEffectFree: Boolean = true - override def toString: String = { - s"Ret(pc=$pc,returnAddresses=${returnAddresses.mkString("(", ",", ")")})" - } + override def toString: String = { + s"Ret(pc=$pc,returnAddresses=${returnAddresses.mkString("(", ",", ")")})" + } } object Ret { - final val ASTID = 2 + final val ASTID = 2 } /** @@ -247,33 +247,33 @@ object Ret { */ 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 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 - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - target = pcToIndex(target) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + target = pcToIndex(target) + } - final override def isSideEffectFree: Boolean = true + final override def isSideEffectFree: Boolean = true - /** - * The first statement of the called subroutine. - * - * @note Calling this method is only supported after the quadruples representation - * is created and the re-mapping of `pc`s to instruction indexes has happened! - */ - def targetStmt: Int = target + /** + * The first statement of the called subroutine. + * + * @note Calling this method is only supported after the quadruples representation + * is created and the re-mapping of `pc`s to instruction indexes has happened! + */ + def targetStmt: Int = target - override def toString: String = s"JSR(pc=$pc,target=$target)" + override def toString: String = s"JSR(pc=$pc,target=$target)" } object JSR { - final val ASTID = 3 + final val ASTID = 3 } /** @@ -286,177 +286,177 @@ object JSR { * were determined to be dead. */ case class Switch[+V <: Var[V]]( - pc: PC, - private var defaultTarget: Int, - index: Expr[V], - private var npairs: RefArray[IntIntPair /*(Case Value, Jump Target)*/ ] + pc: PC, + private var defaultTarget: Int, + index: Expr[V], + private var npairs: RefArray[IntIntPair /*(Case Value, Jump Target)*/ ] ) extends Stmt[V] { - final override def asSwitch: this.type = this - final override def astID: Int = Switch.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { - p(index) - } - - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - npairs._UNSAFE_mapped { x => - val newIndex = pcToIndex(x._2) - // assert(newIndex >= 0) - x.copy(_2 = newIndex) - } - defaultTarget = pcToIndex(defaultTarget) - index.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - Switch(pc, defaultTarget, index.toCanonicalForm, npairs) - } - - final override def isSideEffectFree: Boolean = { - assert(index.isValueExpression) - true - } - - // Calling this method is only supported after the three-address code representation - // is created and the remapping of pcs to instruction indexes has happened! - def caseStmts: IntArray = npairs.map(x => x._2) - - // Calling this method is only supported after the quadruples representation - // is created and the remapping of pcs to instruction indexes has happened! - def defaultStmt: Int = defaultTarget - - override def toString: String = { - val npairs = this.npairs.mkString("(", ",", ")") - s"Switch(pc=$pc,defaultTarget=$defaultTarget,index=$index,npairs=$npairs" - } + final override def asSwitch: this.type = this + final override def astID: Int = Switch.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { + p(index) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + npairs._UNSAFE_mapped { x ⇒ + val newIndex = pcToIndex(x._2) + // assert(newIndex >= 0) + x.copy(_2 = newIndex) + } + defaultTarget = pcToIndex(defaultTarget) + index.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + Switch(pc, defaultTarget, index.toCanonicalForm, npairs) + } + + final override def isSideEffectFree: Boolean = { + assert(index.isValueExpression) + true + } + + // Calling this method is only supported after the three-address code representation + // is created and the remapping of pcs to instruction indexes has happened! + def caseStmts: IntArray = npairs.map(x ⇒ x._2) + + // Calling this method is only supported after the quadruples representation + // is created and the remapping of pcs to instruction indexes has happened! + def defaultStmt: Int = defaultTarget + + override def toString: String = { + val npairs = this.npairs.mkString("(", ",", ")") + s"Switch(pc=$pc,defaultTarget=$defaultTarget,index=$index,npairs=$npairs" + } } object Switch { - final val ASTID = 4 + final val ASTID = 4 } sealed abstract class AssignmentLikeStmt[+V <: Var[V]] extends Stmt[V] { - def expr: Expr[V] - final override def asAssignmentLike: AssignmentLikeStmt[V] = this + def expr: Expr[V] + final override def asAssignmentLike: AssignmentLikeStmt[V] = this } object AssignmentLikeStmt { - def unapply[V <: Var[V]](stmt: Stmt[V]): Option[(PC, Expr[V])] = { - stmt match { - case s: AssignmentLikeStmt[V] => Some((s.pc, s.expr)) - case _ => None + def unapply[V <: Var[V]](stmt: Stmt[V]): Option[(PC, Expr[V])] = { + stmt match { + case s: AssignmentLikeStmt[V] ⇒ Some((s.pc, s.expr)) + case _ ⇒ None + } } - } } case class Assignment[+V <: Var[V]]( - pc: PC, - targetVar: V, - expr: Expr[V] + pc: PC, + targetVar: V, + expr: Expr[V] ) extends AssignmentLikeStmt[V] { - final override def asAssignment: this.type = this - final override def isAssignment: Boolean = true + final override def asAssignment: this.type = this + final override def isAssignment: Boolean = true - final override def astID: Int = Assignment.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { - p(expr) - } + final override def astID: Int = Assignment.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { + p(expr) + } - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - targetVar.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + targetVar.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - Assignment(pc, ev(targetVar).toCanonicalForm, expr.toCanonicalForm) - } + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + Assignment(pc, ev(targetVar).toCanonicalForm, expr.toCanonicalForm) + } - final override def isSideEffectFree: Boolean = expr.isSideEffectFree + final override def isSideEffectFree: Boolean = expr.isSideEffectFree - override def hashCode(): Opcode = (Assignment.ASTID * 1171 + pc) * 31 + expr.hashCode + override def hashCode(): Opcode = (Assignment.ASTID * 1171 + pc) * 31 + expr.hashCode - override def toString: String = s"Assignment(pc=$pc,$targetVar,$expr)" + override def toString: String = s"Assignment(pc=$pc,$targetVar,$expr)" } object Assignment { - final val ASTID = 5 + final val ASTID = 5 } case class ReturnValue[+V <: Var[V]](pc: Int, expr: Expr[V]) extends Stmt[V] { - final override def asReturnValue: this.type = this - final override def astID: Int = ReturnValue.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { - p(expr) - } - - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - ReturnValue(pc, expr.toCanonicalForm) - } - - 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 - false - } - - override def toString: String = s"ReturnValue(pc=$pc,$expr)" + final override def asReturnValue: this.type = this + final override def astID: Int = ReturnValue.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { + p(expr) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + ReturnValue(pc, expr.toCanonicalForm) + } + + 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 + false + } + + override def toString: String = s"ReturnValue(pc=$pc,$expr)" } object ReturnValue { - final val ASTID = 6 + final val ASTID = 6 } sealed abstract class SimpleStmt extends VariableFreeStmt { - /** - * Nothing to do. - */ - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = {} + /** + * Nothing to do. + */ + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = {} } 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 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 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 - false - } + 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 + false + } - override def toString: String = s"Return(pc=$pc)" + override def toString: String = s"Return(pc=$pc)" } object Return { - final val ASTID = 7 + final val ASTID = 7 } /** @@ -483,314 +483,314 @@ object Return { */ case class Nop(pc: Int) extends SimpleStmt { - final override def asNop: this.type = this - final override def astID: Int = Nop.ASTID - final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] => Boolean): Boolean = - true + final override def asNop: this.type = this + final override def astID: Int = Nop.ASTID + final override def forallSubExpressions[W >: Nothing <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = + true - final override def isSideEffectFree: Boolean = true + final override def isSideEffectFree: Boolean = true - override def toString: String = s"Nop(pc=$pc)" + override def toString: String = s"Nop(pc=$pc)" } object Nop { - final val ASTID = 8 + final val ASTID = 8 } sealed abstract class SynchronizationStmt[+V <: Var[V]] extends Stmt[V] { - final override def asSynchronizationStmt: this.type = this + final override def asSynchronizationStmt: this.type = this - def objRef: Expr[V] + def objRef: Expr[V] - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - objRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + objRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } } case class MonitorEnter[+V <: Var[V]](pc: PC, objRef: Expr[V]) extends SynchronizationStmt[V] { - final override def asMonitorEnter: this.type = this + final override def asMonitorEnter: this.type = this - final override def isMonitorEnter: Boolean = true + 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) - } + final override def astID: Int = MonitorEnter.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { + p(objRef) + } - final override def isSideEffectFree: Boolean = { - // IMPROVE Is the lock as such ever used (do we potentially have concurrency)? - false - } + final override def isSideEffectFree: Boolean = { + // IMPROVE Is the lock as such ever used (do we potentially have concurrency)? + false + } - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - MonitorEnter(pc, objRef.toCanonicalForm) - } + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + MonitorEnter(pc, objRef.toCanonicalForm) + } - override def toString: String = s"MonitorEnter(pc=$pc,$objRef)" + override def toString: String = s"MonitorEnter(pc=$pc,$objRef)" } object MonitorEnter { - final val ASTID = 9 + final val ASTID = 9 } case class MonitorExit[+V <: Var[V]](pc: PC, objRef: Expr[V]) extends SynchronizationStmt[V] { - final override def asMonitorExit: this.type = this + 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) - } + 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) + } - final override def isSideEffectFree: Boolean = { - // IMPROVE Is the lock as such ever used (do we potentially have concurrency)? - false - } + final override def isSideEffectFree: Boolean = { + // IMPROVE Is the lock as such ever used (do we potentially have concurrency)? + false + } - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - MonitorExit(pc, objRef.toCanonicalForm) - } + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + MonitorExit(pc, objRef.toCanonicalForm) + } - override def toString: String = s"MonitorExit(pc=$pc,$objRef)" + override def toString: String = s"MonitorExit(pc=$pc,$objRef)" } object MonitorExit { - final val ASTID = 10 + final val ASTID = 10 } case class ArrayStore[+V <: Var[V]]( - pc: PC, - arrayRef: Expr[V], - index: Expr[V], - value: Expr[V] + pc: PC, + arrayRef: Expr[V], + index: Expr[V], + value: Expr[V] ) extends Stmt[V] { - final override def asArrayStore: this.type = this - 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) - } - - final override def isSideEffectFree: Boolean = { - // IMPROVE Is it a redundant write? - false - } - - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - arrayRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - index.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - ArrayStore(pc, arrayRef.toCanonicalForm, index.toCanonicalForm, value.toCanonicalForm) - } - - override def toString: String = s"ArrayStore(pc=$pc,$arrayRef,$index,$value)" + final override def asArrayStore: this.type = this + 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) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE Is it a redundant write? + false + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + arrayRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + index.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + ArrayStore(pc, arrayRef.toCanonicalForm, index.toCanonicalForm, value.toCanonicalForm) + } + + override def toString: String = s"ArrayStore(pc=$pc,$arrayRef,$index,$value)" } object ArrayStore { - final val ASTID = 11 + final val ASTID = 11 } case class Throw[+V <: Var[V]](pc: PC, exception: Expr[V]) extends Stmt[V] { - final override def asThrow: this.type = this - final override def astID: Int = Throw.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { - p(exception) - } + final override def asThrow: this.type = this + final override def astID: Int = Throw.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { + p(exception) + } - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - exception.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + exception.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - Throw(pc, exception.toCanonicalForm) - } + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + Throw(pc, exception.toCanonicalForm) + } - final override def isSideEffectFree: Boolean = false + final override def isSideEffectFree: Boolean = false - override def hashCode(): Opcode = (Throw.ASTID * 1171 + pc) * 31 + exception.hashCode + override def hashCode(): Opcode = (Throw.ASTID * 1171 + pc) * 31 + exception.hashCode - override def toString: String = s"Throw(pc=$pc,$exception)" + override def toString: String = s"Throw(pc=$pc,$exception)" } object Throw { - final val ASTID = 12 + final val ASTID = 12 } sealed abstract class FieldWriteAccessStmt[+V <: Var[V]] extends Stmt[V] { - def declaringClass: ObjectType - def name: String - def declaredFieldType: FieldType - def value: Expr[V] - - final override def asFieldWriteAccessStmt: this.type = this - - /** - * Identifies the field if it can be found. - */ - def resolveField(implicit p: ProjectLike): Option[Field] = { - p.resolveFieldReference(declaringClass, name, declaredFieldType) - } + def declaringClass: ObjectType + def name: String + def declaredFieldType: FieldType + def value: Expr[V] + + final override def asFieldWriteAccessStmt: this.type = this + + /** + * Identifies the field if it can be found. + */ + def resolveField(implicit p: ProjectLike): Option[Field] = { + p.resolveFieldReference(declaringClass, name, declaredFieldType) + } } case class PutStatic[+V <: Var[V]]( - pc: PC, - declaringClass: ObjectType, - name: String, - declaredFieldType: FieldType, - value: Expr[V] + pc: PC, + declaringClass: ObjectType, + name: String, + declaredFieldType: FieldType, + value: Expr[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) - } - - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - PutStatic(pc, declaringClass, name, declaredFieldType, value.toCanonicalForm) - } - - final override def isSideEffectFree: Boolean = { - // IMPROVE Is it a redundant write? - false - } - - override def hashCode(): Opcode = { - ((PutStatic.ASTID * 1171 + pc) * 31 + declaringClass.hashCode) * 31 + name.hashCode - } - - override def toString: String = { - s"PutStatic(pc=$pc,${declaringClass.toJava},$name,${declaredFieldType.toJava},$value)" - } + 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) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + PutStatic(pc, declaringClass, name, declaredFieldType, value.toCanonicalForm) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE Is it a redundant write? + false + } + + override def hashCode(): Opcode = { + ((PutStatic.ASTID * 1171 + pc) * 31 + declaringClass.hashCode) * 31 + name.hashCode + } + + override def toString: String = { + s"PutStatic(pc=$pc,${declaringClass.toJava},$name,${declaredFieldType.toJava},$value)" + } } object PutStatic { - final val ASTID = 13 + final val ASTID = 13 } case class PutField[+V <: Var[V]]( - pc: Int, - declaringClass: ObjectType, - name: String, - declaredFieldType: FieldType, - objRef: Expr[V], - value: Expr[V] + pc: Int, + declaringClass: ObjectType, + name: String, + declaredFieldType: FieldType, + objRef: Expr[V], + value: Expr[V] ) extends FieldWriteAccessStmt[V] { - 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) - } - - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - objRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - val newObjRef = objRef.toCanonicalForm - val newValue = value.toCanonicalForm - PutField(pc, declaringClass, name, declaredFieldType, newObjRef, newValue) - } - - final override def isSideEffectFree: Boolean = { - // IMPROVE Is it a redundant write? - false - } - - override def hashCode(): Opcode = { - ((PutField.ASTID * 1171 + pc) * 31 + declaringClass.hashCode) * 31 + name.hashCode - } - - override def toString: String = { - s"PutField(pc=$pc,${declaringClass.toJava},$name,${declaredFieldType.toJava},$objRef,$value)" - } + 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) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + objRef.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + val newObjRef = objRef.toCanonicalForm + val newValue = value.toCanonicalForm + PutField(pc, declaringClass, name, declaredFieldType, newObjRef, newValue) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE Is it a redundant write? + false + } + + override def hashCode(): Opcode = { + ((PutField.ASTID * 1171 + pc) * 31 + declaringClass.hashCode) * 31 + name.hashCode + } + + override def toString: String = { + s"PutField(pc=$pc,${declaringClass.toJava},$name,${declaredFieldType.toJava},$objRef,$value)" + } } object PutField { - final val ASTID = 14 + final val ASTID = 14 } sealed abstract class MethodCall[+V <: Var[V]] extends Stmt[V] with Call[V] { - final override def isSideEffectFree: Boolean = false // IMPROVE Check if a call has no side-effect + final override def isSideEffectFree: Boolean = false // IMPROVE Check if a call has no side-effect - final override def asMethodCall: this.type = this + final override def asMethodCall: this.type = this } sealed abstract class InstanceMethodCall[+V <: Var[V]] extends MethodCall[V] { - final override def allParams: Seq[Expr[V]] = receiver +: params + final override def allParams: Seq[Expr[V]] = receiver +: params - def receiver: Expr[V] - final override def receiverOption: Some[Expr[V]] = Some(receiver) - final override def asInstanceMethodCall: this.type = this - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { - p(receiver) && params.forall(param => p(param)) - } + def receiver: Expr[V] + final override def receiverOption: Some[Expr[V]] = Some(receiver) + final override def asInstanceMethodCall: this.type = this + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { + p(receiver) && params.forall(param ⇒ p(param)) + } - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - receiver.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - params foreach { p => - p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + receiver.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + params foreach { p ⇒ + p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } } - } } object InstanceMethodCall { - def unapply[V <: Var[V]]( - call: InstanceMethodCall[V] - ): Some[(Int, ReferenceType, Boolean, String, MethodDescriptor, Expr[V], Seq[Expr[V]])] = { - import call._ - Some((pc, declaringClass, isInterface, name, descriptor, receiver, params)) - } + def unapply[V <: Var[V]]( + call: InstanceMethodCall[V] + ): Some[(Int, ReferenceType, Boolean, String, MethodDescriptor, Expr[V], Seq[Expr[V]])] = { + import call._ + Some((pc, declaringClass, isInterface, name, descriptor, receiver, params)) + } } /** @@ -798,177 +798,177 @@ object InstanceMethodCall { * I.e., it is either a super-call, a private instance method call or a constructor call. */ case class NonVirtualMethodCall[+V <: Var[V]]( - pc: Int, - declaringClass: ObjectType, - isInterface: Boolean, - name: String, - descriptor: MethodDescriptor, - receiver: Expr[V], - params: Seq[Expr[V]] + pc: Int, + declaringClass: ObjectType, + isInterface: Boolean, + name: String, + descriptor: MethodDescriptor, + receiver: Expr[V], + params: Seq[Expr[V]] ) extends InstanceMethodCall[V] { - final override def asNonVirtualMethodCall: this.type = this - final override def isNonVirtualMethodCall: Boolean = true - final override def astID: Int = NonVirtualMethodCall.ASTID - - /** - * Identifies the potential call target if it can be found. - * - * @see [ProjectLike#specialCall] for further details. - */ - def resolveCallTarget(callerClassType: ObjectType)(implicit p: ProjectLike): Result[Method] = { - p.specialCall(callerClassType, declaringClass, isInterface, name, descriptor) - } - - // convenience method to enable Call to define a single method to handle all kinds of calls - def resolveCallTargets( - callingContext: ObjectType - )( - implicit - p: ProjectLike, - ev: V <:< DUVar[ValueInformation] - ): Set[Method] = { - resolveCallTarget(callingContext)(p).toSet - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - NonVirtualMethodCall( - pc, - declaringClass, - isInterface, - name, - descriptor, - receiver.toCanonicalForm, - params.map(_.toCanonicalForm) - ) - } - - override def toString: String = { - val sig = descriptor.toJava(name) - val declClass = declaringClass.toJava - val params = this.params.mkString("(", ",", ")") - s"NonVirtualMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$receiver,$params)" - } + final override def asNonVirtualMethodCall: this.type = this + final override def isNonVirtualMethodCall: Boolean = true + final override def astID: Int = NonVirtualMethodCall.ASTID + + /** + * Identifies the potential call target if it can be found. + * + * @see [ProjectLike#specialCall] for further details. + */ + def resolveCallTarget(callerClassType: ObjectType)(implicit p: ProjectLike): Result[Method] = { + p.specialCall(callerClassType, declaringClass, isInterface, name, descriptor) + } + + // convenience method to enable Call to define a single method to handle all kinds of calls + def resolveCallTargets( + callingContext: ObjectType + )( + implicit + p: ProjectLike, + ev: V <:< DUVar[ValueInformation] + ): Set[Method] = { + resolveCallTarget(callingContext)(p).toSet + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + NonVirtualMethodCall( + pc, + declaringClass, + isInterface, + name, + descriptor, + receiver.toCanonicalForm, + params.map(_.toCanonicalForm) + ) + } + + override def toString: String = { + val sig = descriptor.toJava(name) + val declClass = declaringClass.toJava + val params = this.params.mkString("(", ",", ")") + s"NonVirtualMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$receiver,$params)" + } } object NonVirtualMethodCall { - final val ASTID = 15 + final val ASTID = 15 } case class VirtualMethodCall[+V <: Var[V]]( - pc: Int, - declaringClass: ReferenceType, - isInterface: Boolean, - name: String, - descriptor: MethodDescriptor, - receiver: Expr[V], - params: Seq[Expr[V]] + pc: Int, + declaringClass: ReferenceType, + isInterface: Boolean, + name: String, + descriptor: MethodDescriptor, + receiver: Expr[V], + params: Seq[Expr[V]] ) extends InstanceMethodCall[V] with VirtualCall[V] { - final override def asVirtualMethodCall: this.type = this - final override def isVirtualMethodCall: Boolean = true - final override def astID: Int = VirtualMethodCall.ASTID - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - VirtualMethodCall( - pc, - declaringClass, - isInterface, - name, - descriptor, - receiver.toCanonicalForm, - params.map(_.toCanonicalForm) - ) - } - - override def toString: String = { - val sig = descriptor.toJava(name) - val declClass = declaringClass.toJava - val params = this.params.mkString("(", ",", ")") - s"VirtualMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$receiver,$params)" - } + final override def asVirtualMethodCall: this.type = this + final override def isVirtualMethodCall: Boolean = true + final override def astID: Int = VirtualMethodCall.ASTID + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + VirtualMethodCall( + pc, + declaringClass, + isInterface, + name, + descriptor, + receiver.toCanonicalForm, + params.map(_.toCanonicalForm) + ) + } + + override def toString: String = { + val sig = descriptor.toJava(name) + val declClass = declaringClass.toJava + val params = this.params.mkString("(", ",", ")") + s"VirtualMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$receiver,$params)" + } } object VirtualMethodCall { - final val ASTID = 16 + final val ASTID = 16 } case class StaticMethodCall[+V <: Var[V]]( - pc: Int, - declaringClass: ObjectType, - isInterface: Boolean, - name: String, - descriptor: MethodDescriptor, - params: Seq[Expr[V]] + pc: Int, + declaringClass: ObjectType, + isInterface: Boolean, + name: String, + descriptor: MethodDescriptor, + params: Seq[Expr[V]] ) extends MethodCall[V] { - final override def allParams: Seq[Expr[V]] = params - - final override def asStaticMethodCall: this.type = this - final override def isStaticMethodCall: Boolean = true - final override def astID: Int = StaticMethodCall.ASTID - final override def receiverOption: Option[Expr[V]] = None - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { - params.forall(param => p(param)) - } - - /** - * Identifies the potential call target if it can be found. - * - * @see [ProjectLike#staticCall] for further details. - */ - def resolveCallTarget(implicit p: ProjectLike): Result[Method] = { - p.staticCall(declaringClass, isInterface, name, descriptor) - } - - // convenience method to enable Call to define a single method to handle all kinds of calls - def resolveCallTargets( - callingContext: ObjectType - )( - implicit - p: ProjectLike, - ev: V <:< DUVar[ValueInformation] - ): Set[Method] = { - resolveCallTarget(p).toSet - } - - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - params.foreach { p => - p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - StaticMethodCall( - pc, - declaringClass, - isInterface, - name, - descriptor, - params.map(_.toCanonicalForm) - ) - } - - override def toString: String = { - val sig = descriptor.toJava(name) - val declClass = declaringClass.toJava - val params = this.params.mkString("(", ",", ")") - s"StaticMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$params)" - } + final override def allParams: Seq[Expr[V]] = params + + final override def asStaticMethodCall: this.type = this + final override def isStaticMethodCall: Boolean = true + final override def astID: Int = StaticMethodCall.ASTID + final override def receiverOption: Option[Expr[V]] = None + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { + params.forall(param ⇒ p(param)) + } + + /** + * Identifies the potential call target if it can be found. + * + * @see [ProjectLike#staticCall] for further details. + */ + def resolveCallTarget(implicit p: ProjectLike): Result[Method] = { + p.staticCall(declaringClass, isInterface, name, descriptor) + } + + // convenience method to enable Call to define a single method to handle all kinds of calls + def resolveCallTargets( + callingContext: ObjectType + )( + implicit + p: ProjectLike, + ev: V <:< DUVar[ValueInformation] + ): Set[Method] = { + resolveCallTarget(p).toSet + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + params.foreach { p ⇒ + p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + StaticMethodCall( + pc, + declaringClass, + isInterface, + name, + descriptor, + params.map(_.toCanonicalForm) + ) + } + + override def toString: String = { + val sig = descriptor.toJava(name) + val declClass = declaringClass.toJava + val params = this.params.mkString("(", ",", ")") + s"StaticMethodCall(pc=$pc,$declClass,isInterface=$isInterface,$sig,$params)" + } } object StaticMethodCall { - final val ASTID = 17 + final val ASTID = 17 } /** @@ -978,98 +978,98 @@ object StaticMethodCall { * @tparam V The type of the [[Var]]s. */ case class InvokedynamicMethodCall[+V <: Var[V]]( - pc: PC, - bootstrapMethod: BootstrapMethod, - name: String, - descriptor: MethodDescriptor, - params: Seq[Expr[V]] + pc: PC, + bootstrapMethod: BootstrapMethod, + name: String, + descriptor: MethodDescriptor, + params: Seq[Expr[V]] ) extends Stmt[V] { - final override def astID: Int = InvokedynamicMethodCall.ASTID - final override def asInvokedynamicMethodCall: this.type = this - // IMPROVE [FUTURE] Use some analysis to determine if a method call is side effect free - final override def isSideEffectFree: Boolean = false - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { - params.forall(param => p(param)) - } - - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - params.foreach { p => - p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - } - - override def hashCode(): Int = { - (((InvokedynamicMethodCall.ASTID * 1171 + - pc) * 31 + - bootstrapMethod.hashCode) * 31 + - name.hashCode) * 31 + - descriptor.hashCode - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - InvokedynamicMethodCall( - pc, - bootstrapMethod, - name, - descriptor, - params.map(_.toCanonicalForm) - ) - } - - override def toString: String = { - val sig = descriptor.toJava(name) - val params = this.params.mkString("(", ",", ")") - s"InvokedynamicMethodCall(pc=$pc,$bootstrapMethod,$sig,$params)" - } + final override def astID: Int = InvokedynamicMethodCall.ASTID + final override def asInvokedynamicMethodCall: this.type = this + // IMPROVE [FUTURE] Use some analysis to determine if a method call is side effect free + final override def isSideEffectFree: Boolean = false + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { + params.forall(param ⇒ p(param)) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + params.foreach { p ⇒ + p.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + } + + override def hashCode(): Int = { + (((InvokedynamicMethodCall.ASTID * 1171 + + pc) * 31 + + bootstrapMethod.hashCode) * 31 + + name.hashCode) * 31 + + descriptor.hashCode + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + InvokedynamicMethodCall( + pc, + bootstrapMethod, + name, + descriptor, + params.map(_.toCanonicalForm) + ) + } + + override def toString: String = { + val sig = descriptor.toJava(name) + val params = this.params.mkString("(", ",", ")") + s"InvokedynamicMethodCall(pc=$pc,$bootstrapMethod,$sig,$params)" + } } object InvokedynamicMethodCall { final val ASTID = 18 } /** An expression where the value is not further used. */ case class ExprStmt[+V <: Var[V]](pc: Int, expr: Expr[V]) extends AssignmentLikeStmt[V] { - final override def asExprStmt: this.type = this - final override def isExprStmt: Boolean = true - final override def astID: Int = ExprStmt.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { - p(expr) - } - - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def isSideEffectFree: Boolean = { - assert( - !expr.isSideEffectFree, - "useless ExprStmt - the referenced expression is side-effect free" - ) - false - } - - override def hashCode(): Opcode = (ExprStmt.ASTID * 1171 + pc) * 31 + expr.hashCode - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - ExprStmt(pc, expr.toCanonicalForm) - } - - override def toString: String = s"ExprStmt(pc=$pc,$expr)" + final override def asExprStmt: this.type = this + final override def isExprStmt: Boolean = true + final override def astID: Int = ExprStmt.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { + p(expr) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + expr.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def isSideEffectFree: Boolean = { + assert( + !expr.isSideEffectFree, + "useless ExprStmt - the referenced expression is side-effect free" + ) + false + } + + override def hashCode(): Opcode = (ExprStmt.ASTID * 1171 + pc) * 31 + expr.hashCode + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + ExprStmt(pc, expr.toCanonicalForm) + } + + override def toString: String = s"ExprStmt(pc=$pc,$expr)" } object ExprStmt { - final val ASTID = 19 + final val ASTID = 19 } /** @@ -1078,13 +1078,13 @@ object ExprStmt { */ object VirtualFunctionCallStatement { - def unapply[V <: Var[V]](stmt: Stmt[V]): Option[VirtualFunctionCall[V]] = { - stmt match { - case ExprStmt(_, vfc: VirtualFunctionCall[V]) => Some(vfc) - case Assignment(_, _, vfc: VirtualFunctionCall[V]) => Some(vfc) - case _ => None + def unapply[V <: Var[V]](stmt: Stmt[V]): Option[VirtualFunctionCall[V]] = { + stmt match { + case ExprStmt(_, vfc: VirtualFunctionCall[V]) ⇒ Some(vfc) + case Assignment(_, _, vfc: VirtualFunctionCall[V]) ⇒ Some(vfc) + case _ ⇒ None + } } - } } /** @@ -1093,13 +1093,13 @@ object VirtualFunctionCallStatement { */ object NonVirtualFunctionCallStatement { - def unapply[V <: Var[V]](stmt: Stmt[V]): Option[NonVirtualFunctionCall[V]] = { - stmt match { - case ExprStmt(_, vfc: NonVirtualFunctionCall[V]) => Some(vfc) - case Assignment(_, _, vfc: NonVirtualFunctionCall[V]) => Some(vfc) - case _ => None + def unapply[V <: Var[V]](stmt: Stmt[V]): Option[NonVirtualFunctionCall[V]] = { + stmt match { + case ExprStmt(_, vfc: NonVirtualFunctionCall[V]) ⇒ Some(vfc) + case Assignment(_, _, vfc: NonVirtualFunctionCall[V]) ⇒ Some(vfc) + case _ ⇒ None + } } - } } /** @@ -1108,13 +1108,13 @@ object NonVirtualFunctionCallStatement { */ object StaticFunctionCallStatement { - def unapply[V <: Var[V]](stmt: Stmt[V]): Option[StaticFunctionCall[V]] = { - stmt match { - case ExprStmt(_, vfc: StaticFunctionCall[V]) => Some(vfc) - case Assignment(_, _, vfc: StaticFunctionCall[V]) => Some(vfc) - case _ => None + def unapply[V <: Var[V]](stmt: Stmt[V]): Option[StaticFunctionCall[V]] = { + stmt match { + case ExprStmt(_, vfc: StaticFunctionCall[V]) ⇒ Some(vfc) + case Assignment(_, _, vfc: StaticFunctionCall[V]) ⇒ Some(vfc) + case _ ⇒ None + } } - } } /** @@ -1124,78 +1124,78 @@ object StaticFunctionCallStatement { * @note `CaughtException` expression are only created by [[TACAI]]! */ case class CaughtException[+V <: Var[V]]( - pc: PC, - exceptionType: Option[ObjectType], - private var throwingStmts: IntTrieSet + pc: PC, + exceptionType: Option[ObjectType], + private var throwingStmts: IntTrieSet ) extends Stmt[V] { - final override def asCaughtException: CaughtException[V] = this - final override def astID: Int = CaughtException.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = true - - final override def isSideEffectFree: Boolean = false - - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - throwingStmts = throwingStmts map { pc => - ai.remapPC(pcToIndex)(pc) - } - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - this.asInstanceOf[Stmt[DUVar[ValueInformation]]] - } - - /** - * The origin(s) of the caught exception(s). An origin identifies the instruction - * that ex- or implicitly created the exception: - * - If the exception is created locally (`new XXXException`) and also caught within the - * same method, then the origin identifies a normal variable definition site. - * - If the exception is a parameter the parameter's origin (-1,... -n) is returned. - * - If the exception was raised due to a sideeffect of evaluating an expression, then the - * origin is smaller or equal to [[org.opalj.ai.ImmediateVMExceptionsOriginOffset]] and can be - * tranformed to the index of the responsible instruction using - * [[org.opalj.ai#pcOfImmediateVMException]]. - */ - def origins: IntTrieSet = throwingStmts - - /** - * Textual description of the sources of the caught exceptions. If the exception was - * thrown by the JVM due to the evaluation of an expression (e.g., NullPointerException, - * DivisionByZero,..) then the string will be `exception@` where index identifies - * the failing expression. In case an exception is caught that was thrown using `ATHROW` - * the local variable/parameter which stores the local variable is returned. - */ - final def exceptionLocations: Iterator[String] = { - throwingStmts.iterator.map { defSite => - if (defSite < 0) { - if (ai.isImmediateVMException(defSite)) - "exception[VM]@" + ai.pcOfImmediateVMException(defSite) - else if (ai.isMethodExternalExceptionOrigin(defSite)) - "exception@" + ai.pcOfMethodExternalException(defSite) - else - "param" + (-defSite - 1).toHexString - } else { - "lv" + defSite.toHexString - } - } - } - - override def toString: String = { - val exceptionType = this.exceptionType.map(_.toJava).getOrElse("") - val exceptionLocations = this.exceptionLocations.mkString("{", ",", "}") - s"CaughtException(pc=$pc,$exceptionType,caused by=$exceptionLocations)" - } + final override def asCaughtException: CaughtException[V] = this + final override def astID: Int = CaughtException.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = true + + final override def isSideEffectFree: Boolean = false + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + throwingStmts = throwingStmts map { pc ⇒ + ai.remapPC(pcToIndex)(pc) + } + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + this.asInstanceOf[Stmt[DUVar[ValueInformation]]] + } + + /** + * The origin(s) of the caught exception(s). An origin identifies the instruction + * that ex- or implicitly created the exception: + * - If the exception is created locally (`new XXXException`) and also caught within the + * same method, then the origin identifies a normal variable definition site. + * - If the exception is a parameter the parameter's origin (-1,... -n) is returned. + * - If the exception was raised due to a sideeffect of evaluating an expression, then the + * origin is smaller or equal to [[org.opalj.ai.ImmediateVMExceptionsOriginOffset]] and can be + * tranformed to the index of the responsible instruction using + * [[org.opalj.ai#pcOfImmediateVMException]]. + */ + def origins: IntTrieSet = throwingStmts + + /** + * Textual description of the sources of the caught exceptions. If the exception was + * thrown by the JVM due to the evaluation of an expression (e.g., NullPointerException, + * DivisionByZero,..) then the string will be `exception@` where index identifies + * the failing expression. In case an exception is caught that was thrown using `ATHROW` + * the local variable/parameter which stores the local variable is returned. + */ + final def exceptionLocations: Iterator[String] = { + throwingStmts.iterator.map { defSite ⇒ + if (defSite < 0) { + if (ai.isImmediateVMException(defSite)) + "exception[VM]@"+ai.pcOfImmediateVMException(defSite) + else if (ai.isMethodExternalExceptionOrigin(defSite)) + "exception@"+ai.pcOfMethodExternalException(defSite) + else + "param"+(-defSite - 1).toHexString + } else { + "lv"+defSite.toHexString + } + } + } + + override def toString: String = { + val exceptionType = this.exceptionType.map(_.toJava).getOrElse("") + val exceptionLocations = this.exceptionLocations.mkString("{", ",", "}") + s"CaughtException(pc=$pc,$exceptionType,caused by=$exceptionLocations)" + } } object CaughtException { - final val ASTID = 20 + final val ASTID = 20 } @@ -1204,38 +1204,38 @@ object CaughtException { */ case class Checkcast[+V <: Var[V]](pc: PC, value: Expr[V], cmpTpe: ReferenceType) extends Stmt[V] { - final override def asCheckcast: this.type = this - final override def astID: Int = Checkcast.ASTID - final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] => Boolean): Boolean = { - p(value) - } - - override private[tac] def remapIndexes( - pcToIndex: Array[Int], - isIndexOfCaughtExceptionStmt: Int => Boolean - ): Unit = { - value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) - } - - final override def toCanonicalForm( - implicit - ev: V <:< DUVar[ValueInformation] - ): Stmt[DUVar[ValueInformation]] = { - Checkcast(pc, value.toCanonicalForm, cmpTpe) - } - - final override def isSideEffectFree: Boolean = { - // IMPROVE identify (from the JVM verifiers point-of-view) truly useless checkcasts - // A useless checkcast is one where the static intra-procedural type information which - // is available in the bytecode is sufficient to determine that the type is a subtype - // of the tested type (i.e., only those check casts are truly usefull that would not - // lead to a failing validation of the bytecode by the JVM!) - false - } - - override def toString: String = s"Checkcast(pc=$pc,$value,${cmpTpe.toJava})" + final override def asCheckcast: this.type = this + final override def astID: Int = Checkcast.ASTID + final override def forallSubExpressions[W >: V <: Var[W]](p: Expr[W] ⇒ Boolean): Boolean = { + p(value) + } + + override private[tac] def remapIndexes( + pcToIndex: Array[Int], + isIndexOfCaughtExceptionStmt: Int ⇒ Boolean + ): Unit = { + value.remapIndexes(pcToIndex, isIndexOfCaughtExceptionStmt) + } + + final override def toCanonicalForm( + implicit + ev: V <:< DUVar[ValueInformation] + ): Stmt[DUVar[ValueInformation]] = { + Checkcast(pc, value.toCanonicalForm, cmpTpe) + } + + final override def isSideEffectFree: Boolean = { + // IMPROVE identify (from the JVM verifiers point-of-view) truly useless checkcasts + // A useless checkcast is one where the static intra-procedural type information which + // is available in the bytecode is sufficient to determine that the type is a subtype + // of the tested type (i.e., only those check casts are truly usefull that would not + // lead to a failing validation of the bytecode by the JVM!) + false + } + + override def toString: String = s"Checkcast(pc=$pc,$value,${cmpTpe.toJava})" } object Checkcast { - final val ASTID = 21 + final val ASTID = 21 } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index c4bedef66b..d8a2cc517d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -84,200 +84,200 @@ import org.opalj.value.ValueInformation class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - case class State( - field: Field, - var referenceImmutability: ReferenceImmutability = ImmutableReference, - 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, - var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, - var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty - ) { - def hasDependees: Boolean = { - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || - escapeDependees.nonEmpty || tacDependees.nonEmpty + case class State( + field: Field, + var referenceImmutability: ReferenceImmutability = ImmutableReference, + 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, + var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty + } + + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + } } - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + 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) + + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field ⇒ determineReferenceImmutability(field) + case _ ⇒ + val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) } - } - - 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) - - def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field => determineReferenceImmutability(field) - case _ => - val m = entity.getClass.getSimpleName + " is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } - - /** - * Analyzes the mutability of private non-final fields. - * - * 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 determineReferenceImmutability( - field: Field - ): ProperPropertyComputationResult = { - implicit val state: State = State(field) - // Fields are not final if they are read prematurely! - if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) - return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); - if (field.isFinal) - return createResult(); - - state.referenceImmutability = ImmutableReference //EffectivelyFinalField - - val thisType = field.classFile.thisType - - if (field.isPublic || field.isPackagePrivate || field.isProtected) - return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) - - // 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) { - if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - } - project.classesPerPackage(thisType.packageName) - } else { - Set(field.classFile) - } - - val classesHavingAccess: Iterator[ClassFile] = - if (field.isProtected) { - if (typeExtensibility(thisType).isYesOrUnknown) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - } - 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))) - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - - // 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 - // of the class after the reference to the class and (an indirect) reference to the - // field has become available. Consider the following example: - // class X implements Y{ - // - // private Object o; - // - // public Object getO() { return o; } - // - // private static X instance; - // static { - // instance = new X(); - // Z.register(instance); - // // when we reach this point o is now (via getO) publically accessible and - // // X is properly initialized! - // o = new Object(); // o is mutated... - // } - // } /** - * Returns the TACode for a method if available, registering dependencies as necessary. + * Analyzes the mutability of private non-final fields. + * + * 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. */ - 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 eps: InterimEP[Method, TACAI] => - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk => - state.tacDependees += method -> ((epk, pcs)) - None - } - } + private[analyses] def determineReferenceImmutability( + field: Field + ): ProperPropertyComputationResult = { + implicit val state: State = State(field) + // Fields are not final if they are read prematurely! + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) + return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); + if (field.isFinal) + return createResult(); + + state.referenceImmutability = ImmutableReference //EffectivelyFinalField + + val thisType = field.classFile.thisType + + if (field.isPublic || field.isPackagePrivate || field.isProtected) + return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) + + // 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) { + if (!closedPackages.isClosed(thisType.packageName)) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } - for { - (method, pcs) <- fieldAccessInformation.writeAccesses(field) - taCode <- getTACAI(method, pcs) - } { - if (methodUpdatesField(method, taCode, pcs)) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); - } + val classesHavingAccess: Iterator[ClassFile] = + if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + 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))) + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode ← getTACAI(method, pcs) + } { + if (methodUpdatesField(method, taCode, pcs)) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); + } + + } + + if (state.lazyInitInvocation.isDefined) { + val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + handleCalls(calleesEOP) + } - if (state.lazyInitInvocation.isDefined) { - val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - handleCalls(calleesEOP) + createResult() } - createResult() - } - - def handleCalls( - calleesEOP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: State - ): Boolean = { - calleesEOP match { - case FinalP(callees) => - state.calleesDependee = None - handleCallees(callees) - case InterimUBP(callees) => - state.calleesDependee = Some(calleesEOP) - handleCallees(callees) - case _ => - state.calleesDependee = Some(calleesEOP) - false + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: State + ): Boolean = { + calleesEOP match { + case FinalP(callees) ⇒ + state.calleesDependee = None + handleCallees(callees) + case InterimUBP(callees) ⇒ + state.calleesDependee = Some(calleesEOP) + handleCallees(callees) + case _ ⇒ + state.calleesDependee = Some(calleesEOP) + false + } } - } - - def handleCallees(callees: Callees)(implicit state: State): Boolean = { - val pc = state.lazyInitInvocation.get._2 - if (callees.isIncompleteCallSite(pc)) { - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else { - val targets = callees.callees(pc).toTraversable - if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else false + + def handleCallees(callees: Callees)(implicit state: State): Boolean = { + val pc = state.lazyInitInvocation.get._2 + if (callees.isIncompleteCallSite(pc)) { + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else { + val targets = callees.callees(pc).toTraversable + if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else false + } } - } - /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. - */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field * The code below can be used to identify such value, but is not yet adapted to using the * TACAI property */ - /* + /* var constantVal: Option[Any] = None var allInitializeConstant = true @@ -335,773 +335,773 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } constantVal */ - } - - /** - * 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 MutableReference)) //NonFinalFieldByAnalysis)) - InterimResult( - state.field, - MutableReference, //NonFinalFieldByAnalysis, - 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 = { - val isNotFinal = eps.pk match { - case EscapeProperty.key => - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - 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)) - methodUpdatesField(method, newEP.ub.tac.get, pcs) - case Callees.key => - handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key => - isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key => - val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] - state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) - isNonDeterministic(newEP) - case ReferenceImmutability.key => - val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] - state.referenceImmutabilityDependees = - state.referenceImmutabilityDependees.filter(_.e ne newEP.e) - !isImmutableReference(newEP) } - if (isNotFinal) { - Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) - } else - createResult() - } - - /** - * Checks whether a field write may be a lazy initialization. - * - * We only consider simple cases of lazy initialization where the single field write is guarded - * so it is executed only if the field still has its default value and where the written value - * is guaranteed to be the same even if the write is executed multiple times (may happen because - * of the initializing method being executed more than once on concurrent threads). - * - * Also, all field reads must be used only for a guard or their uses have to be guarded - * themselves. - */ - def isLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - )(implicit state: State): Boolean = { - val write = code(writeIndex).asFieldWriteAccessStmt - - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - // Only handle lazy initialization of ints and floats as they are guaranteed to be - // written atomically - return false; + /** + * 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 MutableReference)) //NonFinalFieldByAnalysis)) + InterimResult( + state.field, + MutableReference, //NonFinalFieldByAnalysis, + state.referenceImmutability, + state.dependees, + c + ) + else + Result(state.field, state.referenceImmutability) } - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs => (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return false; // Reads outside the (single) lazy initialization method + /** + * 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 = { + val isNotFinal = eps.pk match { + case EscapeProperty.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + methodUpdatesField(method, newEP.ub.tac.get, pcs) + case Callees.key ⇒ + handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key ⇒ + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + isNonDeterministic(newEP) + case ReferenceImmutability.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + !isImmutableReference(newEP) + } + + if (isNotFinal) { + Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) + } else + createResult() } - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val (guardIndex, guardedIndex, readIndex) = - findGuard(writeIndex, defaultValue, code, cfg) match { - case Some((guard, guarded, read)) => (guard, guarded, read) - case None => return false; - } - - // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) - return false; - - // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - return false; - - // Field reads (except for the guard) may only be executed if the field's value is not the - // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - return false; - - true - } - - def checkWrites( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val definitions = write.value.asVar.definedBy - - val isDeterministic = - if (definitions.size == 1) { - // The value written must be computed deterministically - checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) - } else { - // More than one definition site for the value might lead to differences between - // invocations, but not if this method has no parameters and is deterministic - // (in this case, the definition reaching the write will always be the same) - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - - // The field write must be guarded correctly - isDeterministic && - checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - } - - /** - * Checks if the field write for a lazy initialization is immediately followed by a return of - * the written value (possibly loaded by another field read). - */ - def checkImmediateReturn( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 - - var load = -1 - - while (index < code.length) { - val stmt = code(index) - stmt.astID match { - case Assignment.ASTID => - if (isReadOfCurrentField(stmt.asAssignment.expr)) - load = index - else - return false; // No field read or a field read of a different field - case ReturnValue.ASTID => - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) - return true; // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) - return true; // return of field value loaded by field read - else - return false; // return of different value - case _ => return false; // neither a field read nor a return - } - index += 1 + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + )(implicit state: State): Boolean = { + val write = code(writeIndex).asFieldWriteAccessStmt + + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + return false; + } + + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method + } + + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + case None ⇒ return false; + } + + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) + return false; + + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + + true } - false - } - - def lazyInitializerIsDeterministic( - method: Method, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( - propertyStore(declaredMethods(method), Purity.key) - )) - result - } - - /** - * 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 - for (pc <- pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID => - if (method.isInitializer) { - if (field.isStatic) { - if (method.isConstructor) - return true; + + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val definitions = write.value.asVar.definedBy + + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + + // The field write must be guarded correctly + isDeterministic && + checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID ⇒ + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID ⇒ + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ ⇒ return false; // neither a field read nor a return + } + index += 1 + } + false + } + + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + )) + result + } + + /** + * 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 + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + if (field.isStatic) { + if (method.isConstructor) + return true; + } else { + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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.referenceImmutability == LazyInitializedReference) //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; + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + //TODO Lazy Initialization here + /** + * val liResult = propertyStore(field, ReferenceImmutabilityLazyInitialization.key) + * + * liResult match { + * case FinalP(NoLazyInitialization) => return true; + * case FinalP(NotThreadSafeLazyInitialization) => return true; + * case FinalP(LazyInitialization) => + * state.referenceImmutability = LazyInitializedReference + * case _ => return true; + * } + */ + // A field written outside an initializer must be lazily + // initialized or it is non-final + + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )) + return true; + + state.referenceImmutability = LazyInitializedReference + LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - if (receiverDefs != SelfReferenceParameter) - return true; + // nothing to do as the put field is dead } - } 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.referenceImmutability == LazyInitializedReference) //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; - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - //TODO Lazy Initialization here - /** - * val liResult = propertyStore(field, ReferenceImmutabilityLazyInitialization.key) - * - * liResult match { - * case FinalP(NoLazyInitialization) => return true; - * case FinalP(NotThreadSafeLazyInitialization) => return true; - * case FinalP(LazyInitialization) => - * state.referenceImmutability = LazyInitializedReference - * case _ => return true; - * } - */ - // A field written outside an initializer must be lazily - // initialized or it is non-final - - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - )) - return true; - - state.referenceImmutability = LazyInitializedReference - LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - // 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. - return true; - } - } - case _ => throw new RuntimeException("unexpected field access"); - } - } else { - // nothing to do as the put field is dead + } } - } + false } - false - } - - /** - * 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 eps: InterimEP[Method, TACAI] ⇒ - * state.tacDependees += method → ((eps, pcs)) - * eps.ub.tac - * case epk ⇒ - * state.tacDependees += method → ((epk, pcs)) - * None - * } - * } - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - /** - * 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 // Must be locally created - else { - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - handleEscapeProperty(escape) + + /** + * 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 eps: InterimEP[Method, TACAI] ⇒ + * state.tacDependees += method → ((eps, pcs)) + * eps.ub.tac + * case epk ⇒ + * state.tacDependees += method → ((epk, pcs)) + * None + * } + * } + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) + } + } } - } - } - } - - /** - * 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 = 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 - } - - /** - * Checks if the field write is only executed if the field's value was still the default value. - * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization - * and the field write. - */ - def checkWriteIsGuarded( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val startBB = cfg.bb(writeIndex).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt => - stmt.astID == CaughtException.ASTID - } flatMap { exception => - exception.asCaughtException.origins.map { origin: Int => - if (isImmediateVMException(origin)) - pcOfImmediateVMException(origin) - else - pcOfMethodExternalException(origin) - }.iterator } - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail + /** + * 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 = ep match { + case FinalP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ + false - val startPC = curBB.startPC - val endPC = curBB.endPC + case FinalP(AtMost(_)) ⇒ + true - if (startPC == 0 || startPC == guardedIndex) - return false; // Reached method start or wrong branch of guarding if-Statement + case _: FinalEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + case InterimUBP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ + state.escapeDependees += ep + false - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + case InterimUBP(AtMost(_)) ⇒ + true - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors + case _: InterimEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return + + case _ ⇒ + state.escapeDependees += ep + false } - true - } - - /** - * 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]] - )(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(defSites.head).asAssignment.expr.isConst) true - else { - val expr = code(index).asAssignment.expr - expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { - case Some(field) => - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ => // Unknown field - false - }) + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt ⇒ + stmt.astID == CaughtException.ASTID + } flatMap { exception ⇒ + exception.asCaughtException.origins.map { origin: Int ⇒ + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator + } + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement + + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors } - } - defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) + true } - val value = origin.expr + /** + * 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]] + )(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(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + false + }) + } + } - val isNonConstDeterministic = value.astID match { - case GetStatic.ASTID | GetField.ASTID => - value.asFieldRead.resolveField(p) match { - case Some(field) => - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ => // Unknown field - false + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) } - 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.exists(!isConstant(_))) { - false - } else { - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true + + val value = origin.expr + + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID ⇒ + value.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + false + } + 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.exists(!isConstant(_))) { + false + } else { + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true + } + 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, code) } - 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, code) - } - value.isConst || isNonConstDeterministic - } - - /** - * Checks that all non-dead field reads that are not used for the guarding if-Statement of a - * lazy initialization are only executed if the field did not have its default value or after - * the (single) field write. - */ - def checkReads( - reads: Seq[(Method, PCs)], - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - ): Boolean = { - // There is only a single method with reads aside from initializers (checked by - // isLazilyInitialized), so we have to check only reads from that one method. - reads.filter(!_._1.isInitializer).head._2 forall { readPC => - val index = pcToIndex(readPC) - index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) - } - } - - /** - * Checks that a field read is only executed if the field did not have its default value or - * after the (single) field write. - */ - def checkRead( - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Boolean = { - val startBB = cfg.bb(readIndex).asBasicBlock - val writeBB = cfg.bb(writeIndex) - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - - if (startPC == 0) - return false; // Reached the start of the method but not the guard or field write - - if ((curBB eq writeBB) && writeIndex > readIndex) - return false; // In the basic block of the write, but before the write - - if (startPC != guardedIndex && // Did not reach the guard - (curBB ne writeBB) /* Not the basic block of the write */ ) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } + value.isConst || isNonConstDeterministic } - true - } - - /** - * Gets all predecessor BasicBlocks of a CFGNode. - */ - def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode => - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) - } - result.toList - } - - /** - * 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. - */ - def findGuard( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Option[(Int, Int, Int)] = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int)] = None - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID => - val ifStmt = cfStmt.asIf - ifStmt.condition match { - case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) => - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1)) - } - - case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) => - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None; - } else { - result = Some((endPC, ifStmt.targetStmt)) - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ => - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ => - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) + } } - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use => - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + true } - None - } - - /** - * 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])(implicit state: State): Boolean = { - val field = expr.astID match { - case GetField.ASTID => - val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) None - else expr.asGetField.resolveField(project) - case GetStatic.ASTID => expr.asGetStatic.resolveField(project) - case _ => None + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode ⇒ + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) + } + result.toList } - field.contains(state.field) - } - - /** - * 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]] - )(implicit state: State): Boolean = { /** - * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + * 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. */ - 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 || - expr.isFloatConst && defaultValue == expr.asFloatConst.value - } + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID ⇒ + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + } + + None } /** - * Checks whether the non-constant expression of the if-Statement is a read of the current - * field. + * 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 isGuardInternal(expr: V): Boolean = { - expr.definedBy forall { index => - if (index < 0) false // If the value is from a parameter, this can not be the guard - else isReadOfCurrentField(code(index).asAssignment.expr) - } + def isReadOfCurrentField(expr: Expr[V])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID ⇒ + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) + case _ ⇒ None + } + field.contains(state.field) } - if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { - isGuardInternal(ifStmt.rightExpr.asVar) - } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { - isGuardInternal(ifStmt.leftExpr.asVar) - } else false - } - - /** - * Checks if 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) => true - case eps => - state.prematurelyReadDependee = Some(eps) - 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]] + )(implicit state: State): Boolean = { + + /** + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + */ + 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } + + /** + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. + */ + def isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index ⇒ + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } + } + + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else false } - /** - * Checkes 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. - */ - 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 - } - - /** - * Checkes if 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, ReferenceImmutability] - )(implicit state: State): Boolean = eop match { - case FinalEP(e, ImmutableReference) => true - case FinalEP(e, MutableReference) => false - // - case LBP(ImmutableReference) => true - case UBP(MutableReference) => false + /** + * Checks if 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) ⇒ true + case eps ⇒ + state.prematurelyReadDependee = Some(eps) + false + } + + /** + * Checkes 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. + */ + 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 + } /** - * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ - * true - * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * + * Checkes if the field the value assigned to a (potentially) lazily initialized field is final, + * ensuring that the same value is written even for concurrent executions. */ - case _ => - state.referenceImmutabilityDependees += eop - true - } + def isImmutableReference( + eop: EOptionP[Field, ReferenceImmutability] + )(implicit state: State): Boolean = eop match { + case FinalEP(e, ImmutableReference) ⇒ true + case FinalEP(e, MutableReference) ⇒ false + // + case LBP(ImmutableReference) ⇒ true + case UBP(MutableReference) ⇒ false + + /** + * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ + * true + * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * + */ + case _ ⇒ + state.referenceImmutabilityDependees += eop + true + } } trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.ub(EscapeProperty) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) } @@ -1112,16 +1112,16 @@ object EagerL0ReferenceImmutabilityAnalysis extends L0ReferenceImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -1131,18 +1131,18 @@ object LazyL0ReferenceImmutabilityAnalysis extends L0ReferenceImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - ReferenceImmutability.key, - analysis.determineReferenceImmutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + ReferenceImmutability.key, + analysis.determineReferenceImmutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala index 6afceeed3e..1b2e9e6d8c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala @@ -105,206 +105,206 @@ import scala.collection.mutable class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - case class State( - field: Field, - var referenceImmutability: ReferenceImmutabilityLazyInitialization = NoLazyInitialization, - 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, - var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, - var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty, - var isThreadSafeType: Boolean = true - ) { - def hasDependees: Boolean = { - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || - escapeDependees.nonEmpty || tacDependees.nonEmpty + case class State( + field: Field, + var referenceImmutability: ReferenceImmutabilityLazyInitialization = NoLazyInitialization, + 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, + var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty, + var isThreadSafeType: Boolean = true + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty + } + + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + } } - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + 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) + + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field ⇒ determineReferenceImmutabilityLazyInitialization(field) + case _ ⇒ + val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) } - } - - 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) - - def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field => determineReferenceImmutabilityLazyInitialization(field) - case _ => - val m = entity.getClass.getSimpleName + " is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } - - /** - * Analyzes the mutability of private non-final fields. - * - * 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 determineReferenceImmutabilityLazyInitialization( - field: Field - ): ProperPropertyComputationResult = { - implicit val state: State = State(field) - // Fields are not final if they are read prematurely! - //if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) - // return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); - //if (field.isFinal) - // return createResult(); - - //state.referenceImmutability = ImmutableReference //EffectivelyFinalField - - //val thisType = field.classFile.thisType - - //if (field.isPublic) - // return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) - - // 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) { - * if (!closedPackages.isClosed(thisType.packageName)) { - * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - * } - * project.classesPerPackage(thisType.packageName) - * } else { - * Set(field.classFile) - * } + * Analyzes the mutability of private non-final fields. * - * val classesHavingAccess: Iterator[ClassFile] = - * if (field.isProtected) { - * if (typeExtensibility(thisType).isYesOrUnknown) { - * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - * } - * val subclassesIterator: Iterator[ClassFile] = - * classHierarchy.allSubclassTypes(thisType, reflexive = false).flatMap { ot => - * project.classFile(ot).filter(cf => !initialClasses.contains(cf)) + * 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 determineReferenceImmutabilityLazyInitialization( + field: Field + ): ProperPropertyComputationResult = { + implicit val state: State = State(field) + // Fields are not final if they are read prematurely! + //if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) + // return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); + //if (field.isFinal) + // return createResult(); + + //state.referenceImmutability = ImmutableReference //EffectivelyFinalField + + //val thisType = field.classFile.thisType + + //if (field.isPublic) + // return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) + + // 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) { + * if (!closedPackages.isClosed(thisType.packageName)) { + * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + * } + * project.classesPerPackage(thisType.packageName) + * } else { + * Set(field.classFile) + * } + * + * val classesHavingAccess: Iterator[ClassFile] = + * if (field.isProtected) { + * if (typeExtensibility(thisType).isYesOrUnknown) { + * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + * } + * 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))) + /// return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + + // 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 + // of the class after the reference to the class and (an indirect) reference to the + // field has become available. Consider the following example: + // class X implements Y{ + // + // private Object o; + // + // public Object getO() { return o; } + // + // private static X instance; + // static { + // instance = new X(); + // Z.register(instance); + // // when we reach this point o is now (via getO) publically accessible and + // // X is properly initialized! + // o = new Object(); // o is mutated... + // } + // } + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode ← getTACAI(method, pcs) + } { + //if ( + checkMethod(method, taCode, pcs) //) { + // return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); + //} + + } + + ///if (state.lazyInitInvocation.isDefined) { + /// val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + /// handleCalls(calleesEOP) + ///} + + createResult() + } + + /** + * def handleCalls( + * calleesEOP: EOptionP[DeclaredMethod, Callees] + * )( + * implicit + * state: State + * ): Boolean = { + * calleesEOP match { + * case FinalP(callees) => + * state.calleesDependee = None + * handleCallees(callees) + * case InterimUBP(callees) => + * state.calleesDependee = Some(calleesEOP) + * handleCallees(callees) + * case _ => + * state.calleesDependee = Some(calleesEOP) + * false * } - * initialClasses.iterator ++ subclassesIterator + * }* + */ + /** + * + * def handleCallees(callees: Callees)(implicit state: State): Boolean = { + * val pc = state.lazyInitInvocation.get._2 + * if (callees.isIncompleteCallSite(pc)) { + * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + * true * } else { - * initialClasses.iterator + * val targets = callees.callees(pc).toTraversable + * if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { + * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + * true + * } else false * } + * }* */ - // If there are native methods, we give up - ///if (classesHavingAccess.exists(_.methods.exists(_.isNative))) - /// return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - - // 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 - // of the class after the reference to the class and (an indirect) reference to the - // field has become available. Consider the following example: - // class X implements Y{ - // - // private Object o; - // - // public Object getO() { return o; } - // - // private static X instance; - // static { - // instance = new X(); - // Z.register(instance); - // // when we reach this point o is now (via getO) publically accessible and - // // X is properly initialized! - // o = new Object(); // o is mutated... - // } - // } - /** - * Returns the TACode for a method if available, registering dependencies as necessary. + * Returns the value the field will have after initialization or None if there may be multiple + * values. */ - 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 eps: InterimEP[Method, TACAI] => - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk => - state.tacDependees += method -> ((epk, pcs)) - None - } - } - - for { - (method, pcs) <- fieldAccessInformation.writeAccesses(field) - taCode <- getTACAI(method, pcs) - } { - //if ( - checkMethod(method, taCode, pcs) //) { - // return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); - //} - - } + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - ///if (state.lazyInitInvocation.isDefined) { - /// val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - /// handleCalls(calleesEOP) - ///} - - createResult() - } - - /** - * def handleCalls( - * calleesEOP: EOptionP[DeclaredMethod, Callees] - * )( - * implicit - * state: State - * ): Boolean = { - * calleesEOP match { - * case FinalP(callees) => - * state.calleesDependee = None - * handleCallees(callees) - * case InterimUBP(callees) => - * state.calleesDependee = Some(calleesEOP) - * handleCallees(callees) - * case _ => - * state.calleesDependee = Some(calleesEOP) - * false - * } - * }* - */ - /** - * - * def handleCallees(callees: Callees)(implicit state: State): Boolean = { - * val pc = state.lazyInitInvocation.get._2 - * if (callees.isIncompleteCallSite(pc)) { - * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - * true - * } else { - * val targets = callees.callees(pc).toTraversable - * if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { - * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - * true - * } else false - * } - * }* - */ - /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. - */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field * The code below can be used to identify such value, but is not yet adapted to using the * TACAI property */ - /* + /* var constantVal: Option[Any] = None var allInitializeConstant = true @@ -362,943 +362,943 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p } constantVal */ - } - - /** - * 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 NoLazyInitialization)) //NonFinalFieldByAnalysis)) - InterimResult( - state.field, - NoLazyInitialization, //MutableReference, //NonFinalFieldByAnalysis, - state.referenceImmutability, - state.dependees, - c - ) - else { - if (state.referenceImmutability == LazyInitialization && !state.isThreadSafeType) - Result(state.field, NotThreadSafeLazyInitialization) - 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 = { - val isNotFinal: Boolean = eps.pk match { - case EscapeProperty.key => - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - 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)) - checkMethod(method, newEP.ub.tac.get, pcs) - //case Callees.key => - // handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key => - isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key => - val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] - state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) - isNonDeterministic(newEP) - case ReferenceImmutabilityLazyInitialization.key => - val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] - state.referenceImmutabilityDependees = - state.referenceImmutabilityDependees.filter(_.e ne newEP.e) - !isImmutableReference(newEP) + + /** + * 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 NoLazyInitialization)) //NonFinalFieldByAnalysis)) + InterimResult( + state.field, + NoLazyInitialization, //MutableReference, //NonFinalFieldByAnalysis, + state.referenceImmutability, + state.dependees, + c + ) + else { + if (state.referenceImmutability == LazyInitialization && !state.isThreadSafeType) + Result(state.field, NotThreadSafeLazyInitialization) + 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 = { + val isNotFinal: Boolean = eps.pk match { + case EscapeProperty.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + 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)) + checkMethod(method, newEP.ub.tac.get, pcs) + //case Callees.key => + // handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key ⇒ + isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + isNonDeterministic(newEP) + case ReferenceImmutabilityLazyInitialization.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + !isImmutableReference(newEP) + } + + if (isNotFinal) { + Result(state.field, NoLazyInitialization) //Result(state.field, NonFinalFieldByAnalysis) + } else + createResult() } - if (isNotFinal) { - Result(state.field, NoLazyInitialization) //Result(state.field, NonFinalFieldByAnalysis) - } else - createResult() - } - - /** - * Checks whether a field write may be a lazy initialization. - * - * We only consider simple cases of lazy initialization where the single field write is guarded - * so it is executed only if the field still has its default value and where the written value - * is guaranteed to be the same even if the write is executed multiple times (may happen because - * of the initializing method being executed more than once on concurrent threads). - * - * Also, all field reads must be used only for a guard or their uses have to be guarded - * themselves. - */ - def isLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int], - pcs: PCs - )(implicit state: State): Boolean = { - //xx println("PS: "+propertyStore(declaredMethods(method), Purity.key)) - val write = code(writeIndex).asFieldWriteAccessStmt - //xx println("1") - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - // Only handle lazy initialization of ints and floats as they are guaranteed to be - // written atomically - ////return false; - state.isThreadSafeType = false + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int], + pcs: PCs + )(implicit state: State): Boolean = { + //xx println("PS: "+propertyStore(declaredMethods(method), Purity.key)) + val write = code(writeIndex).asFieldWriteAccessStmt + //xx println("1") + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + ////return false; + state.isThreadSafeType = false + } + //xx println("2") + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method + } + //xx println("3") + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + case None ⇒ return false; + } + //xx println("4") + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) //return false; + // possibly double checked locking + { + //TODO + if (isDoubleCheckedLocking(method, pcs)) { + state.isThreadSafeType = true; + return true; + } else + return false; + } + //xx println("5") + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + //xx println("6") + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + //xx println("7") + true } - //xx println("2") - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs => (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return false; // Reads outside the (single) lazy initialization method + + 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } } - //xx println("3") - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val (guardIndex, guardedIndex, readIndex) = - findGuard(writeIndex, defaultValue, code, cfg) match { - case Some((guard, guarded, read)) => (guard, guarded, read) - case None => return false; - } - //xx println("4") - // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) //return false; - // possibly double checked locking - { - //TODO - if (isDoubleCheckedLocking(method, pcs)) { - state.isThreadSafeType = true; - return true; + + def isDoubleCheckedLocking(method: Method, pcs: PCs)(implicit state: State): Boolean = { + val fieldAccessInformation = project.get(FieldAccessInformationKey) + var guards: List[(Int, Int)] = Nil + var assignments: List[Int] = Nil + var monitorEnter: Option[(Int, Option[ObjectType])] = None + var monitorExit: Option[(Int, Option[ObjectType])] = None + var result = true + val accessingPcs = fieldAccessInformation.allWriteAccesses + .filter(p ⇒ p._1 == state.field) + .head + ._2 + .filter(p ⇒ p._1 == method) + .head + ._2 + + if (state.field.fieldType.isObjectType && method.asMethod.isStatic) { + val tacCode = getTACAI(method, pcs) + tacCode match { + case Some(tac) ⇒ { + val hm = new mutable.HashMap[Int, Assignment[V]]() + //index for tac-code + var i: Int = -1 + if (tac != null && tac.instructions != null) + if (tac != null && tac.instructions != null) + tac.instructions.foreach(instr ⇒ { + i = i + 1 + if (instr.isIfStmt) { + var currentFieldsClassType: ObjectType = null + if (state.field.fieldType.isObjectType) + currentFieldsClassType = state.field.fieldType.asObjectType // method.classFile.thisType + var ifLeftType: ReferenceType = null + if (instr.asIf.left.isVar && instr.asIf.left.asVar.value.isReferenceValue) // && instr.asIf.left.asVar.value.asReferenceValue) + try { + ifLeftType = instr.asIf.left.asVar.value.asReferenceValue.asReferenceType + } catch { + case _: Throwable ⇒ + } + + if ( //is non null check + instr.asIf.condition == NE && + // has same type as field + currentFieldsClassType != null && + ifLeftType != null && + ifLeftType.equals(currentFieldsClassType) //guards field? + ) { + // => guards the value + guards = (i, instr.asIf.target) :: guards + } + } + if (instr.isAssignment) { + hm += (i -> instr.asAssignment) + if (accessingPcs.contains(instr.pc)) + assignments = instr.pc.toInt :: assignments + } + + if (instr.isPutStatic && accessingPcs.contains(instr.pc)) { + assignments = i :: assignments + } + + if (instr.isMonitorEnter) { + val defB = instr.asMonitorEnter.objRef.asVar.definedBy.head + var objTypeOfMonitorEnter: Option[ObjectType] = None + try { + objTypeOfMonitorEnter = + Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) + } catch { + case _: Throwable ⇒ + } + monitorEnter = Some((i, objTypeOfMonitorEnter)) + //hm.foreach(x ⇒ println(x)) + } + + if (instr.isMonitorExit) { + val defB = instr.asMonitorExit.objRef.asVar.definedBy.head + var objTypeOfMonitorExit: Option[ObjectType] = None + try { + objTypeOfMonitorExit = + Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) + } catch { + case _: Throwable ⇒ + } + monitorExit = Some((i, objTypeOfMonitorExit)) + } + }) + } + case _ ⇒ result = false + } + assignments.foreach(a ⇒ guards.foreach(g ⇒ result = result && a > g._1 && a < g._2)) + result = result && assignments.size >= 1 + result = result && monitorEnter != None && monitorExit != None + monitorEnter match { + case Some((n, Some(ot: ObjectType))) ⇒ + result = result && + ot == state.field.fieldType.asObjectType && + //outer guard(s) + guards.filter(x ⇒ n > x._1).size >= 1 && + //inner guard(s) + guards.filter(x ⇒ n < x._1).size >= 1 + case None ⇒ result = false + case _ ⇒ result = false + } + monitorExit match { + case Some((n, Some(ot: ObjectType))) if (ot == state.field.fieldType.asObjectType) ⇒ + case None ⇒ result = false + case _ ⇒ result = false + } + result } else - return false; - } - //xx println("5") - // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - return false; - //xx println("6") - // Field reads (except for the guard) may only be executed if the field's value is not the - // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - return false; - //xx println("7") - true - } - - 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 eps: InterimEP[Method, TACAI] => - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk => - state.tacDependees += method -> ((epk, pcs)) - None + false } - } - - def isDoubleCheckedLocking(method: Method, pcs: PCs)(implicit state: State): Boolean = { - val fieldAccessInformation = project.get(FieldAccessInformationKey) - var guards: List[(Int, Int)] = Nil - var assignments: List[Int] = Nil - var monitorEnter: Option[(Int, Option[ObjectType])] = None - var monitorExit: Option[(Int, Option[ObjectType])] = None - var result = true - val accessingPcs = fieldAccessInformation.allWriteAccesses - .filter(p => p._1 == state.field) - .head - ._2 - .filter(p => p._1 == method) - .head - ._2 - - if (state.field.fieldType.isObjectType && method.asMethod.isStatic) { - val tacCode = getTACAI(method, pcs) - tacCode match { - case Some(tac) => { - val hm = new mutable.HashMap[Int, Assignment[V]]() - //index for tac-code - var i: Int = -1 - if (tac != null && tac.instructions != null) - if (tac != null && tac.instructions != null) - tac.instructions.foreach(instr => { - i = i + 1 - if (instr.isIfStmt) { - var currentFieldsClassType: ObjectType = null - if (state.field.fieldType.isObjectType) - currentFieldsClassType = state.field.fieldType.asObjectType // method.classFile.thisType - var ifLeftType: ReferenceType = null - if (instr.asIf.left.isVar && instr.asIf.left.asVar.value.isReferenceValue) // && instr.asIf.left.asVar.value.asReferenceValue) - try { - ifLeftType = instr.asIf.left.asVar.value.asReferenceValue.asReferenceType - } catch { - case _: Throwable => - } - if (//is non null check - instr.asIf.condition == NE && - // has same type as field - currentFieldsClassType != null && - ifLeftType != null && - ifLeftType.equals(currentFieldsClassType) //guards field? - ) { - // => guards the value - guards = (i, instr.asIf.target) :: guards - } - } - if (instr.isAssignment) { - hm += (i -> instr.asAssignment) - if (accessingPcs.contains(instr.pc)) - assignments = instr.pc.toInt :: assignments - } + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + //xx println("51") + val definitions = write.value.asVar.definedBy + //xx println("52") + val isDeterministic = + if (definitions.size == 1) { + // The value written must be computed deterministically + //xx println("53") + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) + //xx println("54") + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + //xx println("55") + // The field write must be guarded correctly + val r1 = isDeterministic + val r2 = checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + //xx println("isDeterministic: "+r1) + //xx println("checkWriteIsGuarded: "+r2) + //r1 && r2 //TODO check + if (!(r1 && r2) && !state.isThreadSafeType) { + //state.isThreadSafeType = false + true + } else + r1 && r2 + } - if (instr.isPutStatic && accessingPcs.contains(instr.pc)) { - assignments = i :: assignments - } + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 - if (instr.isMonitorEnter) { - val defB = instr.asMonitorEnter.objRef.asVar.definedBy.head - var objTypeOfMonitorEnter: Option[ObjectType] = None - try { - objTypeOfMonitorEnter = - Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) - } catch { - case _: Throwable => - } - monitorEnter = Some((i, objTypeOfMonitorEnter)) - //hm.foreach(x ⇒ println(x)) - } + var load = -1 - if (instr.isMonitorExit) { - val defB = instr.asMonitorExit.objRef.asVar.definedBy.head - var objTypeOfMonitorExit: Option[ObjectType] = None - try { - objTypeOfMonitorExit = - Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) - } catch { - case _: Throwable => - } - monitorExit = Some((i, objTypeOfMonitorExit)) - } - }) + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID ⇒ + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID ⇒ + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ ⇒ return false; // neither a field read nor a return + } + index += 1 } - case _ => result = false - } - assignments.foreach(a => guards.foreach(g => result = result && a > g._1 && a < g._2)) - result = result && assignments.size >= 1 - result = result && monitorEnter != None && monitorExit != None - monitorEnter match { - case Some((n, Some(ot: ObjectType))) => - result = result && - ot == state.field.fieldType.asObjectType && - //outer guard(s) - guards.filter(x => n > x._1).size >= 1 && - //inner guard(s) - guards.filter(x => n < x._1).size >= 1 - case None => result = false - case _ => result = false - } - monitorExit match { - case Some((n, Some(ot: ObjectType))) if (ot == state.field.fieldType.asObjectType) => - case None => result = false - case _ => result = false - } - result - } else - false - } - - def checkWrites( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - //xx println("51") - val definitions = write.value.asVar.definedBy - //xx println("52") - val isDeterministic = - if (definitions.size == 1) { - // The value written must be computed deterministically - //xx println("53") - checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) - } else { - // More than one definition site for the value might lead to differences between - // invocations, but not if this method has no parameters and is deterministic - // (in this case, the definition reaching the write will always be the same) - //xx println("54") - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - //xx println("55") - // The field write must be guarded correctly - val r1 = isDeterministic - val r2 = checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - //xx println("isDeterministic: "+r1) - //xx println("checkWriteIsGuarded: "+r2) - //r1 && r2 //TODO check - if (!(r1 && r2) && !state.isThreadSafeType) { - //state.isThreadSafeType = false - true - } else - r1 && r2 - } - - /** - * Checks if the field write for a lazy initialization is immediately followed by a return of - * the written value (possibly loaded by another field read). - */ - def checkImmediateReturn( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 - - var load = -1 - - while (index < code.length) { - val stmt = code(index) - stmt.astID match { - case Assignment.ASTID => - if (isReadOfCurrentField(stmt.asAssignment.expr)) - load = index - else - return false; // No field read or a field read of a different field - case ReturnValue.ASTID => - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) - return true; // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) - return true; // return of field value loaded by field read - else - return false; // return of different value - case _ => return false; // neither a field read nor a return - } - index += 1 + false + } + + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( + propertyStore(declaredMethods(method), Purity.key) + )) + result } - false - } - - def lazyInitializerIsDeterministic( - method: Method, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( - propertyStore(declaredMethods(method), Purity.key) - )) - result - } - - /** - * Analyzes field writes for a single method, returning false if the field may still be - * effectively final and true otherwise. - */ - def checkMethod( - method: Method, - taCode: TACode[TACMethodParameter, V], - pcs: PCs - )(implicit state: State): Boolean = { - val field = state.field - val stmts = taCode.stmts - for (pc <- pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID => - if (method.isInitializer) { - - if (field.isStatic) { - if (method.isConstructor) - return true; + + /** + * Analyzes field writes for a single method, returning false if the field may still be + * effectively final and true otherwise. + */ + def checkMethod( + method: Method, + taCode: TACode[TACMethodParameter, V], + pcs: PCs + )(implicit state: State): Boolean = { + val field = state.field + val stmts = taCode.stmts + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + + if (field.isStatic) { + if (method.isConstructor) + return true; + } else { + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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.referenceImmutability == NotThreadSafeLazyInitialization || state.referenceImmutability == LazyInitialization) //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; + + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + // A field written outside an initializer must be lazily + // initialized or it is non-final + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex, + pcs + )) + return true; + state.referenceImmutability = LazyInitialization //LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + // 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. + return true; + } + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - if (receiverDefs != SelfReferenceParameter) - return true; + // nothing to do as the put field is dead } - } 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.referenceImmutability == NotThreadSafeLazyInitialization || state.referenceImmutability == LazyInitialization) //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; - - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - // A field written outside an initializer must be lazily - // initialized or it is non-final - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex, - pcs - )) - return true; - state.referenceImmutability = LazyInitialization //LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - // 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. - return true; - } - } - case _ => throw new RuntimeException("unexpected field access"); - } - } else { - // nothing to do as the put field is dead + } } - } + false } - false - } - - /** - * 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 eps: InterimEP[Method, TACAI] ⇒ - * state.tacDependees += method → ((eps, pcs)) - * eps.ub.tac - * case epk ⇒ - * state.tacDependees += method → ((epk, pcs)) - * None - * } - * } - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - /** - * 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 // Must be locally created - else { - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - handleEscapeProperty(escape) + + /** + * 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 eps: InterimEP[Method, TACAI] ⇒ + * state.tacDependees += method → ((eps, pcs)) + * eps.ub.tac + * case epk ⇒ + * state.tacDependees += method → ((epk, pcs)) + * None + * } + * } + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + /** + * 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 // Must be locally created + else { + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + handleEscapeProperty(escape) + } + } } - } - } - } - - /** - * 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 = 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 - } - - /** - * Checks if the field write is only executed if the field's value was still the default value. - * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization - * and the field write. - */ - def checkWriteIsGuarded( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val startBB = cfg.bb(writeIndex).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt => - stmt.astID == CaughtException.ASTID - } flatMap { exception => - exception.asCaughtException.origins.map { origin: Int => - if (isImmediateVMException(origin)) - pcOfImmediateVMException(origin) - else - pcOfMethodExternalException(origin) - }.iterator } - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail + /** + * 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 = ep match { + case FinalP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ + false + + case FinalP(AtMost(_)) ⇒ + true - val startPC = curBB.startPC - val endPC = curBB.endPC + case _: FinalEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return - if (startPC == 0 || startPC == guardedIndex) - return false; // Reached method start or wrong branch of guarding if-Statement + case InterimUBP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ + state.escapeDependees += ep + false - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + case InterimUBP(AtMost(_)) ⇒ + true - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; + case _: InterimEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors + case _ ⇒ + state.escapeDependees += ep + false } - true - } - - /** - * 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]] - )(implicit state: State): Boolean = { - //xx println("begin checkwriteisdeterministic") - def isConstant(uvar: Expr[V]): Boolean = { - val defSites = uvar.asVar.definedBy - - def isConstantDef(index: Int) = { - //xx println("index: "+index) - if (index < 0) false - else if (code(defSites.head).asAssignment.expr.isConst) true - else { - val expr = code(index).asAssignment.expr - //xx println("expr: "+expr) - //println("resolveField(p): " + expr.asFieldRead.resolveField(p)) - expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { - case Some(field) => - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ => // Unknown field - false - }) + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(writeIndex).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt ⇒ + stmt.astID == CaughtException.ASTID + } flatMap { exception ⇒ + exception.asCaughtException.origins.map { origin: Int ⇒ + if (isImmediateVMException(origin)) + pcOfImmediateVMException(origin) + else + pcOfMethodExternalException(origin) + }.iterator } - } - defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) - } + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail - val value = origin.expr + val startPC = curBB.startPC + val endPC = curBB.endPC - //xx println("value.astID: "+value.astID) + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement - val isNonConstDeterministic = value.astID match { - case GetStatic.ASTID | GetField.ASTID => - //xx println("a") - value.asFieldRead.resolveField(p) match { - case Some(field) => - //xx println("b") - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ => // Unknown field - //xx println("c") - false - } - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => - //xx println("d") - // 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.exists(!isConstant(_))) { - //xx println("e") - false - } else { - //xx println("f") - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) + if (!lazyInitializerIsDeterministic(method, code)) + return false; + + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors } - case _ => - //xx println("g") - // 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, code) - } - value.isConst || isNonConstDeterministic - } - - /** - * Checks that all non-dead field reads that are not used for the guarding if-Statement of a - * lazy initialization are only executed if the field did not have its default value or after - * the (single) field write. - */ - def checkReads( - reads: Seq[(Method, PCs)], - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - ): Boolean = { - // There is only a single method with reads aside from initializers (checked by - // isLazilyInitialized), so we have to check only reads from that one method. - reads.filter(!_._1.isInitializer).head._2 forall { readPC => - val index = pcToIndex(readPC) - index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) - } - } - - /** - * Checks that a field read is only executed if the field did not have its default value or - * after the (single) field write. - */ - def checkRead( - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Boolean = { - val startBB = cfg.bb(readIndex).asBasicBlock - val writeBB = cfg.bb(writeIndex) - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - - if (startPC == 0) - return false; // Reached the start of the method but not the guard or field write - - if ((curBB eq writeBB) && writeIndex > readIndex) - return false; // In the basic block of the write, but before the write - - if (startPC != guardedIndex && // Did not reach the guard - (curBB ne writeBB) /* Not the basic block of the write */ ) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } + true } - true - } - - /** - * Gets all predecessor BasicBlocks of a CFGNode. - */ - def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode => - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) + /** + * 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]] + )(implicit state: State): Boolean = { + //xx println("begin checkwriteisdeterministic") + def isConstant(uvar: Expr[V]): Boolean = { + val defSites = uvar.asVar.definedBy + + def isConstantDef(index: Int) = { + //xx println("index: "+index) + if (index < 0) false + else if (code(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + //xx println("expr: "+expr) + //println("resolveField(p): " + expr.asFieldRead.resolveField(p)) + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + false + }) + } + } + + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) + } + + val value = origin.expr + + //xx println("value.astID: "+value.astID) + + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID ⇒ + //xx println("a") + value.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + //xx println("b") + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + //xx println("c") + false + } + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + //xx println("d") + // 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.exists(!isConstant(_))) { + //xx println("e") + false + } else { + //xx println("f") + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true + } + case _ ⇒ + //xx println("g") + // 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, code) + } + + value.isConst || isNonConstDeterministic } - result.toList - } - - /** - * 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. - */ - def findGuard( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Option[(Int, Int, Int)] = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int)] = None - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID => - val ifStmt = cfStmt.asIf - ifStmt.condition match { - case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) => - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1)) - } - - case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) => - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None; - } else { - result = Some((endPC, ifStmt.targetStmt)) - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ => - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ => - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } + + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) + } } - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - var fieldReadUses: IntTrieSet = IntTrieSet.empty - if (definitions.head >= 0 && code(definitions.head).isAssignment) - fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use => - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + true } - None - } - - /** - * 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])(implicit state: State): Boolean = { - val field = expr.astID match { - case GetField.ASTID => - val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) None - else expr.asGetField.resolveField(project) - case GetStatic.ASTID => expr.asGetStatic.resolveField(project) - case _ => None + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode ⇒ + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) + } + result.toList } - field.contains(state.field) - } - - /** - * 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]] - )(implicit state: State): Boolean = { /** - * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + * 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. */ - def isDefaultConst(expr: Expr[V]): Boolean = { - //xx println("Expression: "+expr) - if (expr.isNullExpr) - true //-- - else 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 || - expr.isFloatConst && defaultValue == expr.asFloatConst.value - } + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID ⇒ + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + var fieldReadUses: IntTrieSet = IntTrieSet.empty + if (definitions.head >= 0 && code(definitions.head).isAssignment) + fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + } + + None } /** - * Checks whether the non-constant expression of the if-Statement is a read of the current - * field. + * 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 isGuardInternal(expr: V): Boolean = { - expr.definedBy forall { index => - if (index < 0) false // If the value is from a parameter, this can not be the guard - else isReadOfCurrentField(code(index).asAssignment.expr) - } + def isReadOfCurrentField(expr: Expr[V])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID ⇒ + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) + case _ ⇒ None + } + field.contains(state.field) } - if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { - isGuardInternal(ifStmt.rightExpr.asVar) - } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { - isGuardInternal(ifStmt.leftExpr.asVar) - } else //TODO check - //false - { - - //TODO if expression = Nullexpr - //xx println("----------------------------------------<<<<") - state.isThreadSafeType = false - true //TODO check - } - } - - /** - * Checks if 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) => true - case eps => - state.prematurelyReadDependee = Some(eps) - 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]] + )(implicit state: State): Boolean = { + + /** + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + */ + def isDefaultConst(expr: Expr[V]): Boolean = { + //xx println("Expression: "+expr) + if (expr.isNullExpr) + true //-- + else 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } + + /** + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. + */ + def isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index ⇒ + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } + } + + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else //TODO check + //false + { + + //TODO if expression = Nullexpr + //xx println("----------------------------------------<<<<") + state.isThreadSafeType = false + true //TODO check + } } - /** - * Checkes 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. - */ - 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 - } - - /** - * Checkes if 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, ReferenceImmutability] - )(implicit state: State): Boolean = eop match { - case FinalEP(e, ImmutableReference) => true - case FinalEP(e, MutableReference) => false - // - case LBP(ImmutableReference) => true - case UBP(MutableReference) => false + /** + * Checks if 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) ⇒ true + case eps ⇒ + state.prematurelyReadDependee = Some(eps) + false + } + + /** + * Checkes 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. + */ + 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 + } /** - * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ - * true - * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * + * Checkes if the field the value assigned to a (potentially) lazily initialized field is final, + * ensuring that the same value is written even for concurrent executions. */ - case _ => - state.referenceImmutabilityDependees += eop - true - } + def isImmutableReference( + eop: EOptionP[Field, ReferenceImmutability] + )(implicit state: State): Boolean = eop match { + case FinalEP(e, ImmutableReference) ⇒ true + case FinalEP(e, MutableReference) ⇒ false + // + case LBP(ImmutableReference) ⇒ true + case UBP(MutableReference) ⇒ false + + /** + * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ + * true + * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * + */ + case _ ⇒ + state.referenceImmutabilityDependees += eop + true + } } trait L0ReferenceImmutabilityLazyInitializationAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(ReferenceImmutabilityLazyInitialization), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.ub(EscapeProperty) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(ReferenceImmutabilityLazyInitialization), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.ub(EscapeProperty) + ) - final def derivedProperty: PropertyBounds = - PropertyBounds.lub(ReferenceImmutabilityLazyInitialization) + final def derivedProperty: PropertyBounds = + PropertyBounds.lub(ReferenceImmutabilityLazyInitialization) } @@ -1309,18 +1309,18 @@ object EagerL0ReferenceImmutabilityLazyInitializationAnalysis extends L0ReferenceImmutabilityLazyInitializationAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)( - analysis.determineReferenceImmutabilityLazyInitialization - ) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) + val fields = p.allFields + ps.scheduleEagerComputationsForEntities(fields)( + analysis.determineReferenceImmutabilityLazyInitialization + ) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -1330,18 +1330,18 @@ object LazyL0ReferenceImmutabilityLazyInitializationAnalysis extends L0ReferenceImmutabilityLazyInitializationAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) - ps.registerLazyPropertyComputation( - ReferenceImmutabilityLazyInitialization.key, - analysis.determineReferenceImmutabilityLazyInitialization - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) + ps.registerLazyPropertyComputation( + ReferenceImmutabilityLazyInitialization.key, + analysis.determineReferenceImmutabilityLazyInitialization + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index 6cf59d57a8..70e28f4e51 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -48,213 +48,211 @@ import org.opalj.br.fpcf.properties.TypeImmutability_new * @author Michael Eichberg * @author Tobias Peter Roth */ -class LxTypeImmutabilityAnalysis_new(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 = { - typeExtensibility(t) match { - case Yes | Unknown => - Result(t, MutableType_new) // MutableType) - case No => step2(t) +class LxTypeImmutabilityAnalysis_new( 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") } - } - - 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_new, ub: ClassImmutability_new) => - val thisLB = lb.correspondingTypeImmutability - val thisUB = ub.correspondingTypeImmutability - if (eps.isFinal) - Result(t, thisUB) - else - InterimResult(t, thisLB, thisUB, Seq(eps), c) - } - } - } - val resultToMatch = ps(t, ClassImmutability_new.key) - resultToMatch match { - case x @ FinalP(p) => { - Result(t, p.correspondingTypeImmutability); + /** + * @param t An object type which is not `java.lang.Object`. + */ + def step1( + typeExtensibility: ObjectType ⇒ Answer + )( + t: ObjectType + ): ProperPropertyComputationResult = { + typeExtensibility(t) match { + case Yes | Unknown ⇒ + Result(t, MutableType_new) // 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_new, ub: ClassImmutability_new) ⇒ + val thisLB = lb.correspondingTypeImmutability + val thisUB = ub.correspondingTypeImmutability + if (eps.isFinal) + Result(t, thisUB) + else + InterimResult(t, thisLB, thisUB, Seq(eps), c) + } + } + } + + val resultToMatch = ps(t, ClassImmutability_new.key) + resultToMatch match { + case x @ FinalP(p) ⇒ { + Result(t, p.correspondingTypeImmutability); + } + + case eps @ InterimLUBP(lb, ub) ⇒ + val thisUB = ub.correspondingTypeImmutability + val thisLB = lb.correspondingTypeImmutability + InterimResult(t, thisLB, thisUB, Seq(eps), c) + case epk ⇒ { + InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) + } + //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) + } + } else { + var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] + var joinedImmutability: TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." + var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType + + val resultToMatch2 = ps(t, ClassImmutability_new.key) + resultToMatch2 match { + case FinalP(DeepImmutableClass) ⇒ //ImmutableObject) => + case FinalP(MutableClass) ⇒ //(_: MutableObject) => + return Result(t, MutableType_new); //MutableType); + case FinalP(ShallowImmutableClass) ⇒ //ImmutableContainer) => + joinedImmutability = ShallowImmutableType // ImmutableContainerType + maxImmutability = ShallowImmutableType //ImmutableContainerType + case FinalP(DependentImmutableClass) ⇒ + joinedImmutability = DependentImmutableType + maxImmutability = DependentImmutableType + + case eps @ InterimLUBP(lb, ub) ⇒ + joinedImmutability = lb.correspondingTypeImmutability + maxImmutability = ub.correspondingTypeImmutability + dependencies += (t -> eps) + + case eOptP ⇒ + joinedImmutability = MutableType_new //MutableType + dependencies += (t -> eOptP) + } + + directSubtypes foreach { subtype ⇒ + ps(subtype, TypeImmutability_new.key) match { + case FinalP(DeepImmutableType) ⇒ //ImmutableType) => + case UBP(MutableType_new) ⇒ //MutableType) => + return Result(t, MutableType_new); //MutableType); + + case FinalP(ShallowImmutableType) ⇒ //ImmutableContainerType) => + joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) + maxImmutability = ShallowImmutableType //ImmutableContainerType + + case FinalP(DependentImmutableType) ⇒ + joinedImmutability = joinedImmutability.meet(DependentImmutableType) + maxImmutability = DependentImmutableType + + case eps @ InterimLUBP(subtypeLB, subtypeUB) ⇒ + joinedImmutability = joinedImmutability.meet(subtypeLB) + maxImmutability = maxImmutability.meet(subtypeUB) + dependencies += ((subtype, eps)) + + case epk ⇒ + joinedImmutability = MutableType_new //MutableType + dependencies += ((subtype, epk)) + } + } - case eps @ InterimLUBP(lb, ub) => - val thisUB = ub.correspondingTypeImmutability - val thisLB = lb.correspondingTypeImmutability - InterimResult(t, thisLB, thisUB, Seq(eps), c) - case epk => { - InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) - } - //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) - } - } else { - var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] - var joinedImmutability - : TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." - var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType - - val resultToMatch2 = ps(t, ClassImmutability_new.key) - resultToMatch2 match { - case FinalP(DeepImmutableClass) => //ImmutableObject) => - case FinalP(MutableClass) => //(_: MutableObject) => - return Result(t, MutableType_new); //MutableType); - case FinalP(ShallowImmutableClass) => //ImmutableContainer) => - joinedImmutability = ShallowImmutableType // ImmutableContainerType - maxImmutability = ShallowImmutableType //ImmutableContainerType - case FinalP(DependentImmutableClass) => - joinedImmutability = DependentImmutableType - maxImmutability = DependentImmutableType - - case eps @ InterimLUBP(lb, ub) => - joinedImmutability = lb.correspondingTypeImmutability - maxImmutability = ub.correspondingTypeImmutability - dependencies += (t -> eps) - - case eOptP => - joinedImmutability = MutableType_new //MutableType - dependencies += (t -> eOptP) - } - - directSubtypes foreach { subtype => - ps(subtype, TypeImmutability_new.key) match { - case FinalP(DeepImmutableType) => //ImmutableType) => - case UBP(MutableType_new) => //MutableType) => - return Result(t, MutableType_new); //MutableType); - - case FinalP(ShallowImmutableType) => //ImmutableContainerType) => - joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) - maxImmutability = ShallowImmutableType //ImmutableContainerType - - case FinalP(DependentImmutableType) => - joinedImmutability = joinedImmutability.meet(DependentImmutableType) - maxImmutability = DependentImmutableType - - case eps @ InterimLUBP(subtypeLB, subtypeUB) => - joinedImmutability = joinedImmutability.meet(subtypeLB) - maxImmutability = maxImmutability.meet(subtypeUB) - dependencies += ((subtype, eps)) - - case epk => - joinedImmutability = MutableType_new //MutableType - dependencies += ((subtype, epk)) - } - } - - if (dependencies.isEmpty) { - Result(t, maxImmutability) - } else if (joinedImmutability == maxImmutability) { - // E.g., as soon as one subtype is an ImmutableContainer, we are at most - // ImmutableContainer, even if all other subtype may even be 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) + Result(t, maxImmutability) + } else if (joinedImmutability == maxImmutability) { + // E.g., as soon as one subtype is an ImmutableContainer, we are at most + // ImmutableContainer, even if all other subtype may even be immutable! + Result(t, joinedImmutability) } 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_new => - joinedImmutability = joinedImmutability.meet(lb) - case lb: ClassImmutability_new => - joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) - } else { - joinedImmutability = MutableType_new //MutableType - continue = false + // 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_new ⇒ + joinedImmutability = joinedImmutability.meet(lb) + case lb: ClassImmutability_new ⇒ + joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) + } + else { + joinedImmutability = MutableType_new //MutableType + continue = false + } + } + if (joinedImmutability == maxImmutability) { + assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) + Result(t, maxImmutability) + } else { + InterimResult( + t, + joinedImmutability, + maxImmutability, + dependencies.values, + c + ) + } + } + } + + (eps: @unchecked) match { + case FinalEP(e, DeepImmutableType | DeepImmutableClass) ⇒ //ImmutableType | ImmutableObject) => + dependencies = dependencies - e + nextResult() + + case UBP(x) if (x == MutableType_new || x == MutableClass) ⇒ //MutableType | _: MutableObject) => + Result(t, MutableType_new) //MutableType) + + case FinalEP(e, x) if (x == ShallowImmutableType || x == ShallowImmutableClass) ⇒ //ImmutableContainerType | ImmutableContainer) => + maxImmutability = ShallowImmutableType //ImmutableContainerType + dependencies = dependencies - e + nextResult() + case FinalEP(e, DependentImmutableClass | DependentImmutableType) ⇒ { + //if (x == DependentImmutableClass() || x == DependentImmutableType) => { + maxImmutability = DependentImmutableType + dependencies = dependencies - e + nextResult() + } + case eps @ InterimEUBP(e, subtypeP) ⇒ + dependencies = dependencies.updated(e, eps) + subtypeP match { + case subtypeP: TypeImmutability_new ⇒ + maxImmutability = maxImmutability.meet(subtypeP) + case subtypeP: ClassImmutability_new ⇒ + maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) + } + nextResult() + } } - } - if (joinedImmutability == maxImmutability) { - assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) - Result(t, maxImmutability) - } else { - InterimResult( - t, - joinedImmutability, - maxImmutability, - dependencies.values, - c - ) - } - } - } - - (eps: @unchecked) match { - case FinalEP(e, DeepImmutableType | DeepImmutableClass) => //ImmutableType | ImmutableObject) => - dependencies = dependencies - e - nextResult() - - case UBP(x) - if (x == MutableType_new || x == MutableClass) => //MutableType | _: MutableObject) => - Result(t, MutableType_new) //MutableType) - - case FinalEP(e, x) - if (x == ShallowImmutableType || x == ShallowImmutableClass) => //ImmutableContainerType | ImmutableContainer) => - maxImmutability = ShallowImmutableType //ImmutableContainerType - dependencies = dependencies - e - nextResult() - case FinalEP(e, DependentImmutableClass | DependentImmutableType) => { - //if (x == DependentImmutableClass() || x == DependentImmutableType) => { - maxImmutability = DependentImmutableType - dependencies = dependencies - e - nextResult() + InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) } - case eps @ InterimEUBP(e, subtypeP) => - dependencies = dependencies.updated(e, eps) - subtypeP match { - case subtypeP: TypeImmutability_new => - maxImmutability = maxImmutability.meet(subtypeP) - case subtypeP: ClassImmutability_new => - maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) - } - nextResult() - } } - InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) - } } - } } trait TypeImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) - final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) + final override def uses: Set[PropertyBounds] = + PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) } @@ -267,40 +265,40 @@ object EagerLxTypeImmutabilityAnalysis_new extends TypeImmutabilityAnalysisScheduler_new with BasicFPCFEagerAnalysisScheduler { - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + 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 LxTypeImmutabilityAnalysis_new(project) - val allClassFilesIterator = project.allClassFiles.iterator - val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) + override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val typeExtensibility = project.get(TypeExtensibilityKey) + val analysis = new LxTypeImmutabilityAnalysis_new(project) + val allClassFilesIterator = project.allClassFiles.iterator + val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) - ps.scheduleEagerComputationsForEntities(types) { - analysis.step1(typeExtensibility) - } + ps.scheduleEagerComputationsForEntities(types) { + analysis.step1(typeExtensibility) + } - analysis - } + analysis + } } object LazyLxTypeImmutabilityAnalysis_new extends TypeImmutabilityAnalysisScheduler_new 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 LxTypeImmutabilityAnalysis_new(p) - val analysisRunner: ProperPropertyComputation[Entity] = - analysis.doDetermineTypeImmutability_new(typeExtensibility) - ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) - analysis - } + 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 LxTypeImmutabilityAnalysis_new(p) + val analysisRunner: ProperPropertyComputation[Entity] = + analysis.doDetermineTypeImmutability_new(typeExtensibility) + ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) + analysis + } } From 9ea92b826f4d5916ca40d17b765290cc6008e7fe Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Sat, 7 Mar 2020 16:43:16 +0100 Subject: [PATCH 103/327] tests improvements and formatting --- .../opalj/fpcf/ClassImmutabilityTests.scala | 109 +++++++--------- .../opalj/fpcf/FieldImmutabilityTests.scala | 123 ++++++++++-------- .../org/opalj/fpcf/FieldMutabilityTests.scala | 22 +++- ...eImmutabilityLazyInitializationTests.scala | 120 ++++++++--------- .../fpcf/ReferenceImmutabilityTests.scala | 120 ++++++++++------- .../opalj/fpcf/TypeImmutabilityTests.scala | 94 ++++++++----- .../ClassImmutabilityMatcher.scala | 13 +- ...mmutabilityLazyInitializationMatcher.scala | 62 ++++----- 8 files changed, 367 insertions(+), 296 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala index 3edc1ed830..6b6d14f1d8 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -7,14 +7,19 @@ 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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** @@ -22,67 +27,49 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class ClassImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ => - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures") } - p.get(RTACallGraphKey) - } - /** - * describe("no analysis is scheduled") { - * val as = executeAnalyses(Set.empty) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) - * }* - */ - describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { - println(1) - val as = executeAnalyses( - Set( - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new - ) - ) - as.propertyStore.shutdown() - validateProperties( - as, - classFilesWithAnnotations(as.project).map(tp => (tp._1.thisType, tp._2, tp._3)), - Set("ClassImmutability_new") - ) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ + 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("ClassImmutability_new")) + } + + describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { + println(1) + val as = executeAnalyses( + Set( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties( + as, + //classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1, tp._2, tp._3)), + classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), + Set("ClassImmutability_new") + ) + } } 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 index 944ba41571..0e37acfaf2 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -8,13 +8,18 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.Project import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** @@ -22,62 +27,72 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class FieldImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ => - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/immutability") } - p.get(RTACallGraphKey) - } - describe("no analysis is scheduled") { - val as = executeAnalyses(Set.empty) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) - } + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + } + p.get(RTACallGraphKey) + } - describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { - val as = executeAnalyses( - Set( - LazyTypeImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - EagerL0FieldImmutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new - ) - ) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } + + describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + val as = executeAnalyses( + Set( + LazyTypeImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala index b92877b5b9..e5414be3be 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala @@ -9,11 +9,15 @@ 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.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis 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.EagerL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new /** * Tests if the properties specified in the test project (the classes in the (sub-)package of @@ -45,7 +49,11 @@ class FieldMutabilityTests extends PropertiesTest { val as = executeAnalyses( Set( EagerL0FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new ) ) as.propertyStore.shutdown() @@ -57,7 +65,11 @@ class FieldMutabilityTests extends PropertiesTest { Set( EagerL1FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyInterProceduralEscapeAnalysis + LazyInterProceduralEscapeAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new ) ) as.propertyStore.shutdown() @@ -70,7 +82,11 @@ class FieldMutabilityTests extends PropertiesTest { EagerL2FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis + LazyInterProceduralEscapeAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala index 4d23a7d223..7fa6b66dbc 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala @@ -18,66 +18,70 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class ReferenceImmutabilityLazyInitializationTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ => - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures") } - p.get(RTACallGraphKey) - } - describe("no analysis is scheduled") { - val as = executeAnalyses(Set.empty) - as.propertyStore.shutdown() - validateProperties( - as, - fieldsWithAnnotations(as.project), - Set("ReferenceImmutabilityLazyInitialization") - ) - } + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + } + p.get(RTACallGraphKey) + } - describe("the org.opalj.fpcf.analyses.ReferenceImmutabilityLazyInitialization is executed") { - val as = executeAnalyses( - Set( - EagerL0ReferenceImmutabilityLazyInitializationAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis - ) - ) - as.propertyStore.shutdown() - validateProperties( - as, - fieldsWithAnnotations(as.project), - Set("ReferenceImmutabilityLazyInitialization") - ) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties( + as, + fieldsWithAnnotations(as.project), + Set("ReferenceImmutabilityLazyInitialization") + ) + } + + describe("the org.opalj.fpcf.analyses.ReferenceImmutabilityLazyInitialization is executed") { + val as = executeAnalyses( + Set( + EagerL0ReferenceImmutabilityLazyInitializationAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties( + as, + fieldsWithAnnotations(as.project), + Set("ReferenceImmutabilityLazyInitialization") + ) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index 5cb5df6328..b487e3c8bf 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -6,11 +6,19 @@ 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.LazyClassImmutabilityAnalysis +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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** @@ -18,58 +26,70 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class ReferenceImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ => - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures") } - p.get(RTACallGraphKey) - } - describe("no analysis is scheduled") { - val as = executeAnalyses(Set.empty) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) - } + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + } + p.get(RTACallGraphKey) + } - describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { - val as = executeAnalyses( - Set( - EagerL0ReferenceImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis - ) - ) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + } + + describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { + val as = executeAnalyses( + Set( + EagerL0ReferenceImmutabilityAnalysis, + EagerLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new, + LazyL2FieldMutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala index f6531f392a..d121d1241f 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala @@ -7,14 +7,19 @@ 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.LazyClassImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** @@ -24,42 +29,61 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class TypeImmutabilityTests extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ => - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/immutability") } - p.get(RTACallGraphKey) - } - describe("no analysis is scheduled") { - val as = executeAnalyses(Set.empty) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) - } + 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("TypeImmutability_new")) + * } + */ - describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { - println(1) - val as = executeAnalyses( - Set( - LazyTypeImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new - ) - ) - as.propertyStore.shutdown() - val tmp = classFilesWithAnnotations(as.project).map(tp => (tp._1.thisType, tp._2, tp._3)) - print("===========================>>>>>>>>>>>>>>>>>>>>>>" + tmp) - validateProperties( - as, - tmp, - // fieldsWithAnnotations(as.project), - Set("TypeImmutability_new") - ) //TODO class files ... with annotation - } + describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { + println(1) + val as = executeAnalyses( + Set( + /** + * LazyTypeImmutabilityAnalysis, + * LazyClassImmutabilityAnalysis, + * LazyL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyL0ReferenceImmutabilityAnalysis, + * LazyL0FieldImmutabilityAnalysis, + * EagerLxClassImmutabilityAnalysis_new, + * EagerLxTypeImmutabilityAnalysis_new* + */ + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis, + LazyL2FieldMutabilityAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties( + as, + // fieldsWithAnnotations(as.project), + classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), + Set("TypeImmutability_new") + ) //TODO class files ... with annotation + } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala index b128add15c..ac7646dfd2 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala @@ -45,10 +45,15 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) properties: Traversable[Property] ): Option[String] = { if (!properties.exists(p ⇒ { - p match { - /// case DependentImmutableClass(_) => property == DependentImmutableClass() - case _ ⇒ p == property - } + val tmpProperty = + p match { + case DeepImmutableClass ⇒ + DeepImmutableClass + case _ ⇒ + p + } + + property == tmpProperty })) { //p == property)) { // ... when we reach this point the expected property was not found. Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala index 78eb7a2a70..8ab9e6e7f3 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala @@ -16,40 +16,40 @@ import org.opalj.fpcf.properties.AbstractPropertyMatcher * @author Tobias Peter Roth */ class ReferenceImmutabilityLazyInitializationMatcher( - val property: ReferenceImmutabilityLazyInitialization + val property: ReferenceImmutabilityLazyInitialization ) 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 + 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 NoLazyInitializationMatcher From 339783af8d6476a7f54fed9f5cf34cfbe51dc406 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Sat, 7 Mar 2020 19:58:38 +0100 Subject: [PATCH 104/327] new immutability tests and optimization --- .../generic}/ClassWithGenericField_deep.java | 10 +++-- .../ClassWithGenericField_mutable.java | 7 +--- .../ClassWithGenericField_shallow.java | 3 +- .../DependentClassInheritingMutableOne.java | 13 ++++++ ...DependentClassWithGenericField_deep01.java | 2 +- .../DependentClassWithGenericField_deep1.java | 2 +- ...DependentClassWithGenericField_deep11.java | 2 +- .../DependentClassWithGenericField_deep2.java | 2 +- ...DependentClassWithGenericTypeArgument.java | 14 +++++++ .../classes/generic}/FinalEmptyClass.java | 4 +- .../GenericAndDeepImmutableFields.java | 3 +- .../generic}/GenericAndMutableFields.java | 2 +- .../GenericAndShallowImmutableFields.java | 5 +-- .../GenericClassWithDeepImmParams.java | 21 ++++++++++ .../GenericClassWithExtFinalMutTypes.java | 42 +++++++++++++++++++ .../classes/generic}/Generic_class1.java | 2 +- .../classes/generic}/TrivialMutableClass.java | 4 +- .../classes/inheriting/EmptyClass.java | 10 +++++ .../EmptyClassInheritingEmptyClass.java | 9 ++++ .../classes/interfaces/EmptyInterface.java | 9 ++++ .../TrivialInterfaceWithMethods.java | 15 +++++++ .../DeepGenericTest.java | 19 +++++++++ .../FinalEmptyClass.java | 2 +- .../GenericExt2Deep.java | 16 +++++++ .../GenericExt2Shallow.java | 17 ++++++++ .../Generic_class1.java | 2 +- .../Generic_class1_extString.java | 38 +++++++++++++++++ .../Generic_class2.java | 2 +- .../Generic_class3.java | 3 +- .../Generic_class4_deep.java | 4 +- .../Generic_class4_shallow.java | 2 +- .../multinested_genericClasses/One.java | 37 ++++++++++++++++ .../multinested_genericClasses/OneVirgin.java | 36 ++++++++++++++++ .../multinested_genericClasses/TestTest.java | 35 ++++++++++++++++ .../TrivialMutableClass.java | 2 +- .../multinested_genericClasses/Two.java | 22 ++++++++++ .../multinested_genericClasses/TwoVirgin.java | 18 ++++++++ .../trivials/DependentImmutableClass.java | 13 ++++++ .../classes/trivials/EmptyClass.java | 9 ++++ .../classes/trivials/FinalEmptyClass.java | 9 ++++ .../GenericTypeIsNotUsedAsFieldType.java | 13 ++++++ .../classes/trivials/MutableClass.java | 14 +++++++ ...entImmutableClassBecauseOfPublicField.java | 17 ++++++++ .../trivials/ShallowImmutableClass.java | 14 +++++++ .../field/ClassWithStaticFields.java | 25 +++++++++++ .../field}/FinalEmptyClass.java | 2 +- .../field}/TrivialMutableClass.java | 2 +- .../field}/privateFieldNotBlank_deep.java | 4 +- .../field}/privateFieldNotBlank_shallow.java | 4 +- ...FinalFieldBlank_costructorEscape_deep.java | 2 +- ...alFieldBlank_costructorEscape_shallow.java | 2 +- .../privateFinalFieldNotBlank_deep.java | 2 +- .../privateFinalFieldNotBlank_shallow.java | 2 +- .../field}/private_getterEscape_deep.java | 4 +- .../field}/private_getterEscape_shallow.java | 4 +- .../field}/private_setter_deep.java | 4 +- .../field}/private_setter_shallow.java | 4 +- .../privatefinal_getterEscape_deep.java | 2 +- .../privatefinal_getterEscape_shallow.java | 2 +- .../field}/protectedClass_deep.java | 2 +- .../field}/protectedClass_shallow.java | 2 +- .../reference}/DeclaredFinalFields.java | 2 +- .../reference}/LazyInitialization.java | 8 +--- .../reference}/PrivateFieldUpdater.java | 7 +--- .../reference}/Singleton.java | 7 +--- .../immutability/reference/StaticFields.java | 11 +++++ .../DoubleCheckedLockingClass1.java | 2 +- .../SimpleLazyInstantiation.java | 2 +- .../SimpleLazyIntInstantiation.java | 2 +- .../SimpleLazyObjectsInstantiation.java | 2 +- .../immutability/type/EmptyClass.java | 9 ++++ .../immutability/type/FinalEmptyClass.java | 9 ++++ .../WithMutableAndImmutableFieldType.java | 4 +- 73 files changed, 582 insertions(+), 77 deletions(-) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/ClassWithGenericField_deep.java (77%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/ClassWithGenericField_mutable.java (66%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/ClassWithGenericField_shallow.java (86%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassInheritingMutableOne.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/DependentClassWithGenericField_deep01.java (94%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/DependentClassWithGenericField_deep1.java (94%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/DependentClassWithGenericField_deep11.java (93%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/DependentClassWithGenericField_deep2.java (94%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericTypeArgument.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/FinalEmptyClass.java (51%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/GenericAndDeepImmutableFields.java (90%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/GenericAndMutableFields.java (94%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/GenericAndShallowImmutableFields.java (75%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithDeepImmParams.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithExtFinalMutTypes.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/Generic_class1.java (95%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes/generic}/TrivialMutableClass.java (77%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingEmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/EmptyInterface.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/TrivialInterfaceWithMethods.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/DeepGenericTest.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes}/multinested_genericClasses/FinalEmptyClass.java (76%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericExt2Deep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericExt2Shallow.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes}/multinested_genericClasses/Generic_class1.java (92%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1_extString.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes}/multinested_genericClasses/Generic_class2.java (94%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes}/multinested_genericClasses/Generic_class3.java (90%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes}/multinested_genericClasses/Generic_class4_deep.java (80%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes}/multinested_genericClasses/Generic_class4_shallow.java (91%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TestTest.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{class_immutability => immutability/classes}/multinested_genericClasses/TrivialMutableClass.java (88%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/EmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/FinalEmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/GenericTypeIsNotUsedAsFieldType.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/MutableClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/NoDependentImmutableClassBecauseOfPublicField.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ClassWithStaticFields.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/FinalEmptyClass.java (77%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/TrivialMutableClass.java (92%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/privateFieldNotBlank_deep.java (79%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/privateFieldNotBlank_shallow.java (79%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/privateFinalFieldBlank_costructorEscape_deep.java (92%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/privateFinalFieldBlank_costructorEscape_shallow.java (93%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/privateFinalFieldNotBlank_deep.java (91%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/privateFinalFieldNotBlank_shallow.java (91%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/private_getterEscape_deep.java (80%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/private_getterEscape_shallow.java (81%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/private_setter_deep.java (79%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/private_setter_shallow.java (80%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/privatefinal_getterEscape_deep.java (92%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/privatefinal_getterEscape_shallow.java (92%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/protectedClass_deep.java (90%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{field_immutability => immutability/field}/protectedClass_shallow.java (90%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{reference_immutability => immutability/reference}/DeclaredFinalFields.java (98%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{reference_immutability => immutability/reference}/LazyInitialization.java (95%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{reference_immutability => immutability/reference}/PrivateFieldUpdater.java (70%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{reference_immutability => immutability/reference}/Singleton.java (73%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/StaticFields.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{ => immutability}/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java (75%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{ => immutability}/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java (81%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{ => immutability}/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java (77%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{ => immutability}/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java (83%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/EmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/FinalEmptyClass.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/{type_immutability => immutability/type}/WithMutableAndImmutableFieldType.java (88%) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_deep.java similarity index 77% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_deep.java index d549417ad5..994945fa64 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_deep.java @@ -1,9 +1,11 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; @DeepImmutableTypeAnnotation("") @DeepImmutableClassAnnotation("") @@ -15,7 +17,8 @@ public final class ClassWithGenericField_deep { (new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2()); } - +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") final class Generic_class2 { private T1 t1; @@ -37,7 +40,8 @@ public Generic_class2(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ } } - +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") final class FinalEmptyClass2 { } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_mutable.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_mutable.java similarity index 66% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_mutable.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_mutable.java index d550757bce..77153e5a8c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_mutable.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_mutable.java @@ -1,14 +1,9 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -import org.opalj.fpcf.properties.type_mutability.MutableType; @MutableTypeAnnotation("") @MutableClassAnnotation("") diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java similarity index 86% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java index 2edd749fea..b4742d889e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/ClassWithGenericField_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java @@ -1,7 +1,6 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassInheritingMutableOne.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassInheritingMutableOne.java new file mode 100644 index 0000000000..32c2df516f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassInheritingMutableOne.java @@ -0,0 +1,13 @@ +package org.opalj.fpcf.fixtures.immutability.classes.generic; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +public class DependentClassInheritingMutableOne extends TrivialMutableClass { + private final T field; + public DependentClassInheritingMutableOne(T field) { + this.field = field; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep01.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep01.java similarity index 94% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep01.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep01.java index b018a847e9..0dde411160 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep01.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep01.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java similarity index 94% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep1.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java index b88188748b..d7b653cf05 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep11.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep11.java similarity index 93% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep11.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep11.java index b85faef3ce..a779d2d363 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep11.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep11.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep2.java similarity index 94% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep2.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep2.java index b12357c698..bc7e773c93 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/DependentClassWithGenericField_deep2.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep2.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericTypeArgument.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericTypeArgument.java new file mode 100644 index 0000000000..76c4726cd4 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericTypeArgument.java @@ -0,0 +1,14 @@ +package org.opalj.fpcf.fixtures.immutability.classes.generic; + + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +public class DependentClassWithGenericTypeArgument { + private final Generic_class1 gc1; + DependentClassWithGenericTypeArgument(Generic_class1 gc1) { + this.gc1 = gc1; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/FinalEmptyClass.java similarity index 51% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/FinalEmptyClass.java index 5152cb6190..eca41e36e6 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/FinalEmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/FinalEmptyClass.java @@ -1,7 +1,9 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; +@DeepImmutableTypeAnnotation("") @DeepImmutableClassAnnotation("It has no fields and is final") public final class FinalEmptyClass { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndDeepImmutableFields.java similarity index 90% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndDeepImmutableFields.java index acd93a1b2d..c36f2ca4b6 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndDeepImmutableFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndDeepImmutableFields.java @@ -1,11 +1,10 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; -import org.opalj.fpcf.properties.type_mutability.MutableType; @DependentImmutableTypeAnnotation("") @DependentImmutableClassAnnotation("") diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndMutableFields.java similarity index 94% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndMutableFields.java index d80fcf4ac2..cf7a2668bb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndMutableFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndMutableFields.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndShallowImmutableFields.java similarity index 75% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndShallowImmutableFields.java index 1ffd179a83..6db7823ee6 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/GenericAndShallowImmutableFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndShallowImmutableFields.java @@ -1,12 +1,9 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -import org.opalj.fpcf.properties.type_mutability.MutableType; @MutableTypeAnnotation("") @ShallowImmutableClassAnnotation("") diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithDeepImmParams.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithDeepImmParams.java new file mode 100644 index 0000000000..cb585c1454 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithDeepImmParams.java @@ -0,0 +1,21 @@ +package org.opalj.fpcf.fixtures.immutability.classes.generic; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public class GenericClassWithDeepImmParams { + @DeepImmutableFieldAnnotation("") + private A a; + @DeepImmutableFieldAnnotation("") + private B b; + @DeepImmutableFieldAnnotation("") + private C c; + GenericClassWithDeepImmParams(A a, B b, C c) { + this.a = a; + this.b = b; + this.c = c; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithExtFinalMutTypes.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithExtFinalMutTypes.java new file mode 100644 index 0000000000..9805511d7b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithExtFinalMutTypes.java @@ -0,0 +1,42 @@ +package org.opalj.fpcf.fixtures.immutability.classes.generic; + +//TODO + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +public class GenericClassWithExtFinalMutTypes { + + @ShallowImmutableFieldAnnotation("") +@ImmutableReferenceAnnotation("") + private A a; + + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private B b; + + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private C c; + GenericClassWithExtFinalMutTypes(A a, B b, C c){ + this.a = a; + this.b = b; + this.c = c; + } + +} + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +final class FinalMutableClass{ + public int n = 0; +} + + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Generic_class1.java similarity index 95% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Generic_class1.java index eef7c43847..af9776ab2e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/Generic_class1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Generic_class1.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/TrivialMutableClass.java similarity index 77% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/TrivialMutableClass.java index e79dc0e79f..4e73c769b2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/TrivialMutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/TrivialMutableClass.java @@ -1,9 +1,11 @@ -package org.opalj.fpcf.fixtures.class_immutability; +package org.opalj.fpcf.fixtures.immutability.classes.generic; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +@MutableTypeAnnotation("") @MutableClassAnnotation("It has Mutable Fields") public class TrivialMutableClass { @MutableReferenceAnnotation("public") 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..a8473f1ac2 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClass.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.immutability.classes.inheriting; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@DeepImmutableClassAnnotation("Class has no fields but is not final") +public class EmptyClass { + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingEmptyClass.java new file mode 100644 index 0000000000..07bd311689 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingEmptyClass.java @@ -0,0 +1,9 @@ +package org.opalj.fpcf.fixtures.immutability.classes.inheriting; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@DeepImmutableClassAnnotation("empty class inheriting empty class") +public class EmptyClassInheritingEmptyClass extends EmptyClass{ +} 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..4c4e45618a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/EmptyInterface.java @@ -0,0 +1,9 @@ +package org.opalj.fpcf.fixtures.immutability.classes.interfaces; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public interface EmptyInterface { +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/TrivialInterfaceWithMethods.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/TrivialInterfaceWithMethods.java new file mode 100644 index 0000000000..569facea67 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/TrivialInterfaceWithMethods.java @@ -0,0 +1,15 @@ +package org.opalj.fpcf.fixtures.immutability.classes.interfaces; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public interface TrivialInterfaceWithMethods { + +public String getName(); +public String getAge(); +public void setName(); +public void setAge(); + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/DeepGenericTest.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/DeepGenericTest.java new file mode 100644 index 0000000000..728f2735dd --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/DeepGenericTest.java @@ -0,0 +1,19 @@ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public final class DeepGenericTest { + @DeepImmutableFieldAnnotation("") +@ImmutableReferenceAnnotation("") +private Generic_class1 gc1; + + public DeepGenericTest(Generic_class1 gc1){ + this.gc1 = gc1; + } + +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/FinalEmptyClass.java similarity index 76% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/FinalEmptyClass.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/FinalEmptyClass.java index e347c4e5ea..420f8b2310 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/FinalEmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/FinalEmptyClass.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; 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..8caf9d29ba --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericExt2Deep.java @@ -0,0 +1,16 @@ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; + +//TODO @DeepImmutableClassAnnotation("") +public class GenericExt2Deep { + //TODO @DeepImmutableFieldAnnotation("") + private Generic_class1 gc; + GenericExt2Deep(Generic_class1 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..b7c87d70fb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericExt2Shallow.java @@ -0,0 +1,17 @@ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; + +//TODO @ShallowImmutableClassAnnotation("") +public class GenericExt2Shallow { + //TODO @ShallowImmutableFieldAnnotation("") + private Generic_class1 gc; + GenericExt2Shallow(Generic_class1 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/class_immutability/multinested_genericClasses/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java similarity index 92% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class1.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java index d539ee2c7a..74a22b4cd0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1_extString.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1_extString.java new file mode 100644 index 0000000000..3e8efad03c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1_extString.java @@ -0,0 +1,38 @@ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; + +@ShallowImmutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +public final class Generic_class1_extString { + + @ShallowImmutableFieldAnnotation("") + private T1 t1; + + @DeepImmutableFieldAnnotation(value = "T2") + private T2 t2; + + @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") + private T3 t3; + + @DependentImmutableFieldAnnotation(value = "T4", genericString = "T4") + private T4 t4; + + @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") + private T5 t5; + + public Generic_class1_extString(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + } + +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class2.java similarity index 94% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class2.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class2.java index d61328c0b9..47c9ef35cc 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class2.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class2.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class3.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class3.java similarity index 90% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class3.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class3.java index 5afd485a96..4bc19672e1 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class3.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class3.java @@ -1,6 +1,5 @@ -package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; -import org.opalj.fpcf.FinalE; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_deep.java similarity index 80% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_deep.java index 4795b818cc..bfaee579b9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_deep.java @@ -1,9 +1,7 @@ -package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; -import org.opalj.fpcf.FinalE; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_shallow.java similarity index 91% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_shallow.java index 92a17f3951..10a6dba431 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/Generic_class4_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_shallow.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java new file mode 100644 index 0000000000..1c04d515d3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java @@ -0,0 +1,37 @@ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +public class One { + @DependentImmutableFieldAnnotation(value="",genericString = "A") + @ImmutableReferenceAnnotation("") + private A a; + @DependentImmutableFieldAnnotation(value="",genericString = "B") + @ImmutableReferenceAnnotation("") + private B b; + @DependentImmutableFieldAnnotation(value="",genericString = "C") + @ImmutableReferenceAnnotation("") + private C c; + @DependentImmutableFieldAnnotation(value="",genericString = "D") + @ImmutableReferenceAnnotation("") + private D d; + @MutableFieldAnnotation("") + @MutableReferenceAnnotation("") + TrivialMutableClass tmc; + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Generic_class1 gc1; + public One(A a, B b, C c, D d, TrivialMutableClass tmc){ + gc1 = new Generic_class1(a,b,c,d, tmc); + } + + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java new file mode 100644 index 0000000000..74a5b8b7cb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java @@ -0,0 +1,36 @@ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +public class OneVirgin { + @DependentImmutableFieldAnnotation(value="",genericString = "A") + @ImmutableReferenceAnnotation("") + private A a; + @DependentImmutableFieldAnnotation(value="",genericString = "B") + @ImmutableReferenceAnnotation("") + private B b; + @DependentImmutableFieldAnnotation(value="",genericString = "C") + @ImmutableReferenceAnnotation("") + private C c; + @DependentImmutableFieldAnnotation(value="",genericString = "D") + @ImmutableReferenceAnnotation("") + private D d; + + @MutableFieldAnnotation("") + @MutableReferenceAnnotation("") + TrivialMutableClass tmc; + + Generic_class1 gc1; + public OneVirgin(A a, B b, C c, D d, E e){ + gc1 = new Generic_class1(a, b, c, d, e); + } + + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TestTest.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TestTest.java new file mode 100644 index 0000000000..c0a1ef3898 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TestTest.java @@ -0,0 +1,35 @@ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +public final class TestTest { + + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") + private Generic_class1 t1; + + @DeepImmutableFieldAnnotation(value = "T2") + private T2 t2; + + @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") + private T3 t3; + + @DependentImmutableFieldAnnotation(value = "T4", genericString = "T4") + private T4 t4; + + @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") + private T5 t5; + + public TestTest(Generic_class1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + } + +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TrivialMutableClass.java similarity index 88% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/TrivialMutableClass.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TrivialMutableClass.java index 2997ac5e50..9caf81eb11 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/class_immutability/multinested_genericClasses/TrivialMutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TrivialMutableClass.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.class_immutability.multinested_genericClasses; +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java new file mode 100644 index 0000000000..1697439f37 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java @@ -0,0 +1,22 @@ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +public class Two { + + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Generic_class1, B, B, B, TrivialMutableClass> gc1; + + public Two(A a, B b, TrivialMutableClass tmc, Generic_class1 gc1) { + gc1 = new Generic_class1, B, B, B, TrivialMutableClass>(gc1,b,b,b,tmc); + + } + + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java new file mode 100644 index 0000000000..4423ad6b5e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java @@ -0,0 +1,18 @@ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +public class TwoVirgin { + @DependentImmutableFieldAnnotation(value="",genericString = "") + @ImmutableReferenceAnnotation("") + private Generic_class1, B, C, D, E> gc1; + + public TwoVirgin(A a, B b, C c, Generic_class1 gc1) { + gc1 = new Generic_class1, B, B, B, C>(gc1,b,b,b,c); + } + + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java new file mode 100644 index 0000000000..3ae8b54a55 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java @@ -0,0 +1,13 @@ +package org.opalj.fpcf.fixtures.immutability.classes.trivials; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@DependentImmutableClassAnnotation("Das dependent immutable field") +public class DependentImmutableClass { + private T t; + public DependentImmutableClass(T t){ + this.t = t; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/EmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/EmptyClass.java new file mode 100644 index 0000000000..1c189869bb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/EmptyClass.java @@ -0,0 +1,9 @@ +package org.opalj.fpcf.fixtures.immutability.classes.trivials; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@DeepImmutableClassAnnotation("Class has no fields but is not final") +public class EmptyClass { +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/FinalEmptyClass.java new file mode 100644 index 0000000000..b1303a4ca3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/FinalEmptyClass.java @@ -0,0 +1,9 @@ +package org.opalj.fpcf.fixtures.immutability.classes.trivials; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("Class has no fields and is final") +public final class FinalEmptyClass { +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/GenericTypeIsNotUsedAsFieldType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/GenericTypeIsNotUsedAsFieldType.java new file mode 100644 index 0000000000..460696c6ea --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/GenericTypeIsNotUsedAsFieldType.java @@ -0,0 +1,13 @@ +package org.opalj.fpcf.fixtures.immutability.classes.trivials; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@DeepImmutableClassAnnotation("Generic Type is not used as field Type") +public class GenericTypeIsNotUsedAsFieldType { + private int n = 0; + GenericTypeIsNotUsedAsFieldType(T t){ + String s = t.toString(); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/MutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/MutableClass.java new file mode 100644 index 0000000000..5d446fa5b3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/MutableClass.java @@ -0,0 +1,14 @@ +package org.opalj.fpcf.fixtures.immutability.classes.trivials; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_mutability.MutableType; + +@MutableType("") +@MutableClassAnnotation("") +public class MutableClass { + @MutableFieldAnnotation("") + @MutableReferenceAnnotation("") + public int n = 0; +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/NoDependentImmutableClassBecauseOfPublicField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/NoDependentImmutableClassBecauseOfPublicField.java new file mode 100644 index 0000000000..d47a4819de --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/NoDependentImmutableClassBecauseOfPublicField.java @@ -0,0 +1,17 @@ +package org.opalj.fpcf.fixtures.immutability.classes.trivials; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@MutableClassAnnotation("Generic class but public field") +public class NoDependentImmutableClassBecauseOfPublicField { + @MutableFieldAnnotation("") + @MutableReferenceAnnotation("") + public T t; + NoDependentImmutableClassBecauseOfPublicField(T t){ + this.t = t; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java new file mode 100644 index 0000000000..490c8385f2 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java @@ -0,0 +1,14 @@ +package org.opalj.fpcf.fixtures.immutability.classes.trivials; + +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("has shallow immutable field") +public class ShallowImmutableClass { + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private MutableClass mutableClass = new MutableClass(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ClassWithStaticFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ClassWithStaticFields.java new file mode 100644 index 0000000000..d2676062d9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ClassWithStaticFields.java @@ -0,0 +1,25 @@ +package org.opalj.fpcf.fixtures.immutability.field; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +public class ClassWithStaticFields { + + @MutableFieldAnnotation("") + @MutableReferenceAnnotation("") + 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/field_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/FinalEmptyClass.java similarity index 77% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/FinalEmptyClass.java index d59e446c4a..32c78b00b0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/FinalEmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/FinalEmptyClass.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/TrivialMutableClass.java similarity index 92% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/TrivialMutableClass.java index a29aefed9d..7dbab374f0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/TrivialMutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/TrivialMutableClass.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java similarity index 79% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java index e18e45d65c..dcd30a4715 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java @@ -1,9 +1,11 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +@MutableTypeAnnotation("") @DeepImmutableClassAnnotation("Because it has only Deep Immutable Field Types") public class privateFieldNotBlank_deep { @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java similarity index 79% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java index cd937aafe1..a91602e936 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java @@ -1,9 +1,11 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +@MutableTypeAnnotation("") @ShallowImmutableClassAnnotation("Because it has only Shallow Immutable Fields") public class privateFieldNotBlank_shallow { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java similarity index 92% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java index 6a4dd9da24..ecc426d142 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java similarity index 93% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java index 58207793b2..7d1c430b2b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldBlank_costructorEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_deep.java similarity index 91% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_deep.java index 81ce97c380..b000b5e549 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_deep.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java similarity index 91% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java index c980ed2895..f68b8042d7 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privateFinalFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java similarity index 80% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java index b3bf7e004f..c4a6b44231 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java @@ -1,9 +1,11 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +@MutableTypeAnnotation("") @DeepImmutableClassAnnotation("Only Deep Immutable Fields") public class private_getterEscape_deep { public FinalEmptyClass getFec() { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java similarity index 81% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java index 801cc1b0cc..9cc7ac65fe 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java @@ -1,9 +1,11 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +@MutableTypeAnnotation("") @ShallowImmutableClassAnnotation("Becaus it has only Shallow Immutable Fields") public class private_getterEscape_shallow { public TrivialMutableClass getTmc() { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_deep.java similarity index 79% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_deep.java index 07cb969109..9eb25c264e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_deep.java @@ -1,9 +1,11 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +@MutableTypeAnnotation("") @MutableClassAnnotation("Because it has Mutable Fields") public class private_setter_deep { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_shallow.java similarity index 80% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_shallow.java index 7edcb57d35..1d9a7bc65a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/private_setter_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_shallow.java @@ -1,9 +1,11 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +@MutableTypeAnnotation("") @MutableClassAnnotation("Because it has Mutable Fields") public class private_setter_shallow { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java similarity index 92% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java index 1c79a391c0..4963d71a63 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java similarity index 92% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java index 922e39bf2f..02713bd81a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/privatefinal_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_deep.java similarity index 90% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_deep.java index 21e1621fc6..b9793fb839 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_deep.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_shallow.java similarity index 90% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_shallow.java index a9d738b700..2af008583c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/protectedClass_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_shallow.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.field_immutability; +package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/DeclaredFinalFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java similarity index 98% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/DeclaredFinalFields.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java index f32e29b2a5..0136d6a953 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/DeclaredFinalFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java @@ -26,7 +26,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -package org.opalj.fpcf.fixtures.reference_immutability; +package org.opalj.fpcf.fixtures.immutability.reference; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/LazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java similarity index 95% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/LazyInitialization.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java index 2547759f2e..31c86ccdce 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/LazyInitialization.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java @@ -26,18 +26,12 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -package org.opalj.fpcf.fixtures.reference_immutability; +package org.opalj.fpcf.fixtures.immutability.reference; -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.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; -import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; /** * Test classes for simple lazy initialization patterns and anti-patterns. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/PrivateFieldUpdater.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFieldUpdater.java similarity index 70% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/PrivateFieldUpdater.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFieldUpdater.java index 01b9c43677..3513eb4956 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/PrivateFieldUpdater.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFieldUpdater.java @@ -1,13 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.fixtures.reference_immutability; +package org.opalj.fpcf.fixtures.immutability.reference; -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.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; -import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; /** * Simple demo class which updates the private field of another instance of this class. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java similarity index 73% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java index 47c79df0d4..105a9d77c5 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability/Singleton.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java @@ -1,13 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.fixtures.reference_immutability; +package org.opalj.fpcf.fixtures.immutability.reference; -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.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; -import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; public class Singleton { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/StaticFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/StaticFields.java new file mode 100644 index 0000000000..3843500c02 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/StaticFields.java @@ -0,0 +1,11 @@ +package org.opalj.fpcf.fixtures.immutability.reference; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; + +@DeepImmutableClassAnnotation("") +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/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java similarity index 75% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java index dd566fb057..0276ce053d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.LazyInitializationAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java similarity index 81% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java index aabf03c0bb..bbc089942c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.reference_immutability_lazy_initialization; +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.NotThreadSafeLazyInitializationAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java similarity index 77% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java index b5c65fb42f..6be5b03d14 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.reference_immutability_lazy_initialization; +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.LazyInitializationAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java similarity index 83% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java index a53170923b..b0b9e87c3a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.reference_immutability_lazy_initialization; +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.NotThreadSafeLazyInitializationAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/EmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/EmptyClass.java new file mode 100644 index 0000000000..37cafb3690 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/EmptyClass.java @@ -0,0 +1,9 @@ +package org.opalj.fpcf.fixtures.immutability.type; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_mutability.MutableType; + +@MutableType("Class has no fields but is not final") +@DeepImmutableClassAnnotation("") +public class EmptyClass { +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/FinalEmptyClass.java new file mode 100644 index 0000000000..b60da020e4 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/FinalEmptyClass.java @@ -0,0 +1,9 @@ +package org.opalj.fpcf.fixtures.immutability.type; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + +@DeepImmutableClassAnnotation("") +@DeepImmutableTypeAnnotation("Class has no fields and is final") +public final class FinalEmptyClass { +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/WithMutableAndImmutableFieldType.java similarity index 88% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/WithMutableAndImmutableFieldType.java index 658f54d05d..b758ac9228 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/WithMutableAndImmutableFieldType.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/WithMutableAndImmutableFieldType.java @@ -1,10 +1,10 @@ -package org.opalj.fpcf.fixtures.type_immutability; +package org.opalj.fpcf.fixtures.immutability.type; +import org.opalj.fpcf.fixtures.immutability.field.TrivialMutableClass; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; @ShallowImmutableTypeAnnotation("has shallow mutable fields") From 73d30551bef4ea0fd63b506d3e899234053f8596 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Sat, 7 Mar 2020 20:00:12 +0100 Subject: [PATCH 105/327] improving test classes --- .../scala/org/opalj/fpcf/ClassImmutabilityTests.scala | 2 +- .../opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala | 2 +- .../opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala | 3 +-- .../scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala | 2 +- .../fpcf/ReferenceImmutabilityTests_withNewPurity.scala | 8 +++++++- .../opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala | 4 ++-- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala index 6b6d14f1d8..65ef2c643a 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -28,7 +28,7 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis class ClassImmutabilityTests extends PropertiesTest { override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures") + List("org/opalj/fpcf/fixtures/immutability") } override def init(p: Project[URL]): Unit = { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala index bf1f442902..2a18d034f6 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala @@ -25,7 +25,7 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new class ClassImmutabilityTests_withNewPurity extends PropertiesTest { override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures") + List("org/opalj/fpcf/fixtures/immutability") } override def init(p: Project[URL]): Unit = { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala index 1a91c935eb..70c0bf6b77 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala @@ -25,7 +25,7 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new class FieldImmutabilityTests_withNewPurity extends PropertiesTest { override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures") + List("org/opalj/fpcf/fixtures/immutability") } override def init(p: Project[URL]): Unit = { @@ -55,7 +55,6 @@ class FieldImmutabilityTests_withNewPurity extends PropertiesTest { LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis - ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index b487e3c8bf..4e62f27752 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -27,7 +27,7 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis class ReferenceImmutabilityTests extends PropertiesTest { override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures") + List("org/opalj/fpcf/fixtures/immutability") } override def init(p: Project[URL]): Unit = { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala index 2bcf6c549c..dface72f87 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala @@ -7,11 +7,14 @@ 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.LazyL0FieldMutabilityAnalysis 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.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new @@ -22,7 +25,7 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures") + List("org/opalj/fpcf/fixtures/immutability") } override def init(p: Project[URL]): Unit = { @@ -42,6 +45,9 @@ class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { val as = executeAnalyses( Set( EagerL0ReferenceImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new, + LazyL0FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, LazyInterProceduralEscapeAnalysis, diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala index 45f423a143..f762d626cb 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala @@ -27,7 +27,7 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new class TypeImmutabilityTests_withNewPurity extends PropertiesTest { override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures") + List("org/opalj/fpcf/fixtures/immutability") } override def init(p: Project[URL]): Unit = { @@ -36,6 +36,7 @@ class TypeImmutabilityTests_withNewPurity extends PropertiesTest { } p.get(RTACallGraphKey) } + /** * describe("no analysis is scheduled") { * val as = executeAnalyses(Set.empty) @@ -43,7 +44,6 @@ class TypeImmutabilityTests_withNewPurity extends PropertiesTest { * validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) * } */ - describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { println(1) val as = executeAnalyses( From c0febcf7989a7d79d0558f2b96fc76b106ebdfee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Mon, 9 Mar 2020 14:23:36 +0100 Subject: [PATCH 106/327] more efficient hashcode&equals --- .../main/scala/org/opalj/fpcf/par/PKECPropertyStore.scala | 7 +++++++ 1 file changed, 7 insertions(+) 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 de9caaf4e6..ca1cc2561a 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 @@ -746,6 +746,13 @@ case class EPKState( suppressedDependers: java.util.HashSet[SomeEPK] = new java.util.HashSet() ) { + override def hashCode(): Int = eOptP.hashCode() + + override def equals(obj: Any): Boolean = obj match { + case other: EPKState ⇒ eOptP == other.eOptP + case _ ⇒ false + } + def setFinal(finalEP: FinalEP[Entity, Property], unnotifiedPKs: Set[PropertyKind])(implicit ps: PKECPropertyStore): Unit = { var theEOptP: SomeEOptionP = null this.synchronized { From 230d75c16d7208eb011d0f3bbd71438905c69973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Mon, 9 Mar 2020 14:28:37 +0100 Subject: [PATCH 107/327] use better check order --- .../src/main/scala/org/opalj/fpcf/par/PKECPropertyStore.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 ca1cc2561a..b22c554dba 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 @@ -620,8 +620,7 @@ class PKECPropertyStore( if (propertyKindsComputedInThisPhase(pkId)) { ps(pkId).forEachValue(Long.MaxValue, { epkState: EPKState ⇒ val eOptP = epkState.eOptP - if (getResponsibleTId(eOptP.e) == ownTId && - epkState.eOptP.isRefinable) { + if (eOptP.isRefinable && getResponsibleTId(eOptP.e) == ownTId) { localInterimStates.append(epkState) } }) From 668d2f7ebb11c6c02cf56bf7394f63ef8e9ab0fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Mon, 9 Mar 2020 17:20:04 +0100 Subject: [PATCH 108/327] changed API to use Set for dependees --- .../fpcf/analyses/LBFieldValuesAnalysis.scala | 4 +- .../LBMethodReturnValuesAnalysis.scala | 4 +- .../analyses/ClassImmutabilityAnalysis.scala | 4 +- .../L0AllocationFreenessAnalysis.scala | 4 +- .../L0CompileTimeConstancyAnalysis.scala | 4 +- .../br/fpcf/analyses/L0PurityAnalysis.scala | 4 +- .../L0SelfReferenceLeakageAnalysis.scala | 4 +- .../analyses/StaticDataUsageAnalysis.scala | 4 +- .../analyses/TypeImmutabilityAnalysis.scala | 10 +-- ...VirtualCallAggregatingEscapeAnalysis.scala | 3 +- ...tualMethodAllocationFreenessAnalysis.scala | 3 +- .../VirtualMethodPurityAnalysis.scala | 3 +- ...VirtualMethodStaticDataUsageAnalysis.scala | 3 +- .../fpcf/PropertyComputationResult.scala | 18 ++--- .../fpcf/PropertyComputationResultsTest.scala | 24 +++--- .../org/opalj/fpcf/PropertyStoreTest.scala | 74 +++++++++---------- .../tac/fpcf/analyses/APIBasedAnalysis.scala | 6 +- .../fpcf/analyses/AbstractIFDSAnalysis.scala | 10 +-- .../fpcf/analyses/FieldLocalityState.scala | 6 +- .../tac/fpcf/analyses/L0TACAIAnalysis.scala | 4 +- .../analyses/L2FieldMutabilityAnalysis.scala | 45 +++++------ .../SystemPropertiesAnalysisScheduler.scala | 2 +- .../analyses/TACAIBasedAPIBasedAnalysis.scala | 8 +- .../analyses/TACAIBasedAnalysisState.scala | 6 +- .../analyses/cg/LoadedClassesAnalysis.scala | 6 +- .../analyses/cg/ReachableMethodAnalysis.scala | 2 +- .../cg/StaticInitializerAnalysis.scala | 2 +- .../cg/pointsto/PointsToBasedCGState.scala | 4 +- .../cg/rta/InstantiatedTypesAnalysis.scala | 2 +- .../tac/fpcf/analyses/cg/rta/RTAState.scala | 7 +- .../cg/xta/InstantiatedTypesAnalysis.scala | 2 +- ...ntiatedTypesBasedEntryPointsAnalysis.scala | 2 +- .../cg/xta/PropagationBasedCGState.scala | 15 ++-- .../cg/xta/TypePropagationState.scala | 8 +- .../escape/AbstractEscapeAnalysisState.scala | 9 ++- .../escape/ReturnValueFreshnessAnalysis.scala | 4 +- .../pointsto/AbstractPointsToAnalysis.scala | 2 +- .../pointsto/PointsToAnalysisBase.scala | 18 ++--- .../purity/AbstractPurityAnalysis.scala | 4 +- .../analyses/purity/L2PurityAnalysis.scala | 5 +- 40 files changed, 172 insertions(+), 177 deletions(-) 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/scala/org/opalj/br/fpcf/analyses/ClassImmutabilityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ClassImmutabilityAnalysis.scala index c116c173ac..b7955ad86e 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/ClassImmutabilityAnalysis.scala @@ -382,13 +382,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 { 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/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/L0PurityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0PurityAnalysis.scala index fb58860158..d64f5d6198 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 @@ -299,10 +299,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)) 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/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/TypeImmutabilityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/TypeImmutabilityAnalysis.scala index 499e0586aa..c4d56c78c3 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/TypeImmutabilityAnalysis.scala @@ -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,9 +90,9 @@ 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, ImmutableType, Set(epk), c) } } else { var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] @@ -185,7 +185,7 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal } else { InterimResult( t, joinedImmutability, maxImmutability, - dependencies.values, c + dependencies.valuesIterator.toSet, c ) } } @@ -217,7 +217,7 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal } } - InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) + InterimResult(t, joinedImmutability, maxImmutability, dependencies.valuesIterator.toSet, c) } } } 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/si/src/main/scala/org/opalj/fpcf/PropertyComputationResult.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyComputationResult.scala index f9dc5e8925..aae67555a1 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], //IMPROVE: require sets or EOptionPSets + val dependees: Set[SomeEOptionP], //IMPROVE: require sets or EOptionPSets val c: ProperOnUpdateContinuation ) extends ProperPropertyComputationResult { result ⇒ @@ -196,7 +196,7 @@ object InterimResult { 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 +206,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 +217,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 +237,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 +246,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 +388,7 @@ object PartialResult { private[fpcf] final val id = 6 } */ case class InterimPartialResult[SE >: Null <: Property]( us: Traversable[SomePartialResult], // can be empty! - dependees: Traversable[SomeEOptionP], //IMPROVE: require sets or EOptionPSets + dependees: Set[SomeEOptionP], //IMPROVE: require sets or EOptionPSets c: OnUpdateContinuation ) extends ProperPropertyComputationResult { @@ -409,7 +409,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 +428,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/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 170a75fbde..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 ) } } @@ -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( @@ -920,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 = @@ -937,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) } } @@ -946,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 = { @@ -955,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, @@ -987,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) { @@ -996,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) ⇒ @@ -1030,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)) @@ -1041,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) @@ -1547,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( @@ -1729,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") @@ -1774,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) @@ -1842,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" @@ -1913,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 { @@ -1955,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) ) } @@ -1966,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) ) @@ -1975,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) ) @@ -2032,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) ) } @@ -2043,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) ) @@ -2052,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) ) @@ -2103,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) ) } @@ -2114,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) ) @@ -2123,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/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/L2FieldMutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldMutabilityAnalysis.scala index 5970c63b5b..b35ad9bbf9 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/L2FieldMutabilityAnalysis.scala @@ -10,33 +10,10 @@ 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.Entity 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 @@ -45,9 +22,23 @@ 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.SomeEOptionP import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP 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.LazyInitializedField +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.properties.NotPrematurelyReadField +import org.opalj.br.fpcf.properties.PrematurelyReadField +import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler import org.opalj.br.ClassFile import org.opalj.br.ComputationalTypeFloat import org.opalj.br.ComputationalTypeInt @@ -113,9 +104,9 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext 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 ++ + fieldMutabilityDependees ++ escapeDependees).toSet } } 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/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/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..2b427f046c 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/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/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/purity/AbstractPurityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis.scala index 3831cb7691..b839c48377 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 @@ -601,9 +601,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/L2PurityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis.scala index 8d97a35f9a..2dd679728f 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 @@ -13,7 +13,6 @@ 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 @@ -134,7 +133,7 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst 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 +142,7 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst calleesDependee ++ rvfDependees.valuesIterator.map(_._1) ++ staticDataUsage ++ - tacai).toTraversable + tacai).toSet def addFieldLocalityDependee( f: Field, From 87316e6c7e9ecb2567c1d841eb20338dae3820a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Mon, 9 Mar 2020 17:20:16 +0100 Subject: [PATCH 109/327] performance improvements --- .../opalj/fpcf/par/PKECPropertyStore.scala | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) 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 b22c554dba..1d81ba1906 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,8 +4,9 @@ package fpcf package par import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.PriorityBlockingQueue import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.LinkedBlockingQueue +import java.util.PriorityQueue import scala.collection.mutable.ArrayBuffer import scala.util.control.ControlThrowable @@ -39,8 +40,8 @@ class PKECPropertyStore( private[this] val triggeredComputations: Array[Array[SomePropertyComputation]] = new Array(PropertyKind.SupportedPropertyKinds) - private[this] val queues: Array[PriorityBlockingQueue[QualifiedTask]] = - Array.fill(THREAD_COUNT) { new PriorityBlockingQueue() } + private[this] val queues: Array[LinkedBlockingQueue[QualifiedTask]] = + Array.fill(THREAD_COUNT) { new LinkedBlockingQueue[QualifiedTask]() } private[this] var setAndPreinitializedValues: List[SomeEPK] = List.empty @@ -349,7 +350,7 @@ class PKECPropertyStore( private[this] def handleInterimResult( interimEP: InterimEP[Entity, _ >: Null <: Property], c: ProperOnUpdateContinuation, - dependees: Traversable[SomeEOptionP] + dependees: Set[SomeEOptionP] ): Unit = { val SomeEPS(e, pk) = interimEP var isFresh = false @@ -418,7 +419,7 @@ class PKECPropertyStore( previous.eOptP.asInstanceOf[EOptionP[E, P]] } } else { - val newState = EPKState(epk, d ⇒ new Result(transformer._2(e, d.asFinal.p)), Some(dependee)) + 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, Some(dependee)) @@ -544,13 +545,14 @@ class PKECPropertyStore( class WorkerThread(ownTId: Int) extends PKECThread(s"PropertyStoreThread-#$ownTId") { - val tasks = queues(ownTId) + val tasksQueue = queues(ownTId) override def run(): Unit = { try { while (!doTerminate) { - val curTask = tasks.poll() - if (curTask eq null) { + val tasks = new PriorityQueue[QualifiedTask]() + tasksQueue.drainTo(tasks) + if (tasks.isEmpty) { val active = activeTasks.get() if (active == 0) { threads.foreach { t ⇒ @@ -559,14 +561,15 @@ class PKECPropertyStore( } return ; } else { - val nextTask = tasks.take() + val nextTask = tasksQueue.take() if (!doTerminate) { nextTask.apply() activeTasks.decrementAndGet() } } } else { - if (!doTerminate) { + var curTask: QualifiedTask = null + while ({ curTask = tasks.poll(); curTask != null } && !doTerminate) { curTask.apply() activeTasks.decrementAndGet() } @@ -740,7 +743,7 @@ class PKECPropertyStore( case class EPKState( var eOptP: SomeEOptionP, var c: OnUpdateContinuation, - var dependees: Traversable[SomeEOptionP], + var dependees: Set[SomeEOptionP], dependers: java.util.HashSet[SomeEPK] = new java.util.HashSet(), suppressedDependers: java.util.HashSet[SomeEPK] = new java.util.HashSet() ) { @@ -776,7 +779,7 @@ case class EPKState( def interimUpdate( interimEP: InterimEP[Entity, Property], newC: OnUpdateContinuation, - newDependees: Traversable[SomeEOptionP] + newDependees: Set[SomeEOptionP] )(implicit ps: PKECPropertyStore): Unit = { var theEOptP: SomeEOptionP = null @@ -873,13 +876,10 @@ case class EPKState( } def applyContinuation(oldDependee: SomeEOptionP, isSuppressed: Boolean)(implicit ps: PKECPropertyStore): Unit = { - val epk = oldDependee.toEPK this.synchronized { val theDependees = dependees // We are still interessted in that dependee? - if (theDependees != null && theDependees.exists { d ⇒ - (d eq oldDependee) || (isSuppressed && epk == d.toEPK) - }) { + if (theDependees != null && 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 From 5f523e881c5c6f94b2b363de307c83dff642f469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Mon, 9 Mar 2020 18:16:15 +0100 Subject: [PATCH 110/327] minor --- .../main/scala/org/opalj/fpcf/par/PKECPropertyStore.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 1d81ba1906..12948b4253 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 @@ -545,12 +545,11 @@ class PKECPropertyStore( class WorkerThread(ownTId: Int) extends PKECThread(s"PropertyStoreThread-#$ownTId") { - val tasksQueue = queues(ownTId) - override def run(): Unit = { try { + val tasksQueue = queues(ownTId) + val tasks = new PriorityQueue[QualifiedTask]() while (!doTerminate) { - val tasks = new PriorityQueue[QualifiedTask]() tasksQueue.drainTo(tasks) if (tasks.isEmpty) { val active = activeTasks.get() From 5756d332f40dbfd544057f6f8d7d8e2c81497fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Wed, 11 Mar 2020 13:39:34 +0100 Subject: [PATCH 111/327] used lazy vals for hashcodes and pks --- .../main/scala/org/opalj/fpcf/EOptionP.scala | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) 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 dd9d6c3e74..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) @@ -568,7 +568,7 @@ final class InterimELUBP[+E <: Entity, +P <: Property]( } } - 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 From d5513cb25704be0719647ee8427c039b48ee6606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Wed, 11 Mar 2020 13:42:25 +0100 Subject: [PATCH 112/327] workstealing and initial queues --- .../opalj/fpcf/par/PKECPropertyStore.scala | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) 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 12948b4253..c7529f6859 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 @@ -6,7 +6,7 @@ package par import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.LinkedBlockingQueue -import java.util.PriorityQueue +import java.util.ArrayDeque import scala.collection.mutable.ArrayBuffer import scala.util.control.ControlThrowable @@ -43,6 +43,9 @@ class PKECPropertyStore( private[this] val queues: Array[LinkedBlockingQueue[QualifiedTask]] = Array.fill(THREAD_COUNT) { new LinkedBlockingQueue[QualifiedTask]() } + private[this] val initialQueues: Array[ArrayDeque[QualifiedTask]] = + Array.fill(THREAD_COUNT) { new ArrayDeque[QualifiedTask](50000 / THREAD_COUNT) } + private[this] var setAndPreinitializedValues: List[SomeEPK] = List.empty override def shutdown(): Unit = {} @@ -222,9 +225,14 @@ class PKECPropertyStore( // -------------------------------------------------------------------------------------------- private[par] def scheduleTask(task: QualifiedTask): Unit = { - activeTasks.incrementAndGet() - scheduledTasks.incrementAndGet() - queues(getResponsibleTId(task)).offer(task) + 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]( @@ -547,8 +555,13 @@ class PKECPropertyStore( override def run(): Unit = { try { + val initialTasks = initialQueues(ownTId) + var curInitialTask: QualifiedTask = null + while ({ curInitialTask = initialTasks.poll(); curInitialTask != null }) { + curInitialTask.apply() + } val tasksQueue = queues(ownTId) - val tasks = new PriorityQueue[QualifiedTask]() + val tasks = new ArrayDeque[QualifiedTask](50000 / THREAD_COUNT) while (!doTerminate) { tasksQueue.drainTo(tasks) if (tasks.isEmpty) { @@ -560,10 +573,17 @@ class PKECPropertyStore( } return ; } else { - val nextTask = tasksQueue.take() - if (!doTerminate) { - nextTask.apply() - activeTasks.decrementAndGet() + // try workstealing: + val largestQueue = queues.maxBy(_.size()) + val largestQueueSize = largestQueue.size() + if (largestQueueSize > 100) { + largestQueue.drainTo(tasks, largestQueueSize / (THREAD_COUNT + 1)) + } else { + val nextTask = tasksQueue.take() + if (!doTerminate) { + nextTask.apply() + activeTasks.decrementAndGet() + } } } } else { @@ -712,18 +732,19 @@ class PKECPropertyStore( } } - class ContinuationTask(depender: SomeEPK, oldDependee: SomeEOptionP) extends QualifiedTask { + class ContinuationTask( + depender: SomeEPK, oldDependee: SomeEOptionP, dependeeState: EPKState + ) extends QualifiedTask { scheduledOnUpdateComputations.incrementAndGet() - val priority = { + val priority = 0 /*{ val dependerState = ps(depender.pk.id).get(depender.e) val dependerDependees = if (dependerState == null) null else dependerState.dependees val dependerDependeesSize = if (dependerDependees == null) 0 else dependerDependees.size - val dependeeState = ps(oldDependee.pk.id).get(oldDependee.e) val dependeeDependersSize = dependeeState.dependers.size() + dependeeState.suppressedDependers.size() taskManager.weight(depender, oldDependee, dependerDependeesSize, dependeeDependersSize) - } + }*/ override def apply(): Unit = { val epkState = ps(depender.pk.id).get(depender.e) From 05dac407bb928c32da009d2a3899efd1eda85654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Wed, 11 Mar 2020 13:43:38 +0100 Subject: [PATCH 113/327] avoid double read --- .../src/main/scala/org/opalj/fpcf/par/PKECPropertyStore.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c7529f6859..79a7d27d26 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 @@ -654,7 +654,7 @@ class PKECPropertyStore( successors(ownTId) = (interimEPKState: EPKState) ⇒ { val dependees = interimEPKState.dependees if (dependees != null) { - interimEPKState.dependees.map { eOptionP ⇒ + dependees.map { eOptionP ⇒ ps(eOptionP.pk.id).get(eOptionP.e) } } else { From 3cac30852c925dcd61685c70a6b19a2e001e82ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Wed, 11 Mar 2020 13:44:18 +0100 Subject: [PATCH 114/327] =?UTF-8?q?don=E2=80=99t=20use=20Integer.compareTo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/scala/org/opalj/fpcf/par/PKECPropertyStore.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 79a7d27d26..ab02076502 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 @@ -683,8 +683,7 @@ class PKECPropertyStore( trait QualifiedTask extends (() ⇒ Unit) with Comparable[QualifiedTask] { val priority: Int - override def compareTo(other: QualifiedTask): Int = - Integer.compare(this.priority, other.priority) + override def compareTo(other: QualifiedTask): Int = priority - other.priority } class ExecuteTask(f: ⇒ Unit) extends QualifiedTask { From 5ae7b92977391599390740a3ef62214c8be7730d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Wed, 11 Mar 2020 14:23:23 +0100 Subject: [PATCH 115/327] minor improvements --- .../org/opalj/fpcf/par/PKECPropertyStore.scala | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) 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 ab02076502..284c14bf6e 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 @@ -382,12 +382,13 @@ class PKECPropertyStore( ePKState.partialUpdate(update) } - def updateDependees(depender: EPKState, newDependees: Traversable[SomeEOptionP]): Unit = { + def updateDependees(depender: EPKState, newDependees: Set[SomeEOptionP]): Unit = { val dependerEpk = depender.eOptP.toEPK val suppressedPKs = suppressInterimUpdates(dependerEpk.pk.id) newDependees.forall { dependee ⇒ - val dependeeState = ps(dependee.pk.id).get(dependee.e) - dependeeState.addDependerOrScheduleContinuation(dependerEpk, dependee, suppressedPKs) + val dependeePK = dependee.pk.id + val dependeeState = ps(dependeePK).get(dependee.e) + dependeeState.addDependerOrScheduleContinuation(dependerEpk, dependee, dependeePK, suppressedPKs) } } @@ -430,7 +431,7 @@ class PKECPropertyStore( 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, Some(dependee)) + updateDependees(newState, Set(dependee)) epk } else { previous.eOptP.asInstanceOf[EOptionP[E, P]] @@ -850,6 +851,7 @@ case class EPKState( def addDependerOrScheduleContinuation( depender: SomeEPK, dependee: SomeEOptionP, + dependeePK: Int, suppressedPKs: Array[Boolean] )(implicit ps: PKECPropertyStore): Boolean = { dependers.synchronized { @@ -857,11 +859,11 @@ case class EPKState( // 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). if ((theEOptP ne dependee) && - (theEOptP.isFinal || !suppressedPKs(dependee.pk.id))) { - ps.scheduleTask(new ps.ContinuationTask(depender, dependee)) + (theEOptP.isFinal || !suppressedPKs(dependeePK))) { + ps.scheduleTask(new ps.ContinuationTask(depender, dependee, this)) false } else { - if (suppressedPKs(theEOptP.pk.id)) { + if (suppressedPKs(dependeePK)) { suppressedDependers.add(depender) } else { dependers.add(depender) @@ -886,7 +888,7 @@ case class EPKState( )(implicit ps: PKECPropertyStore): Unit = { theDependers.forEach { depender ⇒ if (!unnotifiedPKs.contains(depender.pk)) { - ps.scheduleTask(new ps.ContinuationTask(depender, oldEOptP)) + ps.scheduleTask(new ps.ContinuationTask(depender, oldEOptP, this)) } } From dab1fcdcda91f69c3515f202ba07f789897b534a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20K=C3=BCbler?= Date: Wed, 11 Mar 2020 17:06:53 +0100 Subject: [PATCH 116/327] wip --- .../opalj/fpcf/par/PKECPropertyStore.scala | 66 ++++++++----------- 1 file changed, 29 insertions(+), 37 deletions(-) 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 284c14bf6e..6c482394ef 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 @@ -310,22 +310,15 @@ class PKECPropertyStore( val e = new FakeEntity() val epk = EPK(e, AnalysisKey) - val epkState = EPKState( - epk, - { dependee: SomeEPS ⇒ - val result = c(dependee) + val epkState = EPKState(epk, null, dependees) + epkState.c = { dependee: SomeEPS ⇒ + val result = c(dependee) - val state = ps(AnalysisKeyId).remove(e) - state.dependees = null + val state = ps(AnalysisKeyId).remove(e) + state.dependees = null - dependees.foreach { dependee ⇒ - ps(dependee.pk.id).get(dependee.e).removeDepender(epk) - } - - result - }, - dependees - ) + result + } ps(AnalysisKeyId).put(e, epkState) @@ -388,7 +381,7 @@ class PKECPropertyStore( newDependees.forall { dependee ⇒ val dependeePK = dependee.pk.id val dependeeState = ps(dependeePK).get(dependee.e) - dependeeState.addDependerOrScheduleContinuation(dependerEpk, dependee, dependeePK, suppressedPKs) + dependeeState.addDependerOrScheduleContinuation(depender, dependee, dependeePK, suppressedPKs) } } @@ -537,14 +530,14 @@ class PKECPropertyStore( for (cSCC ← cSCCs) { for (interimEPKState ← cSCC) { - val dependees = interimEPKState.dependees - val epk = interimEPKState.eOptP.toEPK + interimEPKState.dependees = null + /*val dependees = interimEPKState.dependees dependees.foreach { dependee ⇒ // during execution, no other thread accesses the dependers of the EPKState val dependeeState = ps(dependee.pk.id).get(dependee.e) - dependeeState.dependers.remove(epk) - dependeeState.suppressedDependers.remove(epk) - } + dependeeState.dependers.remove(interimEPKState) + dependeeState.suppressedDependers.remove(interimEPKState) + }*/ scheduleTask(new SetTask(interimEPKState.eOptP.toFinalEP)) } } @@ -733,7 +726,7 @@ class PKECPropertyStore( } class ContinuationTask( - depender: SomeEPK, oldDependee: SomeEOptionP, dependeeState: EPKState + depender: EPKState, oldDependee: SomeEOptionP, dependeeState: EPKState ) extends QualifiedTask { scheduledOnUpdateComputations.incrementAndGet() @@ -747,10 +740,9 @@ class PKECPropertyStore( }*/ override def apply(): Unit = { - val epkState = ps(depender.pk.id).get(depender.e) - if (epkState ne null) { - val isSuppressed = suppressInterimUpdates(depender.pk.id)(oldDependee.pk.id) - epkState.applyContinuation(oldDependee, isSuppressed) + if (depender ne null) { + val isSuppressed = suppressInterimUpdates(depender.eOptP.pk.id)(oldDependee.pk.id) + depender.applyContinuation(oldDependee, isSuppressed) } } } @@ -764,11 +756,11 @@ case class EPKState( var eOptP: SomeEOptionP, var c: OnUpdateContinuation, var dependees: Set[SomeEOptionP], - dependers: java.util.HashSet[SomeEPK] = new java.util.HashSet(), - suppressedDependers: java.util.HashSet[SomeEPK] = new java.util.HashSet() + dependers: java.util.HashSet[EPKState] = new java.util.HashSet(), + suppressedDependers: java.util.HashSet[EPKState] = new java.util.HashSet() ) { - override def hashCode(): Int = eOptP.hashCode() + override lazy val hashCode: Int = eOptP.hashCode() override def equals(obj: Any): Boolean = obj match { case other: EPKState ⇒ eOptP == other.eOptP @@ -849,7 +841,7 @@ case class EPKState( } def addDependerOrScheduleContinuation( - depender: SomeEPK, + depender: EPKState, dependee: SomeEOptionP, dependeePK: Int, suppressedPKs: Array[Boolean] @@ -873,22 +865,22 @@ case class EPKState( } } - def removeDepender(epk: SomeEPK): Unit = { + def removeDepender(dependerState: EPKState): Unit = { dependers.synchronized { - dependers.remove(epk) - suppressedDependers.remove(epk) + dependers.remove(dependerState) + suppressedDependers.remove(dependerState) } } def notifyAndClearDependers( theEOptP: SomeEPS, oldEOptP: SomeEOptionP, - theDependers: java.util.HashSet[SomeEPK], - unnotifiedPKs: Set[PropertyKind] = Set.empty + theDependers: java.util.HashSet[EPKState], + unnotifiedPKs: Set[PropertyKind] = Set.empty )(implicit ps: PKECPropertyStore): Unit = { - theDependers.forEach { depender ⇒ - if (!unnotifiedPKs.contains(depender.pk)) { - ps.scheduleTask(new ps.ContinuationTask(depender, oldEOptP, this)) + theDependers.forEach { dependerState ⇒ + if (!unnotifiedPKs.contains(dependerState.eOptP.pk) && dependerState.dependees != null) { + ps.scheduleTask(new ps.ContinuationTask(dependerState, oldEOptP, this)) } } From 5b02ccff956f19898688d78482f07f0f76b9ff76 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Sat, 14 Mar 2020 10:25:01 +0100 Subject: [PATCH 117/327] Revise Demos for evaluation purpose --- .../ClassImmutabilityAnalysisDemo.scala | 110 ++++++++++------- ...AnalysisDemo_performanceMeasurements.scala | 69 ++++++----- ...mutabilityAnalysisDemo_withNewPurity.scala | 111 ++++++++++-------- .../FieldImmutabilityAnalysisDemo.scala | 78 +++++++----- ...AnalysisDemo_performanceMeasurements.scala | 83 ++++++++----- ...mutabilityAnalysisDemo_withNewPurity.scala | 84 +++++++------ .../ReferenceImmutabilityAnalysisDemo.scala | 81 +++++++------ ...AnalysisDemo_performanceMeasurements.scala | 67 ++++++----- ...mutabilityAnalysisDemo_withNewPurity.scala | 103 ++++++++-------- .../TypeImmutabilityAnalysisDemo.scala | 74 +++++++----- ...nalysisDemo_performanceMeasurements.scala} | 69 ++++++----- ...mutabilityAnalysisDemo_withNewPurity.scala | 92 ++++++++------- 12 files changed, 601 insertions(+), 420 deletions(-) rename DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/{TypeImmutabilityAnalysisDemo_performanceMeasurement.scala => TypeImmutabilityAnalysisDemo_performanceMeasurements.scala} (61%) 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 index 2b99226af8..b285c858e2 100644 --- 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 @@ -8,6 +8,8 @@ import java.net.URL import java.util.Calendar import org.opalj.br.ObjectType +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.util.PerformanceEvaluation.memory //import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new @@ -29,7 +31,6 @@ import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis @@ -58,35 +59,39 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { - - val analysesManager = project.get(FPCFAnalysesManagerKey) - - analysesManager.project.get(RTACallGraphKey) - + var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds + memory { + val analysesManager = project.get(FPCFAnalysesManagerKey) + + analysesManager.project.get(RTACallGraphKey) + + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + } { mu ⇒ + memoryConsumption = mu } val sb = new StringBuilder @@ -99,15 +104,15 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .map(x ⇒ x.toString+" |Mutable Class\n") ) sb.append("\nShallow Immutable Class:\n") - val shallowImmutableClasses = propertyStore.finalEntities(ShallowImmutableClass) - .toList + val shallowImmutableClasses = propertyStore.finalEntities(ShallowImmutableClass).toList sb.append( shallowImmutableClasses .map(x ⇒ x.toString+" |Shallow Immutable Class\n") ) sb.append("\nDependent Immutable Class: \n") val dependentImmutableClasses = propertyStore - .finalEntities(DependentImmutableClass).toList + .finalEntities(DependentImmutableClass) + .toList sb.append( dependentImmutableClasses .map(x ⇒ x.toString+" |Dependent Immutable Class\n") @@ -116,34 +121,49 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { sb.append("\nDeep Immutable Class Classes:\n") val allInterfaces = project.allClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet val deepImmutableClasses = propertyStore - .finalEntities(DeepImmutableClass).toList - .filter(x ⇒ !x.isInstanceOf[ObjectType] || !allInterfaces.contains(x.asInstanceOf[ObjectType])) + .finalEntities(DeepImmutableClass) + .toList + .filter( + x ⇒ !x.isInstanceOf[ObjectType] || !allInterfaces.contains(x.asInstanceOf[ObjectType]) + ) sb.append( - deepImmutableClasses - .toList + deepImmutableClasses.toList .map(x ⇒ x.toString+" |Deep Immutable Class\n") ) sb.append("\nDeep Immutable Class Classes: Interface\n") val deepImmutableClassesInterfaces = propertyStore - .finalEntities(DeepImmutableClass).toList + .finalEntities(DeepImmutableClass) + .toList .filter(x ⇒ x.isInstanceOf[ObjectType] && allInterfaces.contains(x.asInstanceOf[ObjectType])) sb.append( deepImmutableClassesInterfaces .map(x ⇒ x.toString+" |Deep Immutable Class Interface\n") ) - sb.append("\n\n") - sb.append("mutable Classes: "+mutableClasses.size+"\n") - sb.append("shallow immutable classes: "+shallowImmutableClasses.size+"\n") - sb.append("dependent immutable classes: "+dependentImmutableClasses.size+"\n") - sb.append("deep immutable classes: "+deepImmutableClasses.size+"\n") - sb.append("deep immutable classes interfaces: "+deepImmutableClassesInterfaces.size+"\n") + sb.append(s""" + | 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} + | + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes + |"""".stripMargin) - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/classImm"+dateString+".txt") + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results/classImm_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) val bw = new BufferedWriter(new FileWriter(file)) bw.write(sb.toString()) bw.close() - " took : "+analysisTime+" seconds" + s""" + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes + |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala index 8fff676954..497ae0f339 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -1,12 +1,17 @@ /* 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.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.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis @@ -16,9 +21,9 @@ import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds; @@ -45,7 +50,7 @@ object ClassImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnal def analyze(theProject: Project[URL]): String = { var times: List[Seconds] = Nil: List[Seconds] for (i ← 0 until 10) { - val project = Project.recreate(theProject) + val project = theProject.recreate() val analysesManager = project.get(FPCFAnalysesManagerKey) analysesManager.project.get(RTACallGraphKey) var propertyStore: PropertyStore = null @@ -54,15 +59,16 @@ object ClassImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnal propertyStore = analysesManager .runAll( LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, + LazyL2PurityAnalysis_new, LazyL0ReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, LazyLxTypeImmutabilityAnalysis_new, EagerLxClassImmutabilityAnalysis_new, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, LazyStaticDataUsageAnalysis, - LazySimpleEscapeAnalysis + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis ) ._1 propertyStore.waitOnPhaseCompletion(); @@ -72,26 +78,33 @@ object ClassImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnal times = analysisTime :: times } - /** - * "Mutable Class: "+propertyStore - * .finalEntities(MutableClass) - * .toList - * .toString()+"\n"+ - * "Dependent Immutable Class: "+propertyStore - * .entities(ClassImmutability_new.key) - * .toList - * .toString()+"\n"+ - * "Shallow Immutable Class: "+propertyStore - * .finalEntities(ShallowImmutableClass) - * .toList - * .toString()+"\n"+ - * "Deep Immutable Class: "+propertyStore - * .finalEntities(DeepImmutableClass) - * .toList - * .toString()+"\n" - */ - times.foreach(s ⇒ println(s+" seconds")) - val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size - f"took: $aver seconds on average" + val sortedList = times.sortWith(_.timeSpan < _.timeSpan) + val median = sortedList((times.size - 1) / 2) + + val output = + s""" + | + |${sortedList.mkString("\n")} + | + |Median: $median + |lowest: ${sortedList(0)} + |highest: ${sortedList(sortedList.size - 1)} + |""".stripMargin + + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results_time/classImm_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(output) + bw.close() + + s""" + | $output + | took: $median seconds as median + |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala index 159557d53b..62c80b757e 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala @@ -7,15 +7,19 @@ 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_new +import org.opalj.util.PerformanceEvaluation.memory + +//import org.opalj.br.ObjectType +import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new 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.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.DeepImmutableClass import org.opalj.br.fpcf.properties.DependentImmutableClass @@ -23,14 +27,11 @@ import org.opalj.br.fpcf.properties.MutableClass import org.opalj.br.fpcf.properties.ShallowImmutableClass import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -55,35 +56,36 @@ object ClassImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic } def analyze(project: Project[URL]): String = { - - val analysesManager = project.get(FPCFAnalysesManagerKey) - - analysesManager.project.get(RTACallGraphKey) - + var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds + memory { + val analysesManager = project.get(FPCFAnalysesManagerKey) + + analysesManager.project.get(RTACallGraphKey) + + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + } { mu ⇒ + memoryConsumption = mu } val sb = new StringBuilder @@ -96,49 +98,66 @@ object ClassImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic .map(x ⇒ x.toString+" |Mutable Class\n") ) sb.append("\nShallow Immutable Class:\n") - val shallowImmutableClasses = propertyStore.finalEntities(ShallowImmutableClass) - .toList + val shallowImmutableClasses = propertyStore.finalEntities(ShallowImmutableClass).toList sb.append( shallowImmutableClasses .map(x ⇒ x.toString+" |Shallow Immutable Class\n") ) sb.append("\nDependent Immutable Class: \n") val dependentImmutableClasses = propertyStore - .finalEntities(DependentImmutableClass).toList + .finalEntities(DependentImmutableClass) + .toList sb.append( dependentImmutableClasses .map(x ⇒ x.toString+" |Dependent Immutable Class\n") ) sb.append("\nDeep Immutable Class Classes:\n") + val allInterfaces = project.allClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet val deepImmutableClasses = propertyStore .finalEntities(DeepImmutableClass) + .toList + .filter( + x ⇒ !x.isInstanceOf[ObjectType] || !allInterfaces.contains(x.asInstanceOf[ObjectType]) + ) sb.append( - deepImmutableClasses - .toList + deepImmutableClasses.toList .map(x ⇒ x.toString+" |Deep Immutable Class\n") ) sb.append("\nDeep Immutable Class Classes: Interface\n") val deepImmutableClassesInterfaces = propertyStore .finalEntities(DeepImmutableClass) .toList + .filter(x ⇒ x.isInstanceOf[ObjectType] && allInterfaces.contains(x.asInstanceOf[ObjectType])) sb.append( deepImmutableClassesInterfaces .map(x ⇒ x.toString+" |Deep Immutable Class Interface\n") ) - sb.append("\n\n") - sb.append("mutable Classes: "+mutableClasses.size+"\n") - sb.append("shallow immutable classes: "+shallowImmutableClasses.size+"\n") - sb.append("dependent immutable classes: "+dependentImmutableClasses.size+"\n") - sb.append("deep immutable classes: "+deepImmutableClasses.size+"\n") - sb.append("deep immutable classes interfaces: "+deepImmutableClassesInterfaces.size+"\n") + sb.append(s""" + | 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} + | + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes + |"""".stripMargin) - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/classImm"+dateString+".txt") + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results/classImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) val bw = new BufferedWriter(new FileWriter(file)) bw.write(sb.toString()) bw.close() - " took : "+analysisTime+" seconds" + s""" + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes + |""".stripMargin } } 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 index 62586d6bcd..2773724891 100644 --- 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 @@ -25,12 +25,13 @@ import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new 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.util.PerformanceEvaluation.memory import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -56,35 +57,40 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - - analysesManager.project.get(RTACallGraphKey) - + var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - time { + memory { + val analysesManager = project.get(FPCFAnalysesManagerKey) + + analysesManager.project.get(RTACallGraphKey) - propertyStore = analysesManager - .runAll( - LazyLxClassImmutabilityAnalysis_new, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds + time { + + propertyStore = analysesManager + .runAll( + LazyLxClassImmutabilityAnalysis_new, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + } { mu ⇒ + memoryConsumption = mu } val sb: StringBuilder = new StringBuilder @@ -130,14 +136,26 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { | shallow immutable fields: ${shallowImmutableFields.size} | dependent immutable fields: ${dependentImmutableFields.size} | deep immutable fields: ${deepImmutableFields.size} + | + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes |""".stripMargin ) - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/fieldImm_"+dateString+".txt") + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results/fieldImm_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) + val bw = new BufferedWriter(new FileWriter(file)) bw.write(sb.toString()) bw.close() - " took : "+analysisTime+" seconds" + s""" + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes + |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala index 8cbbce302b..434bf9fd9e 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -1,20 +1,29 @@ /* 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.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.EagerL0FieldImmutabilityAnalysis +import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -40,22 +49,33 @@ object FieldImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnal } def analyze(theProject: Project[URL]): String = { + var tmpProject = theProject var times: List[Seconds] = Nil: List[Seconds] + println("before") for (i ← 0 until 10) { - val project = Project.recreate(theProject) + println( + "--------------------------------------------------------------------------------------------<<<<" + ) + val project = tmpProject.recreate() val analysesManager = project.get(FPCFAnalysesManagerKey) analysesManager.project.get(RTACallGraphKey) var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None + println(s"i: $i") time { propertyStore = analysesManager .runAll( LazyLxClassImmutabilityAnalysis_new, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, + LazyL2PurityAnalysis_new, LazyL0ReferenceImmutabilityAnalysis, EagerL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new + LazyLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis ) ._1 propertyStore.waitOnPhaseCompletion(); @@ -63,32 +83,37 @@ object FieldImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnal analysisTime = t.toSeconds } times = analysisTime :: times + println("after") + tmpProject = project } - /** - * "Mutable Fields: "+propertyStore - * .finalEntities(MutableField) - * .toList - * .toString()+"\n"+ - * "Shallow Immutable Fields: "+propertyStore - * .finalEntities(ShallowImmutableField) - * .toList - * .toString()+"\n"+ - * "Dependet Immutable Fields:"+propertyStore - * .finalEntities(DependentImmutableField(None)) - * .toList - * .toString()+"\n"+ - * "Deep Immutable Fields: "+propertyStore - * .finalEntities(DeepImmutableField) - * .toList - * .toString()+"\n"+ - * propertyStore - * .entities(FieldImmutability.key) - * .toList - * .toString* - */ - times.foreach(s ⇒ println(s+" seconds")) - val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size - f"took: $aver seconds on average" + val sortedList = times.sortWith(_.timeSpan < _.timeSpan) + val median = sortedList((times.size - 1) / 2) + + val output = + s""" + | + |${sortedList.mkString("\n")} + | + |Median: $median + |lowest: ${sortedList(0)} + |highest: ${sortedList(sortedList.size - 1)} + |""".stripMargin + + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results_time/fieldImm_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(output) + bw.close() + + s""" + | $output + | took: $median seconds as median + |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala index 08523bf559..fa98bea6f5 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala @@ -2,8 +2,8 @@ package org.opalj.fpcf.analyses import java.io.BufferedWriter -import java.io.File import java.io.FileWriter +import java.io.File import java.net.URL import java.util.Calendar @@ -12,10 +12,8 @@ import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis 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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.DeepImmutableField import org.opalj.br.fpcf.properties.DependentImmutableField @@ -25,12 +23,12 @@ import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +import org.opalj.util.PerformanceEvaluation.memory import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -56,34 +54,34 @@ object FieldImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic } def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - - analysesManager.project.get(RTACallGraphKey) - + var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyLxClassImmutabilityAnalysis_new, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds + memory { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + time { + propertyStore = analysesManager + .runAll( + LazyLxClassImmutabilityAnalysis_new, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + } { mu ⇒ + memoryConsumption = mu } val sb: StringBuilder = new StringBuilder @@ -126,17 +124,29 @@ object FieldImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic sb.append("\n\n") sb.append( s""" mutable fields: ${mutableFields.size} - | shallow immutable fields: ${shallowImmutableFields.size} - | dependent immutable fields: ${dependentImmutableFields.size} - | deep immutable fields: ${deepImmutableFields.size} - |""".stripMargin + | shallow immutable fields: ${shallowImmutableFields.size} + | dependent immutable fields: ${dependentImmutableFields.size} + | deep immutable fields: ${deepImmutableFields.size} + | + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes + |""".stripMargin + ) + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results/fieldImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" ) - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/fieldImm_"+dateString+".txt") + val bw = new BufferedWriter(new FileWriter(file)) bw.write(sb.toString()) bw.close() - " took : "+analysisTime+" seconds" + s""" + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes + |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index a896610888..787e6d462e 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -18,6 +18,7 @@ import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds +import org.opalj.util.PerformanceEvaluation.memory import java.io._ import java.util.Calendar @@ -26,7 +27,7 @@ import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis /** @@ -51,30 +52,36 @@ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyFieldLocalityAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds + memory { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyFieldLocalityAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion() + + } { t ⇒ + analysisTime = t.toSeconds + } + } { mu ⇒ + memoryConsumption = mu } var sb: StringBuilder = new StringBuilder() sb = sb.append("Mutable References: \n") @@ -93,31 +100,37 @@ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .toString() ) - /** - * .toList - * .toString() + "\n" +* - */ sb = sb.append("\nImmutable References: \n") val immutableReferences = propertyStore.finalEntities(ImmutableReference).toList sb = sb.append( immutableReferences.map(x ⇒ x.toString+"\n").toString() ) - sb.append("\n\n") - sb.append(" took : "+analysisTime+" seconds\n") sb.append( - s""" mutable References: ${mutableReferences.size} + s""" + | mutable References: ${mutableReferences.size} | lazy initialized References: ${lazyInitializedReferences.size} | immutable References: ${immutableReferences.size} + | + | took : $analysisTime seconds + | needed: ${memoryConsumption / 1024 / 1024} MBytes + | |""".stripMargin - ) - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/refImm"+dateString+".txt") + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results/refImm_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) val bw = new BufferedWriter(new FileWriter(file)) bw.write(sb.toString()) bw.close() + s""" + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes + |""".stripMargin - " took : "+analysisTime+" seconds" } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala index b80f3e7a46..cda67e440f 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -1,25 +1,28 @@ /* 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.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.LazyClassImmutabilityAnalysis 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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new 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.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -58,15 +61,14 @@ object ReferenceImmutabilityAnalysisDemo_performanceMeasurements .runAll( EagerL0ReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, + LazyL2PurityAnalysis_new, LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, LazyFieldLocalityAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis + LazyL0CompileTimeConstancyAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new ) ._1 propertyStore.waitOnPhaseCompletion(); @@ -75,25 +77,34 @@ object ReferenceImmutabilityAnalysisDemo_performanceMeasurements } times = analysisTime :: times } + val sortedList = times.sortWith(_.timeSpan < _.timeSpan) + val median = sortedList((times.size - 1) / 2) + + val output = + s""" + | + |${sortedList.mkString("\n")} + | + |Median: $median + |lowest: ${sortedList(0)} + |highest: ${sortedList(sortedList.size - 1)} + |""".stripMargin + + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results_time/refImm_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(output) + bw.close() - /** - * "Mutable References: "+ - * propertyStore - * .finalEntities(MutableReference) - * .toList - * .toString()+"\n"+ - * "Lazy Initialized Reference: "+propertyStore - * .finalEntities(LazyInitializedReference) - * .toList - * .toString()+"\n"+ - * "Immutable References: "+propertyStore - * .finalEntities(ImmutableReference) - * .toList - * .toString()+"\n"+* - */ - times.foreach(s ⇒ println(s+" seconds")) - val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size - f"took: $aver seconds on average" + s""" + |$output + |took: $median seconds as median + |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala index 28e3094898..f01201dd2c 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala @@ -1,18 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.analyses -import java.io._ import java.net.URL -import java.util.Calendar 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.LazyClassImmutabilityAnalysis -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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference @@ -20,13 +14,19 @@ import org.opalj.br.fpcf.properties.MutableReference import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -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.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds +import org.opalj.util.PerformanceEvaluation.memory +import java.io._ +import java.util.Calendar +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.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. @@ -50,30 +50,35 @@ object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisAp } def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyFieldLocalityAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds + memory { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyFieldLocalityAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new + ) + ._1 + propertyStore.waitOnPhaseCompletion() + + } { t ⇒ + analysisTime = t.toSeconds + } + } { mu ⇒ + memoryConsumption = mu } var sb: StringBuilder = new StringBuilder() sb = sb.append("Mutable References: \n") @@ -92,31 +97,37 @@ object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisAp .toString() ) - /** - * .toList - * .toString() + "\n" +* - */ sb = sb.append("\nImmutable References: \n") val immutableReferences = propertyStore.finalEntities(ImmutableReference).toList sb = sb.append( immutableReferences.map(x ⇒ x.toString+"\n").toString() ) - sb.append("\n\n") - sb.append(" took : "+analysisTime+" seconds\n") sb.append( - s""" mutable References: ${mutableReferences.size} - | lazy initialized References: ${lazyInitializedReferences.size} - | immutable References: ${immutableReferences.size} - |""".stripMargin - + s""" + | mutable References: ${mutableReferences.size} + | lazy initialized References: ${lazyInitializedReferences.size} + | immutable References: ${immutableReferences.size} + | + | took : $analysisTime seconds + | needed: ${memoryConsumption / 1024 / 1024} MBytes + | + |""".stripMargin ) - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/refImm"+dateString+".txt") + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results/refImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) val bw = new BufferedWriter(new FileWriter(file)) bw.write(sb.toString()) bw.close() + s""" + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes + |""".stripMargin - " took : "+analysisTime+" seconds" } } 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 index e71b5d6e48..cdb9f35d33 100644 --- 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 @@ -30,9 +30,10 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.util.PerformanceEvaluation.memory /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -55,32 +56,38 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - EagerLxTypeImmutabilityAnalysis_new, - LazyLxClassImmutabilityAnalysis_new, - LazyFieldLocalityAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds + memory { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new, + LazyFieldLocalityAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + } { mu ⇒ + memoryConsumption = mu } val sb: StringBuilder = new StringBuilder sb.append("\nMutableTypes: \n") @@ -112,15 +119,26 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { | shallow immutable types: ${shallowImmutableTypes.size} | dependent immutable types: ${dependentImmutableTypes.size} | deep immutable types: ${deepImmutableTypes.size} + | + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes |""".stripMargin ) - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/typeImm"+dateString+".txt") + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results/typeImm_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) val bw = new BufferedWriter(new FileWriter(file)) bw.write(sb.toString()) bw.close() - " took : "+analysisTime+" seconds" + s""" + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes + |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala similarity index 61% rename from DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala rename to DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala index b3835b40c7..48984c7541 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurement.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -1,12 +1,17 @@ /* 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.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.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis @@ -16,9 +21,9 @@ import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -27,7 +32,7 @@ import org.opalj.util.Seconds * * @author Tobias Peter Roth */ -object TypeImmutabilityAnalysisDemo_performanceMeasurement extends ProjectAnalysisApplication { +object TypeImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" @@ -46,7 +51,7 @@ object TypeImmutabilityAnalysisDemo_performanceMeasurement extends ProjectAnalys var times: List[Seconds] = Nil: List[Seconds] for (i ← 0 until 10) { - val project = Project.recreate(theProject) + val project = theProject.recreate() val analysesManager = project.get(FPCFAnalysesManagerKey) analysesManager.project.get(RTACallGraphKey) var propertyStore: PropertyStore = null @@ -55,15 +60,16 @@ object TypeImmutabilityAnalysisDemo_performanceMeasurement extends ProjectAnalys propertyStore = analysesManager .runAll( LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, + LazyL2PurityAnalysis_new, LazyL0ReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, EagerLxTypeImmutabilityAnalysis_new, LazyLxClassImmutabilityAnalysis_new, LazyFieldLocalityAnalysis, - LazySimpleEscapeAnalysis, LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis ) ._1 propertyStore.waitOnPhaseCompletion(); @@ -73,26 +79,33 @@ object TypeImmutabilityAnalysisDemo_performanceMeasurement extends ProjectAnalys times = analysisTime :: times } - /** - * "Mutable Type: "+propertyStore - * .finalEntities(MutableType_new) - * .toList - * .toString()+"\n"+ - * "Shallow Immutable Type: "+propertyStore - * .finalEntities(ShallowImmutableType) - * .toList - * .toString()+"\n"+ - * "Dependent Immutable Type: "+propertyStore - * .finalEntities(DependentImmutableType) - * .toList - * .toString()+"\n"+ - * "Deep Immutable Type: "+propertyStore - * .finalEntities(DeepImmutableType) - * .toList - * .toString()+"\n"* - */ - times.foreach(s ⇒ println(s+" seconds")) - val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size - f"took: $aver seconds on average" + val sortedList = times.sortWith(_.timeSpan < _.timeSpan) + val median = sortedList((times.size - 1) / 2) + + val output = + s""" + | + |${sortedList.mkString("\n")} + | + |Median: $median + |lowest: ${sortedList(0)} + |highest: ${sortedList(sortedList.size - 1)} + |""".stripMargin + + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results_time/typeImm_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(output) + bw.close() + + s""" + | $output + | took: $median seconds as median + |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala index d8ec91a4f6..8a1c9e6c46 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala @@ -11,11 +11,9 @@ 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.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.DeepImmutableType import org.opalj.br.fpcf.properties.DependentImmutableType @@ -25,15 +23,14 @@ import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL1FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazySimpleEscapeAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds +import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.util.PerformanceEvaluation.memory /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -56,33 +53,35 @@ object TypeImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplica } def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - EagerLxTypeImmutabilityAnalysis_new, - LazyLxClassImmutabilityAnalysis_new, - LazyFieldLocalityAnalysis, - LazySimpleEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds + memory { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + EagerLxTypeImmutabilityAnalysis_new, + LazyLxClassImmutabilityAnalysis_new, + LazyFieldLocalityAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds + } + } { mu ⇒ + memoryConsumption = mu } val sb: StringBuilder = new StringBuilder sb.append("\nMutableTypes: \n") @@ -110,19 +109,30 @@ object TypeImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplica sb.append("\n\n") sb.append( s""" - | mutable types: ${mutableTypes.size} - | shallow immutable types: ${shallowImmutableTypes.size} - | dependent immutable types: ${dependentImmutableTypes.size} - | deep immutable types: ${deepImmutableTypes.size} - |""".stripMargin + | mutable types: ${mutableTypes.size} + | shallow immutable types: ${shallowImmutableTypes.size} + | dependent immutable types: ${dependentImmutableTypes.size} + | deep immutable types: ${deepImmutableTypes.size} + | + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes + |""".stripMargin ) - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/typeImm"+dateString+".txt") + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results/typeImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) val bw = new BufferedWriter(new FileWriter(file)) bw.write(sb.toString()) bw.close() - " took : "+analysisTime+" seconds" + s""" + | took : $analysisTime seconds + | needs : ${memoryConsumption / 1024 / 1024} MBytes + |""".stripMargin } } From 6386f8f375f5c0f280b2f94aefecc8ffcb8da178 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Sat, 14 Mar 2020 10:27:08 +0100 Subject: [PATCH 118/327] revise tests --- .../fpcf/fixtures/field_immutability/Test.java | 13 ------------- .../classes/inheriting/EmptyClass.java | 2 +- .../EmptyClassInheritingEmptyClass.java | 2 +- .../EmptyClassInheritingMutableClass.java | 10 ++++++++++ .../classes/inheriting/MutableClass.java | 14 ++++++++++++++ .../classes/interfaces/EmptyInterface.java | 4 ++-- .../TrivialInterfaceWithMethods.java | 4 ++-- .../trivials/DependentImmutableClass.java | 6 +++++- .../classes/trivials/EmptyClass.java | 2 +- .../classes/trivials/FinalEmptyClass.java | 2 +- .../GenericTypeIsNotUsedAsFieldType.java | 2 +- .../classes/trivials/MutableClass.java | 8 ++++---- ...dentImmutableClassBecauseOfPublicField.java | 6 +++--- .../trivials/ShallowImmutableClass.java | 6 +++--- .../immutability/field/FinalEmptyClass.java | 2 ++ .../field/privateFieldNotBlank_deep.java | 2 +- .../field/privateFieldNotBlank_shallow.java | 2 +- .../field/private_getterEscape_deep.java | 2 +- .../field/private_getterEscape_shallow.java | 4 ++-- .../field/private_setter_deep.java | 2 +- .../field/private_setter_shallow.java | 2 +- .../reference/DeclaredFinalFields.java | 3 ++- .../reference/LazyInitialization.java | 3 ++- .../fixtures/immutability/type/EmptyClass.java | 2 +- .../immutability/type/FinalEmptyClass.java | 4 ++-- .../type/WithMutableAndImmutableFieldType.java | 10 +++++----- .../type_immutability/FinalEmptyClass.java | 10 ---------- .../type_immutability/TrivialMutableClass.java | 18 ------------------ .../fpcf/ReferenceImmutabilityTests.scala | 4 ---- 29 files changed, 69 insertions(+), 82 deletions(-) delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingMutableClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/MutableClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/FinalEmptyClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/TrivialMutableClass.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java deleted file mode 100644 index faebb9737e..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/field_immutability/Test.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.opalj.fpcf.fixtures.field_immutability; - - -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; - -@ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") -public class Test { - @ShallowImmutableFieldAnnotation("Because of Immutable Reference and Immutable Field Type") - @ImmutableReferenceAnnotation("Effectively immutable") - private String name = "name"; -} 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 index a8473f1ac2..7905b333a1 100644 --- 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 @@ -3,7 +3,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") +@MutableTypeAnnotation("Not final class") @DeepImmutableClassAnnotation("Class has no fields but is not final") public class EmptyClass { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingEmptyClass.java index 07bd311689..819ce5354b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingEmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingEmptyClass.java @@ -3,7 +3,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") +@MutableTypeAnnotation("Not final class") @DeepImmutableClassAnnotation("empty class inheriting empty class") public class EmptyClassInheritingEmptyClass extends EmptyClass{ } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingMutableClass.java new file mode 100644 index 0000000000..c1e01d5301 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingMutableClass.java @@ -0,0 +1,10 @@ +package org.opalj.fpcf.fixtures.immutability.classes.inheriting; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.type_mutability.MutableType; + +@MutableType("Because of mutable class") +@MutableClassAnnotation("Because of extending mutable class") +public final class EmptyClassInheritingMutableClass extends MutableClass { + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/MutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/MutableClass.java new file mode 100644 index 0000000000..15fb955390 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/MutableClass.java @@ -0,0 +1,14 @@ +package org.opalj.fpcf.fixtures.immutability.classes.inheriting; + +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; + +@MutableTypeAnnotation("Because of mutable class") +@MutableClassAnnotation("Because of mutable field") +public class MutableClass { + @MutableFieldAnnotation("Mutable reference") + @MutableReferenceAnnotation("public field") + public int n= 0; +} 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 index 4c4e45618a..20f13f83c3 100644 --- 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 @@ -3,7 +3,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") +@MutableTypeAnnotation("Not final interface") +@DeepImmutableClassAnnotation("Empty Interface") public interface EmptyInterface { } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/TrivialInterfaceWithMethods.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/TrivialInterfaceWithMethods.java index 569facea67..7b16c9767e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/TrivialInterfaceWithMethods.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/TrivialInterfaceWithMethods.java @@ -3,8 +3,8 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") +@MutableTypeAnnotation("Not final Interface") +@DeepImmutableClassAnnotation("Interface") public interface TrivialInterfaceWithMethods { public String getName(); diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java index 3ae8b54a55..a718eea2dc 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java @@ -1,11 +1,15 @@ package org.opalj.fpcf.fixtures.immutability.classes.trivials; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") +@MutableTypeAnnotation("Because of not final class") @DependentImmutableClassAnnotation("Das dependent immutable field") public class DependentImmutableClass { + @DependentImmutableFieldAnnotation(value = "Because of type T",genericString = "T") + @ImmutableReferenceAnnotation("Private effectively final field") private T t; public DependentImmutableClass(T t){ this.t = t; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/EmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/EmptyClass.java index 1c189869bb..ef7402982a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/EmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/EmptyClass.java @@ -3,7 +3,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") +@MutableTypeAnnotation("Because not final class") @DeepImmutableClassAnnotation("Class has no fields but is not final") public class EmptyClass { } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/FinalEmptyClass.java index b1303a4ca3..42c55c83f5 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/FinalEmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/FinalEmptyClass.java @@ -3,7 +3,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; -@DeepImmutableTypeAnnotation("") +@DeepImmutableTypeAnnotation("Because of final deep immutable class") @DeepImmutableClassAnnotation("Class has no fields and is final") public final class FinalEmptyClass { } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/GenericTypeIsNotUsedAsFieldType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/GenericTypeIsNotUsedAsFieldType.java index 460696c6ea..a24f943256 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/GenericTypeIsNotUsedAsFieldType.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/GenericTypeIsNotUsedAsFieldType.java @@ -3,7 +3,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") +@MutableTypeAnnotation("Because of not final class") @DeepImmutableClassAnnotation("Generic Type is not used as field Type") public class GenericTypeIsNotUsedAsFieldType { private int n = 0; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/MutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/MutableClass.java index 5d446fa5b3..853040c499 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/MutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/MutableClass.java @@ -5,10 +5,10 @@ import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.type_mutability.MutableType; -@MutableType("") -@MutableClassAnnotation("") +@MutableType("Because of not final mutable class") +@MutableClassAnnotation("Because of mutable field") public class MutableClass { - @MutableFieldAnnotation("") - @MutableReferenceAnnotation("") + @MutableFieldAnnotation("Because of mutable reference") + @MutableReferenceAnnotation("Because of public field") public int n = 0; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/NoDependentImmutableClassBecauseOfPublicField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/NoDependentImmutableClassBecauseOfPublicField.java index d47a4819de..fa4d5cc953 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/NoDependentImmutableClassBecauseOfPublicField.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/NoDependentImmutableClassBecauseOfPublicField.java @@ -5,11 +5,11 @@ import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") +@MutableTypeAnnotation("Because of mutable not final class") @MutableClassAnnotation("Generic class but public field") public class NoDependentImmutableClassBecauseOfPublicField { - @MutableFieldAnnotation("") - @MutableReferenceAnnotation("") + @MutableFieldAnnotation("Because of mutable reference") + @MutableReferenceAnnotation("Field is public") public T t; NoDependentImmutableClassBecauseOfPublicField(T t){ this.t = t; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java index 490c8385f2..ed2d8b13d2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java @@ -5,10 +5,10 @@ import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") +@MutableTypeAnnotation("Because of not final class") @ShallowImmutableClassAnnotation("has shallow immutable field") public class ShallowImmutableClass { - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @ShallowImmutableFieldAnnotation("Because of mutable type") + @ImmutableReferenceAnnotation("Because it is private") private MutableClass mutableClass = new MutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/FinalEmptyClass.java index 32c78b00b0..5aae58f878 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/FinalEmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/FinalEmptyClass.java @@ -1,7 +1,9 @@ package org.opalj.fpcf.fixtures.immutability.field; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; +@DeepImmutableTypeAnnotation("Because of deep immutable final class") @DeepImmutableClassAnnotation("Because of Emptiness") public final class FinalEmptyClass { } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java index dcd30a4715..d78125203f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java @@ -5,7 +5,7 @@ import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") +@MutableTypeAnnotation("Because of not final class") @DeepImmutableClassAnnotation("Because it has only Deep Immutable Field Types") public class privateFieldNotBlank_deep { @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java index a91602e936..ddf2ed4b11 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java @@ -5,7 +5,7 @@ import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") +@MutableTypeAnnotation("Because of not final class") @ShallowImmutableClassAnnotation("Because it has only Shallow Immutable Fields") public class privateFieldNotBlank_shallow { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java index c4a6b44231..c59b4e1c82 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java @@ -5,7 +5,7 @@ import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") +@MutableTypeAnnotation("Because of not final class") @DeepImmutableClassAnnotation("Only Deep Immutable Fields") public class private_getterEscape_deep { public FinalEmptyClass getFec() { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java index 9cc7ac65fe..9ed4ec4eb1 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java @@ -5,8 +5,8 @@ import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("Becaus it has only Shallow Immutable Fields") +@MutableTypeAnnotation("Because of not final class") +@ShallowImmutableClassAnnotation("Because it has only Shallow Immutable Fields") public class private_getterEscape_shallow { public TrivialMutableClass getTmc() { return tmc; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_deep.java index 9eb25c264e..4aae64534c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_deep.java @@ -5,7 +5,7 @@ import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") +@MutableTypeAnnotation("Because the class is mutable and not final") @MutableClassAnnotation("Because it has Mutable Fields") public class private_setter_deep { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_shallow.java index 1d9a7bc65a..385fdcd5a6 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_shallow.java @@ -5,7 +5,7 @@ import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -@MutableTypeAnnotation("") +@MutableTypeAnnotation("Because of not final class") @MutableClassAnnotation("Because it has Mutable Fields") public class private_setter_shallow { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java index 0136d6a953..cd16812cc3 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java @@ -46,10 +46,11 @@ public Super(){ } /** - * Tests for fields that are declared final. Some of them are not strictly final because they can + * 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 Peter Roth */ public class DeclaredFinalFields extends Super { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java index 31c86ccdce..9575272188 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java @@ -34,9 +34,10 @@ import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; /** - * Test classes for simple lazy initialization patterns and anti-patterns. + * Test classes for simple lazy initialization patterns and anti-patterns regarding reference immutability analysis. * * @author Dominik Helm + * @author Tobias Peter Roth */ class Simple { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/EmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/EmptyClass.java index 37cafb3690..0f4e690167 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/EmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/EmptyClass.java @@ -4,6 +4,6 @@ import org.opalj.fpcf.properties.type_mutability.MutableType; @MutableType("Class has no fields but is not final") -@DeepImmutableClassAnnotation("") +@DeepImmutableClassAnnotation("Class has no fields") public class EmptyClass { } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/FinalEmptyClass.java index b60da020e4..02fd29241b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/FinalEmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/FinalEmptyClass.java @@ -3,7 +3,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; -@DeepImmutableClassAnnotation("") -@DeepImmutableTypeAnnotation("Class has no fields and is final") +@DeepImmutableClassAnnotation("Class has no fields and is final") +@DeepImmutableTypeAnnotation("Class has no fields") public 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 index b758ac9228..9bfee1f8d2 100644 --- 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 @@ -7,16 +7,16 @@ import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; -@ShallowImmutableTypeAnnotation("has shallow mutable fields") -@ShallowImmutableClassAnnotation("has shallow imm fields") +@ShallowImmutableTypeAnnotation("has shallow and mutable fields") +@ShallowImmutableClassAnnotation("has shallow and immutable fields") public final class WithMutableAndImmutableFieldType { - @DeepImmutableFieldAnnotation("imm reference and deep immutable type") - @ImmutableReferenceAnnotation("private") + @DeepImmutableFieldAnnotation("immutable reference and deep immutable type") + @ImmutableReferenceAnnotation("private field") private FinalEmptyClass fec = new FinalEmptyClass(); @ShallowImmutableFieldAnnotation("imm reference and mutable type") - @ImmutableReferenceAnnotation("private") + @ImmutableReferenceAnnotation("private field") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/FinalEmptyClass.java deleted file mode 100644 index 37f683f188..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/FinalEmptyClass.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.opalj.fpcf.fixtures.type_immutability; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; - -@DeepImmutableTypeAnnotation("It has no fields and is final") -@DeepImmutableClassAnnotation("It has no fields and is final") -public final class FinalEmptyClass { - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/TrivialMutableClass.java deleted file mode 100644 index 554b76d5c4..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/type_immutability/TrivialMutableClass.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.opalj.fpcf.fixtures.type_immutability; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("is has mutable fields") -@MutableClassAnnotation("It has Mutable Fields") -public class TrivialMutableClass { - @MutableReferenceAnnotation("public") - @MutableFieldAnnotation("mutable reference") - public int n = 0; - - @MutableReferenceAnnotation("public") - @MutableFieldAnnotation("mutable reference") - public String name = "name"; -} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index 4e62f27752..68262f4ede 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -13,8 +13,6 @@ import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis @@ -47,8 +45,6 @@ class ReferenceImmutabilityTests extends PropertiesTest { val as = executeAnalyses( Set( EagerL0ReferenceImmutabilityAnalysis, - EagerLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new, LazyL2FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, From d3f2a186c5be735e341facf8e7177f0a52995621 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Sat, 14 Mar 2020 10:28:20 +0100 Subject: [PATCH 119/327] bug fixing in class imm analysis --- .../analyses/LxClassImmutabilityAnalysis_new.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index 883af1e199..b71c4f4a29 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -389,9 +389,9 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case ELBP(e, ShallowImmutableField | DeepImmutableField) ⇒ //FinalField) => dependees -= e - if (minLocalImmutability != ShallowImmutableClass) // && // ImmutableContainer && - //!dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + //xx if (minLocalImmutability != ShallowImmutableClass) // && // ImmutableContainer && + //!dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) + //xx minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible case UBP(DeepImmutableField) ⇒ //_: FinalField) ⇒ // no information about field mutability case UBP(ShallowImmutableField) ⇒ maxLocalImmutability = ShallowImmutableClass @@ -415,9 +415,9 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal */ // Lift lower bound once no dependencies other than field type mutabilities are left - if (minLocalImmutability != ShallowImmutableClass) // && //ImmutableContainer && - //dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer + //xxx if (minLocalImmutability != ShallowImmutableClass) // && //ImmutableContainer && + //dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) + //xxx minLocalImmutability = ShallowImmutableClass //ImmutableContainer if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { /*[DEBUG] assert( From dd957b25b376fa7945b8fee1fbb9d49dc2ebd54b Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 18 Mar 2020 15:13:55 +0100 Subject: [PATCH 120/327] removing unnecessary import --- .../class_immutability/DependentImmutableClassAnnotation.java | 1 - 1 file changed, 1 deletion(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java index d2f047a50b..0e60482695 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java @@ -1,7 +1,6 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.class_immutability; -import javafx.util.Pair; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; From 73bf290b60d0b3f65bb75612a368ce2755a05166 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 18 Mar 2020 15:16:38 +0100 Subject: [PATCH 121/327] analyze online project files --- .../tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala | 2 +- .../tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index d8a2cc517d..0f37c15118 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -1114,7 +1114,7 @@ object EagerL0ReferenceImmutabilityAnalysis final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allFields + val fields = p.allProjectClassFiles.toIterator.flatMap { _.fields } ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) analysis } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index 70e28f4e51..f008915817 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -272,13 +272,12 @@ object EagerLxTypeImmutabilityAnalysis_new override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val typeExtensibility = project.get(TypeExtensibilityKey) val analysis = new LxTypeImmutabilityAnalysis_new(project) - val allClassFilesIterator = project.allClassFiles.iterator + val allClassFilesIterator = project.allProjectClassFiles //project.allClassFiles.iterator val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) ps.scheduleEagerComputationsForEntities(types) { analysis.step1(typeExtensibility) } - analysis } } From d5b2974bfd8f8ea504b59f511ea227b0a445a806 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 18 Mar 2020 15:18:26 +0100 Subject: [PATCH 122/327] improvements: hardcoding deep immutability of String, handling of array types --- .../L0FieldImmutabilityAnalysis.scala | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index d35cc5797d..cc87aff4a6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -52,7 +52,9 @@ case class State(f: Field) { var field: Field = f var typeImmutability: Option[Boolean] = Some(true) var referenceImmutability: Option[Boolean] = None - var dependentImmutability: Option[DependentImmutabilityKind] = Some(DependentImmutabilityKind.dependent) + var dependentImmutability: Option[DependentImmutabilityKind] = Some( + DependentImmutabilityKind.dependent + ) var genericTypeSetNotDeepImmutable = false } @@ -121,14 +123,23 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def handleTypeImmutability(state: State) = { val objectType = field.fieldType.asFieldType - if (objectType.isArrayType) { + if (!objectType.isArrayType && objectType.isBaseType) { //state.typeImmutability = Some(true) // true is default - } else if (objectType.isBaseType) { + } else if (!objectType.isArrayType && objectType == ObjectType("java/lang/String")) { //state.typeImmutability = Some(true) // true is default - } else if (objectType.asObjectType == ObjectType.Object) + } else if (!objectType.isArrayType && objectType.asObjectType == ObjectType.Object) state.typeImmutability = Some(false) - else { - val result = propertyStore(objectType, TypeImmutability_new.key) + else if (objectType.isArrayType && (objectType.asArrayType.componentType.isBaseType || objectType.asArrayType.componentType == ObjectType( + "java/lang/String" + ))) { + //state.typeImmutability = Some(true) // true is default + } else { + val result = + if (objectType.isArrayType) { + propertyStore(objectType.asArrayType.componentType, TypeImmutability_new.key) + } else { + propertyStore(objectType, TypeImmutability_new.key) + } dependencies = dependencies.filter(_.e ne result.e) result match { case FinalEP(e, DeepImmutableType) ⇒ @@ -347,7 +358,7 @@ object EagerL0FieldImmutabilityAnalysis final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L0FieldImmutabilityAnalysis(p) - val fields = p.allFields + val fields = p.allProjectClassFiles.toIterator.flatMap { _.fields } ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) analysis } From e6b401bbd948316cc055883dba3b6fb47a708701 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 18 Mar 2020 15:20:22 +0100 Subject: [PATCH 123/327] improvement: of class and interface separation, and filtering of mutable fields - only project class fields --- ...ssImmutabilityAnalysisDemo_withNewPurity.scala | 15 +++++++-------- ...ldImmutabilityAnalysisDemo_withNewPurity.scala | 3 +++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala index 62c80b757e..9ecaf692e1 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala @@ -114,21 +114,19 @@ object ClassImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic sb.append("\nDeep Immutable Class Classes:\n") val allInterfaces = project.allClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet - val deepImmutableClasses = propertyStore + val deepImmutables = propertyStore .finalEntities(DeepImmutableClass) .toList - .filter( - x ⇒ !x.isInstanceOf[ObjectType] || !allInterfaces.contains(x.asInstanceOf[ObjectType]) - ) + val deepImmutableClassesInterfaces = deepImmutables + .filter(x ⇒ x.isInstanceOf[ObjectType] && allInterfaces.contains(x.asInstanceOf[ObjectType])) + val deepImmutableClasses = + deepImmutables.filter(!deepImmutableClassesInterfaces.toSet.contains(_)) sb.append( deepImmutableClasses.toList .map(x ⇒ x.toString+" |Deep Immutable Class\n") ) sb.append("\nDeep Immutable Class Classes: Interface\n") - val deepImmutableClassesInterfaces = propertyStore - .finalEntities(DeepImmutableClass) - .toList - .filter(x ⇒ x.isInstanceOf[ObjectType] && allInterfaces.contains(x.asInstanceOf[ObjectType])) + sb.append( deepImmutableClassesInterfaces .map(x ⇒ x.toString+" |Deep Immutable Class Interface\n") @@ -139,6 +137,7 @@ object ClassImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic | dependent immutable classes: ${dependentImmutableClasses.size} | deep immutable classes: ${deepImmutableClasses.size} | deep immutable classes interfaces: ${deepImmutableClassesInterfaces.size} + | deep immutables: ${deepImmutables.size} | | took : $analysisTime seconds | needs : ${memoryConsumption / 1024 / 1024} MBytes diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala index fa98bea6f5..82f568a169 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala @@ -7,6 +7,7 @@ import java.io.File import java.net.URL import java.util.Calendar +import org.opalj.br.Field import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication @@ -86,9 +87,11 @@ object FieldImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic val sb: StringBuilder = new StringBuilder sb.append("Mutable Fields: \n") + val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet val mutableFields = propertyStore .finalEntities(MutableField) .toList + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) sb.append( mutableFields .map(x ⇒ x.toString+" |Mutable Field\n") From 6e27b902b2a7466cc1e86d81e8e124685bef15d9 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 18 Mar 2020 15:21:56 +0100 Subject: [PATCH 124/327] New test-cases for fields with strings and arrays --- .../immutability/field/ArrayAndString.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ArrayAndString.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ArrayAndString.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ArrayAndString.java new file mode 100644 index 0000000000..957c23519c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ArrayAndString.java @@ -0,0 +1,31 @@ +package org.opalj.fpcf.fixtures.immutability.field; + +import org.opalj.br.fpcf.properties.DependentImmutableField; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; + +public class ArrayAndString { + @DeepImmutableFieldAnnotation("") + private String[] stringArray; + @DeepImmutableFieldAnnotation("") + private int[] intArray; + @DeepImmutableFieldAnnotation("") + private String string; + @DeepImmutableFieldAnnotation("") + private int i; + @ShallowImmutableFieldAnnotation("") + private TrivialMutableClass[] tmc; + @ShallowImmutableFieldAnnotation("") + private T[] tArray; + + ArrayAndString(String[] stringArray, int[] intArray, String string, int i, TrivialMutableClass[] tmc, T[] tArray) { + this.stringArray = stringArray; + this.intArray = intArray; + this.string = string; + this.i = i; + this.tmc = tmc; + this.tArray = tArray; + } + +} From f9b2c550179e83359ac2a82f6b3deecf36251f66 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Sat, 21 Mar 2020 18:42:31 +0100 Subject: [PATCH 125/327] Improving and Bug Fixing --- .../L0FieldImmutabilityAnalysis.scala | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index cc87aff4a6..8d22e12377 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -123,16 +123,23 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def handleTypeImmutability(state: State) = { val objectType = field.fieldType.asFieldType - if (!objectType.isArrayType && objectType.isBaseType) { - //state.typeImmutability = Some(true) // true is default + if (objectType == ObjectType.Object) { + state.typeImmutability = Some(false) + } //else //if (objectType.isArrayType || objectType.isBaseType) {} /// + else if (!objectType.isArrayType && objectType.isBaseType) { + /// //state.typeImmutability = Some(true) // true is default } else if (!objectType.isArrayType && objectType == ObjectType("java/lang/String")) { //state.typeImmutability = Some(true) // true is default - } else if (!objectType.isArrayType && objectType.asObjectType == ObjectType.Object) + } /**else if (!objectType.isArrayType && objectType.asObjectType == ObjectType.Object) { state.typeImmutability = Some(false) - else if (objectType.isArrayType && (objectType.asArrayType.componentType.isBaseType || objectType.asArrayType.componentType == ObjectType( - "java/lang/String" - ))) { + }**/ else if (objectType.isArrayType && + (objectType.asArrayType.componentType.isBaseType || + objectType.asArrayType.componentType == ObjectType( + "java/lang/String" + ))) { //state.typeImmutability = Some(true) // true is default + } else if (objectType.isArrayType && objectType.asArrayType.componentType.isArrayType) { + state.typeImmutability = Some(false) } else { val result = if (objectType.isArrayType) { From ce8de7b356de0f8ca5d6c7f8b93801ec265929f1 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 25 Mar 2020 16:01:26 +0100 Subject: [PATCH 126/327] extending Field with the property is synthetic (isSyn) --- OPAL/br/src/main/scala/org/opalj/br/Field.scala | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/Field.scala b/OPAL/br/src/main/scala/org/opalj/br/Field.scala index 18fe1cfa55..1b63086f4b 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/Field.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/Field.scala @@ -61,7 +61,10 @@ sealed abstract class JVMField extends ClassMember with Ordered[JVMField] { private[br] def prepareClassFileAttachement(): Field = { new Field( null /*will be set by class file*/ , - accessFlags, name, fieldType, attributes + accessFlags, + name, + fieldType, + attributes ) } @@ -97,6 +100,8 @@ sealed abstract class JVMField extends ClassMember with Ordered[JVMField] { def isTransient: Boolean = (ACC_TRANSIENT.mask & accessFlags) != 0 + def isSyn: Boolean = super.isSynthetic + def isVolatile: Boolean = (ACC_VOLATILE.mask & accessFlags) != 0 /** @@ -219,7 +224,9 @@ object Field { fieldAttributeBuilder: FieldAttributeBuilder ): FieldTemplate = { this( - accessFlags, name, fieldType, + accessFlags, + name, + fieldType, RefArray(fieldAttributeBuilder(accessFlags, name, fieldType)) ) } From ac8f417220703ecfa019c19bdb382e0e5f8e05d7 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 25 Mar 2020 16:03:48 +0100 Subject: [PATCH 127/327] improving demos and test for evaluation --- ...AnalysisDemo_performanceMeasurements.scala | 4 +- ...mutabilityAnalysisDemo_withNewPurity.scala | 73 +++++++++-------- ...AnalysisDemo_performanceMeasurements.scala | 9 +-- ...mutabilityAnalysisDemo_withNewPurity.scala | 63 +++++++-------- ...AnalysisDemo_performanceMeasurements.scala | 4 +- ...mutabilityAnalysisDemo_withNewPurity.scala | 66 ++++++++------- .../TypeImmutabilityAnalysisDemo.scala | 14 +++- ...AnalysisDemo_performanceMeasurements.scala | 2 +- ...mutabilityAnalysisDemo_withNewPurity.scala | 81 +++++++++++-------- ...renceImmutabilityTests_withNewPurity.scala | 2 - 10 files changed, 171 insertions(+), 147 deletions(-) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala index 497ae0f339..6474e8ed9c 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -49,7 +49,7 @@ object ClassImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnal def analyze(theProject: Project[URL]): String = { var times: List[Seconds] = Nil: List[Seconds] - for (i ← 0 until 10) { + for (i ← 0 to 10) { val project = theProject.recreate() val analysesManager = project.get(FPCFAnalysesManagerKey) analysesManager.project.get(RTACallGraphKey) @@ -79,7 +79,7 @@ object ClassImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnal } val sortedList = times.sortWith(_.timeSpan < _.timeSpan) - val median = sortedList((times.size - 1) / 2) + val median = sortedList(5) val output = s""" diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala index 9ecaf692e1..4de458d8a8 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala @@ -9,9 +9,7 @@ import java.util.Calendar import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new -import org.opalj.util.PerformanceEvaluation.memory -//import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.Project @@ -56,49 +54,49 @@ object ClassImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic } def analyze(project: Project[URL]): String = { - var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - memory { - val analysesManager = project.get(FPCFAnalysesManagerKey) - - analysesManager.project.get(RTACallGraphKey) - - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - } { mu ⇒ - memoryConsumption = mu + val analysesManager = project.get(FPCFAnalysesManagerKey) + + analysesManager.project.get(RTACallGraphKey) + + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds } + val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet val sb = new StringBuilder sb.append("Mutable Class: \n") val mutableClasses = propertyStore .finalEntities(MutableClass) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) .toList sb.append( mutableClasses .map(x ⇒ x.toString+" |Mutable Class\n") ) sb.append("\nShallow Immutable Class:\n") - val shallowImmutableClasses = propertyStore.finalEntities(ShallowImmutableClass).toList + val shallowImmutableClasses = propertyStore + .finalEntities(ShallowImmutableClass) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) + .toList sb.append( shallowImmutableClasses .map(x ⇒ x.toString+" |Shallow Immutable Class\n") @@ -106,6 +104,7 @@ object ClassImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic sb.append("\nDependent Immutable Class: \n") val dependentImmutableClasses = propertyStore .finalEntities(DependentImmutableClass) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) .toList sb.append( dependentImmutableClasses @@ -113,14 +112,20 @@ object ClassImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic ) sb.append("\nDeep Immutable Class Classes:\n") - val allInterfaces = project.allClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet + val allInterfaces = + project.allProjectClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet + val deepImmutables = propertyStore .finalEntities(DeepImmutableClass) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) .toList val deepImmutableClassesInterfaces = deepImmutables .filter(x ⇒ x.isInstanceOf[ObjectType] && allInterfaces.contains(x.asInstanceOf[ObjectType])) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) val deepImmutableClasses = - deepImmutables.filter(!deepImmutableClassesInterfaces.toSet.contains(_)) + deepImmutables + .filter(!deepImmutableClassesInterfaces.toSet.contains(_)) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) sb.append( deepImmutableClasses.toList .map(x ⇒ x.toString+" |Deep Immutable Class\n") @@ -140,7 +145,6 @@ object ClassImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic | deep immutables: ${deepImmutables.size} | | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes |"""".stripMargin) val calendar = Calendar.getInstance() @@ -156,7 +160,6 @@ object ClassImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic s""" | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala index 434bf9fd9e..7d55bc9dab 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -52,16 +52,12 @@ object FieldImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnal var tmpProject = theProject var times: List[Seconds] = Nil: List[Seconds] println("before") - for (i ← 0 until 10) { - println( - "--------------------------------------------------------------------------------------------<<<<" - ) + for (i ← 0 to 10) { val project = tmpProject.recreate() val analysesManager = project.get(FPCFAnalysesManagerKey) analysesManager.project.get(RTACallGraphKey) var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - println(s"i: $i") time { propertyStore = analysesManager .runAll( @@ -83,12 +79,11 @@ object FieldImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnal analysisTime = t.toSeconds } times = analysisTime :: times - println("after") tmpProject = project } val sortedList = times.sortWith(_.timeSpan < _.timeSpan) - val median = sortedList((times.size - 1) / 2) + val median = sortedList(5) val output = s""" diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala index 82f568a169..0231beb03a 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala @@ -29,7 +29,6 @@ import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new -import org.opalj.util.PerformanceEvaluation.memory import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -55,43 +54,40 @@ object FieldImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic } def analyze(project: Project[URL]): String = { - var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - memory { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - time { - propertyStore = analysesManager - .runAll( - LazyLxClassImmutabilityAnalysis_new, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - } { mu ⇒ - memoryConsumption = mu + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + time { + propertyStore = analysesManager + .runAll( + LazyL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + EagerL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds } - val sb: StringBuilder = new StringBuilder sb.append("Mutable Fields: \n") - val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet + val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields } + //.filter(f ⇒ (!f.isTransient && !f.isSyn)) // for ReImComparison + .toSet val mutableFields = propertyStore .finalEntities(MutableField) - .toList .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) + .toList + sb.append( mutableFields .map(x ⇒ x.toString+" |Mutable Field\n") @@ -100,6 +96,7 @@ object FieldImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic sb.append("\nShallow Immutable Fields: \n") val shallowImmutableFields = propertyStore .finalEntities(ShallowImmutableField) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) .toList sb.append( shallowImmutableFields @@ -109,6 +106,7 @@ object FieldImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic sb.append("\nDependet Immutable Fields: \n") val dependentImmutableFields = propertyStore .finalEntities(DependentImmutableField) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) .toList sb.append( dependentImmutableFields @@ -118,6 +116,7 @@ object FieldImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic sb.append("Deep Immutable Fields: ") val deepImmutableFields = propertyStore .finalEntities(DeepImmutableField) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) .toList sb.append( deepImmutableFields @@ -131,8 +130,9 @@ object FieldImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic | dependent immutable fields: ${dependentImmutableFields.size} | deep immutable fields: ${deepImmutableFields.size} | + | count: ${mutableFields.size + shallowImmutableFields.size + dependentImmutableFields.size + deepImmutableFields.size} + | | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes |""".stripMargin ) val calendar = Calendar.getInstance() @@ -149,7 +149,6 @@ object FieldImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplic s""" | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala index cda67e440f..daa413a9e6 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -50,7 +50,7 @@ object ReferenceImmutabilityAnalysisDemo_performanceMeasurements def analyze(theProject: Project[URL]): String = { var times: List[Seconds] = Nil: List[Seconds] - for (i ← 0 until 10) { + for (i ← 0 to 10) { val project = Project.recreate(theProject) val analysesManager = project.get(FPCFAnalysesManagerKey) analysesManager.project.get(RTACallGraphKey) @@ -78,7 +78,7 @@ object ReferenceImmutabilityAnalysisDemo_performanceMeasurements times = analysisTime :: times } val sortedList = times.sortWith(_.timeSpan < _.timeSpan) - val median = sortedList((times.size - 1) / 2) + val median = sortedList(5) val output = s""" diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala index f01201dd2c..9ba5dac4bd 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala @@ -17,9 +17,10 @@ import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds -import org.opalj.util.PerformanceEvaluation.memory import java.io._ 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 @@ -50,39 +51,40 @@ object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisAp } def analyze(project: Project[URL]): String = { - var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - memory { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyFieldLocalityAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - LazyLxClassImmutabilityAnalysis_new - ) - ._1 - propertyStore.waitOnPhaseCompletion() + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion() - } { t ⇒ - analysisTime = t.toSeconds - } - } { mu ⇒ - memoryConsumption = mu + } { t ⇒ + analysisTime = t.toSeconds } var sb: StringBuilder = new StringBuilder() + val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields } + //.filter(f ⇒ (!f.isTransient && !f.isSyn)) // for ReImComparison + .toSet sb = sb.append("Mutable References: \n") - val mutableReferences = propertyStore.finalEntities(MutableReference).toList + val mutableReferences = propertyStore + .finalEntities(MutableReference) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) + .toList sb = sb.append( mutableReferences.map(x ⇒ x.toString+"\n").toString() ) @@ -90,6 +92,7 @@ object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisAp sb = sb.append("\n Lazy Initialized Reference: \n") val lazyInitializedReferences = propertyStore .finalEntities(LazyInitializedReference) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) .toList sb = sb.append( lazyInitializedReferences @@ -98,7 +101,10 @@ object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisAp ) sb = sb.append("\nImmutable References: \n") - val immutableReferences = propertyStore.finalEntities(ImmutableReference).toList + val immutableReferences = propertyStore + .finalEntities(ImmutableReference) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) + .toList sb = sb.append( immutableReferences.map(x ⇒ x.toString+"\n").toString() ) @@ -108,8 +114,7 @@ object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisAp | lazy initialized References: ${lazyInitializedReferences.size} | immutable References: ${immutableReferences.size} | - | took : $analysisTime seconds - | needed: ${memoryConsumption / 1024 / 1024} MBytes + | took : $analysisTime seconds | |""".stripMargin ) @@ -126,7 +131,6 @@ object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisAp bw.close() s""" | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes |""".stripMargin } 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 index cdb9f35d33..b59da12e81 100644 --- 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 @@ -7,6 +7,7 @@ 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 @@ -91,25 +92,34 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } val sb: StringBuilder = new StringBuilder sb.append("\nMutableTypes: \n") + val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet val mutableTypes = propertyStore .finalEntities(MutableType_new) .toList + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) sb.append( mutableTypes .map(x ⇒ x.toString+" |Mutable Type\n") .toString ) sb.append("\nShallow Immutable Types:\n") - val shallowImmutableTypes = propertyStore.finalEntities(ShallowImmutableType).toList + val shallowImmutableTypes = propertyStore + .finalEntities(ShallowImmutableType) + .toList + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) sb.append(shallowImmutableTypes.map(x ⇒ x.toString+" |Shallow Immutable Type\n")) sb.append("\nDependent Immutable Types: \n") - val dependentImmutableTypes = propertyStore.finalEntities(DependentImmutableType).toList + val dependentImmutableTypes = propertyStore + .finalEntities(DependentImmutableType) + .toList + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) sb.append( dependentImmutableTypes.map(x ⇒ x.toString+" |Dependent Immutable Type\n") ) sb.append("\nDeep Immutable Types:\n") val deepImmutableTypes = propertyStore.finalEntities(DeepImmutableType).toList sb.append(deepImmutableTypes.map(x ⇒ x.toString+" |Deep Immutable Type\n")) + sb.append(s"\nType immutability analysis took: $analysisTime on average") sb.append("\n\n") diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala index 48984c7541..ec94f7dfc1 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -80,7 +80,7 @@ object TypeImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnaly } val sortedList = times.sortWith(_.timeSpan < _.timeSpan) - val median = sortedList((times.size - 1) / 2) + val median = sortedList(5) val output = s""" diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala index 8a1c9e6c46..834e2b7c03 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala @@ -7,6 +7,7 @@ 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 @@ -24,13 +25,12 @@ import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis -import org.opalj.util.PerformanceEvaluation.memory +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -53,40 +53,41 @@ object TypeImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplica } def analyze(project: Project[URL]): String = { - var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - memory { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - EagerLxTypeImmutabilityAnalysis_new, - LazyLxClassImmutabilityAnalysis_new, - LazyFieldLocalityAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - } { mu ⇒ - memoryConsumption = mu + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds } val sb: StringBuilder = new StringBuilder + val allProjectClassFilesIterator = project.allProjectClassFiles + val types = + allProjectClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType).toSet sb.append("\nMutableTypes: \n") val mutableTypes = propertyStore .finalEntities(MutableType_new) + .filter({ x ⇒ + types.contains(x.asInstanceOf[ObjectType]) + }) .toList sb.append( mutableTypes @@ -94,15 +95,30 @@ object TypeImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplica .toString ) sb.append("\nShallow Immutable Types:\n") - val shallowImmutableTypes = propertyStore.finalEntities(ShallowImmutableType).toList + val shallowImmutableTypes = propertyStore + .finalEntities(ShallowImmutableType) + .filter({ x ⇒ + types.contains(x.asInstanceOf[ObjectType]) + }) + .toList sb.append(shallowImmutableTypes.map(x ⇒ x.toString+" |Shallow Immutable Type\n")) sb.append("\nDependent Immutable Types: \n") - val dependentImmutableTypes = propertyStore.finalEntities(DependentImmutableType).toList + val dependentImmutableTypes = propertyStore + .finalEntities(DependentImmutableType) + .filter({ x ⇒ + types.contains(x.asInstanceOf[ObjectType]) + }) + .toList sb.append( dependentImmutableTypes.map(x ⇒ x.toString+" |Dependent Immutable Type\n") ) sb.append("\nDeep Immutable Types:\n") - val deepImmutableTypes = propertyStore.finalEntities(DeepImmutableType).toList + val deepImmutableTypes = propertyStore + .finalEntities(DeepImmutableType) + .filter({ x ⇒ + types.contains(x.asInstanceOf[ObjectType]) + }) + .toList sb.append(deepImmutableTypes.map(x ⇒ x.toString+" |Deep Immutable Type\n")) sb.append(s"\nType immutability analysis took: $analysisTime on average") @@ -114,8 +130,8 @@ object TypeImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplica | dependent immutable types: ${dependentImmutableTypes.size} | deep immutable types: ${deepImmutableTypes.size} | + | sum: ${mutableTypes.size + shallowImmutableTypes.size + dependentImmutableTypes.size + deepImmutableTypes.size} | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes |""".stripMargin ) @@ -132,7 +148,6 @@ object TypeImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplica s""" | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes |""".stripMargin } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala index dface72f87..2ff752b6ed 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala @@ -7,7 +7,6 @@ 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.LazyL0FieldMutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey @@ -47,7 +46,6 @@ class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { EagerL0ReferenceImmutabilityAnalysis, LazyLxClassImmutabilityAnalysis_new, LazyLxTypeImmutabilityAnalysis_new, - LazyL0FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, LazyInterProceduralEscapeAnalysis, From fc7384f75fa0f24e67ec8013bc89662877a61b23 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 25 Mar 2020 16:11:18 +0100 Subject: [PATCH 128/327] improvements and fixing of the missing case in class imm analysis of dep imm superclass --- .../L0FieldImmutabilityAnalysis.scala | 27 +++++------- .../LxClassImmutabilityAnalysis_new.scala | 44 +++++-------------- .../LxTypeImmutabilityAnalysis_new.scala | 4 +- 3 files changed, 26 insertions(+), 49 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index 8d22e12377..eb12dde236 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -111,8 +111,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case _ ⇒ } ) - if (result.size > 0) + if (result.size > 0) { classFormalTypeParameters = Some(result) + } + } def isInClassesGenericTypeParameters(string: String): Boolean = { if (classFormalTypeParameters == None) @@ -124,19 +126,13 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def handleTypeImmutability(state: State) = { val objectType = field.fieldType.asFieldType if (objectType == ObjectType.Object) { - state.typeImmutability = Some(false) - } //else //if (objectType.isArrayType || objectType.isBaseType) {} /// - else if (!objectType.isArrayType && objectType.isBaseType) { - /// //state.typeImmutability = Some(true) // true is default - } else if (!objectType.isArrayType && objectType == ObjectType("java/lang/String")) { + state.typeImmutability = Some(false) //handling generic fields + } else if (objectType.isBaseType || objectType == ObjectType("java/lang/String")) { //state.typeImmutability = Some(true) // true is default - } /**else if (!objectType.isArrayType && objectType.asObjectType == ObjectType.Object) { - state.typeImmutability = Some(false) - }**/ else if (objectType.isArrayType && - (objectType.asArrayType.componentType.isBaseType || - objectType.asArrayType.componentType == ObjectType( - "java/lang/String" - ))) { + } else if (objectType.isArrayType && (objectType.asArrayType.componentType.isBaseType || + objectType.asArrayType.componentType == ObjectType( + "java/lang/String" + ))) { //state.typeImmutability = Some(true) // true is default } else if (objectType.isArrayType && objectType.asArrayType.componentType.isArrayType) { state.typeImmutability = Some(false) @@ -174,8 +170,8 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.field.asField.attributes.foreach( _ match { case RuntimeInvisibleAnnotationTable(_) ⇒ + case SourceFile(_) ⇒ case TypeVariableSignature(t) ⇒ - //state.typeImmutability = Some(false) // respects the cas flag_onlyDeep = false if (!isInClassesGenericTypeParameters(t)) { flag_notShallow = false @@ -317,6 +313,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) c(state) ) } + val state: State = new State(field) val result = propertyStore(state.field, ReferenceImmutability.key) result match { @@ -327,10 +324,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) dependencies += x } } + loadFormalTypeparameters() handleTypeImmutability(state) hasGenericType(state) - if (dependencies.isEmpty) createResult(state) else diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala index b71c4f4a29..abd32357ff 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala @@ -90,7 +90,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal t: ObjectType, immutability: ClassImmutability_new //MutableObject ): MultiResult = { - //println("I") val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) val r = allSubtypes.map { st ⇒ new FinalEP(st, immutability) @@ -103,7 +102,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal cfMutabilityIsFinal: Boolean, result: ProperPropertyComputationResult ): IncrementalResult[ClassFile] = { - // println("J") var results: List[ProperPropertyComputationResult] = List(result) var nextComputations: List[(PropertyComputation[ClassFile], ClassFile)] = Nil val directSubtypes = classHierarchy.directSubtypesOf(t) @@ -128,7 +126,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } def determineGenericTypeBounds(classFile: ClassFile): Set[(String, String)] = { - // println("K") var genericTypeBounds: Set[(String, String)] = Set.empty classFile.attributes.toList.collectFirst({ case ClassSignature(typeParameters, _, _) ⇒ @@ -157,7 +154,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { - //println("L") e match { case t: ObjectType ⇒ //this is safe @@ -215,7 +211,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal )( cf: ClassFile ): ProperPropertyComputationResult = { - //println("M") val t = cf.thisType var dependees = Map.empty[Entity, EOptionP[Entity, Property]] @@ -235,12 +230,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) - //println("start") - //println(superClassType.simpleName) fieldsPropertyStoreInformation.foreach( f ⇒ { - //println("A") - //println(f) f match { case FinalP(MutableField) ⇒ { if (lazyComputation) @@ -270,8 +261,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } } ) - //println("B") - //println(superClassMutabilityIsFinal) + /** * var minLocalImmutability: ClassImmutability_new = * if (!superClassMutabilityIsFinal) { @@ -283,8 +273,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal */ var minLocalImmutability: ClassImmutability_new = MutableClass - //println("C") - //println(superClassInformation) // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability_new = superClassInformation match { case UBP(MutableClass) ⇒ MutableClass @@ -292,15 +280,12 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case UBP(DependentImmutableClass) ⇒ DependentImmutableClass case _ ⇒ DeepImmutableClass // ImmutableObject } - //println("D") - //println(hasShallowImmutableFields) if (hasShallowImmutableFields) { maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } //if (!dependentImmutableFields.isEmpty && maxLocalImmutability != ShallowImmutableClass) { if (hasDependentImmutableFields && maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { - //println("E") maxLocalImmutability = DependentImmutableClass } @@ -308,7 +293,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal // 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. - //println("F") maxLocalImmutability = ShallowImmutableClass //ImmutableContainer } @@ -318,7 +302,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal // <=> all fields are (effectively) final // <=> the type mutability of all fields is final // (i.e., ImmutableType or ImmutableContainerType) - //println("G") if (lazyComputation) return Result(t, maxLocalImmutability); @@ -331,7 +314,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } def c(someEPS: SomeEPS): ProperPropertyComputationResult = { - //println("H") //[DEBUG] //val oldDependees = dependees dependees = dependees.filter(_._1 ne someEPS.e) @@ -349,6 +331,10 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal maxLocalImmutability = ShallowImmutableClass //ImmutableContainer //dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) //TypeImmutability.key) + case UBP(DependentImmutableClass) ⇒ + if (someEPS.isFinal) dependees -= SuperClassKey + maxLocalImmutability = DependentImmutableClass + case LBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is a least immutable container if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) @@ -373,7 +359,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal */ //} else - // maxLocalImmutability = DependentImmutableClass // DependentImmutableClass //TODO possibly here? + // maxLocalImmutability = DependentImmutableClass // DependentImmutableClass } // Field Mutability related dependencies: @@ -415,9 +401,9 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal */ // Lift lower bound once no dependencies other than field type mutabilities are left - //xxx if (minLocalImmutability != ShallowImmutableClass) // && //ImmutableContainer && + ////if (minLocalImmutability != ShallowImmutableClass) // && //ImmutableContainer && //dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) - //xxx minLocalImmutability = ShallowImmutableClass //ImmutableContainer + //// minLocalImmutability = ShallowImmutableClass //ImmutableContainer if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { /*[DEBUG] assert( @@ -441,20 +427,14 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal Result(t, maxLocalImmutability) } else { - //println("c interim") - //println("min: "+minLocalImmutability) - //println("max: "+maxLocalImmutability) - //println(dependees.values) - - // if (oldDependees == dependees) { - // Result(t, minLocalImmutability) - //} else + /* + * if (oldDependees == dependees) { + * Result(t, minLocalImmutability) + * } else**/ //For debugging purposes InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) } } - //[DEBUG] assert(initialImmutability.isRefinable) - val result = InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) if (lazyComputation) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala index f008915817..0cae1e97fc 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala @@ -272,8 +272,8 @@ object EagerLxTypeImmutabilityAnalysis_new override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val typeExtensibility = project.get(TypeExtensibilityKey) val analysis = new LxTypeImmutabilityAnalysis_new(project) - val allClassFilesIterator = project.allProjectClassFiles //project.allClassFiles.iterator - val types = allClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) + val allProjectClassFilesIterator = project.allProjectClassFiles + val types = allProjectClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) ps.scheduleEagerComputationsForEntities(types) { analysis.step1(typeExtensibility) From 05784529baccbf5eecc44ef97678ae5126b92b04 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 26 Mar 2020 12:42:08 +0100 Subject: [PATCH 129/327] correction --- .../immutability/field/ArrayAndString.java | 4 ++-- .../analyses/L0FieldImmutabilityAnalysis.scala | 14 ++------------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ArrayAndString.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ArrayAndString.java index 957c23519c..8ff0d12f75 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ArrayAndString.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ArrayAndString.java @@ -6,9 +6,9 @@ import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; public class ArrayAndString { - @DeepImmutableFieldAnnotation("") + @ShallowImmutableFieldAnnotation("") private String[] stringArray; - @DeepImmutableFieldAnnotation("") + @ShallowImmutableFieldAnnotation("") private int[] intArray; @DeepImmutableFieldAnnotation("") private String string; diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala index eb12dde236..61a52da278 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala @@ -129,20 +129,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.typeImmutability = Some(false) //handling generic fields } else if (objectType.isBaseType || objectType == ObjectType("java/lang/String")) { //state.typeImmutability = Some(true) // true is default - } else if (objectType.isArrayType && (objectType.asArrayType.componentType.isBaseType || - objectType.asArrayType.componentType == ObjectType( - "java/lang/String" - ))) { - //state.typeImmutability = Some(true) // true is default - } else if (objectType.isArrayType && objectType.asArrayType.componentType.isArrayType) { + } else if (objectType.isArrayType) { state.typeImmutability = Some(false) } else { - val result = - if (objectType.isArrayType) { - propertyStore(objectType.asArrayType.componentType, TypeImmutability_new.key) - } else { - propertyStore(objectType, TypeImmutability_new.key) - } + val result = propertyStore(objectType, TypeImmutability_new.key) dependencies = dependencies.filter(_.e ne result.e) result match { case FinalEP(e, DeepImmutableType) ⇒ From b16d051f44b48de83bdeb70e4af5e0832a62dc3c Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Thu, 7 May 2020 10:53:28 +0200 Subject: [PATCH 130/327] Use thread local evaluation up to MaxEvaluationDepth --- .../opalj/fpcf/par/PKECPropertyStore.scala | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) 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 6c482394ef..8d5596adb4 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 @@ -11,6 +11,8 @@ import java.util.ArrayDeque import scala.collection.mutable.ArrayBuffer import scala.util.control.ControlThrowable +import com.typesafe.config.Config + import org.opalj.log.LogContext import org.opalj.fpcf.PropertyKey.fallbackPropertyBasedOnPKId @@ -20,7 +22,8 @@ import org.opalj.fpcf.PropertyKey.fallbackPropertyBasedOnPKId * @author Dominik Helm */ class PKECPropertyStore( - final val ctx: Map[Class[_], AnyRef] + final val ctx: Map[Class[_], AnyRef], + override val MaxEvaluationDepth: Int )( implicit val logContext: LogContext @@ -32,7 +35,7 @@ class PKECPropertyStore( val taskManager: PKECTaskManager = PKECFIFOTaskManager - override def MaxEvaluationDepth: Int = 0 + var evaluationDepth: Int = 0 val ps: Array[ConcurrentHashMap[Entity, EPKState]] = Array.fill(PropertyKind.SupportedPropertyKinds) { new ConcurrentHashMap() } @@ -376,8 +379,7 @@ class PKECPropertyStore( } def updateDependees(depender: EPKState, newDependees: Set[SomeEOptionP]): Unit = { - val dependerEpk = depender.eOptP.toEPK - val suppressedPKs = suppressInterimUpdates(dependerEpk.pk.id) + val suppressedPKs = suppressInterimUpdates(depender.eOptP.pk.id) newDependees.forall { dependee ⇒ val dependeePK = dependee.pk.id val dependeeState = ps(dependeePK).get(dependee.e) @@ -392,18 +394,25 @@ class PKECPropertyStore( ): EOptionP[E, P] = { val current = ps(pkId).get(e) if (current eq null) { - val lazyComputation = lazyComputations(pkId) + 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) { - scheduleTask( - new LazyComputationTask( - e, - lazyComputation.asInstanceOf[E ⇒ PropertyComputationResult], - pkId + if (evaluationDepth < MaxEvaluationDepth) { + evaluationDepth += 1 + handleResult(lazyComputation(e)) + evaluationDepth -= 1 + ps(pkId).get(e).eOptP.asInstanceOf[EOptionP[E, P]] + } else { + scheduleTask( + new LazyComputationTask( + e, + lazyComputation, + pkId + ) ) - ) - epk + epk + } } else { previous.eOptP.asInstanceOf[EOptionP[E, P]] } @@ -1031,7 +1040,15 @@ object PKECPropertyStore extends PropertyStoreFactory[PKECPropertyStore] { ): PKECPropertyStore = { val contextMap: Map[Class[_], AnyRef] = context.map(_.asTuple).toMap - val ps = new PKECPropertyStore(contextMap) + val config = + contextMap.get(classOf[Config]) match { + case Some(config: Config) ⇒ config + case _ ⇒ org.opalj.BaseConfig + } + + val maxEvaluationDepth = config.getInt(MaxEvaluationDepthKey) + + val ps = new PKECPropertyStore(contextMap, maxEvaluationDepth) ps } } \ No newline at end of file From 37b60c9a6277d362403bd8887c4ecf39f0046068 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Thu, 7 May 2020 10:57:10 +0200 Subject: [PATCH 131/327] Set thread count on PKECPropertyStore --- .../scala/org/opalj/support/info/CallGraph.scala | 12 +++--------- .../main/scala/org/opalj/support/info/Purity.scala | 12 +++--------- .../scala/org/opalj/fpcf/par/PKECPropertyStore.scala | 7 ++++--- 3 files changed, 10 insertions(+), 21 deletions(-) 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 a24a95773e..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 @@ -139,19 +139,13 @@ object CallGraph extends ProjectAnalysisApplication { PropertyStoreKey, (context: List[PropertyStoreContext[AnyRef]]) ⇒ { implicit val lg: LogContext = project.logContext - /*val threads = numThreads.getOrElse(0) // We chose the sequential store as default + val threads = numThreads.getOrElse(0) // We chose the sequential store as default 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: _*) - }*/ - org.opalj.fpcf.par.PKECPropertyStore(context: _*) - //org.opalj.fpcf.par.DHTPropertyStore(context: _*) - //org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) - //org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = 4 - //org.opalj.fpcf.par.PKECPropertyStore(context: _*) + } } ) 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 36903beb7c..0d2a1baa61 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 @@ -194,18 +194,12 @@ object Purity { PropertyStoreKey, (context: List[PropertyStoreContext[AnyRef]]) ⇒ { implicit val lg: LogContext = project.logContext - /*if (numThreads == 0) { + 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: _*) - }*/ - org.opalj.fpcf.par.PKECPropertyStore(context: _*) - //org.opalj.fpcf.par.DHTPropertyStore(context: _*) - //org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) - //org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = 4 - //org.opalj.fpcf.par.PKECPropertyStore(context: _*) + } } ) 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 8d5596adb4..defbdbe642 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 @@ -23,6 +23,7 @@ import org.opalj.fpcf.PropertyKey.fallbackPropertyBasedOnPKId */ class PKECPropertyStore( final val ctx: Map[Class[_], AnyRef], + val THREAD_COUNT: Int, override val MaxEvaluationDepth: Int )( implicit @@ -31,8 +32,6 @@ class PKECPropertyStore( implicit val propertyStore: PKECPropertyStore = this - val THREAD_COUNT = 4 - val taskManager: PKECTaskManager = PKECFIFOTaskManager var evaluationDepth: Int = 0 @@ -1032,6 +1031,8 @@ object PKECPropertyStore extends PropertyStoreFactory[PKECPropertyStore] { final val MaxEvaluationDepthKey = "org.opalj.fpcf.par.PKECPropertyStore.MaxEvaluationDepth" + @volatile var MaxThreads = org.opalj.concurrent.NumberOfThreadsForCPUBoundTasks + def apply( context: PropertyStoreContext[_ <: AnyRef]* )( @@ -1048,7 +1049,7 @@ object PKECPropertyStore extends PropertyStoreFactory[PKECPropertyStore] { val maxEvaluationDepth = config.getInt(MaxEvaluationDepthKey) - val ps = new PKECPropertyStore(contextMap, maxEvaluationDepth) + val ps = new PKECPropertyStore(contextMap, MaxThreads, maxEvaluationDepth) ps } } \ No newline at end of file From c875b31a424acfd7886391373196595e483d348a Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Thu, 7 May 2020 11:17:19 +0200 Subject: [PATCH 132/327] Clean up task managers --- .../opalj/fpcf/par/PKECPropertyStore.scala | 120 +++++++++--------- 1 file changed, 60 insertions(+), 60 deletions(-) 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 defbdbe642..ad0687e80b 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 @@ -22,8 +22,8 @@ import org.opalj.fpcf.PropertyKey.fallbackPropertyBasedOnPKId * @author Dominik Helm */ class PKECPropertyStore( - final val ctx: Map[Class[_], AnyRef], - val THREAD_COUNT: Int, + final val ctx: Map[Class[_], AnyRef], + val THREAD_COUNT: Int, override val MaxEvaluationDepth: Int )( implicit @@ -32,7 +32,7 @@ class PKECPropertyStore( implicit val propertyStore: PKECPropertyStore = this - val taskManager: PKECTaskManager = PKECFIFOTaskManager + val taskManager: PKECTaskManager = PKECNoPriorityTaskManager var evaluationDepth: Int = 0 @@ -464,7 +464,7 @@ class PKECPropertyStore( private[this] val activeTasks = new AtomicInteger(0) private[this] val threads: Array[PKECThread] = Array.fill(THREAD_COUNT) { null } - private[this] def startThreads(thread: (Int) ⇒ PKECThread): Unit = { + private[this] def startThreads(thread: Int ⇒ PKECThread): Unit = { var tId = 0 while (tId < THREAD_COUNT) { val t = thread(tId) @@ -734,18 +734,11 @@ class PKECPropertyStore( } class ContinuationTask( - depender: EPKState, oldDependee: SomeEOptionP, dependeeState: EPKState + depender: EPKState, oldDependee: SomeEOptionP, dependee: EPKState ) extends QualifiedTask { scheduledOnUpdateComputations.incrementAndGet() - val priority = 0 /*{ - val dependerState = ps(depender.pk.id).get(depender.e) - val dependerDependees = if (dependerState == null) null else dependerState.dependees - val dependerDependeesSize = if (dependerDependees == null) 0 else dependerDependees.size - - val dependeeDependersSize = dependeeState.dependers.size() + dependeeState.suppressedDependers.size() - taskManager.weight(depender, oldDependee, dependerDependeesSize, dependeeDependersSize) - }*/ + val priority = taskManager.weight(depender, dependee) override def apply(): Unit = { if (depender ne null) { @@ -786,8 +779,8 @@ case class EPKState( dependers.synchronized { eOptP = finalEP - notifyAndClearDependers(finalEP, theEOptP, dependers, unnotifiedPKs) - notifyAndClearDependers(finalEP, theEOptP, suppressedDependers, unnotifiedPKs) + notifyAndClearDependers(theEOptP, dependers, unnotifiedPKs) + notifyAndClearDependers(theEOptP, suppressedDependers, unnotifiedPKs) } } dependees = null @@ -813,7 +806,7 @@ case class EPKState( dependers.synchronized { eOptP = interimEP - notifyAndClearDependers(interimEP, theEOptP, dependers) + notifyAndClearDependers(theEOptP, dependers) } } c = newC @@ -837,7 +830,7 @@ case class EPKState( dependers.synchronized { eOptP = interimEP - notifyAndClearDependers(interimEP, theEOptP, dependers) + notifyAndClearDependers(theEOptP, dependers) } interimEP case _ ⇒ @@ -881,7 +874,6 @@ case class EPKState( } def notifyAndClearDependers( - theEOptP: SomeEPS, oldEOptP: SomeEOptionP, theDependers: java.util.HashSet[EPKState], unnotifiedPKs: Set[PropertyKind] = Set.empty @@ -913,20 +905,39 @@ case class EPKState( trait PKECTaskManager { def weight( - taskEPK: SomeEPK, - updatedEOptionP: SomeEOptionP, // the current eOptionP to which the task is related - updatedEOptionPDependees: Int, // the dependees of the eOptionP - currentDependersOfUpdatedEOptionP: Int + 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( - taskEPK: SomeEPK, - updatedEOptionP: SomeEOptionP, - updatedEOptionPDependees: Int, - currentDependersOfUpdatedEOptionP: Int + depender: EPKState, + dependee: EPKState ): Int = { counter.getAndIncrement() } @@ -936,10 +947,8 @@ case object PKECLIFOTaskManager extends PKECTaskManager { val counter = new AtomicInteger(Int.MaxValue) override def weight( - taskEPK: SomeEPK, - updatedEOptionP: SomeEOptionP, - updatedEOptionPDependees: Int, - currentDependersOfUpdatedEOptionP: Int + depender: EPKState, + dependee: EPKState ): Int = { counter.getAndDecrement() } @@ -949,12 +958,10 @@ case object PKECManyDependeesFirstTaskManager extends PKECTaskManager { val counter = new AtomicInteger(Int.MaxValue) override def weight( - taskEPK: SomeEPK, - updatedEOptionP: SomeEOptionP, - dependeesCount: Int, - deoendersCount: Int + depender: EPKState, + dependee: EPKState ): Int = { - -dependeesCount + -PKECTaskManager.dependeesCount(depender) } } @@ -962,12 +969,10 @@ case object PKECManyDependeesLastTaskManager extends PKECTaskManager { val counter = new AtomicInteger(Int.MaxValue) override def weight( - taskEPK: SomeEPK, - updatedEOptionP: SomeEOptionP, - dependeesCount: Int, - deoendersCount: Int + depender: EPKState, + dependee: EPKState ): Int = { - dependeesCount + PKECTaskManager.dependeesCount(depender) } } @@ -975,12 +980,10 @@ case object PKECManyDependersFirstTaskManager extends PKECTaskManager { val counter = new AtomicInteger(Int.MaxValue) override def weight( - taskEPK: SomeEPK, - updatedEOptionP: SomeEOptionP, - dependeesCount: Int, - deoendersCount: Int + depender: EPKState, + dependee: EPKState ): Int = { - -deoendersCount + -PKECTaskManager.dependersCount(dependee) } } @@ -988,12 +991,10 @@ case object PKECManyDependersLastTaskManager extends PKECTaskManager { val counter = new AtomicInteger(Int.MaxValue) override def weight( - taskEPK: SomeEPK, - updatedEOptionP: SomeEOptionP, - dependeesCount: Int, - deoendersCount: Int + depender: EPKState, + dependee: EPKState ): Int = { - deoendersCount + PKECTaskManager.dependersCount(dependee) } } @@ -1001,12 +1002,12 @@ case object PKECManyDependenciesFirstTaskManager extends PKECTaskManager { val counter = new AtomicInteger(Int.MaxValue) override def weight( - taskEPK: SomeEPK, - updatedEOptionP: SomeEOptionP, - dependeesCount: Int, - deoendersCount: Int + depender: EPKState, + dependee: EPKState ): Int = { - -(Math.max(1, deoendersCount) * Math.max(dependeesCount, 1)) + + -(Math.max(1, PKECTaskManager.dependersCount(dependee)) * + Math.max(PKECTaskManager.dependeesCount(depender), 1)) } } @@ -1014,12 +1015,11 @@ case object PKECManyDependenciesLastTaskManager extends PKECTaskManager { val counter = new AtomicInteger(Int.MaxValue) override def weight( - taskEPK: SomeEPK, - updatedEOptionP: SomeEOptionP, - dependeesCount: Int, - deoendersCount: Int + depender: EPKState, + dependee: EPKState ): Int = { - Math.max(1, deoendersCount) * Math.max(dependeesCount, 1) + Math.max(1, PKECTaskManager.dependersCount(dependee)) * + Math.max(PKECTaskManager.dependeesCount(depender), 1) } } From f2856d801ae8f1020a6bc1de3fdaed90304f5276 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Thu, 7 May 2020 15:05:49 +0200 Subject: [PATCH 133/327] Fixing some minor problems --- .../org/opalj/fpcf/par/PKECPropertyStore.scala | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) 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 ad0687e80b..4271a19294 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 @@ -295,14 +295,13 @@ class PKECPropertyStore( case PartialResult.id ⇒ val PartialResult(e, pk, u) = r - handlePartialResult(r, u, e, pk) + handlePartialResult(u, e, pk) case InterimPartialResult.id ⇒ val InterimPartialResult(prs, dependees, c) = r prs foreach { pr ⇒ handlePartialResult( - pr, pr.u.asInstanceOf[SomeEOptionP ⇒ Option[SomeInterimEP]], pr.e, pr.pk @@ -368,7 +367,6 @@ class PKECPropertyStore( } private[this] def handlePartialResult( - pr: PropertyComputationResult, update: UpdateComputation[Entity, Property], e: Entity, pk: PropertyKey[Property] @@ -738,12 +736,11 @@ class PKECPropertyStore( ) extends QualifiedTask { scheduledOnUpdateComputations.incrementAndGet() - val priority = taskManager.weight(depender, dependee) + val priority: Int = taskManager.weight(depender, dependee) override def apply(): Unit = { if (depender ne null) { - val isSuppressed = suppressInterimUpdates(depender.eOptP.pk.id)(oldDependee.pk.id) - depender.applyContinuation(oldDependee, isSuppressed) + depender.applyContinuation(oldDependee) } } } @@ -888,7 +885,7 @@ case class EPKState( theDependers.clear() } - def applyContinuation(oldDependee: SomeEOptionP, isSuppressed: Boolean)(implicit ps: PKECPropertyStore): Unit = { + def applyContinuation(oldDependee: SomeEOptionP)(implicit ps: PKECPropertyStore): Unit = { this.synchronized { val theDependees = dependees // We are still interessted in that dependee? @@ -1031,7 +1028,7 @@ object PKECPropertyStore extends PropertyStoreFactory[PKECPropertyStore] { final val MaxEvaluationDepthKey = "org.opalj.fpcf.par.PKECPropertyStore.MaxEvaluationDepth" - @volatile var MaxThreads = org.opalj.concurrent.NumberOfThreadsForCPUBoundTasks + @volatile var MaxThreads: Int = org.opalj.concurrent.NumberOfThreadsForCPUBoundTasks def apply( context: PropertyStoreContext[_ <: AnyRef]* From 7a08da40a4d052ef15f600e87243f9737bbf22a8 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Thu, 7 May 2020 15:15:22 +0200 Subject: [PATCH 134/327] Fixed test suite, tests still failing, though --- .../test/scala/org/opalj/fpcf/par/PKECPropertyStoreTest.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 5cc61083eb..ceb83045f2 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 @@ -15,7 +15,7 @@ class PKECPropertyStoreTestWithDebugging extends AbstractPKECPropertyStoreTestWithDebugging { def createPropertyStore(): PKECPropertyStore = { - val ps = new PKECPropertyStore(Map.empty) + val ps = PKECPropertyStore() ps.suppressError = true ps } @@ -33,7 +33,7 @@ class PKECPropertyStoreTestWithoutDebugging extends AbstractPKECPropertyStoreTestWithoutDebugging { def createPropertyStore(): PKECPropertyStore = { - val ps = new PKECPropertyStore(Map.empty) + val ps = PKECPropertyStore() ps.suppressError = true ps } From 95c21562fa1d45a860abc95c050b4063c4ed817e Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Fri, 8 May 2020 11:43:18 +0200 Subject: [PATCH 135/327] Propagate final updates under suppression --- .../org/opalj/fpcf/par/PKECPropertyStore.scala | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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 4271a19294..806f22b04e 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 @@ -739,9 +739,7 @@ class PKECPropertyStore( val priority: Int = taskManager.weight(depender, dependee) override def apply(): Unit = { - if (depender ne null) { - depender.applyContinuation(oldDependee) - } + depender.applyContinuation(oldDependee) } } @@ -777,7 +775,7 @@ case class EPKState( eOptP = finalEP notifyAndClearDependers(theEOptP, dependers, unnotifiedPKs) - notifyAndClearDependers(theEOptP, suppressedDependers, unnotifiedPKs) + notifyAndClearDependers(finalEP, suppressedDependers, unnotifiedPKs) } } dependees = null @@ -850,7 +848,10 @@ case class EPKState( // AND that update must not be suppressed (either final or not a suppressed PK). if ((theEOptP ne dependee) && (theEOptP.isFinal || !suppressedPKs(dependeePK))) { - ps.scheduleTask(new ps.ContinuationTask(depender, dependee, this)) + if (theEOptP.isFinal) + ps.scheduleTask(new ps.ContinuationTask(depender, theEOptP, this)) + else + ps.scheduleTask(new ps.ContinuationTask(depender, dependee, this)) false } else { if (suppressedPKs(dependeePK)) { @@ -889,7 +890,8 @@ case class EPKState( this.synchronized { val theDependees = dependees // We are still interessted in that dependee? - if (theDependees != null && theDependees.contains(oldDependee)) { + 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 From 4347038f8205af3c510e5680821056e2a3e7fd04 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Mon, 11 May 2020 10:27:01 +0200 Subject: [PATCH 136/327] Minor improvements/fixes --- OPAL/si/src/main/resources/reference.conf | 2 +- .../main/scala/org/opalj/fpcf/PropertyComputationResult.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OPAL/si/src/main/resources/reference.conf b/OPAL/si/src/main/resources/reference.conf index 587e154187..9d3f08204c 100644 --- a/OPAL/si/src/main/resources/reference.conf +++ b/OPAL/si/src/main/resources/reference.conf @@ -4,7 +4,7 @@ org.opalj { // to support the debugging of analyses developed using the property store. // I.e., debug performs a wide range of additionaly checks to identify errors as // early as possible. - fpcf.PropertyStore.Debug = true + fpcf.PropertyStore.Debug = false fpcf.PropertyStore.TraceFallbacks = false fpcf.PropertyStore.TraceSuppressedNotifications = false 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 aae67555a1..ca4fceb4a0 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: Set[SomeEOptionP], //IMPROVE: require sets or EOptionPSets + val dependees: Set[SomeEOptionP], //IMPROVE: require EOptionPSets? val c: ProperOnUpdateContinuation ) extends ProperPropertyComputationResult { result ⇒ @@ -388,7 +388,7 @@ object PartialResult { private[fpcf] final val id = 6 } */ case class InterimPartialResult[SE >: Null <: Property]( us: Traversable[SomePartialResult], // can be empty! - dependees: Set[SomeEOptionP], //IMPROVE: require sets or EOptionPSets + dependees: Set[SomeEOptionP], //IMPROVE: require EOptionPSets? c: OnUpdateContinuation ) extends ProperPropertyComputationResult { From 5936e5e9bafad2f9a126945f355189af8c8c5115 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Mon, 11 May 2020 10:27:11 +0200 Subject: [PATCH 137/327] More extensive testing --- .../fpcf/par/PKECPropertyStoreTest.scala | 114 +++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) 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 ceb83045f2..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 @@ -15,7 +15,62 @@ class PKECPropertyStoreTestWithDebugging extends AbstractPKECPropertyStoreTestWithDebugging { def createPropertyStore(): PKECPropertyStore = { - val ps = PKECPropertyStore() + val ps = new PKECPropertyStore(Map.empty, 8, 32) + ps.suppressError = true + ps + } + +} + +class PKECPropertyStoreTestWithDebuggingSingleThreaded + extends AbstractPKECPropertyStoreTestWithDebugging { + + def createPropertyStore(): PKECPropertyStore = { + val ps = new PKECPropertyStore(Map.empty, 1, 32) + ps.suppressError = true + ps + } + +} + +class PKECPropertyStoreTestWithDebuggingNoLocalEvaluation + extends AbstractPKECPropertyStoreTestWithDebugging { + + def createPropertyStore(): PKECPropertyStore = { + val ps = new PKECPropertyStore(Map.empty, 8, 0) + ps.suppressError = true + ps + } + +} + +class PKECPropertyStoreTestWithDebuggingSingleThreadedNoLocalEvaluation + extends AbstractPKECPropertyStoreTestWithDebugging { + + def createPropertyStore(): PKECPropertyStore = { + val ps = new PKECPropertyStore(Map.empty, 1, 0) + ps.suppressError = true + ps + } + +} + +class PKECPropertyStoreTestWithDebugging128Threads + extends AbstractPKECPropertyStoreTestWithDebugging { + + def createPropertyStore(): PKECPropertyStore = { + 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 } @@ -33,7 +88,62 @@ class PKECPropertyStoreTestWithoutDebugging extends AbstractPKECPropertyStoreTestWithoutDebugging { def createPropertyStore(): PKECPropertyStore = { - val ps = 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 = new PKECPropertyStore(Map.empty, 1, 0) + ps.suppressError = true + ps + } + +} + +class PKECPropertyStoreTestWithoutDebugging128Threads + extends AbstractPKECPropertyStoreTestWithoutDebugging { + + def createPropertyStore(): PKECPropertyStore = { + val ps = new PKECPropertyStore(Map.empty, 128, 32) + ps.suppressError = true + ps + } + +} + +class PKECPropertyStoreTestWithoutDebugging128ThreadsNoLocalEvaluation + extends AbstractPKECPropertyStoreTestWithoutDebugging { + + def createPropertyStore(): PKECPropertyStore = { + val ps = new PKECPropertyStore(Map.empty, 128, 0) ps.suppressError = true ps } From 715d567f0dc883e65ee3e21539a9eb59fdc0cfc3 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Mon, 11 May 2020 11:34:08 +0200 Subject: [PATCH 138/327] Formatting & ScalaDoc --- .../scala/org/opalj/br/fpcf/properties/cg/Callees.scala | 4 ++-- .../org/opalj/br/instructions/ClassFileFactory.scala | 4 ++-- .../main/scala/org/opalj/util/PerformanceEvaluation.scala | 8 ++++---- .../tac/fpcf/analyses/cg/AbstractCallGraphAnalysis.scala | 2 +- .../fpcf/analyses/cg/DoPrivilegedPointsToCGAnalysis.scala | 2 +- .../fpcf/analyses/cg/xta/PropagationBasedCGState.scala | 2 +- .../analyses/pointsto/ArraycopyPointsToAnalysis.scala | 2 +- .../fpcf/analyses/pointsto/UnsafePointsToAnalysis.scala | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) 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/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/xta/PropagationBasedCGState.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/PropagationBasedCGState.scala index 2b427f046c..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 @@ -106,6 +106,6 @@ class PropagationBasedCGState( } override def dependees: Set[SomeEOptionP] = { - super.dependees ++ _instantiatedTypesDependeeMap.valuesIterator + super.dependees ++ _instantiatedTypesDependeeMap.valuesIterator } } \ No newline at end of file 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/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 */ From 2b8e7d3cca2d7417bf6c9cb10d24bf41946e36ae Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Wed, 13 May 2020 10:47:31 +0200 Subject: [PATCH 139/327] Prevent threads from terminating while there are initial tasks --- .../opalj/fpcf/par/PKECPropertyStore.scala | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) 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 806f22b04e..bdfce92925 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 @@ -485,6 +485,8 @@ class PKECPropertyStore( 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 { @@ -496,14 +498,14 @@ class PKECPropertyStore( startThreads(new FallbackThread(_)) - continueFallbacks = !queues.forall(_.isEmpty) + continueFallbacks = activeTasks.get() != 0 //!queues.forall(_.isEmpty) } while (continueFallbacks) startThreads(new CycleResolutionThread(_)) resolveCycles() - continueCycles = !queues.forall(_.isEmpty) + continueCycles = activeTasks.get() != 0 //!queues.forall(_.isEmpty) } while (continueCycles) startThreads(new PartialPropertiesFinalizerThread(_)) @@ -537,13 +539,6 @@ class PKECPropertyStore( for (cSCC ← cSCCs) { for (interimEPKState ← cSCC) { interimEPKState.dependees = null - /*val dependees = interimEPKState.dependees - dependees.foreach { dependee ⇒ - // during execution, no other thread accesses the dependers of the EPKState - val dependeeState = ps(dependee.pk.id).get(dependee.e) - dependeeState.dependers.remove(interimEPKState) - dependeeState.suppressedDependers.remove(interimEPKState) - }*/ scheduleTask(new SetTask(interimEPKState.eOptP.toFinalEP)) } } @@ -556,10 +551,12 @@ class PKECPropertyStore( 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) { @@ -567,10 +564,6 @@ class PKECPropertyStore( if (tasks.isEmpty) { val active = activeTasks.get() if (active == 0) { - threads.foreach { t ⇒ - if (t ne this) - t.interrupt() - } return ; } else { // try workstealing: From ab806b5f2ca0ffbb9535c57b2fbf57015f1f2113 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 5 Jun 2020 11:32:54 +0200 Subject: [PATCH 140/327] first try to solve lazy initialization test errors: by reading all fields instead of all projectfiles fields --- .../tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index 0f37c15118..d8a2cc517d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -1114,7 +1114,7 @@ object EagerL0ReferenceImmutabilityAnalysis final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allProjectClassFiles.toIterator.flatMap { _.fields } + val fields = p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) analysis } From f797096af9983bfd42597ae4a8619178e33e476f Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 5 Jun 2020 12:31:12 +0200 Subject: [PATCH 141/327] second try to solve lazy initialization test errors: reverting the ref im tests to see if there was a state where it worked --- .../fpcf/ReferenceImmutabilityTests.scala | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index 68262f4ede..c235785d31 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -6,17 +6,11 @@ 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.LazyClassImmutabilityAnalysis -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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** @@ -48,44 +42,10 @@ class ReferenceImmutabilityTests extends PropertiesTest { LazyL2FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis + LazyInterProceduralEscapeAnalysis ) ) as.propertyStore.shutdown() validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ - } From 5f96b02034d4a6f54b80128f76485940f6690046 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 5 Jun 2020 13:48:39 +0200 Subject: [PATCH 142/327] Adding LazyL0FieldImmutabilityAnalysis --- .../ReferenceImmutabilityAnalysisDemo_withNewPurity.scala | 2 ++ .../opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala | 2 ++ 2 files changed, 4 insertions(+) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala index 9ba5dac4bd..d40820d38a 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala @@ -22,6 +22,7 @@ import java.util.Calendar import org.opalj.br.Field import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis +import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new @@ -60,6 +61,7 @@ object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisAp propertyStore = analysesManager .runAll( EagerL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, LazyLxClassImmutabilityAnalysis_new, LazyLxTypeImmutabilityAnalysis_new, LazyUnsoundPrematurelyReadFieldsAnalysis, diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala index 2ff752b6ed..808d325b7d 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala @@ -7,6 +7,7 @@ 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.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey @@ -44,6 +45,7 @@ class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { val as = executeAnalyses( Set( EagerL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, LazyLxClassImmutabilityAnalysis_new, LazyLxTypeImmutabilityAnalysis_new, LazyUnsoundPrematurelyReadFieldsAnalysis, From d985cd642aa7a336ff0070e4f675d16a96e20dfb Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Mon, 8 Jun 2020 12:41:55 +0200 Subject: [PATCH 143/327] test 3: for finding test errors --- .../L0ReferenceImmutabilityAnalysis.scala | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala index d8a2cc517d..ec105244e7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala @@ -45,8 +45,6 @@ import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.LazyInitializedField import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.NoEscape -import org.opalj.br.fpcf.properties.NotPrematurelyReadField -import org.opalj.br.fpcf.properties.PrematurelyReadField import org.opalj.br.fpcf.properties.ReferenceImmutability import org.opalj.fpcf.EOptionP import org.opalj.fpcf.Entity @@ -1041,17 +1039,22 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec */ def isPrematurelyRead( eop: EOptionP[Field, FieldPrematurelyRead] - )(implicit state: State): Boolean = - eop match { - case LBP(NotPrematurelyReadField) ⇒ - state.prematurelyReadDependee = None - false - case UBP(PrematurelyReadField) ⇒ true - case eps ⇒ - state.prematurelyReadDependee = Some(eps) - false - } + )(implicit state: State): Boolean = { + val s = state.referenceImmutability + s != s //TODO revert + } + /** + * eop match { + * case LBP(NotPrematurelyReadField) ⇒ + * state.prematurelyReadDependee = None + * false + * case UBP(PrematurelyReadField) ⇒ true + * case eps ⇒ + * state.prematurelyReadDependee = Some(eps) + * false + * } * + */ /** * Checkes 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 From 6b35b74002ed0599c6e058e832e58bf6f2d4ad18 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 9 Jun 2020 11:23:51 +0200 Subject: [PATCH 144/327] sandbox test 1 for finding test errors --- .../ExceptionInitialization.java | 26 ++++++++ .../ReferenceImmutabilityTests_sandbox.scala | 63 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_sandbox/ExceptionInitialization.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_sandbox.scala diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_sandbox/ExceptionInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_sandbox/ExceptionInitialization.java new file mode 100644 index 0000000000..b7195baa0d --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_sandbox/ExceptionInitialization.java @@ -0,0 +1,26 @@ +package org.opalj.fpcf.fixtures.reference_immutability_sandbox; + +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +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. + */ + @ImmutableReferenceAnnotation("") + 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; + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_sandbox.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_sandbox.scala new file mode 100644 index 0000000000..31a6ecd358 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_sandbox.scala @@ -0,0 +1,63 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +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.LazyL0FieldImmutabilityAnalysis +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.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new + +/** + * @author Tobias Peter Roth + */ +class ReferenceImmutabilityTests_sandbox extends PropertiesTest { + + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/reference_immutability_sandbox") + } + + 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("ReferenceImmutability")) + } + + describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { + val as = executeAnalyses( + Set( + EagerL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + } +} From 77ebc32630c4c594760563aee2fab06e0b93c8f4 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Mon, 22 Jun 2020 13:36:30 +0200 Subject: [PATCH 145/327] eff deep imm fields introduction; still with debugging printlns --- .../ReferenceImmutabilityAnalysisDemo.scala | 188 +- ...AnalysisDemo_performanceMeasurements.scala | 6 +- ...mutabilityAnalysisDemo_withNewPurity.scala | 30 +- ...bilityLazyInitializationAnalysisDemo.scala | 2 +- ...AnalysisDemo_performanceMeasurements.scala | 2 +- .../ClassWithGenericField_shallow.java | 12 +- .../multinested_genericClasses/One.java | 7 +- .../multinested_genericClasses/OneVirgin.java | 11 +- .../multinested_genericClasses/Two.java | 2 +- .../multinested_genericClasses/TwoVirgin.java | 6 +- .../trivials/ShallowImmutableClass.java | 8 +- .../field/privateFieldNotBlank_shallow.java | 8 +- .../privateFinalFieldNotBlank_shallow.java | 9 +- .../WithMutableAndImmutableFieldType.java | 11 +- .../ExceptionInitialization.java | 26 - .../DeepImmutableClassAnnotation.java | 2 +- .../DependentImmutableClassAnnotation.java | 2 +- .../MutableClassAnnotation.java | 3 +- .../ShallowImmutableClassAnnotation.java | 3 +- .../DeepImmutableFieldAnnotation.java | 2 +- .../DependentImmutableFieldAnnotation.java | 2 +- .../MutableFieldAnnotation.java | 2 +- .../ShallowImmutableFieldAnnotation.java | 2 +- .../ImmutableReferenceAnnotation.java | 2 +- .../LazyInitializedReferenceAnnotation.java | 2 +- .../MutableReferenceAnnotation.java | 2 +- .../NoLazyInitializationAnnotation.java | 2 - ...hreadSafeLazyInitializationAnnotation.java | 2 - .../DeepImmutableTypeAnnotation.java | 2 +- .../DependentImmutableTypeAnnotation.java | 2 +- .../MutableTypeAnnotation.java | 2 +- .../ShallowImmutableTypeAnnotation.java | 2 +- .../opalj/fpcf/ClassImmutabilityTests.scala | 8 +- ...ClassImmutabilityTests_withNewPurity.scala | 8 +- .../opalj/fpcf/FieldImmutabilityTests.scala | 9 +- ...FieldImmutabilityTests_withNewPurity.scala | 8 +- .../org/opalj/fpcf/FieldMutabilityTests.scala | 12 +- .../fpcf/ReferenceImmutabilityTests.scala | 8 +- .../ReferenceImmutabilityTests_sandbox.scala | 8 +- ...renceImmutabilityTests_withNewPurity.scala | 18 +- .../opalj/fpcf/TypeImmutabilityTests.scala | 10 +- .../TypeImmutabilityTests_withNewPurity.scala | 8 +- .../ReferenceImmutabilityMatcher.scala | 12 +- .../properties/ReferenceImmutability.scala | 66 +- .../src/main/scala/org/opalj/tac/Stmt.scala | 3 +- .../L0ReferenceImmutabilityAnalysis.scala | 1151 ----------- .../L0FieldImmutabilityAnalysis.scala | 34 +- ...mutabilityLazyInitializationAnalysis.scala | 13 +- .../LxClassImmutabilityAnalysis_new.scala | 44 +- .../LxTypeImmutabilityAnalysis_new.scala | 36 +- ...bstractReferenceImmutabilityAnalysis.scala | 172 ++ ...mutabilityAnalysisLazyInitialization.scala | 535 +++++ .../L0ReferenceImmutabilityAnalysis.scala | 791 ++++++++ .../purity/AbstractPurityAnalysis_new.scala | 1145 +++++------ .../purity/L2PurityAnalysis_new.scala | 1727 +++++++++-------- 55 files changed, 3302 insertions(+), 2888 deletions(-) delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_sandbox/ExceptionInitialization.java delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/{ => immutability}/L0FieldImmutabilityAnalysis.scala (91%) rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/{ => immutability}/L0ReferenceImmutabilityLazyInitializationAnalysis.scala (99%) rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/{ => immutability}/LxClassImmutabilityAnalysis_new.scala (99%) rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/{ => immutability}/LxTypeImmutabilityAnalysis_new.scala (99%) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index 787e6d462e..a9432a6662 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -8,27 +8,26 @@ 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.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds import org.opalj.util.PerformanceEvaluation.memory -import java.io._ -import java.util.Calendar - import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis 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.properties.ImmutableReference +import org.opalj.fpcf.SomeEPS import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis /** * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. @@ -37,100 +36,117 @@ import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis */ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + 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 memoryConsumption: Long = 0 - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - memory { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + def analyze(project: Project[URL]): String = { + var memoryConsumption: Long = 0 + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + memory { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyFieldLocalityAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion() + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyFieldLocalityAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion() - } { t ⇒ - analysisTime = t.toSeconds - } - } { mu ⇒ - memoryConsumption = mu - } - var sb: StringBuilder = new StringBuilder() - sb = sb.append("Mutable References: \n") - val mutableReferences = propertyStore.finalEntities(MutableReference).toList - sb = sb.append( - mutableReferences.map(x ⇒ x.toString+"\n").toString() - ) + } { t => + analysisTime = t.toSeconds + } + } { mu => + memoryConsumption = mu + } + var sb: StringBuilder = new StringBuilder() + sb = sb.append("Mutable References: \n") + val mutableReferences = propertyStore.finalEntities(MutableReference).toList + sb = sb.append( + mutableReferences.mkString(", \n") + ) - sb = sb.append("\n Lazy Initialized Reference: \n") - val lazyInitializedReferences = propertyStore - .finalEntities(LazyInitializedReference) - .toList - sb = sb.append( - lazyInitializedReferences - .map(x ⇒ x.toString+"\n") - .toString() - ) + sb = sb.append("\n Lazy Initialized Reference: \n") + val lazyInitializedReferences = propertyStore + .finalEntities(LazyInitializedReference) + .toList + sb = sb.append( + lazyInitializedReferences.mkString(", \n") + ) - sb = sb.append("\nImmutable References: \n") - val immutableReferences = propertyStore.finalEntities(ImmutableReference).toList - sb = sb.append( - immutableReferences.map(x ⇒ x.toString+"\n").toString() - ) - sb.append( - s""" + val immutableReferencesTrue = propertyStore.entities({ eps: SomeEPS => + eps.ub match { + case ImmutableReference(true) => true + case _ => false + } + }) + val immutableReferencesFalse = propertyStore.entities({ eps: SomeEPS => + eps.ub match { + case ImmutableReference(false) => true + case _ => false + } + }) + + sb = sb.append( + s""" + | imm ref true: + |${immutableReferencesTrue.mkString(", \n")} + | + | + | imm ref false: + | ${immutableReferencesFalse.mkString(", \n")} + |""".stripMargin + ) + + sb.append( + s""" | mutable References: ${mutableReferences.size} | lazy initialized References: ${lazyInitializedReferences.size} - | immutable References: ${immutableReferences.size} + | immutable References: ${} | | took : $analysisTime seconds | needed: ${memoryConsumption / 1024 / 1024} MBytes | |""".stripMargin - ) + ) - val calendar = Calendar.getInstance() - val file = new File( - s"C:/MA/results/refImm_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() - s""" - | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes - |""".stripMargin - - } + /* val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results/refImm_${calendar.get(Calendar.YEAR)}_" + + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_" + + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_" + + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() **/ + //s""" + // | took : $analysisTime seconds + // | needs : ${memoryConsumption / 1024 / 1024} MBytes + // |""".stripMargin + sb.toString() + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala index daa413a9e6..b92a36f5bb 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -16,12 +16,12 @@ import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala index d40820d38a..23f13f9118 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala @@ -8,12 +8,10 @@ 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.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -22,12 +20,13 @@ import java.util.Calendar import org.opalj.br.Field import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis -import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** @@ -70,7 +69,8 @@ object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisAp LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis + LazyFieldLocalityAnalysis, + LazyLxTypeImmutabilityAnalysis_new ) ._1 propertyStore.waitOnPhaseCompletion() @@ -103,18 +103,20 @@ object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisAp ) sb = sb.append("\nImmutable References: \n") - val immutableReferences = propertyStore - .finalEntities(ImmutableReference) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - sb = sb.append( - immutableReferences.map(x ⇒ x.toString+"\n").toString() - ) + + /** + * val immutableReferences = propertyStore + * .filter(x => allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) + * .toList + * sb = sb.append( + * immutableReferences.map(x => x.toString + "\n").toString() + * )* + */ sb.append( s""" | mutable References: ${mutableReferences.size} | lazy initialized References: ${lazyInitializedReferences.size} - | immutable References: ${immutableReferences.size} + | immutable References: ${} | | took : $analysisTime seconds | diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala index 4daa6d1b84..f0d97dcd47 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala @@ -22,10 +22,10 @@ import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala index f8dc592e0c..cafd2bb0e1 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala @@ -15,10 +15,10 @@ import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java index b4742d889e..6029193461 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java @@ -1,14 +1,14 @@ package org.opalj.fpcf.fixtures.immutability.classes.generic; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; -@ShallowImmutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") public final class ClassWithGenericField_shallow { - @ShallowImmutableFieldAnnotation("") + @DeepImmutableFieldAnnotation("") @ImmutableReferenceAnnotation("") private Generic_class1 gc = new Generic_class1 diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java index 1c04d515d3..1c38425c0c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java @@ -30,7 +30,12 @@ public class One { @ImmutableReferenceAnnotation("") private Generic_class1 gc1; public One(A a, B b, C c, D d, TrivialMutableClass tmc){ - gc1 = new Generic_class1(a,b,c,d, tmc); + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.tmc = tmc; + this.gc1 = new Generic_class1(this.a,this.b,this.c,this.d, this.tmc); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java index 74a5b8b7cb..8e3b7f1d29 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java @@ -27,9 +27,14 @@ public class OneVirgin { @MutableReferenceAnnotation("") TrivialMutableClass tmc; - Generic_class1 gc1; - public OneVirgin(A a, B b, C c, D d, E e){ - gc1 = new Generic_class1(a, b, c, d, e); + Generic_class1 gc1; + public OneVirgin(A a, B b, C c, D d, TrivialMutableClass e){ + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.tmc = tmc; + this.gc1 = new Generic_class1(this.a, this.b, this.c, this.d, this.tmc); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java index 1697439f37..8b0998cf0d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java @@ -14,7 +14,7 @@ public class Two { private Generic_class1, B, B, B, TrivialMutableClass> gc1; public Two(A a, B b, TrivialMutableClass tmc, Generic_class1 gc1) { - gc1 = new Generic_class1, B, B, B, TrivialMutableClass>(gc1,b,b,b,tmc); + this.gc1 = new Generic_class1, B, B, B, TrivialMutableClass>(gc1,b,b,b,tmc); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java index 4423ad6b5e..d7ba861186 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java @@ -8,10 +8,10 @@ public class TwoVirgin { @DependentImmutableFieldAnnotation(value="",genericString = "") @ImmutableReferenceAnnotation("") - private Generic_class1, B, C, D, E> gc1; + private Generic_class1, B, C, C, C> gc1; - public TwoVirgin(A a, B b, C c, Generic_class1 gc1) { - gc1 = new Generic_class1, B, B, B, C>(gc1,b,b,b,c); + public TwoVirgin(A a, B b, C c, Generic_class1 gc1) { + this.gc1 = new Generic_class1, B, C, C, C>(gc1,b,c,c,c); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java index ed2d8b13d2..130dfce68b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java @@ -1,14 +1,14 @@ package org.opalj.fpcf.fixtures.immutability.classes.trivials; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("Because of not final class") -@ShallowImmutableClassAnnotation("has shallow immutable field") +@DeepImmutableClassAnnotation("has shallow immutable field") public class ShallowImmutableClass { - @ShallowImmutableFieldAnnotation("Because of mutable type") + @DeepImmutableFieldAnnotation("Because of mutable type") @ImmutableReferenceAnnotation("Because it is private") private MutableClass mutableClass = new MutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java index ddf2ed4b11..a96a595112 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java @@ -1,15 +1,15 @@ package org.opalj.fpcf.fixtures.immutability.field; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("Because of not final class") -@ShallowImmutableClassAnnotation("Because it has only Shallow Immutable Fields") +@DeepImmutableClassAnnotation("Because it has only Shallow Immutable Fields") public class privateFieldNotBlank_shallow { - @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") + @DeepImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") @ImmutableReferenceAnnotation("Effectively Immutable Reference") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java index f68b8042d7..f010426abe 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java @@ -1,13 +1,14 @@ package org.opalj.fpcf.fixtures.immutability.field; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -@ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") +@DeepImmutableClassAnnotation("It has only Shallow Immutable Fields") public class privateFinalFieldNotBlank_shallow { - @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") + @DeepImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") @ImmutableReferenceAnnotation("Declared final Field") private final TrivialMutableClass tmc = new TrivialMutableClass(); } 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 index 9bfee1f8d2..2b279385cf 100644 --- 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 @@ -1,21 +1,20 @@ package org.opalj.fpcf.fixtures.immutability.type; import org.opalj.fpcf.fixtures.immutability.field.TrivialMutableClass; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; -@ShallowImmutableTypeAnnotation("has shallow and mutable fields") -@ShallowImmutableClassAnnotation("has shallow and immutable fields") +@DeepImmutableTypeAnnotation("has shallow and mutable fields") +@DeepImmutableClassAnnotation("has shallow and immutable fields") public final class WithMutableAndImmutableFieldType { @DeepImmutableFieldAnnotation("immutable reference and deep immutable type") @ImmutableReferenceAnnotation("private field") private FinalEmptyClass fec = new FinalEmptyClass(); - @ShallowImmutableFieldAnnotation("imm reference and mutable type") + @DeepImmutableFieldAnnotation("imm reference and mutable type") @ImmutableReferenceAnnotation("private field") private TrivialMutableClass tmc = new TrivialMutableClass(); diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_sandbox/ExceptionInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_sandbox/ExceptionInitialization.java deleted file mode 100644 index b7195baa0d..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/reference_immutability_sandbox/ExceptionInitialization.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.opalj.fpcf.fixtures.reference_immutability_sandbox; - -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; - -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. - */ - @ImmutableReferenceAnnotation("") - 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; - } -} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java index 36557c4675..8ca5c1ed51 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.LxClassImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java index 0e60482695..3c39d38b6b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.LxClassImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java index 73e6289a9d..fc5cd17938 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java @@ -3,8 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; -import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.LxClassImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java index 92812b409f..89972bb2b9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java @@ -3,8 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.fpcf.properties.class_mutability.MutableObjectMatcher; -import org.opalj.tac.fpcf.analyses.LxClassImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.LxClassImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java index 05c2833d38..c9712da753 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java @@ -2,7 +2,7 @@ package org.opalj.fpcf.properties.field_immutability; import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L0FieldImmutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import java.lang.annotation.Documented; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java index ed0bc13b0e..ab97301e87 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java @@ -2,7 +2,7 @@ package org.opalj.fpcf.properties.field_immutability; import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L0FieldImmutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import java.lang.annotation.Documented; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java index b62adbe54f..15aeac615e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java @@ -2,7 +2,7 @@ package org.opalj.fpcf.properties.field_immutability; import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L0FieldImmutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java index c522fc6f5d..5b39e6b03e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java @@ -2,7 +2,7 @@ package org.opalj.fpcf.properties.field_immutability; import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.L0FieldImmutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java index c4b12d7c0a..15f389d409 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.reference.L0ReferenceImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java index 45b58b85ab..ab2bdefdf5 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.reference.L0ReferenceImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java index 1adf72d4b7..2723ec0c23 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.reference.L0ReferenceImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java index 3a7eb903b5..74a85e9600 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java @@ -3,8 +3,6 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceMatcher; -import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; import java.lang.annotation.Documented; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java index d26cfd01e9..4154597d3a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java @@ -3,8 +3,6 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceMatcher; -import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityAnalysis; import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; import java.lang.annotation.Documented; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java index 3400216ea3..c5aae8139e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.LxTypeImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.LxTypeImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java index 2781c70abb..c13b6b40b5 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.LxTypeImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.LxTypeImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java index a31dc77bcb..692c4adaf7 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.LxTypeImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.LxTypeImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java index 6bea537699..6b42c1ab61 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.LxTypeImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.LxTypeImmutabilityAnalysis_new; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala index 65ef2c643a..371a11b6d8 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -8,18 +8,18 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.Project import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis -import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala index 2a18d034f6..064907abef 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala @@ -7,16 +7,16 @@ 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.LazyL0FieldImmutabilityAnalysis 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.EagerLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** 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 index 0e37acfaf2..2303f029ad 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -6,7 +6,6 @@ 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.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis @@ -14,12 +13,13 @@ import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis 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.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** @@ -61,7 +61,6 @@ class FieldImmutabilityTests extends PropertiesTest { LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis - ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala index 70c0bf6b77..02588c7884 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala @@ -6,17 +6,17 @@ 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.EagerL0FieldImmutabilityAnalysis 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.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala index e5414be3be..f7047444ca 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala @@ -9,15 +9,15 @@ 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.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis 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.EagerL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis /** * Tests if the properties specified in the test project (the classes in the (sub-)package of @@ -29,8 +29,8 @@ import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new class FieldMutabilityTests extends PropertiesTest { override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - _ ⇒ Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) } p.get(RTACallGraphKey) } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index c235785d31..abe27a4069 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -8,9 +8,10 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.Project import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** @@ -18,6 +19,8 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis */ class ReferenceImmutabilityTests extends PropertiesTest { + override def withRT = true + override def fixtureProjectPackage: List[String] = { List("org/opalj/fpcf/fixtures/immutability") } @@ -42,7 +45,8 @@ class ReferenceImmutabilityTests extends PropertiesTest { LazyL2FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis + LazyInterProceduralEscapeAnalysis, + LazyLxTypeImmutabilityAnalysis_new ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_sandbox.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_sandbox.scala index 31a6ecd358..683f6b22e4 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_sandbox.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_sandbox.scala @@ -7,16 +7,16 @@ 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.LazyL0FieldImmutabilityAnalysis 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.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala index 808d325b7d..661786cbf1 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala @@ -7,16 +7,17 @@ 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.LazyL0FieldImmutabilityAnalysis 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.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** @@ -24,6 +25,8 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new */ class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { + override def withRT = true + override def fixtureProjectPackage: List[String] = { List("org/opalj/fpcf/fixtures/immutability") } @@ -49,12 +52,15 @@ class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { LazyLxClassImmutabilityAnalysis_new, LazyLxTypeImmutabilityAnalysis_new, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, LazyInterProceduralEscapeAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis + LazyFieldLocalityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyL2PurityAnalysis_new, + LazyInterProceduralEscapeAnalysis, + LazyLxTypeImmutabilityAnalysis_new ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala index d121d1241f..b2f9d2ceb9 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala @@ -8,18 +8,18 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.Project import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis -import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis /** @@ -39,6 +39,7 @@ class TypeImmutabilityTests extends PropertiesTest { } p.get(RTACallGraphKey) } + /** * describe("no analysis is scheduled") { * val as = executeAnalyses(Set.empty) @@ -46,7 +47,6 @@ class TypeImmutabilityTests extends PropertiesTest { * validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) * } */ - describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { println(1) val as = executeAnalyses( diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala index f762d626cb..cbd9142a01 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala @@ -7,16 +7,16 @@ 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.LazyL0FieldImmutabilityAnalysis 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.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala index fa412de678..725777e70a 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala @@ -41,7 +41,15 @@ class ReferenceImmutabilityMatcher(val property: ReferenceImmutability) a: AnnotationLike, properties: Traversable[Property] ): Option[String] = { - if (!properties.exists(p ⇒ p == property)) { + if (!properties.exists(p ⇒ { + val tmpP = { + p match { + case ImmutableReference(_) ⇒ ImmutableReference(true) + case _ ⇒ p + } + } + tmpP == property + })) { // ... when we reach this point the expected property was not found. Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) } else { @@ -52,4 +60,4 @@ class ReferenceImmutabilityMatcher(val property: ReferenceImmutability) class LazyInitializedReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedReference) -class ImmutableReferenceMatcher extends ReferenceImmutabilityMatcher(ImmutableReference) +class ImmutableReferenceMatcher extends ReferenceImmutabilityMatcher(ImmutableReference(true)) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala index c84041e9be..930728dac9 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -9,7 +9,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - type Self = ReferenceImmutability + type Self = ReferenceImmutability } @@ -30,53 +30,57 @@ sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaIn sealed trait ReferenceImmutability extends OrderedProperty with ReferenceImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key + final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key } object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { - final val PropertyKeyName = "opalj.ReferenceImmutability" + var notEscapes: Boolean = false - final val key: PropertyKey[ReferenceImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableReference - ) - } + final val PropertyKeyName = "opalj.ReferenceImmutability" + + final val key: PropertyKey[ReferenceImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableReference + ) + } } -case object ImmutableReference extends ReferenceImmutability { - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: ReferenceImmutability): ReferenceImmutability = - if (this == that) - this - else - that +case class ImmutableReference(notEscapes: Boolean) extends ReferenceImmutability { + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + def meet(that: ReferenceImmutability): ReferenceImmutability = + if (this == that) + this + else + that } case object LazyInitializedReference extends ReferenceImmutability { - def meet(other: ReferenceImmutability): properties.ReferenceImmutability = { - if (other == MutableReference) { - other - } else { - this - } + def meet(other: ReferenceImmutability): properties.ReferenceImmutability = { + if (other == MutableReference) { + other + } else { + this } + } - override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { - if (other == ImmutableReference) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - } + override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { + other match { + case ImmutableReference(_) => + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + case _ => } + } } case object MutableReference extends ReferenceImmutability { - def meet(other: ReferenceImmutability): this.type = this + def meet(other: ReferenceImmutability): this.type = this - override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { - if (other != MutableReference) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other => $this") - } + override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { + if (other != MutableReference) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other => $this") } + } } 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 6c47b88589..f59b319e0b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala @@ -88,6 +88,7 @@ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { def isMonitorEnter: Boolean = false def isMonitorExit: Boolean = false def isPutStatic: Boolean = false + def isPutField: Boolean = false } /** @@ -711,7 +712,7 @@ 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 = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala deleted file mode 100644 index ec105244e7..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityAnalysis.scala +++ /dev/null @@ -1,1151 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package tac -package fpcf -package analyses - -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.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.FieldAccessInformationKey -import org.opalj.br.analyses.SomeProject -import org.opalj.br.analyses.cg.ClosedPackagesKey -import org.opalj.br.analyses.cg.TypeExtensibilityKey -import org.opalj.br.cfg.BasicBlock -import org.opalj.br.cfg.CFG -import org.opalj.br.cfg.CFGNode -import org.opalj.br.fpcf.FPCFAnalysisScheduler -import org.opalj.br.fpcf.properties.EscapeProperty -import org.opalj.br.fpcf.properties.FieldPrematurelyRead -import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.properties.cg.Callees -import org.opalj.ai.isImmediateVMException -import org.opalj.ai.pcOfImmediateVMException -import org.opalj.ai.pcOfMethodExternalException -import org.opalj.br.ClassFile -import org.opalj.br.ComputationalTypeFloat -import org.opalj.br.ComputationalTypeInt -import org.opalj.br.DeclaredMethod -import org.opalj.br.Field -import org.opalj.br.FloatType -import org.opalj.br.Method -import org.opalj.br.PC -import org.opalj.br.PCs -import org.opalj.br.fpcf.properties.AtMost -import org.opalj.br.fpcf.properties.EscapeInCallee -import org.opalj.br.fpcf.properties.EscapeViaReturn -import org.opalj.br.fpcf.properties.ImmutableReference -import org.opalj.br.fpcf.properties.LazyInitializedReference -import org.opalj.br.fpcf.properties.LazyInitializedField -import org.opalj.br.fpcf.properties.MutableReference -import org.opalj.br.fpcf.properties.NoEscape -import org.opalj.br.fpcf.properties.ReferenceImmutability -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.LBP -import org.opalj.fpcf.ProperPropertyComputationResult -import org.opalj.fpcf.Property -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyComputationResult -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.SomeEPS -import org.opalj.fpcf.UBP -import org.opalj.fpcf.Result -import org.opalj.tac.common.DefinitionSite -import org.opalj.tac.common.DefinitionSitesKey -import org.opalj.tac.fpcf.properties.TACAI -import org.opalj.value.ValueInformation - -/** - * - * Implementation is used from the old L2FieldMutability implementation - * but the lattice is mapped to the new reference immutability lattice. - * - * @note Requires that the 3-address code's expressions are not deeply nested. - * - * @author Dominik Helm - * @author Florian Kübler - * @author Michael Eichberg - * @author Tobias Peter Roth - */ -class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProject) - extends FPCFAnalysis { - - case class State( - field: Field, - var referenceImmutability: ReferenceImmutability = ImmutableReference, - 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, - var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, - var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty - ) { - def hasDependees: Boolean = { - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || - escapeDependees.nonEmpty || tacDependees.nonEmpty - } - - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) - } - } - - 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) - - def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field ⇒ determineReferenceImmutability(field) - case _ ⇒ - val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } - - /** - * Analyzes the mutability of private non-final fields. - * - * 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 determineReferenceImmutability( - field: Field - ): ProperPropertyComputationResult = { - implicit val state: State = State(field) - // Fields are not final if they are read prematurely! - if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) - return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); - if (field.isFinal) - return createResult(); - - state.referenceImmutability = ImmutableReference //EffectivelyFinalField - - val thisType = field.classFile.thisType - - if (field.isPublic || field.isPackagePrivate || field.isProtected) - return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) - - // 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) { - if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - } - project.classesPerPackage(thisType.packageName) - } else { - Set(field.classFile) - } - - val classesHavingAccess: Iterator[ClassFile] = - if (field.isProtected) { - if (typeExtensibility(thisType).isYesOrUnknown) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - } - 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))) - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - - // 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 - // of the class after the reference to the class and (an indirect) reference to the - // field has become available. Consider the following example: - // class X implements Y{ - // - // private Object o; - // - // public Object getO() { return o; } - // - // private static X instance; - // static { - // instance = new X(); - // Z.register(instance); - // // when we reach this point o is now (via getO) publically accessible and - // // X is properly initialized! - // o = new Object(); // o is mutated... - // } - // } - - /** - * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) - None - } - } - - for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode ← getTACAI(method, pcs) - } { - if (methodUpdatesField(method, taCode, pcs)) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); - } - - } - - if (state.lazyInitInvocation.isDefined) { - val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - handleCalls(calleesEOP) - } - - createResult() - } - - def handleCalls( - calleesEOP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: State - ): Boolean = { - calleesEOP match { - case FinalP(callees) ⇒ - state.calleesDependee = None - handleCallees(callees) - case InterimUBP(callees) ⇒ - state.calleesDependee = Some(calleesEOP) - handleCallees(callees) - case _ ⇒ - state.calleesDependee = Some(calleesEOP) - false - } - } - - def handleCallees(callees: Callees)(implicit state: State): Boolean = { - val pc = state.lazyInitInvocation.get._2 - if (callees.isIncompleteCallSite(pc)) { - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else { - val targets = callees.callees(pc).toTraversable - if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else false - } - } - - /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. - */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field - * The code below can be used to identify such value, but is not yet adapted to using the - * TACAI property */ - /* - var constantVal: Option[Any] = None - var allInitializeConstant = true - - val field = state.field - var constructors: Set[Method] = - if(field.isStatic) Set.empty else field.classFile.constructors.toSet - - val writesIterator = fieldAccessInformation.writeAccesses(field).iterator - while (writesIterator.hasNext && allInitializeConstant) { - val (method, pc) = writesIterator.next() - constructors -= method - val code = tacai(method).stmts - - val index = pcToIndex(pc) - val stmt = code(index) - if (stmt.astID == PutStatic.ASTID || - stmt.asPutField.objRef.asVar.definedBy == SelfReferenceParameter) { - val write = stmt.asFieldWriteAccessStmt - if (write.resolveField(p).contains(state.field)) { - val defs = write.value.asVar.definedBy - if (defs.size == 1 && defs.head >= 0) { - val defSite = code(defs.head).asAssignment.expr - val const = if (defSite.isIntConst) - Some(defSite.asIntConst.value) - else if (defSite.isFloatConst) - Some(defSite.asFloatConst.value) - else None - if (const.isDefined) { - if (constantVal.isDefined) { - if (constantVal != const) { - allInitializeConstant = false - constantVal = None - } - } else constantVal = const - } else { - allInitializeConstant = false - constantVal = None - } - } - } - } - } - - for (constructor ← constructors) { - // TODO iterate all statements - val NonVirtualMethodCall(_, declClass, _, name, _, rcvr, _) = stmt - // Consider calls to other constructors as initializations as either - // the called constructor will initialize the field or delegate to yet - // another constructor - if (declClass != state.field.classFile.thisType || name != "" || - rcvr.asVar.definedBy != SelfReferenceParameter) { - if (constantVal.isDefined) allInitializeConstant = false - else constantVal = Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - } - } - - constantVal */ - } - - /** - * 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 MutableReference)) //NonFinalFieldByAnalysis)) - InterimResult( - state.field, - MutableReference, //NonFinalFieldByAnalysis, - 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 = { - val isNotFinal = eps.pk match { - case EscapeProperty.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - 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)) - methodUpdatesField(method, newEP.ub.tac.get, pcs) - case Callees.key ⇒ - handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key ⇒ - isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] - state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) - isNonDeterministic(newEP) - case ReferenceImmutability.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] - state.referenceImmutabilityDependees = - state.referenceImmutabilityDependees.filter(_.e ne newEP.e) - !isImmutableReference(newEP) - } - - if (isNotFinal) { - Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) - } else - createResult() - } - - /** - * Checks whether a field write may be a lazy initialization. - * - * We only consider simple cases of lazy initialization where the single field write is guarded - * so it is executed only if the field still has its default value and where the written value - * is guaranteed to be the same even if the write is executed multiple times (may happen because - * of the initializing method being executed more than once on concurrent threads). - * - * Also, all field reads must be used only for a guard or their uses have to be guarded - * themselves. - */ - def isLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - )(implicit state: State): Boolean = { - val write = code(writeIndex).asFieldWriteAccessStmt - - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - // Only handle lazy initialization of ints and floats as they are guaranteed to be - // written atomically - return false; - } - - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return false; // Reads outside the (single) lazy initialization method - } - - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val (guardIndex, guardedIndex, readIndex) = - findGuard(writeIndex, defaultValue, code, cfg) match { - case Some((guard, guarded, read)) ⇒ (guard, guarded, read) - case None ⇒ return false; - } - - // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) - return false; - - // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - return false; - - // Field reads (except for the guard) may only be executed if the field's value is not the - // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - return false; - - true - } - - def checkWrites( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val definitions = write.value.asVar.definedBy - - val isDeterministic = - if (definitions.size == 1) { - // The value written must be computed deterministically - checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) - } else { - // More than one definition site for the value might lead to differences between - // invocations, but not if this method has no parameters and is deterministic - // (in this case, the definition reaching the write will always be the same) - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - - // The field write must be guarded correctly - isDeterministic && - checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - } - - /** - * Checks if the field write for a lazy initialization is immediately followed by a return of - * the written value (possibly loaded by another field read). - */ - def checkImmediateReturn( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 - - var load = -1 - - while (index < code.length) { - val stmt = code(index) - stmt.astID match { - case Assignment.ASTID ⇒ - if (isReadOfCurrentField(stmt.asAssignment.expr)) - load = index - else - return false; // No field read or a field read of a different field - case ReturnValue.ASTID ⇒ - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) - return true; // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) - return true; // return of field value loaded by field read - else - return false; // return of different value - case _ ⇒ return false; // neither a field read nor a return - } - index += 1 - } - false - } - - def lazyInitializerIsDeterministic( - method: Method, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( - propertyStore(declaredMethods(method), Purity.key) - )) - result - } - - /** - * 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 - for (pc ← pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID ⇒ - if (method.isInitializer) { - if (field.isStatic) { - if (method.isConstructor) - return true; - } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - 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.referenceImmutability == LazyInitializedReference) //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; - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - //TODO Lazy Initialization here - /** - * val liResult = propertyStore(field, ReferenceImmutabilityLazyInitialization.key) - * - * liResult match { - * case FinalP(NoLazyInitialization) => return true; - * case FinalP(NotThreadSafeLazyInitialization) => return true; - * case FinalP(LazyInitialization) => - * state.referenceImmutability = LazyInitializedReference - * case _ => return true; - * } - */ - // A field written outside an initializer must be lazily - // initialized or it is non-final - - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - )) - return true; - - state.referenceImmutability = LazyInitializedReference - LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - // 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. - return true; - } - } - case _ ⇒ throw new RuntimeException("unexpected field access"); - } - } else { - // nothing to do as the put field is dead - } - } - } - false - } - - /** - * 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 eps: InterimEP[Method, TACAI] ⇒ - * state.tacDependees += method → ((eps, pcs)) - * eps.ub.tac - * case epk ⇒ - * state.tacDependees += method → ((epk, pcs)) - * None - * } - * } - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - /** - * 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 // Must be locally created - else { - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - handleEscapeProperty(escape) - } - } - } - } - - /** - * 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 = 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 - } - - /** - * Checks if the field write is only executed if the field's value was still the default value. - * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization - * and the field write. - */ - def checkWriteIsGuarded( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val startBB = cfg.bb(writeIndex).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt ⇒ - stmt.astID == CaughtException.ASTID - } flatMap { exception ⇒ - exception.asCaughtException.origins.map { origin: Int ⇒ - if (isImmediateVMException(origin)) - pcOfImmediateVMException(origin) - else - pcOfMethodExternalException(origin) - }.iterator - } - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - if (startPC == 0 || startPC == guardedIndex) - return false; // Reached method start or wrong branch of guarding if-Statement - - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; - - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; - - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - true - } - - /** - * 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]] - )(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(defSites.head).asAssignment.expr.isConst) true - else { - val expr = code(index).asAssignment.expr - expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ ⇒ // Unknown field - false - }) - } - } - - defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) - } - - val value = origin.expr - - val isNonConstDeterministic = value.astID match { - case GetStatic.ASTID | GetField.ASTID ⇒ - value.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ ⇒ // Unknown field - false - } - 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.exists(!isConstant(_))) { - false - } else { - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true - } - 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, code) - } - - value.isConst || isNonConstDeterministic - } - - /** - * Checks that all non-dead field reads that are not used for the guarding if-Statement of a - * lazy initialization are only executed if the field did not have its default value or after - * the (single) field write. - */ - def checkReads( - reads: Seq[(Method, PCs)], - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - ): Boolean = { - // There is only a single method with reads aside from initializers (checked by - // isLazilyInitialized), so we have to check only reads from that one method. - reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ - val index = pcToIndex(readPC) - index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) - } - } - - /** - * Checks that a field read is only executed if the field did not have its default value or - * after the (single) field write. - */ - def checkRead( - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Boolean = { - val startBB = cfg.bb(readIndex).asBasicBlock - val writeBB = cfg.bb(writeIndex) - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - - if (startPC == 0) - return false; // Reached the start of the method but not the guard or field write - - if ((curBB eq writeBB) && writeIndex > readIndex) - return false; // In the basic block of the write, but before the write - - if (startPC != guardedIndex && // Did not reach the guard - (curBB ne writeBB) /* Not the basic block of the write */ ) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - true - } - - /** - * Gets all predecessor BasicBlocks of a CFGNode. - */ - def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode ⇒ - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) - } - result.toList - } - - /** - * 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. - */ - def findGuard( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Option[(Int, Int, Int)] = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int)] = None - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID ⇒ - val ifStmt = cfStmt.asIf - ifStmt.condition match { - case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1)) - } - - case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None; - } else { - result = Some((endPC, ifStmt.targetStmt)) - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard - } - - None - } - - /** - * 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])(implicit state: State): Boolean = { - val field = expr.astID match { - case GetField.ASTID ⇒ - val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) None - else expr.asGetField.resolveField(project) - case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) - case _ ⇒ None - } - field.contains(state.field) - } - - /** - * 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]] - )(implicit state: State): Boolean = { - - /** - * Checks if an expression is an IntConst or FloatConst with the corresponding default value. - */ - 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 || - expr.isFloatConst && defaultValue == expr.asFloatConst.value - } - } - - /** - * Checks whether the non-constant expression of the if-Statement is a read of the current - * field. - */ - def isGuardInternal(expr: V): Boolean = { - expr.definedBy forall { index ⇒ - if (index < 0) false // If the value is from a parameter, this can not be the guard - else isReadOfCurrentField(code(index).asAssignment.expr) - } - } - - if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { - isGuardInternal(ifStmt.rightExpr.asVar) - } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { - isGuardInternal(ifStmt.leftExpr.asVar) - } else false - } - - /** - * Checks if 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 = { - val s = state.referenceImmutability - s != s //TODO revert - } - - /** - * eop match { - * case LBP(NotPrematurelyReadField) ⇒ - * state.prematurelyReadDependee = None - * false - * case UBP(PrematurelyReadField) ⇒ true - * case eps ⇒ - * state.prematurelyReadDependee = Some(eps) - * false - * } * - */ - /** - * Checkes 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. - */ - 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 - } - - /** - * Checkes if 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, ReferenceImmutability] - )(implicit state: State): Boolean = eop match { - case FinalEP(e, ImmutableReference) ⇒ true - case FinalEP(e, MutableReference) ⇒ false - // - case LBP(ImmutableReference) ⇒ true - case UBP(MutableReference) ⇒ false - - /** - * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ - * true - * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * - */ - case _ ⇒ - state.referenceImmutabilityDependees += eop - true - } -} - -trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.ub(EscapeProperty) - ) - - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) - -} - -/** - * Executor for the field mutability analysis. - */ -object EagerL0ReferenceImmutabilityAnalysis - extends L0ReferenceImmutabilityAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { - - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) - analysis - } - - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty -} - -/** - * Executor for the lazy field mutability analysis. - */ -object LazyL0ReferenceImmutabilityAnalysis - extends L0ReferenceImmutabilityAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { - - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - ReferenceImmutability.key, - analysis.determineReferenceImmutability - ) - analysis - } - - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala similarity index 91% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index 61a52da278..76ae813639 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.br.fpcf.analyses +package org.opalj.tac.fpcf.analyses.immutability import org.opalj.br.ClassSignature import org.opalj.br.ClassTypeSignature @@ -19,7 +19,6 @@ 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.analyses.DependentImmutabilityKind.DependentImmutabilityKind import org.opalj.br.fpcf.properties.DeepImmutableField import org.opalj.br.fpcf.properties.DeepImmutableType import org.opalj.br.fpcf.properties.DependentImmutableField @@ -47,11 +46,13 @@ import org.opalj.fpcf.PropertyComputationResult import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS +import org.opalj.tac.fpcf.analyses.immutability.DependentImmutabilityKind.DependentImmutabilityKind case class State(f: Field) { var field: Field = f var typeImmutability: Option[Boolean] = Some(true) var referenceImmutability: Option[Boolean] = None + var referenceNotEscapes: Boolean = true var dependentImmutability: Option[DependentImmutabilityKind] = Some( DependentImmutabilityKind.dependent ) @@ -252,13 +253,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(true) ⇒ Result(field, DeepImmutableField) case Some(false) | None ⇒ { - state.dependentImmutability match { - case Some(DependentImmutabilityKind.notShallowOrMutable) ⇒ - Result(field, DependentImmutableField) - case Some(DependentImmutabilityKind.onlyDeepImmutable) ⇒ - Result(field, DeepImmutableField) - case _ ⇒ Result(field, ShallowImmutableField) - } + if (state.referenceNotEscapes) + Result(field, DeepImmutableField) + else + state.dependentImmutability match { + case Some(DependentImmutabilityKind.notShallowOrMutable) ⇒ + Result(field, DependentImmutableField) + case Some(DependentImmutabilityKind.onlyDeepImmutable) ⇒ + Result(field, DeepImmutableField) + case _ ⇒ Result(field, ShallowImmutableField) + } } } } @@ -287,7 +291,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.typeImmutability = Some(false) return Result(field, MutableField); } - case x @ FinalP(ImmutableReference | LazyInitializedReference) ⇒ { //TODO + case x @ FinalP(ImmutableReference(notEscapes)) ⇒ { + state.referenceImmutability = Some(true) + state.referenceNotEscapes = notEscapes + } + case x @ FinalP(LazyInitializedReference) ⇒ { //TODO state.referenceImmutability = Some(true) } case x @ _ ⇒ @@ -307,7 +315,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val state: State = new State(field) val result = propertyStore(state.field, ReferenceImmutability.key) result match { - case FinalEP(_, ImmutableReference | LazyInitializedReference) ⇒ + case FinalP(ImmutableReference(notEscapes)) ⇒ { + state.referenceImmutability = Some(true) + state.referenceNotEscapes = notEscapes + } + case FinalEP(_, LazyInitializedReference) ⇒ state.referenceImmutability = Some(true) case FinalP(MutableReference) ⇒ return Result(field, MutableField); case x @ _ ⇒ { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0ReferenceImmutabilityLazyInitializationAnalysis.scala similarity index 99% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0ReferenceImmutabilityLazyInitializationAnalysis.scala index 1b2e9e6d8c..69c6da4f1a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0ReferenceImmutabilityLazyInitializationAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0ReferenceImmutabilityLazyInitializationAnalysis.scala @@ -307,17 +307,14 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p /* var constantVal: Option[Any] = None var allInitializeConstant = true - val field = state.field var constructors: Set[Method] = if(field.isStatic) Set.empty else field.classFile.constructors.toSet - val writesIterator = fieldAccessInformation.writeAccesses(field).iterator while (writesIterator.hasNext && allInitializeConstant) { val (method, pc) = writesIterator.next() constructors -= method val code = tacai(method).stmts - val index = pcToIndex(pc) val stmt = code(index) if (stmt.astID == PutStatic.ASTID || @@ -347,7 +344,6 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p } } } - for (constructor ← constructors) { // TODO iterate all statements val NonVirtualMethodCall(_, declClass, _, name, _, rcvr, _) = stmt @@ -360,7 +356,6 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p else constantVal = Some(if (state.field.fieldType eq FloatType) 0.0f else 0) } } - constantVal */ } @@ -1269,11 +1264,11 @@ class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val p def isImmutableReference( eop: EOptionP[Field, ReferenceImmutability] )(implicit state: State): Boolean = eop match { - case FinalEP(e, ImmutableReference) ⇒ true - case FinalEP(e, MutableReference) ⇒ false + case FinalEP(e, ImmutableReference(_)) ⇒ true + case FinalEP(e, MutableReference) ⇒ false // - case LBP(ImmutableReference) ⇒ true - case UBP(MutableReference) ⇒ false + case LBP(ImmutableReference(_)) ⇒ true + case UBP(MutableReference) ⇒ false /** * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala similarity index 99% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala index abd32357ff..ce4e6ffbcc 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses +package org.opalj.tac.fpcf.analyses.immutability import org.opalj.br.ClassFile import org.opalj.br.ClassSignature @@ -7,17 +7,28 @@ import org.opalj.br.ClassTypeSignature import org.opalj.br.FormalTypeParameter import org.opalj.br.ObjectType import org.opalj.br.SimpleClassTypeSignature -import org.opalj.log.LogContext -import org.opalj.log.OPALLogger +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_new +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.FinalEP -import org.opalj.fpcf.Entity import org.opalj.fpcf.EPK import org.opalj.fpcf.EPS +import org.opalj.fpcf.Entity +import org.opalj.fpcf.FinalEP import org.opalj.fpcf.FinalP -import org.opalj.fpcf.Result -import org.opalj.fpcf.Results import org.opalj.fpcf.IncrementalResult import org.opalj.fpcf.InterimE import org.opalj.fpcf.InterimResult @@ -29,23 +40,12 @@ 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.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_new -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.log.LogContext +import org.opalj.log.OPALLogger /** * diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala similarity index 99% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala index 0cae1e97fc..886863f700 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala @@ -1,15 +1,31 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses +package org.opalj.tac.fpcf.analyses.immutability import org.opalj.Answer import org.opalj.No import org.opalj.Unknown import org.opalj.Yes 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_new +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_new +import org.opalj.br.fpcf.properties.ShallowImmutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.br.fpcf.properties.TypeImmutability_new import org.opalj.fpcf.ELUBP -import org.opalj.fpcf.Entity 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 @@ -24,22 +40,6 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP -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_new -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_new -import org.opalj.br.fpcf.properties.ShallowImmutableClass -import org.opalj.br.fpcf.properties.ShallowImmutableType -import org.opalj.br.fpcf.properties.TypeImmutability_new /** * Determines the mutability of a specific type by checking if all subtypes of a specific diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala new file mode 100644 index 0000000000..a2be91ce97 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala @@ -0,0 +1,172 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.immutability.reference + +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.ImmutableReference +import org.opalj.br.fpcf.properties.MutableReference +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.ReferenceImmutability +import org.opalj.br.fpcf.properties.TypeImmutability_new +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.EUBP +import org.opalj.fpcf.Entity +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.InterimEP +import org.opalj.fpcf.LBP +import org.opalj.fpcf.Property +import org.opalj.fpcf.UBP +import org.opalj.tac.DUVar +import org.opalj.tac.Stmt +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACode +import org.opalj.tac.common.DefinitionSite +import org.opalj.tac.common.DefinitionSitesKey +import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.value.ValueInformation + +/*** + * + */ +trait AbstractReferenceImmutabilityAnalysis 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: ReferenceImmutability = ImmutableReference(true), + 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, + var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, + var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty, + var notEscapes: Boolean = true, + var typeDependees: Set[EOptionP[ObjectType, TypeImmutability_new]] = Set.empty, + var lazyInitializerIsDeterministicFlag: Boolean = false + ) { + def hasDependees: Boolean = { + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || + escapeDependees.nonEmpty || tacDependees.nonEmpty || typeDependees.nonEmpty + } + + def dependees: Traversable[EOptionP[Entity, Property]] = { + prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) ++ typeDependees + } + } + + /** + * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } + } + + /** + * Checkes 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. + */ + def isNonDeterministic( + eop: EOptionP[DeclaredMethod, Purity] + )(implicit state: State): Boolean = eop match { + case LBP(p: Purity) if p.isDeterministic ⇒ + println("XXX: false is non deterministic"); false + case EUBP(e, p: Purity) if !p.isDeterministic ⇒ + println("e: "+e+",XXX: true is non deterministic"); true + case _ ⇒ + println("XXX: else"); + state.purityDependees += eop + false + } + + def lazyInitializerIsDeterministic( + method: Method, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + + val propertyStoreResult = propertyStore(declaredMethods(method), Purity.key) + println("property store purity result: "+propertyStoreResult) + val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( + propertyStoreResult + )) + result + } + + /** + * Checkes if 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, ReferenceImmutability] + )(implicit state: State): Boolean = eop match { + case FinalEP(e, ImmutableReference(_)) ⇒ true + case FinalEP(e, MutableReference) ⇒ false + // + case LBP(ImmutableReference(_)) ⇒ true + case UBP(MutableReference) ⇒ false + + /** + * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ + * true + * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * + */ + case _ ⇒ + state.referenceImmutabilityDependees += eop + true + } + + /** + * Checks if 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) ⇒ true + case eps ⇒ + state.prematurelyReadDependee = Some(eps) + false + } + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala new file mode 100644 index 0000000000..6f71e6b3dd --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -0,0 +1,535 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.immutability.reference + +import org.opalj.RelationalOperators.EQ +import org.opalj.RelationalOperators.NE +import org.opalj.ai.isImmediateVMException +import org.opalj.ai.pcOfImmediateVMException +import org.opalj.ai.pcOfMethodExternalException +import org.opalj.br.ComputationalTypeFloat +import org.opalj.br.ComputationalTypeInt +import org.opalj.br.Method +import org.opalj.br.PCs +import org.opalj.br.cfg.BasicBlock +import org.opalj.br.cfg.CFG +import org.opalj.br.cfg.CFGNode +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.properties.ReferenceImmutability +import org.opalj.collection.immutable.IntTrieSet +import org.opalj.tac.SelfReferenceParameter +import org.opalj.tac.Assignment +import org.opalj.tac.CaughtException +import org.opalj.tac.Expr +import org.opalj.tac.FieldWriteAccessStmt +import org.opalj.tac.GetField +import org.opalj.tac.GetStatic +import org.opalj.tac.If +import org.opalj.tac.NonVirtualFunctionCall +import org.opalj.tac.ReturnValue +import org.opalj.tac.StaticFunctionCall +import org.opalj.tac.Stmt +import org.opalj.tac.TACStmts +import org.opalj.tac.VirtualFunctionCall + +import scala.annotation.switch + +trait AbstractReferenceImmutabilityAnalysisLazyInitialization + extends AbstractReferenceImmutabilityAnalysis + with FPCFAnalysis { + + /** + * Checks whether a field write may be a lazy initialization. + * + * We only consider simple cases of lazy initialization where the single field write is guarded + * so it is executed only if the field still has its default value and where the written value + * is guaranteed to be the same even if the write is executed multiple times (may happen because + * of the initializing method being executed more than once on concurrent threads). + * + * Also, all field reads must be used only for a guard or their uses have to be guarded + * themselves. + */ + def isLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + )(implicit state: State): Boolean = { + val write = code(writeIndex).asFieldWriteAccessStmt + println("0001") + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + // Only handle lazy initialization of ints and floats as they are guaranteed to be + // written atomically + return false; + } + println("0002") + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return false; // Reads outside the (single) lazy initialization method + } + println("0003") + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val (guardIndex, guardedIndex, readIndex) = + findGuard(writeIndex, defaultValue, code, cfg) match { + case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + case None ⇒ return false; + } + println("0004") + // Detect only simple patterns where the lazily initialized value is returned immediately + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) + return false; + println("0005") + // The value written must be computed deterministically and the writes guarded correctly + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) + return false; + println("0006") + // Field reads (except for the guard) may only be executed if the field's value is not the + // default value + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + return false; + println("0007") + true + } + + /** + * 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. + */ + def findGuard( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Option[(Int, Int, Int)] = { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID ⇒ + val ifStmt = cfStmt.asIf + ifStmt.condition match { + case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; + } else { + result = Some((endPC, endPC + 1)) + } + + case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None; + } else { + result = Some((endPC, ifStmt.targetStmt)) + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + } + + None + } + + /** + * Checks if the field write is only executed if the field's value was still the default value. + * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization + * and the field write. + */ + def checkWriteIsGuarded( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + println("check write is guarded") + println("-a") + val startBB = cfg.bb(writeIndex).asBasicBlock + println("-b") + var enqueuedBBs: Set[CFGNode] = Set(startBB) + println("-c") + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + println("-d") + val abnormalReturnNode = cfg.abnormalReturnNode + println("-e") + val caughtExceptions = code filter { stmt ⇒ + println("-f") + stmt.astID == CaughtException.ASTID + + } flatMap { exception ⇒ + exception.asCaughtException.origins.map { origin: Int ⇒ + if (isImmediateVMException(origin)) { + println("-i") + pcOfImmediateVMException(origin) + } else { + println("-j") + pcOfMethodExternalException(origin) + } + }.iterator + } + println("-k") + while (worklist.nonEmpty) { + println("-l") + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + println("-m") + if (startPC == 0 || startPC == guardedIndex) + return false; // Reached method start or wrong branch of guarding if-Statement + println("-n") + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) { + println("nn") + if (!lazyInitializerIsDeterministic(method, code)) { + println("oo") + return false + } + }; + println("ooo") + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) { + println("ooo") + if (!lazyInitializerIsDeterministic(method, code)) { + println("ooooo") + return false + } + + }; + println("-p") + // Check all predecessors except for the one that contains the guarding if-Statement + println("-q") + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + println("-r") + worklist ++= predecessors + println("-s") + enqueuedBBs ++= predecessors + } + println("--true") + true + } + + /** + * Gets all predecessor BasicBlocks of a CFGNode. + */ + def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.predecessors.iterator flatMap { curNode ⇒ + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) + } + result.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]] + )(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(defSites.head).asAssignment.expr.isConst) true + else { + val expr = code(index).asAssignment.expr + expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + false + }) + } + } + + defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) + } + + val value = origin.expr + + val isNonConstDeterministic = value.astID match { + case GetStatic.ASTID | GetField.ASTID ⇒ + value.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + false + } + 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.exists(!isConstant(_))) { + false + } else { + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true + } + 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, code) + } + + value.isConst || isNonConstDeterministic + } + + /** + * Checks that all non-dead field reads that are not used for the guarding if-Statement of a + * lazy initialization are only executed if the field did not have its default value or after + * the (single) field write. + */ + def checkReads( + reads: Seq[(Method, PCs)], + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int] + ): Boolean = { + // There is only a single method with reads aside from initializers (checked by + // isLazilyInitialized), so we have to check only reads from that one method. + reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ + val index = pcToIndex(readPC) + index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) + } + } + + /** + * Checks that a field read is only executed if the field did not have its default value or + * after the (single) field write. + */ + def checkRead( + readIndex: Int, + guardedIndex: Int, + writeIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Boolean = { + val startBB = cfg.bb(readIndex).asBasicBlock + val writeBB = cfg.bb(writeIndex) + + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + + if (startPC == 0) + return false; // Reached the start of the method but not the guard or field write + + if ((curBB eq writeBB) && writeIndex > readIndex) + return false; // In the basic block of the write, but before the write + + if (startPC != guardedIndex && // Did not reach the guard + (curBB ne writeBB) /* Not the basic block of the write */ ) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + true + } + + /** + * 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])(implicit state: State): Boolean = { + val field = expr.astID match { + case GetField.ASTID ⇒ + val objRefDefinition = expr.asGetField.objRef.asVar.definedBy + if (objRefDefinition != SelfReferenceParameter) None + else expr.asGetField.resolveField(project) + case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) + case _ ⇒ None + } + field.contains(state.field) + } + + /** + * 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]] + )(implicit state: State): Boolean = { + + /** + * Checks if an expression is an IntConst or FloatConst with the corresponding default value. + */ + 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 || + expr.isFloatConst && defaultValue == expr.asFloatConst.value + } + } + + /** + * Checks whether the non-constant expression of the if-Statement is a read of the current + * field. + */ + def isGuardInternal(expr: V): Boolean = { + expr.definedBy forall { index ⇒ + if (index < 0) false // If the value is from a parameter, this can not be the guard + else isReadOfCurrentField(code(index).asAssignment.expr) + } + } + + if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { + isGuardInternal(ifStmt.rightExpr.asVar) + } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { + isGuardInternal(ifStmt.leftExpr.asVar) + } else false + } + + def checkWrites( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): Boolean = { + println("check Writes on field: "+state.field.name) + val definitions = write.value.asVar.definedBy + println("a") + val isDeterministic = + if (definitions.size == 1) { + println("b") + // The value written must be computed deterministically + checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) + } else { + println("c") + // More than one definition site for the value might lead to differences between + // invocations, but not if this method has no parameters and is deterministic + // (in this case, the definition reaching the write will always be the same) + method.descriptor.parametersCount == 0 && + !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) + } + println("d") + val checkWriteIsGuardedResult = + checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) + println(s""" isDeterministic: $isDeterministic + | checkWriteIsGuarded: ${checkWriteIsGuardedResult} + |""".stripMargin) + // The field write must be guarded correctly + isDeterministic && checkWriteIsGuardedResult + + } + + /** + * Checks if the field write for a lazy initialization is immediately followed by a return of + * the written value (possibly loaded by another field read). + */ + def checkImmediateReturn( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case Assignment.ASTID ⇒ + if (isReadOfCurrentField(stmt.asAssignment.expr)) + load = index + else + return false; // No field read or a field read of a different field + case ReturnValue.ASTID ⇒ + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) + return true; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) + return true; // return of field value loaded by field read + else + return false; // return of different value + case _ ⇒ return false; // neither a field read nor a return + } + index += 1 + } + false + } + +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala new file mode 100644 index 0000000000..73e1ba2c89 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -0,0 +1,791 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.immutability.reference + +import org.opalj.br.ClassFile +import org.opalj.br.DeclaredMethod +import org.opalj.br.Field +import org.opalj.br.FloatType +import org.opalj.br.Method +import org.opalj.br.ObjectType +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.DeepImmutableField +import org.opalj.br.fpcf.properties.DependentImmutableType +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.FieldImmutability +import org.opalj.br.fpcf.properties.FieldPrematurelyRead +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.MutableReference +import org.opalj.br.fpcf.properties.NoEscape +import org.opalj.br.fpcf.properties.Purity +import org.opalj.br.fpcf.properties.ReferenceImmutability +import org.opalj.br.fpcf.properties.TypeImmutability_new +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.Assignment +import org.opalj.tac.DVar +import org.opalj.tac.GetField +import org.opalj.tac.NonVirtualMethodCall +import org.opalj.tac.PutField +import org.opalj.tac.PutStatic +import org.opalj.tac.Stmt +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACode +import org.opalj.tac.UVar +import org.opalj.tac.common.DefinitionSite +import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.tac.SelfReferenceParameter + +/** + * + * Implementation is used from the old L2FieldMutability implementation + * but the lattice is mapped to the new reference immutability lattice. + * + * @note Requires that the 3-address code's expressions are not deeply nested. + * + * @author Dominik Helm + * @author Florian Kübler + * @author Michael Eichberg + * @author Tobias Peter Roth + */ +class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProject) + extends AbstractReferenceImmutabilityAnalysisLazyInitialization + with AbstractReferenceImmutabilityAnalysis + with FPCFAnalysis { + + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field ⇒ determineReferenceImmutability(field) + case _ ⇒ + val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + + /** + * Analyzes the mutability of private non-final fields. + * + * 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 determineReferenceImmutability( + field: Field + ): ProperPropertyComputationResult = { + println( + "start determine ref imm analysis=====================================================================================================" + ) + println("field: "+field.name) + implicit val state: State = State(field) + // Fields are not final if they are read prematurely! + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { + println("field is prematurely read") + return Result(field, MutableReference) + }; //Result(field, NonFinalFieldByAnalysis); + println("01") + state.referenceImmutability = ImmutableReference(true) //EffectivelyFinalField + + val thisType = field.classFile.thisType + + if ((field.isPublic || field.isPackagePrivate || field.isProtected)) { + println("field: "+field+" is public, pack priv or protected") + if (!field.isFinal) + return Result(field, MutableReference) + else + state.notEscapes = false + }; //Result(field, NonFinalFieldByLackOfInformation) + println("02") + // 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) { + if (!closedPackages.isClosed(thisType.packageName)) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } + println("03") + val classesHavingAccess: Iterator[ClassFile] = + if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown && !state.field.isFinal) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + 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 + } + println("04") + // If there are native methods, we give up + if (classesHavingAccess.exists(_.methods.exists(_.isNative)) && !state.field.isFinal) { + println("native") + return Result(field, MutableReference) + }; //return Result(field, NonFinalFieldByLackOfInformation); + println("05") + + println("1-----------------------------------------------------------------------") + for { + (method, pcs) ← fieldAccessInformation.readAccesses(field) + taCode ← getTACAI(method, pcs) + } { + pcs.foreach(pc ⇒ { + val index = taCode.pcToIndex(pc) + if (index > 0) { + taCode.stmts(index) match { + case Assignment(pc3, targetVar, GetField(pc4, cl, name, _, value)) ⇒ + if (name == field.name) { + state.notEscapes = false + } + case _ ⇒ + } + } else state.notEscapes = false + }) + } + println("0------------------") + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode ← getTACAI(method, pcs) + } { + println("2-----------------------------------------------------------------------") + println("method: "+method) + for (pc ← pcs) { + println("pc: "+pc) + val index = taCode.pcToIndex(pc) + if (index > 0) { + val stmt = taCode.stmts(index) + println("stmt: "+stmt) + stmt match { + case PutField(_, _, _, _, _, value) ⇒ + value match { + case v @ UVar(defSites, value2) ⇒ // SObjectValue(t,_,_,_)) => + //if (!v.defSites.filter(_ < 1).isEmpty) + //escape var + { + v.defSites.foreach( + i ⇒ { + println("def site: "+i) + if (i > 0) { + val stmt2 = taCode.stmts(i) + println("stmt2: "+stmt2) + stmt2 match { + case Assignment(pcAssignment, dv @ DVar(useSites, value), expr) ⇒ + //useSites + dv.useSites.foreach( + x ⇒ { + val innerstmt = taCode.stmts(x) + innerstmt match { + case NonVirtualMethodCall( + pc, + bdeclaringClass, + isInterface, + name, + descriptor, + receiver, + params + ) ⇒ { + params.foreach(expr ⇒ { + expr match { + case vrs @ UVar(defSites, value) ⇒ { //: SObjectValue(tpe,isNull,isPrecise) + //val isStaticIndex = if (method.isStatic) 0 else -1 + vrs.defSites.foreach( + dfste ⇒ { + if (dfste < 0) //(0 + isStaticIndex)) + state.notEscapes = false + else { + val stmtDefSite = taCode.stmts(dfste) + println("stmt def site: "+stmtDefSite) + stmtDefSite match { + case Assignment( + pcA, + targetVar, + GetField( + pc, + declaringClass, + name, + declaredFieldType, + objRef + ) + ) ⇒ { + val fs = project.allFields.filter( + { f ⇒ + f.name.equals(name) && f.fieldType.equals( + declaredFieldType + ) && f.classFile.equals( + state.field.classFile + ) + } + ) + fs.foreach(f ⇒ { + val result = + propertyStore(f, FieldImmutability.key) + result match { + case FinalP(DeepImmutableField) ⇒ + case _ ⇒ state.notEscapes = false + } + }) + } + case _ ⇒ + } + } + + } + ) + } + } + }) + } + case _ ⇒ + } + /** + * case NonVirtualMethodCall(pc,org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses.Generic_class1, + * isInterface=false, + * void (java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object), + * UVar(defSites={1},value=SObjectValue(type=org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses.Generic_class1,isNull=No,isPrecise=true)),(UVar(defSites={-5},value=SObjectValue(type=org.opalj.fpcf.fixt + * ures.immutability.classes.multinested_genericClasses.Generic_class1,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-4},value=SObjectValue(type=org.opalj.fpcf.fixtures.immutability.classes.mu + * ltinested_genericClasses.TrivialMutableClass,isNull=Unknown,isPrecise=false)))) => * + */ + + } + ) + if (expr.isNew || expr.isConst) { + if (expr.isNew) { + + /** + * val NEW = expr.asNew + * + * val oType = NEW.tpe + * println("otype: "+oType) + * + * if (oType == ObjectType.Object) + * state.notEscapes = false + * else { + * val result = propertyStore(oType, TypeImmutability_new.key) + * result match { + * case FinalP(DependentImmutableType) ⇒ { + * println("depenent type") + * state.notEscapes = false + * } + * case fp @ FinalP(_) ⇒ println("etc. final: "+fp) + * case ep @ _ ⇒ { + * println("type imm not yet computed") + * state.typeDependees += ep + * } + * } + * }* + */ + + } + /** + * propertyStore( + * definitionSites(method, pc), + * EscapeProperty.key + * ) match { + * case FinalP(NoEscape) ⇒ // nothing to do + * case FinalP(AtMost(EscapeViaParameter)) ⇒ // introduced via this + * case _ ⇒ + * state.notEscapes = false + * } + * } else { + * state.notEscapes = false* + */ //TODO + } + case _ ⇒ state.notEscapes = false + } + } else { + //constructor ?? + state.notEscapes = false + } + } + ) + } + case _ ⇒ state.notEscapes = false + } + case _ ⇒ state.notEscapes = false + } + } else state.notEscapes = false + } + + if (methodUpdatesField(method, taCode, pcs) && !state.field.isFinal) { + println("method does updates field") + return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnfalysis); + } else println("method does not updates fields") + println("st.ref imm 1: "+state.referenceImmutability+", reference: "+state.field) + } + println("st.ref imm 2: "+state.referenceImmutability+", reference: "+state.field) + + if (state.lazyInitInvocation.isDefined) { + //val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + //TODO //handleCalls(calleesEOP) + } + println("st.ref imm 3: "+state.referenceImmutability+", reference: "+state.field) + println("finish; go to create results") + createResult() + } + + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: State + ): Boolean = { + println("cEOP: "+calleesEOP) + calleesEOP match { + case FinalP(callees) ⇒ + state.calleesDependee = None + handleCallees(callees) + case InterimUBP(callees) ⇒ + state.calleesDependee = Some(calleesEOP) + handleCallees(callees) + case r @ _ ⇒ + state.calleesDependee = Some(calleesEOP) + println("false: ; + result "+r) + false + } + } + + def handleCallees(callees: Callees)(implicit state: State): Boolean = { + val pc = state.lazyInitInvocation.get._2 + if (callees.isIncompleteCallSite(pc)) { + println("is incomplete call site") + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else { + val targets = callees.callees(pc).toTraversable + if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { + val nonDeterministicTargets = + targets.filter(target ⇒ isNonDeterministic(propertyStore(target, Purity.key))) + println( + "target non deterministic: "+nonDeterministicTargets + .mkString(", ") + ) + nonDeterministicTargets.foreach(t ⇒ println(propertyStore(t, Purity.key))) + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else false + } + } + + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + * The code below can be used to identify such value, but is not yet adapted to using the + * TACAI property */ + /* + var constantVal: Option[Any] = None + var allInitializeConstant = true + + val field = state.field + var constructors: Set[Method] = + if(field.isStatic) Set.empty else field.classFile.constructors.toSet + + val writesIterator = fieldAccessInformation.writeAccesses(field).iterator + while (writesIterator.hasNext && allInitializeConstant) { + val (method, pc) = writesIterator.next() + constructors -= method + val code = tacai(method).stmts + + val index = pcToIndex(pc) + val stmt = code(index) + if (stmt.astID == PutStatic.ASTID || + stmt.asPutField.objRef.asVar.definedBy == SelfReferenceParameter) { + val write = stmt.asFieldWriteAccessStmt + if (write.resolveField(p).contains(state.field)) { + val defs = write.value.asVar.definedBy + if (defs.size == 1 && defs.head >= 0) { + val defSite = code(defs.head).asAssignment.expr + val const = if (defSite.isIntConst) + Some(defSite.asIntConst.value) + else if (defSite.isFloatConst) + Some(defSite.asFloatConst.value) + else None + if (const.isDefined) { + if (constantVal.isDefined) { + if (constantVal != const) { + allInitializeConstant = false + constantVal = None + } + } else constantVal = const + } else { + allInitializeConstant = false + constantVal = None + } + } + } + } + } + + for (constructor ← constructors) { + // TODO iterate all statements + val NonVirtualMethodCall(_, declClass, _, name, _, rcvr, _) = stmt + // Consider calls to other constructors as initializations as either + // the called constructor will initialize the field or delegate to yet + // another constructor + if (declClass != state.field.classFile.thisType || name != "" || + rcvr.asVar.definedBy != SelfReferenceParameter) { + if (constantVal.isDefined) allInitializeConstant = false + else constantVal = Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + } + } + + constantVal */ + } + + /** + * Prepares the PropertyComputation result, either as IntermediateResult if there are still + * dependees or as Result otherwise. + */ + def createResult()(implicit state: State): ProperPropertyComputationResult = { + println("create results: ") + println("current ref imm: "+state.referenceImmutability) + if (state.hasDependees && (state.referenceImmutability ne MutableReference)) { //NonFinalFieldByAnalysis)) + println("has still dependendees") + println("state dependees: "+state.dependees) + InterimResult( + state.field, + MutableReference, //NonFinalFieldByAnalysis, + state.referenceImmutability, + state.dependees, + c + ) + } else { + println("end") + println("state.reference immutability: "+state.referenceImmutability) + println("state: "+state.notEscapes) + if (state.field.isFinal) + Result(state.field, ImmutableReference(state.notEscapes)) + else + state.referenceImmutability match { + case ImmutableReference(_) ⇒ Result(state.field, ImmutableReference(state.notEscapes)) + case _ ⇒ 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 = { + println("continuation") + var isNotFinal = false + eps.pk match { + case EscapeProperty.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + val escapes = handleEscapeProperty(newEP) + if (escapes) + state.notEscapes = false + isNotFinal = escapes + 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 ⇒ + isNotFinal = handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key ⇒ + isNotFinal = isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + val r = isNonDeterministic(newEP) + //if (!r) state.referenceImmutability = LazyInitializedReference + println("continuation purity result: "+r) + isNotFinal = r + println("cont. is not final: "+isNotFinal) + case ReferenceImmutability.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + isNotFinal = !isImmutableReference(newEP) + case TypeImmutability_new.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] + state.typeDependees = state.typeDependees.filter(_.e ne newEP.e) + newEP match { + case FinalP(DependentImmutableType) ⇒ state.notEscapes = false + case FinalP(_) ⇒ + case ep @ _ ⇒ state.typeDependees += ep + } + } + + if (isNotFinal && !state.field.isFinal) { + Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) + } else + 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 = { + println("method updates field?") + val field = state.field + val stmts = taCode.stmts + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + if (field.isStatic) { + if (method.isConstructor) + return true; + } else { + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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.referenceImmutability == LazyInitializedReference) //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; + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + //TODO Lazy Initialization here + /** + * val liResult = propertyStore(field, ReferenceImmutabilityLazyInitialization.key) + * + * liResult match { + * case FinalP(NoLazyInitialization) => return true; + * case FinalP(NotThreadSafeLazyInitialization) => return true; + * case FinalP(LazyInitialization) => + * state.referenceImmutability = LazyInitializedReference + * case _ => return true; + * } + */ + // A field written outside an initializer must be lazily + // initialized or it is non-final + + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )) { + println("is not lazy initialized") + println("method: "+method) + return true + }; + println("is lazy initialized") + state.referenceImmutability = LazyInitializedReference + //LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + + // 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. + return true; + } + + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead + } + } + } + false + } + + /** + * 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 eps: InterimEP[Method, TACAI] ⇒ + * state.tacDependees += method → ((eps, pcs)) + * eps.ub.tac + * case epk ⇒ + * state.tacDependees += method → ((epk, pcs)) + * None + * } + * } + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + /** + * 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 = { + println("ref: "+ref) + println("defined by: "+ref.definedBy) + + ref.definedBy.forall { defSite ⇒ + println("0") + println("field: "+state.field) + println("defsite: "+defSite) + if (defSite < 0) true // Must be locally created + else { + println("1") + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + println("escape property: "+escape) + handleEscapeProperty(escape) + } + } + } + } + + /** + * 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 = { + 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 + } + } + +} + +trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { + + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.ub(EscapeProperty), + PropertyBounds.lub(TypeImmutability_new) + ) + + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) + +} + +/** + * Executor for the field mutability analysis. + */ +object EagerL0ReferenceImmutabilityAnalysis + extends L0ReferenceImmutabilityAnalysisScheduler + with BasicFPCFEagerAnalysisScheduler { + + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + val fields = p.allProjectClassFiles.flatMap(_.fields) + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) + analysis + } + + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty +} + +/** + * Executor for the lazy field mutability analysis. + */ +object LazyL0ReferenceImmutabilityAnalysis + extends L0ReferenceImmutabilityAnalysisScheduler + with BasicFPCFLazyAnalysisScheduler { + + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + ReferenceImmutability.key, + analysis.determineReferenceImmutability + ) + analysis + } + + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala index ccdea6f9cd..e7fbc27b28 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala @@ -23,6 +23,7 @@ import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.ImpureByAnalysis import org.opalj.br.fpcf.properties.ImpureByLackOfInformation import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.Pure import org.opalj.br.fpcf.properties.Purity import org.opalj.br.fpcf.properties.ReferenceImmutability @@ -114,610 +115,618 @@ import scala.annotation.switch */ trait AbstractPurityAnalysis_new extends FPCFAnalysis { - /** The type of the TAC domain. */ - type V = DUVar[ValueInformation] - - /** - * The state of the analysis. - * Analyses are expected to extend this trait with the information they need. - * - * lbPurity - The current minimum possible purity level for the method - * ubPurity - The current maximum purity level for the method - * method - The currently analyzed method - * declClass - The declaring class of the currently analyzed method - * code - The code of the currently analyzed method - */ - trait AnalysisState { - var lbPurity: Purity - var ubPurity: Purity - val method: Method - val definedMethod: DeclaredMethod - val declClass: ObjectType - var pcToIndex: Array[Int] - var code: Array[Stmt[V]] + /** The type of the TAC domain. */ + type V = DUVar[ValueInformation] + + /** + * The state of the analysis. + * Analyses are expected to extend this trait with the information they need. + * + * lbPurity - The current minimum possible purity level for the method + * ubPurity - The current maximum purity level for the method + * method - The currently analyzed method + * declClass - The declaring class of the currently analyzed method + * code - The code of the currently analyzed method + */ + trait AnalysisState { + var lbPurity: Purity + var ubPurity: Purity + val method: Method + val definedMethod: DeclaredMethod + val declClass: ObjectType + var pcToIndex: Array[Int] + var code: Array[Stmt[V]] + } + + type StateType <: AnalysisState + + protected[this] def raterFqn: String + + val rater: DomainSpecificRater + + implicit protected[this] val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + + val configuredPurity: ConfiguredPurity = project.get(ConfiguredPurityKey) + + /** + * Reduces the maxPurity of the current method to at most the given purity level. + */ + def reducePurityLB(newLevel: Purity)(implicit state: StateType): Unit = { + state.lbPurity = state.lbPurity meet newLevel + } + + /** + * Reduces the minPurity and maxPurity of the current method to at most the given purity level. + */ + def atMost(newLevel: Purity)(implicit state: StateType): Unit = { + state.lbPurity = state.lbPurity meet newLevel + state.ubPurity = state.ubPurity meet newLevel + } + + /** + * Examines whether the given expression denotes an object/array that is local to the current + * method, i.e. the method has control over the object/array and actions on it might not + * influence purity. + * + * @param otherwise The maxPurity will be reduced to at most this level if the expression is not + * local. + */ + def isLocal( + expr: Expr[V], + otherwise: Purity, + excludedDefSites: IntTrieSet = EmptyIntTrieSet + )(implicit state: StateType): Boolean + + /** + * Checks whether the statement, which is the origin of an exception, directly created the + * exception or if the VM instantiated the exception. Here, we are only concerned about the + * exceptions thrown by the instructions not about exceptions that are transitively thrown; + * e.g. if a method is called. + * TODO We need this method because currently, for exceptions that terminate the method, no + * definitions are recorded. Once this is done, use that information instead to determine + * whether it may be an immediate exception or not. + */ + def isSourceOfImmediateException(origin: ValueOrigin)(implicit state: StateType): Boolean = { + + def evaluationMayCauseVMLevelException(expr: Expr[V]): Boolean = { + (expr.astID: @switch) match { + + case NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => + val rcvr = expr.asInstanceFunctionCall.receiver + !rcvr.isVar || rcvr.asVar.value.asReferenceValue.isNull.isYesOrUnknown + + case StaticFunctionCall.ASTID => false + + case _ => true + } } - type StateType <: AnalysisState + val stmt = state.code(origin) + (stmt.astID: @switch) match { + case StaticMethodCall.ASTID => false // We are looking for implicit exceptions only - protected[this] def raterFqn: String + case Throw.ASTID => + stmt.asThrow.exception.asVar.value.asReferenceValue.isNull.isNotNo - val rater: DomainSpecificRater + case NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID => + val rcvr = stmt.asInstanceMethodCall.receiver + !rcvr.isVar || rcvr.asVar.value.asReferenceValue.isNull.isNotNo - protected[this] implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + case Assignment.ASTID => evaluationMayCauseVMLevelException(stmt.asAssignment.expr) - val configuredPurity: ConfiguredPurity = project.get(ConfiguredPurityKey) + case ExprStmt.ASTID => evaluationMayCauseVMLevelException(stmt.asExprStmt.expr) - /** - * Reduces the maxPurity of the current method to at most the given purity level. - */ - def reducePurityLB(newLevel: Purity)(implicit state: StateType): Unit = { - state.lbPurity = state.lbPurity meet newLevel + case _ => true } - - /** - * Reduces the minPurity and maxPurity of the current method to at most the given purity level. - */ - def atMost(newLevel: Purity)(implicit state: StateType): Unit = { - state.lbPurity = state.lbPurity meet newLevel - state.ubPurity = state.ubPurity meet newLevel - } - - /** - * Examines whether the given expression denotes an object/array that is local to the current - * method, i.e. the method has control over the object/array and actions on it might not - * influence purity. - * - * @param otherwise The maxPurity will be reduced to at most this level if the expression is not - * local. - */ - def isLocal( - expr: Expr[V], - otherwise: Purity, - excludedDefSites: IntTrieSet = EmptyIntTrieSet - )(implicit state: StateType): Boolean - - /** - * Checks whether the statement, which is the origin of an exception, directly created the - * exception or if the VM instantiated the exception. Here, we are only concerned about the - * exceptions thrown by the instructions not about exceptions that are transitively thrown; - * e.g. if a method is called. - * TODO We need this method because currently, for exceptions that terminate the method, no - * definitions are recorded. Once this is done, use that information instead to determine - * whether it may be an immediate exception or not. - */ - def isSourceOfImmediateException(origin: ValueOrigin)(implicit state: StateType): Boolean = { - - def evaluationMayCauseVMLevelException(expr: Expr[V]): Boolean = { - (expr.astID: @switch) match { - - case NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ - val rcvr = expr.asInstanceFunctionCall.receiver - !rcvr.isVar || rcvr.asVar.value.asReferenceValue.isNull.isYesOrUnknown - - case StaticFunctionCall.ASTID ⇒ false - - case _ ⇒ true - } - } - - val stmt = state.code(origin) - (stmt.astID: @switch) match { - case StaticMethodCall.ASTID ⇒ false // We are looking for implicit exceptions only - - case Throw.ASTID ⇒ - stmt.asThrow.exception.asVar.value.asReferenceValue.isNull.isNotNo - - case NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ - val rcvr = stmt.asInstanceMethodCall.receiver - !rcvr.isVar || rcvr.asVar.value.asReferenceValue.isNull.isNotNo - - case Assignment.ASTID ⇒ evaluationMayCauseVMLevelException(stmt.asAssignment.expr) - - case ExprStmt.ASTID ⇒ evaluationMayCauseVMLevelException(stmt.asExprStmt.expr) - - case _ ⇒ true + } + + /** + * Examines whether a call constitutes a domain-specific action using the domain-specific rater. + * If it is, the maxPurity will be reduced to at most the domain-specific purity returned by the + * domain-specific rater. + */ + def isDomainSpecificCall( + call: Call[V], + receiver: Option[Expr[V]] + )(implicit state: StateType): Boolean = { + implicit val code: Array[Stmt[V]] = state.code + val ratedResult = rater.handleCall(call, receiver) + if (ratedResult.isDefined) + atMost(ratedResult.get) + ratedResult.isDefined + } + + /** + * Examines a statement for its influence on the method's purity. + * This method will return false for impure statements, so evaluation can be terminated early. + */ + def checkPurityOfStmt(stmt: Stmt[V])(implicit state: StateType): Boolean = { + val isStmtNotImpure = (stmt.astID: @switch) match { + // For method calls, purity will be checked later + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID => + true + + // We don't handle unresolved Invokedynamics + // - either OPAL removes it or we forget about it + case InvokedynamicMethodCall.ASTID => + atMost(ImpureByAnalysis) + false + + // Returning objects/arrays is pure, if the returned object/array is locally initialized + // and non-escaping or the object is immutable + case ReturnValue.ASTID => + checkPurityOfReturn(stmt.asReturnValue.expr) + true + case Throw.ASTID => + checkPurityOfReturn(stmt.asThrow.exception) + true + + // Synchronization on non-escaping locally initialized objects/arrays is pure (and + // useless...) + case MonitorEnter.ASTID | MonitorExit.ASTID => + isLocal(stmt.asSynchronizationStmt.objRef, ImpureByAnalysis) + + // Storing into non-escaping locally initialized objects/arrays is pure + case ArrayStore.ASTID => isLocal(stmt.asArrayStore.arrayRef, ImpureByAnalysis) + case PutField.ASTID => isLocal(stmt.asPutField.objRef, ImpureByAnalysis) + + case PutStatic.ASTID => + // Note that a putstatic is not necessarily pure/sideeffect free, even if it + // is executed within a static initializer to initialize a field of + // `the` class; it is possible that the initialization triggers the + // initialization of another class which reads the value of this static field. + // See + // https://stackoverflow.com/questions/6416408/static-circular-dependency-in-java + // for an in-depth discussion. + // (Howevever, if we would check for cycles, we could determine that it is pure, + // but this is not considered to be too useful...) + atMost(ImpureByAnalysis) + false + + // Creating implicit exceptions is side-effect free (because of fillInStackTrace) + // but it may be ignored as domain-specific + case CaughtException.ASTID => + for { + origin <- stmt.asCaughtException.origins + if isImmediateVMException(origin) + } { + val baseOrigin = state.code(ai.underlyingPC(origin)) + val ratedResult = rater.handleException(baseOrigin) + if (ratedResult.isDefined) atMost(ratedResult.get) + else atMost(SideEffectFree) } + true + + // Reference comparisons may have different results for structurally equal values + case If.ASTID => + val If(_, left, _, right, _) = stmt + if (left.cTpe eq ComputationalTypeReference) + if (!(isLocal(left, CompileTimePure) || isLocal(right, CompileTimePure))) + atMost(SideEffectFree) + true + + // The following statements do not further influence purity + case Goto.ASTID | JSR.ASTID | Ret.ASTID | Switch.ASTID | Assignment.ASTID | Return.ASTID | + Nop.ASTID | ExprStmt.ASTID | Checkcast.ASTID => + true } - /** - * Examines whether a call constitutes a domain-specific action using the domain-specific rater. - * If it is, the maxPurity will be reduced to at most the domain-specific purity returned by the - * domain-specific rater. - */ - def isDomainSpecificCall( - call: Call[V], - receiver: Option[Expr[V]] - )(implicit state: StateType): Boolean = { + isStmtNotImpure && stmt.forallSubExpressions(checkPurityOfExpr) + } + + /** + * Examines an expression for its influence on the method's purity. + * This method will return false for impure expressions, so evaluation can be terminated early. + */ + def checkPurityOfExpr(expr: Expr[V])(implicit state: StateType): Boolean = { + val isExprNotImpure = (expr.astID: @switch) match { + // For function calls, purity will be checked later + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => + true + + // Field/array loads are pure if the field is (effectively) final or the object/array is + // local and non-escaping + case GetStatic.ASTID => implicit val code: Array[Stmt[V]] = state.code - val ratedResult = rater.handleCall(call, receiver) - if (ratedResult.isDefined) - atMost(ratedResult.get) - ratedResult.isDefined - } + val ratedResult = rater.handleGetStatic(expr.asGetStatic) + if (ratedResult.isDefined) atMost(ratedResult.get) + else checkPurityOfFieldRef(expr.asGetStatic) + true + case GetField.ASTID => + checkPurityOfFieldRef(expr.asGetField) + true + case ArrayLoad.ASTID => + if (state.ubPurity.isDeterministic) + isLocal(expr.asArrayLoad.arrayRef, SideEffectFree) + true + + // We don't handle unresolved Invokedynamic + // - either OPAL removes it or we forget about it + case InvokedynamicFunctionCall.ASTID => + atMost(ImpureByAnalysis) + false + + // The following expressions do not further influence purity, potential exceptions are + // handled explicitly + case New.ASTID | NewArray.ASTID | InstanceOf.ASTID | Compare.ASTID | Param.ASTID | + MethodTypeConst.ASTID | MethodHandleConst.ASTID | IntConst.ASTID | LongConst.ASTID | + FloatConst.ASTID | DoubleConst.ASTID | StringConst.ASTID | ClassConst.ASTID | + NullExpr.ASTID | BinaryExpr.ASTID | PrefixExpr.ASTID | PrimitiveTypecastExpr.ASTID | + ArrayLength.ASTID | Var.ASTID => + true - /** - * Examines a statement for its influence on the method's purity. - * This method will return false for impure statements, so evaluation can be terminated early. - */ - def checkPurityOfStmt(stmt: Stmt[V])(implicit state: StateType): Boolean = { - val isStmtNotImpure = (stmt.astID: @switch) match { - // For method calls, purity will be checked later - case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ - true - - // We don't handle unresolved Invokedynamics - // - either OPAL removes it or we forget about it - case InvokedynamicMethodCall.ASTID ⇒ - atMost(ImpureByAnalysis) - false - - // Returning objects/arrays is pure, if the returned object/array is locally initialized - // and non-escaping or the object is immutable - case ReturnValue.ASTID ⇒ - checkPurityOfReturn(stmt.asReturnValue.expr) - true - case Throw.ASTID ⇒ - checkPurityOfReturn(stmt.asThrow.exception) - true - - // Synchronization on non-escaping locally initialized objects/arrays is pure (and - // useless...) - case MonitorEnter.ASTID | MonitorExit.ASTID ⇒ - isLocal(stmt.asSynchronizationStmt.objRef, ImpureByAnalysis) - - // Storing into non-escaping locally initialized objects/arrays is pure - case ArrayStore.ASTID ⇒ isLocal(stmt.asArrayStore.arrayRef, ImpureByAnalysis) - case PutField.ASTID ⇒ isLocal(stmt.asPutField.objRef, ImpureByAnalysis) - - case PutStatic.ASTID ⇒ - // Note that a putstatic is not necessarily pure/sideeffect free, even if it - // is executed within a static initializer to initialize a field of - // `the` class; it is possible that the initialization triggers the - // initialization of another class which reads the value of this static field. - // See - // https://stackoverflow.com/questions/6416408/static-circular-dependency-in-java - // for an in-depth discussion. - // (Howevever, if we would check for cycles, we could determine that it is pure, - // but this is not considered to be too useful...) - atMost(ImpureByAnalysis) - false - - // Creating implicit exceptions is side-effect free (because of fillInStackTrace) - // but it may be ignored as domain-specific - case CaughtException.ASTID ⇒ - for { - origin ← stmt.asCaughtException.origins - if isImmediateVMException(origin) - } { - val baseOrigin = state.code(ai.underlyingPC(origin)) - val ratedResult = rater.handleException(baseOrigin) - if (ratedResult.isDefined) atMost(ratedResult.get) - else atMost(SideEffectFree) - } - true - - // Reference comparisons may have different results for structurally equal values - case If.ASTID ⇒ - val If(_, left, _, right, _) = stmt - if (left.cTpe eq ComputationalTypeReference) - if (!(isLocal(left, CompileTimePure) || isLocal(right, CompileTimePure))) - atMost(SideEffectFree) - true - - // The following statements do not further influence purity - case Goto.ASTID | JSR.ASTID | Ret.ASTID | Switch.ASTID | Assignment.ASTID | - Return.ASTID | Nop.ASTID | ExprStmt.ASTID | Checkcast.ASTID ⇒ - true - } - - isStmtNotImpure && stmt.forallSubExpressions(checkPurityOfExpr) } - /** - * Examines an expression for its influence on the method's purity. - * This method will return false for impure expressions, so evaluation can be terminated early. - */ - def checkPurityOfExpr(expr: Expr[V])(implicit state: StateType): Boolean = { - val isExprNotImpure = (expr.astID: @switch) match { - // For function calls, purity will be checked later - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | - VirtualFunctionCall.ASTID ⇒ - true - - // Field/array loads are pure if the field is (effectively) final or the object/array is - // local and non-escaping - case GetStatic.ASTID ⇒ - implicit val code: Array[Stmt[V]] = state.code - val ratedResult = rater.handleGetStatic(expr.asGetStatic) - if (ratedResult.isDefined) atMost(ratedResult.get) - else checkPurityOfFieldRef(expr.asGetStatic) - true - case GetField.ASTID ⇒ - checkPurityOfFieldRef(expr.asGetField) - true - case ArrayLoad.ASTID ⇒ - if (state.ubPurity.isDeterministic) - isLocal(expr.asArrayLoad.arrayRef, SideEffectFree) - true - - // We don't handle unresolved Invokedynamic - // - either OPAL removes it or we forget about it - case InvokedynamicFunctionCall.ASTID ⇒ - atMost(ImpureByAnalysis) - false - - // The following expressions do not further influence purity, potential exceptions are - // handled explicitly - case New.ASTID | NewArray.ASTID | InstanceOf.ASTID | Compare.ASTID | Param.ASTID | - MethodTypeConst.ASTID | MethodHandleConst.ASTID | IntConst.ASTID | LongConst.ASTID | - FloatConst.ASTID | DoubleConst.ASTID | StringConst.ASTID | ClassConst.ASTID | - NullExpr.ASTID | BinaryExpr.ASTID | PrefixExpr.ASTID | PrimitiveTypecastExpr.ASTID | - ArrayLength.ASTID | Var.ASTID ⇒ - true - - } - - isExprNotImpure && expr.forallSubExpressions(checkPurityOfExpr) + isExprNotImpure && expr.forallSubExpressions(checkPurityOfExpr) + } + + def checkPurityOfMethod( + callee: DeclaredMethod, + params: Seq[Expr[V]] + )(implicit state: StateType): Boolean = { + if (callee.hasSingleDefinedMethod && (callee.definedMethod eq state.method)) { + true + } else { + val calleePurity = propertyStore(callee, Purity.key) + checkMethodPurity(calleePurity, params) } - - def checkPurityOfMethod( - callee: DeclaredMethod, - params: Seq[Expr[V]] - )(implicit state: StateType): Boolean = { - if (callee.hasSingleDefinedMethod && (callee.definedMethod eq state.method)) { - true - } else { - val calleePurity = propertyStore(callee, Purity.key) - checkMethodPurity(calleePurity, params) - } + } + + def getCall(stmt: Stmt[V])(implicit state: StateType): Call[V] = stmt.astID match { + case StaticMethodCall.ASTID => stmt.asStaticMethodCall + case NonVirtualMethodCall.ASTID => stmt.asNonVirtualMethodCall + case VirtualMethodCall.ASTID => stmt.asVirtualMethodCall + case Assignment.ASTID => stmt.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID => stmt.asExprStmt.expr.asFunctionCall + case CaughtException.ASTID => + /* + * There is no caught exception instruction in bytecode, so it might be the case, that + * in the three-address code, there is a CaughtException stmt right before the call + * with the same pc. Therefore, we have to get the call stmt after the current stmt. + * + * Example: + * void foo() { + * try { + * ... + * } catch (Exception e) { + * e.printStackTrace(); + * } + * } + * + * In TAC: + * 12: pc=52 caught java.lang.Exception ... + * 13: pc=52 java.lang.Exception.printStackTrace() + */ + getCall(state.code(state.pcToIndex(stmt.pc) + 1)) + case _ => + throw new IllegalStateException(s"unexpected stmt $stmt") + } + + /** + * Examines the influence of the purity property of a method on the examined method's purity. + * + * @note Adds dependendies when necessary. + */ + def checkMethodPurity( + ep: EOptionP[DeclaredMethod, Purity], + params: Seq[Expr[V]] = Seq.empty + )(implicit state: StateType): Boolean + + /** + * Examines whether a field read influences a method's purity. + * Reading values from fields that are not (effectively) final may cause nondeterministic + * behavior, so the method can only be side-effect free. + */ + def checkPurityOfFieldRef( + fieldRef: FieldRead[V] + )(implicit state: StateType): Unit = { + // Don't do dependee checks if already non-deterministic + if (state.ubPurity.isDeterministic) { + fieldRef.asFieldRead.resolveField match { + case Some(field) if field.isStatic => + checkFieldMutability(propertyStore(field, ReferenceImmutability.key), None) //FieldMutability.key), None) + case Some(field) => + checkFieldMutability( + propertyStore(field, ReferenceImmutability.key), + Some(fieldRef.asGetField.objRef) //FieldMutability.key), Some(fieldRef.asGetField.objRef) + ) + case _ => // Unknown field + if (fieldRef.isGetField) isLocal(fieldRef.asGetField.objRef, SideEffectFree) + else atMost(SideEffectFree) + } } - - def getCall(stmt: Stmt[V])(implicit state: StateType): Call[V] = stmt.astID match { - case StaticMethodCall.ASTID ⇒ stmt.asStaticMethodCall - case NonVirtualMethodCall.ASTID ⇒ stmt.asNonVirtualMethodCall - case VirtualMethodCall.ASTID ⇒ stmt.asVirtualMethodCall - case Assignment.ASTID ⇒ stmt.asAssignment.expr.asFunctionCall - case ExprStmt.ASTID ⇒ stmt.asExprStmt.expr.asFunctionCall - case CaughtException.ASTID ⇒ - /* - * There is no caught exception instruction in bytecode, so it might be the case, that - * in the three-address code, there is a CaughtException stmt right before the call - * with the same pc. Therefore, we have to get the call stmt after the current stmt. - * - * Example: - * void foo() { - * try { - * ... - * } catch (Exception e) { - * e.printStackTrace(); - * } - * } - * - * In TAC: - * 12: pc=52 caught java.lang.Exception ... - * 13: pc=52 java.lang.Exception.printStackTrace() - */ - getCall(state.code(state.pcToIndex(stmt.pc) + 1)) - case _ ⇒ - throw new IllegalStateException(s"unexpected stmt $stmt") + } + + /** + * Examines the influence that a given field mutability has on the method's purity. + */ + def checkFieldMutability( + ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], + objRef: Option[Expr[V]] + )(implicit state: StateType): Unit = ep match { + + //case LBP(ImmutableReference(_)) ⇒ + //case LBP(LazyInitializedReference) ⇒ + case LBP(ImmutableReference(_) | LazyInitializedReference) => + println("====>>>> EP: " + ep) //_: FinalField) ⇒ // Final fields don't impede purity + case FinalP(MutableReference) => + println("====>>>> EP: " + ep) //_: FinalEP[Field, ReferenceImmutability] ⇒ //FieldMutability] ⇒ // Mutable field + if (objRef.isDefined) { + if (state.ubPurity.isDeterministic) + isLocal(objRef.get, SideEffectFree) + } else atMost(SideEffectFree) + case _ => + reducePurityLB(SideEffectFree) + if (state.ubPurity.isDeterministic) + handleUnknownFieldMutability(ep, objRef) + + } + + /** + * Handles what to do when the mutability of a field is not yet known. + * Analyses must implement this method with the behavior they need, e.g. registering dependees. + */ + def handleUnknownFieldMutability( + ep: EOptionP[Field, ReferenceImmutability], //FieldMutability], + objRef: Option[Expr[V]] + )(implicit state: StateType): Unit + + /** + * Examines the effect of returning a value on the method's purity. + * Returning a reference to a mutable object or array may cause nondeterministic behavior + * as the object/array may be modified between invocations of the method, so the method can + * only be side-effect free. E.g., a given parameter which references a mutable object is + * returned (and not otherwise accessed). + */ + def checkPurityOfReturn(returnValue: Expr[V])(implicit state: StateType): Unit = { + if (returnValue.cTpe != ComputationalTypeReference) + return; // Only non-primitive return values influence purity. + + if (!state.ubPurity.isDeterministic) + return; // If the method can't be pure, the return value is not important. + + if (!returnValue.isVar) { + // The expression could refer to further expressions in a non-flat representation. To + // avoid special handling, we just fallback to SideEffectFreeWithoutAllocations here if + // the return value is not local as the analysis is intended to be used on flat + // representations anyway. + isLocal(returnValue, SideEffectFree) + return; } - /** - * Examines the influence of the purity property of a method on the examined method's purity. - * - * @note Adds dependendies when necessary. - */ - def checkMethodPurity( - ep: EOptionP[DeclaredMethod, Purity], - params: Seq[Expr[V]] = Seq.empty - )(implicit state: StateType): Boolean - - /** - * Examines whether a field read influences a method's purity. - * Reading values from fields that are not (effectively) final may cause nondeterministic - * behavior, so the method can only be side-effect free. - */ - def checkPurityOfFieldRef( - fieldRef: FieldRead[V] - )(implicit state: StateType): Unit = { - // Don't do dependee checks if already non-deterministic - if (state.ubPurity.isDeterministic) { - fieldRef.asFieldRead.resolveField match { - case Some(field) if field.isStatic ⇒ - checkFieldMutability(propertyStore(field, ReferenceImmutability.key), None) //FieldMutability.key), None) - case Some(field) ⇒ - checkFieldMutability( - propertyStore(field, ReferenceImmutability.key), Some(fieldRef.asGetField.objRef) //FieldMutability.key), Some(fieldRef.asGetField.objRef) - ) - case _ ⇒ // Unknown field - if (fieldRef.isGetField) isLocal(fieldRef.asGetField.objRef, SideEffectFree) - else atMost(SideEffectFree) - } - } - } + val value = returnValue.asVar.value.asReferenceValue + if (value.isNull.isYes) + return; // Null is immutable - /** - * Examines the influence that a given field mutability has on the method's purity. - */ - def checkFieldMutability( - ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], - objRef: Option[Expr[V]] - )(implicit state: StateType): Unit = ep match { - case LBP(ImmutableReference | LazyInitializedReference) ⇒ //_: FinalField) ⇒ // Final fields don't impede purity - case _: FinalEP[Field, ReferenceImmutability] ⇒ //FieldMutability] ⇒ // Mutable field - if (objRef.isDefined) { - if (state.ubPurity.isDeterministic) - isLocal(objRef.get, SideEffectFree) - } else atMost(SideEffectFree) - case _ ⇒ - reducePurityLB(SideEffectFree) - if (state.ubPurity.isDeterministic) - handleUnknownFieldMutability(ep, objRef) + if (value.upperTypeBound.exists(_.isArrayType)) { + // Arrays are always mutable + isLocal(returnValue, SideEffectFree) + return; } - /** - * Handles what to do when the mutability of a field is not yet known. - * Analyses must implement this method with the behavior they need, e.g. registering dependees. - */ - def handleUnknownFieldMutability( - ep: EOptionP[Field, ReferenceImmutability], //FieldMutability], - objRef: Option[Expr[V]] - )(implicit state: StateType): Unit - - /** - * Examines the effect of returning a value on the method's purity. - * Returning a reference to a mutable object or array may cause nondeterministic behavior - * as the object/array may be modified between invocations of the method, so the method can - * only be side-effect free. E.g., a given parameter which references a mutable object is - * returned (and not otherwise accessed). - */ - def checkPurityOfReturn(returnValue: Expr[V])(implicit state: StateType): Unit = { - if (returnValue.cTpe != ComputationalTypeReference) - return ; // Only non-primitive return values influence purity. - - if (!state.ubPurity.isDeterministic) - return ; // If the method can't be pure, the return value is not important. - - if (!returnValue.isVar) { - // The expression could refer to further expressions in a non-flat representation. To - // avoid special handling, we just fallback to SideEffectFreeWithoutAllocations here if - // the return value is not local as the analysis is intended to be used on flat - // representations anyway. - isLocal(returnValue, SideEffectFree) - return ; - } - - val value = returnValue.asVar.value.asReferenceValue - if (value.isNull.isYes) - return ; // Null is immutable + if (value.isPrecise) { // Precise class known, use ClassImmutability + val returnType = value.upperTypeBound.head + + val classImmutability = + propertyStore( + returnType, + ClassImmutability_new.key // ClassImmutability.key + ).asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]] // ClassImmutability]] + checkTypeMutability(classImmutability, returnValue) + + } else { // Precise class unknown, use TypeImmutability + // IMPROVE Use ObjectType once we attach the respective information to ObjectTypes + val returnTypes = value.upperTypeBound + + returnTypes.forall { returnType => + val typeImmutability = + propertyStore( + returnType, + TypeImmutability_new.key //.key + ).asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] //TypeImmutability]] + checkTypeMutability(typeImmutability, returnValue) + } + } + } + + /** + * Examines the effect that the mutability of a returned value's type has on the method's + * purity. + */ + def checkTypeMutability( + ep: EOptionP[ObjectType, Property], + returnValue: Expr[V] + )(implicit state: StateType): Boolean = ep match { + // Returning immutable object is pure + case LBP(DeepImmutableType | DeepImmutableClass) => + true // ImmutableType | ImmutableObject) ⇒ true7 + case _: FinalEP[ObjectType, Property] => + atMost(Pure) // Can not be compile time pure if mutable object is returned + if (state.ubPurity.isDeterministic) + isLocal(returnValue, SideEffectFree) + false // Return early if we are already side-effect free + case _ => + reducePurityLB(SideEffectFree) + if (state.ubPurity.isDeterministic) + handleUnknownTypeMutability(ep, returnValue) + true + } + + /** + * Handles what to do when the mutability of a type is not yet known. + * Analyses must implement this method with the behavior they need, e.g. registering dependees. + */ + def handleUnknownTypeMutability( + ep: EOptionP[ObjectType, Property], + expr: Expr[V] + )(implicit state: StateType): Unit + + /** + * Examines the effect that the purity of all potential callees has on the purity of the method. + */ + def checkPurityOfCallees( + calleesEOptP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: StateType + ): Boolean = { + handleCalleesUpdate(calleesEOptP) + calleesEOptP match { + case UBPS(p: Callees, isFinal) => + if (!isFinal) reducePurityLB(ImpureByAnalysis) + + val hasIncompleteCallSites = + p.incompleteCallSites.exists { pc => + val index = state.pcToIndex(pc) + if (index < 0) + false // call will not be executed + else { + val call = getCall(state.code(state.pcToIndex(pc))) + !isDomainSpecificCall(call, call.receiverOption) + } + } - if (value.upperTypeBound.exists(_.isArrayType)) { - // Arrays are always mutable - isLocal(returnValue, SideEffectFree) - return ; + if (hasIncompleteCallSites) { + atMost(ImpureByAnalysis) + return false; } - if (value.isPrecise) { // Precise class known, use ClassImmutability - val returnType = value.upperTypeBound.head - - val classImmutability = - propertyStore( - returnType, - ClassImmutability_new.key // ClassImmutability.key - ).asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]] // ClassImmutability]] - checkTypeMutability(classImmutability, returnValue) - - } else { // Precise class unknown, use TypeImmutability - // IMPROVE Use ObjectType once we attach the respective information to ObjectTypes - val returnTypes = value.upperTypeBound - - returnTypes.forall { returnType ⇒ - val typeImmutability = - propertyStore( - returnType, - TypeImmutability_new.key //.key - ).asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] //TypeImmutability]] - checkTypeMutability(typeImmutability, returnValue) + val noDirectCalleeIsImpure = p.directCallSites().forall { + case (pc, callees) => + val index = state.pcToIndex(pc) + if (index < 0) + true // call will not be executed + else { + val call = getCall(state.code(index)) + isDomainSpecificCall(call, call.receiverOption) || + callees.forall { callee => + checkPurityOfMethod( + callee, + call.receiverOption.orNull +: call.params + ) + } } } - } - - /** - * Examines the effect that the mutability of a returned value's type has on the method's - * purity. - */ - def checkTypeMutability( - ep: EOptionP[ObjectType, Property], - returnValue: Expr[V] - )(implicit state: StateType): Boolean = ep match { - // Returning immutable object is pure - case LBP(DeepImmutableType | DeepImmutableClass) ⇒ true // ImmutableType | ImmutableObject) ⇒ true7 - case _: FinalEP[ObjectType, Property] ⇒ - atMost(Pure) // Can not be compile time pure if mutable object is returned - if (state.ubPurity.isDeterministic) - isLocal(returnValue, SideEffectFree) - false // Return early if we are already side-effect free - case _ ⇒ - reducePurityLB(SideEffectFree) - if (state.ubPurity.isDeterministic) - handleUnknownTypeMutability(ep, returnValue) - true - } - /** - * Handles what to do when the mutability of a type is not yet known. - * Analyses must implement this method with the behavior they need, e.g. registering dependees. - */ - def handleUnknownTypeMutability( - ep: EOptionP[ObjectType, Property], - expr: Expr[V] - )(implicit state: StateType): Unit - - /** - * Examines the effect that the purity of all potential callees has on the purity of the method. - */ - def checkPurityOfCallees( - calleesEOptP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: StateType - ): Boolean = { - handleCalleesUpdate(calleesEOptP) - calleesEOptP match { - case UBPS(p: Callees, isFinal) ⇒ - if (!isFinal) reducePurityLB(ImpureByAnalysis) - - val hasIncompleteCallSites = - p.incompleteCallSites.exists { pc ⇒ - val index = state.pcToIndex(pc) - if (index < 0) - false // call will not be executed - else { - val call = getCall(state.code(state.pcToIndex(pc))) - !isDomainSpecificCall(call, call.receiverOption) - } + if (!noDirectCalleeIsImpure) + return false; + + val noIndirectCalleeIsImpure = p.indirectCallSites().forall { + case (pc, callees) => + val index = state.pcToIndex(pc) + if (index < 0) + true // call will not be executed + else { + val call = getCall(state.code(index)) + isDomainSpecificCall(call, call.receiverOption) || + callees.forall { callee => + checkPurityOfMethod( + callee, + p.indirectCallReceiver(pc, callee) + .map(receiver => uVarForDefSites(receiver, state.pcToIndex)) + .orNull +: + p.indirectCallParameters(pc, callee).map { paramO => + paramO.map(uVarForDefSites(_, state.pcToIndex)).orNull } - - if (hasIncompleteCallSites) { - atMost(ImpureByAnalysis) - return false; - } - - val noDirectCalleeIsImpure = p.directCallSites().forall { - case (pc, callees) ⇒ - val index = state.pcToIndex(pc) - if (index < 0) - true // call will not be executed - else { - val call = getCall(state.code(index)) - isDomainSpecificCall(call, call.receiverOption) || - callees.forall { callee ⇒ - checkPurityOfMethod( - callee, - call.receiverOption.orNull +: call.params - ) - } - } - } - - if (!noDirectCalleeIsImpure) - return false; - - val noIndirectCalleeIsImpure = p.indirectCallSites().forall { - case (pc, callees) ⇒ - val index = state.pcToIndex(pc) - if (index < 0) - true // call will not be executed - else { - val call = getCall(state.code(index)) - isDomainSpecificCall(call, call.receiverOption) || - callees.forall { callee ⇒ - checkPurityOfMethod( - callee, - p.indirectCallReceiver(pc, callee).map(receiver ⇒ - uVarForDefSites(receiver, state.pcToIndex)).orNull +: - p.indirectCallParameters(pc, callee).map { paramO ⇒ - paramO.map(uVarForDefSites(_, state.pcToIndex)).orNull - } - ) - } - } - } - - noIndirectCalleeIsImpure - - case _ ⇒ - reducePurityLB(ImpureByAnalysis) - true + ) + } + } } - } - /** - * Handles what to do when the set of potential callees changes. - * Analyses must implement this method with the behavior they need, e.g. registering dependees. - */ - def handleCalleesUpdate( - callees: EOptionP[DeclaredMethod, Callees] - )(implicit state: StateType): Unit - - /** - * Handles what to do if the TACAI is not yet final. - */ - def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: StateType): Unit - - /** - * Retrieves and commits the methods purity as calculated for its declaring class type for the - * current DefinedMethod that represents the non-overwritten method in a subtype. - */ - def baseMethodPurity(dm: DefinedMethod): ProperPropertyComputationResult = { - - 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) - case epk ⇒ - InterimResult(dm, ImpureByAnalysis, CompileTimePure, Seq(epk), c) - } + noIndirectCalleeIsImpure - c(propertyStore(declaredMethods(dm.definedMethod), Purity.key)) + case _ => + reducePurityLB(ImpureByAnalysis) + true } - - /** - * Determines the purity of the given method. - * - * @param definedMethod A defined method with a body. - */ - def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult - - /** Called when the analysis is scheduled lazily. */ - def doDeterminePurity(e: Entity): ProperPropertyComputationResult = { - e match { - case dm: DefinedMethod if dm.definedMethod.body.isDefined ⇒ - determinePurity(dm) - case dm: DeclaredMethod ⇒ Result(dm, ImpureByLackOfInformation) - case _ ⇒ - throw new IllegalArgumentException(s"$e is not a declared method") - } + } + + /** + * Handles what to do when the set of potential callees changes. + * Analyses must implement this method with the behavior they need, e.g. registering dependees. + */ + def handleCalleesUpdate( + callees: EOptionP[DeclaredMethod, Callees] + )(implicit state: StateType): Unit + + /** + * Handles what to do if the TACAI is not yet final. + */ + def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: StateType): Unit + + /** + * Retrieves and commits the methods purity as calculated for its declaring class type for the + * current DefinedMethod that represents the non-overwritten method in a subtype. + */ + def baseMethodPurity(dm: DefinedMethod): ProperPropertyComputationResult = { + + 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) + case epk => + InterimResult(dm, ImpureByAnalysis, CompileTimePure, Seq(epk), c) } - /** - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - def getTACAI( - method: Method - )(implicit state: StateType): Option[TACode[TACMethodParameter, V]] = { - propertyStore(method, TACAI.key) match { - case finalEP: FinalEP[Method, TACAI] ⇒ - handleTACAI(finalEP) - finalEP.ub.tac - case eps @ InterimUBP(ub: TACAI) ⇒ - reducePurityLB(ImpureByAnalysis) - handleTACAI(eps) - ub.tac - case epk ⇒ - reducePurityLB(ImpureByAnalysis) - handleTACAI(epk) - None - } + c(propertyStore(declaredMethods(dm.definedMethod), Purity.key)) + } + + /** + * Determines the purity of the given method. + * + * @param definedMethod A defined method with a body. + */ + def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult + + /** Called when the analysis is scheduled lazily. */ + def doDeterminePurity(e: Entity): ProperPropertyComputationResult = { + e match { + case dm: DefinedMethod if dm.definedMethod.body.isDefined => + determinePurity(dm) + case dm: DeclaredMethod => Result(dm, ImpureByLackOfInformation) + case _ => + throw new IllegalArgumentException(s"$e is not a declared method") } - - def resolveDomainSpecificRater(fqn: String): DomainSpecificRater = { - import scala.reflect.runtime.universe.runtimeMirror - val mirror = runtimeMirror(getClass.getClassLoader) - try { - val module = mirror.staticModule(fqn) - mirror.reflectModule(module).instance.asInstanceOf[DomainSpecificRater] - } catch { - case ex @ (_: ScalaReflectionException | _: ClassCastException) ⇒ - OPALLogger.error( - "analysis configuration", - "resolve of domain specific rater failed, change "+ - s"org.opalj.fpcf.${this.getClass.getName}.domainSpecificRater in "+ - "ai/reference.conf to an existing DomainSpecificRater implementation", - ex - )(GlobalLogContext) - new BaseDomainSpecificRater // Provide a safe default if resolution failed - } + } + + /** + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + def getTACAI( + method: Method + )(implicit state: StateType): Option[TACode[TACMethodParameter, V]] = { + propertyStore(method, TACAI.key) match { + case finalEP: FinalEP[Method, TACAI] => + handleTACAI(finalEP) + finalEP.ub.tac + case eps @ InterimUBP(ub: TACAI) => + reducePurityLB(ImpureByAnalysis) + handleTACAI(eps) + ub.tac + case epk => + reducePurityLB(ImpureByAnalysis) + handleTACAI(epk) + None + } + } + + def resolveDomainSpecificRater(fqn: String): DomainSpecificRater = { + import scala.reflect.runtime.universe.runtimeMirror + val mirror = runtimeMirror(getClass.getClassLoader) + try { + val module = mirror.staticModule(fqn) + mirror.reflectModule(module).instance.asInstanceOf[DomainSpecificRater] + } catch { + case ex @ (_: ScalaReflectionException | _: ClassCastException) => + OPALLogger.error( + "analysis configuration", + "resolve of domain specific rater failed, change " + + s"org.opalj.fpcf.${this.getClass.getName}.domainSpecificRater in " + + "ai/reference.conf to an existing DomainSpecificRater implementation", + ex + )(GlobalLogContext) + new BaseDomainSpecificRater // Provide a safe default if resolution failed } + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala index 0566b1b90d..ca38f81c69 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala @@ -93,927 +93,960 @@ import org.opalj.tac.fpcf.properties.TACAI * as `ExternallyPure`. * @author Dominik Helm */ -class L2PurityAnalysis_new private[analyses] (val project: SomeProject) extends AbstractPurityAnalysis_new { - - /** - * Holds the state of this analysis. - * @param lbPurity The current minimum purity level for the method - * @param ubPurity The current maximum purity level for the method that will be assigned by - * checkPurityOfX methods to aggregrate the purity - * @param method The currently analyzed method - * @param definedMethod The corresponding DefinedMethod we report results for - * @param declClass The declaring class of the currently analyzed method - * @param code The code of the currently analyzed method - */ - class State( - val method: Method, - val definedMethod: DeclaredMethod, - val declClass: ObjectType, - var pcToIndex: Array[Int] = Array.empty, - var code: Array[Stmt[V]] = Array.empty, - var lbPurity: Purity = CompileTimePure, - var ubPurity: Purity = CompileTimePure - ) extends AnalysisState { - var fieldLocalityDependees: Map[Field, (EOptionP[Field, FieldLocality], Set[(Expr[V], Purity)])] = Map.empty - - var fieldMutabilityDependees: Map[Field, (EOptionP[Field, ReferenceImmutability], Set[Option[Expr[V]]])] = Map.empty - //FieldMutability - - var classImmutabilityDependees: Map[ObjectType, (EOptionP[ObjectType, ClassImmutability_new], Set[Expr[V]])] = Map.empty - //ClassImmutability - var typeImmutabilityDependees: Map[ObjectType, (EOptionP[ObjectType, TypeImmutability_new], Set[Expr[V]])] = Map.empty - //TypeImmutability - - 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]] = - (fieldLocalityDependees.valuesIterator.map(_._1) ++ - fieldMutabilityDependees.valuesIterator.map(_._1) ++ - classImmutabilityDependees.valuesIterator.map(_._1) ++ - typeImmutabilityDependees.valuesIterator.map(_._1) ++ - purityDependees.valuesIterator.map(_._1) ++ - calleesDependee ++ - rvfDependees.valuesIterator.map(_._1) ++ - staticDataUsage ++ - tacai).toTraversable - - def addFieldLocalityDependee( - f: Field, - eop: EOptionP[Field, FieldLocality], - data: (Expr[V], Purity) - ): Unit = { - if (fieldLocalityDependees.contains(f)) { - val (_, oldValues) = fieldLocalityDependees(f) - fieldLocalityDependees += ((f, (eop, oldValues + data))) - } else { - fieldLocalityDependees += ((f, (eop, Set(data)))) - } - } - - def addFieldMutabilityDependee( - f: Field, - eop: EOptionP[Field, ReferenceImmutability], // FieldMutability], - owner: Option[Expr[V]] - ): Unit = { - if (fieldMutabilityDependees.contains(f)) { - val (_, oldOwners) = fieldMutabilityDependees(f) - fieldMutabilityDependees += ((f, (eop, oldOwners + owner))) - } else { - fieldMutabilityDependees += ((f, (eop, Set(owner)))) - } - } +class L2PurityAnalysis_new private[analyses] (val project: SomeProject) + extends AbstractPurityAnalysis_new { + + /** + * Holds the state of this analysis. + * @param lbPurity The current minimum purity level for the method + * @param ubPurity The current maximum purity level for the method that will be assigned by + * checkPurityOfX methods to aggregrate the purity + * @param method The currently analyzed method + * @param definedMethod The corresponding DefinedMethod we report results for + * @param declClass The declaring class of the currently analyzed method + * @param code The code of the currently analyzed method + */ + class State( + val method: Method, + val definedMethod: DeclaredMethod, + val declClass: ObjectType, + var pcToIndex: Array[Int] = Array.empty, + var code: Array[Stmt[V]] = Array.empty, + var lbPurity: Purity = CompileTimePure, + var ubPurity: Purity = CompileTimePure + ) extends AnalysisState { + var fieldLocalityDependees + : Map[Field, (EOptionP[Field, FieldLocality], Set[(Expr[V], Purity)])] = Map.empty + + var fieldMutabilityDependees + : Map[Field, (EOptionP[Field, ReferenceImmutability], Set[Option[Expr[V]]])] = Map.empty + //FieldMutability + + var classImmutabilityDependees + : Map[ObjectType, (EOptionP[ObjectType, ClassImmutability_new], Set[Expr[V]])] = Map.empty + //ClassImmutability + var typeImmutabilityDependees + : Map[ObjectType, (EOptionP[ObjectType, TypeImmutability_new], Set[Expr[V]])] = Map.empty + //TypeImmutability + + 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]] = + (fieldLocalityDependees.valuesIterator.map(_._1) ++ + fieldMutabilityDependees.valuesIterator.map(_._1) ++ + classImmutabilityDependees.valuesIterator.map(_._1) ++ + typeImmutabilityDependees.valuesIterator.map(_._1) ++ + purityDependees.valuesIterator.map(_._1) ++ + calleesDependee ++ + rvfDependees.valuesIterator.map(_._1) ++ + staticDataUsage ++ + tacai).toTraversable + + def addFieldLocalityDependee( + f: Field, + eop: EOptionP[Field, FieldLocality], + data: (Expr[V], Purity) + ): Unit = { + if (fieldLocalityDependees.contains(f)) { + val (_, oldValues) = fieldLocalityDependees(f) + fieldLocalityDependees += ((f, (eop, oldValues + data))) + } else { + fieldLocalityDependees += ((f, (eop, Set(data)))) + } + } - def addClassImmutabilityDependee( - t: ObjectType, - eop: EOptionP[ObjectType, ClassImmutability_new], //ClassImmutability], - value: Expr[V] - ): Unit = { - if (classImmutabilityDependees.contains(t)) { - val (_, oldValues) = classImmutabilityDependees(t) - classImmutabilityDependees += ((t, (eop, oldValues + value))) - } else { - classImmutabilityDependees += ((t, (eop, Set(value)))) - } - } + def addFieldMutabilityDependee( + f: Field, + eop: EOptionP[Field, ReferenceImmutability], // FieldMutability], + owner: Option[Expr[V]] + ): Unit = { + if (fieldMutabilityDependees.contains(f)) { + val (_, oldOwners) = fieldMutabilityDependees(f) + fieldMutabilityDependees += ((f, (eop, oldOwners + owner))) + } else { + fieldMutabilityDependees += ((f, (eop, Set(owner)))) + } + } - def addTypeImmutabilityDependee( - t: ObjectType, - eop: EOptionP[ObjectType, TypeImmutability_new], // TypeImmutability], - value: Expr[V] - ): Unit = { - if (typeImmutabilityDependees.contains(t)) { - val (_, oldValues) = typeImmutabilityDependees(t) - typeImmutabilityDependees += ((t, (eop, oldValues + value))) - } else { - typeImmutabilityDependees += ((t, (eop, Set(value)))) - } - } + def addClassImmutabilityDependee( + t: ObjectType, + eop: EOptionP[ObjectType, ClassImmutability_new], //ClassImmutability], + value: Expr[V] + ): Unit = { + if (classImmutabilityDependees.contains(t)) { + val (_, oldValues) = classImmutabilityDependees(t) + classImmutabilityDependees += ((t, (eop, oldValues + value))) + } else { + classImmutabilityDependees += ((t, (eop, Set(value)))) + } + } - def addPurityDependee( - dm: DeclaredMethod, - eop: EOptionP[DeclaredMethod, Purity], - params: Seq[Expr[V]] - ): Unit = { - if (purityDependees.contains(dm)) { - val (_, oldParams) = purityDependees(dm) - purityDependees += ((dm, (eop, oldParams + params))) - } else { - purityDependees += ((dm, (eop, Set(params)))) - } - } + def addTypeImmutabilityDependee( + t: ObjectType, + eop: EOptionP[ObjectType, TypeImmutability_new], // TypeImmutability], + value: Expr[V] + ): Unit = { + if (typeImmutabilityDependees.contains(t)) { + val (_, oldValues) = typeImmutabilityDependees(t) + typeImmutabilityDependees += ((t, (eop, oldValues + value))) + } else { + typeImmutabilityDependees += ((t, (eop, Set(value)))) + } + } - def updateCalleesDependee(eps: EOptionP[DeclaredMethod, Callees]): Unit = { - if (eps.isFinal) calleesDependee = None - else calleesDependee = Some(eps) - if (eps.hasUBP) - callees = Some(eps.ub) - } + def addPurityDependee( + dm: DeclaredMethod, + eop: EOptionP[DeclaredMethod, Purity], + params: Seq[Expr[V]] + ): Unit = { + if (purityDependees.contains(dm)) { + val (_, oldParams) = purityDependees(dm) + purityDependees += ((dm, (eop, oldParams + params))) + } else { + purityDependees += ((dm, (eop, Set(params)))) + } + } - def addRVFDependee( - dm: DeclaredMethod, - eop: EOptionP[DeclaredMethod, ReturnValueFreshness], - data: (Option[Expr[V]], Purity) - ): Unit = { - if (rvfDependees.contains(dm)) { - val (_, oldValues) = rvfDependees(dm) - rvfDependees += ((dm, (eop, oldValues + data))) - } else { - rvfDependees += ((dm, (eop, Set(data)))) - } - } + def updateCalleesDependee(eps: EOptionP[DeclaredMethod, Callees]): Unit = { + if (eps.isFinal) calleesDependee = None + else calleesDependee = Some(eps) + if (eps.hasUBP) + callees = Some(eps.ub) + } - def removeFieldLocalityDependee(f: Field): Unit = fieldLocalityDependees -= f - def removeFieldMutabilityDependee(f: Field): Unit = fieldMutabilityDependees -= f - def removeClassImmutabilityDependee(t: ObjectType): Unit = classImmutabilityDependees -= t - def removeTypeImmutabilityDependee(t: ObjectType): Unit = typeImmutabilityDependees -= t - def removePurityDependee(dm: DeclaredMethod): Unit = purityDependees -= dm - def removeRVFDependee(dm: DeclaredMethod): Unit = rvfDependees -= dm + def addRVFDependee( + dm: DeclaredMethod, + eop: EOptionP[DeclaredMethod, ReturnValueFreshness], + data: (Option[Expr[V]], Purity) + ): Unit = { + if (rvfDependees.contains(dm)) { + val (_, oldValues) = rvfDependees(dm) + rvfDependees += ((dm, (eop, oldValues + data))) + } else { + rvfDependees += ((dm, (eop, Set(data)))) + } + } - def updateStaticDataUsage(eps: Option[EOptionP[DeclaredMethod, StaticDataUsage]]): Unit = { - staticDataUsage = eps - } + def removeFieldLocalityDependee(f: Field): Unit = fieldLocalityDependees -= f + def removeFieldMutabilityDependee(f: Field): Unit = fieldMutabilityDependees -= f + def removeClassImmutabilityDependee(t: ObjectType): Unit = classImmutabilityDependees -= t + def removeTypeImmutabilityDependee(t: ObjectType): Unit = typeImmutabilityDependees -= t + def removePurityDependee(dm: DeclaredMethod): Unit = purityDependees -= dm + def removeRVFDependee(dm: DeclaredMethod): Unit = rvfDependees -= dm - def updateTacai(eps: EOptionP[Method, TACAI]): Unit = { - if (eps.isFinal) tacai = None - else tacai = Some(eps) - if (eps.hasUBP && eps.ub.tac.isDefined) { - val tac = eps.ub.tac.get - pcToIndex = tac.pcToIndex - code = tac.stmts - } - } + def updateStaticDataUsage(eps: Option[EOptionP[DeclaredMethod, StaticDataUsage]]): Unit = { + staticDataUsage = eps } - type StateType = State - - val raterFqn: String = project.config.as[String]( - "org.opalj.fpcf.analyses.L2PurityAnalysis.domainSpecificRater" + def updateTacai(eps: EOptionP[Method, TACAI]): Unit = { + if (eps.isFinal) tacai = None + else tacai = Some(eps) + if (eps.hasUBP && eps.ub.tac.isDefined) { + val tac = eps.ub.tac.get + pcToIndex = tac.pcToIndex + code = tac.stmts + } + } + } + + type StateType = State + + val raterFqn: String = project.config.as[String]( + "org.opalj.fpcf.analyses.L2PurityAnalysis.domainSpecificRater" + ) + + val rater: DomainSpecificRater = + L2PurityAnalysis.rater.getOrElse(resolveDomainSpecificRater(raterFqn)) + + /** + * Examines whether the given expression denotes an object/array that is local to the current + * method, i.e. the method has control over the object/array and actions on it might not + * influence purity. + * + * @param otherwise The maxPurity will be reduced to at most this level if the expression is not + * local. + */ + override def isLocal( + expr: Expr[V], + otherwise: Purity, + excludedDefSites: IntTrieSet = EmptyIntTrieSet + )(implicit state: State): Boolean = { + isLocalInternal( + expr, + otherwise, + _ => CompileTimePure, + treatParamsAsFresh = false ) + } + + /** + * Examines whether the given expression denotes an object/array that is local to the current + * method, i.e. the method has control over the object/array and actions on it might not + * influence purity. + * + * @note Fresh references can be treated as non-escaping as the analysis result will be impure + * if anything escapes the method via parameters, static field assignments or calls. + * + * @param otherwise The maxPurity will be reduced to at most this level if the expression is not + * local + * @param onParameter The maxPurity will be reduced to at most this level if the expression can + * be a parameter + * @param treatParamsAsFresh The value to be returned if the expression can be a parameter + */ + def isLocalInternal( + expr: Expr[V], + otherwise: Purity, + onParameter: Int => Purity, + 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.isConst) + return true; + + if (!expr.isVar) { + // The expression could refer to further expressions in a non-flat representation. + // In that case it could be, e.g., a GetStatic. In that case the reference is not + // locally created and/or initialized. To avoid special handling, we just fallback to + // false here as the analysis is intended to be used on flat representations anyway. + atMost(otherwise) + return false; + } - val rater: DomainSpecificRater = - L2PurityAnalysis.rater.getOrElse(resolveDomainSpecificRater(raterFqn)) - - /** - * Examines whether the given expression denotes an object/array that is local to the current - * method, i.e. the method has control over the object/array and actions on it might not - * influence purity. - * - * @param otherwise The maxPurity will be reduced to at most this level if the expression is not - * local. - */ - override def isLocal( - expr: Expr[V], - otherwise: Purity, - excludedDefSites: IntTrieSet = EmptyIntTrieSet - )(implicit state: State): Boolean = { - isLocalInternal( - expr, - otherwise, - _ ⇒ CompileTimePure, - treatParamsAsFresh = false + // Primitive values are always local (required for parameters of contextually pure calls) + // TODO (value is null for the self reference of a throwable constructor...) + if (expr.asVar.value != null && + (expr.asVar.value.computationalType ne ComputationalTypeReference)) + return true; + + val defSites = expr.asVar.definedBy -- excludedDefSites + val isLocal = + defSites.forall( + isLocalDefsite( + _, + otherwise, + onParameter, + treatParamsAsFresh, + defSites, + excludedDefSites ) + ) + if (!isLocal) atMost(otherwise) + isLocal + } + + /** + * Examines whether the given defsite denotes an object/array that is local to the current + * method, i.e. the method has control over the object/array and actions on it might not + * influence purity. + * + * @param otherwise The maxPurity will be reduced to at most this level if the defsite is not + * local + * @param onParameter The maxPurity will be reduced to at most this level if the defsite is a + * parameter + * @param treatParamsAsFresh The value to be returned if the defsite is a parameter + */ + def isLocalDefsite( + defSite: Int, + otherwise: Purity, + onParameter: Int => Purity, + treatParamsAsFresh: Boolean, + defSites: IntTrieSet, + excludedDefSites: IntTrieSet + )(implicit state: State): Boolean = { + if (isImmediateVMException(defSite)) + return true; // VMLevelValues are freshly created + + if (ai.isMethodExternalExceptionOrigin(defSite)) + return false; // Method external exceptions are not freshly created + + if (defSite == OriginOfThis) { + if (!state.method.isConstructor) { + atMost(onParameter(0)) + } + return treatParamsAsFresh | state.method.isConstructor; } - /** - * Examines whether the given expression denotes an object/array that is local to the current - * method, i.e. the method has control over the object/array and actions on it might not - * influence purity. - * - * @note Fresh references can be treated as non-escaping as the analysis result will be impure - * if anything escapes the method via parameters, static field assignments or calls. - * - * @param otherwise The maxPurity will be reduced to at most this level if the expression is not - * local - * @param onParameter The maxPurity will be reduced to at most this level if the expression can - * be a parameter - * @param treatParamsAsFresh The value to be returned if the expression can be a parameter - */ - def isLocalInternal( - expr: Expr[V], - otherwise: Purity, - onParameter: Int ⇒ Purity, - 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.isConst) - return true; - - if (!expr.isVar) { - // The expression could refer to further expressions in a non-flat representation. - // In that case it could be, e.g., a GetStatic. In that case the reference is not - // locally created and/or initialized. To avoid special handling, we just fallback to - // false here as the analysis is intended to be used on flat representations anyway. - atMost(otherwise) - return false; - } + if (defSite < 0) { + atMost(onParameter(-defSite - 1)) + return treatParamsAsFresh; + } - // Primitive values are always local (required for parameters of contextually pure calls) - // TODO (value is null for the self reference of a throwable constructor...) - if (expr.asVar.value != null && - (expr.asVar.value.computationalType ne ComputationalTypeReference)) - return true; - - val defSites = expr.asVar.definedBy -- excludedDefSites - val isLocal = - defSites.forall( - isLocalDefsite( - _, - otherwise, - onParameter, - treatParamsAsFresh, - defSites, - excludedDefSites - ) + val stmt = state.code(defSite) + assert(stmt.astID == Assignment.ASTID, "defSite should be assignment") + + val rhs = stmt.asAssignment.expr + if (rhs.isConst) + return true; + + (rhs.astID: @switch) match { + case New.ASTID | NewArray.ASTID => true + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => + val oldPurityLevel = + state.rvfCallSites.get(stmt.pc).map(_._2).getOrElse(CompileTimePure) + val data = (rhs.asFunctionCall.receiverOption, otherwise meet oldPurityLevel) + if (state.callees.isDefined) { + checkFreshnessOfReturn(stmt.pc, data, state.callees.get) + } else { + state.rvfCallSites += stmt.pc -> data + reducePurityLB(otherwise) + } + true + case GetField.ASTID => + val GetField(_, declClass, name, fieldType, objRef) = rhs + project.resolveFieldReference(declClass, name, fieldType) match { + case Some(field) => + val locality = propertyStore(field, FieldLocality.key) + checkLocalityOfField(locality, (objRef, otherwise)) && + isLocalInternal( + objRef, + otherwise, + onParameter, + treatParamsAsFresh, + excludedDefSites ++ defSites ) - if (!isLocal) atMost(otherwise) - isLocal - } - - /** - * Examines whether the given defsite denotes an object/array that is local to the current - * method, i.e. the method has control over the object/array and actions on it might not - * influence purity. - * - * @param otherwise The maxPurity will be reduced to at most this level if the defsite is not - * local - * @param onParameter The maxPurity will be reduced to at most this level if the defsite is a - * parameter - * @param treatParamsAsFresh The value to be returned if the defsite is a parameter - */ - def isLocalDefsite( - defSite: Int, - otherwise: Purity, - onParameter: Int ⇒ Purity, - treatParamsAsFresh: Boolean, - defSites: IntTrieSet, - excludedDefSites: IntTrieSet - )(implicit state: State): Boolean = { - if (isImmediateVMException(defSite)) - return true; // VMLevelValues are freshly created - - if (ai.isMethodExternalExceptionOrigin(defSite)) - return false; // Method external exceptions are not freshly created - - if (defSite == OriginOfThis) { - if (!state.method.isConstructor) { - atMost(onParameter(0)) - } - return treatParamsAsFresh | state.method.isConstructor; + case None => false } - - if (defSite < 0) { - atMost(onParameter(-defSite - 1)) - return treatParamsAsFresh; + case _ => false + } + } + + def checkLocalityOfField( + ep: EOptionP[Field, FieldLocality], + data: (Expr[V], Purity) + )(implicit state: State): Boolean = { + val isLocal = ep match { + case FinalP(LocalField | LocalFieldWithGetter) => + true + case FinalP(ExtensibleLocalField | ExtensibleLocalFieldWithGetter) => + if (data._1.isVar) { + val value = data._1.asVar.value.asReferenceValue + value.isPrecise && + !classHierarchy.isSubtypeOf(value.asReferenceType, ObjectType.Cloneable) + } else + false + case UBP(NoLocalField) => + false + case _ => + reducePurityLB(data._2) + if (data._2 meet state.ubPurity ne state.ubPurity) + state.addFieldLocalityDependee(ep.e, ep, data) + true + } + if (!isLocal) + atMost(data._2) + isLocal + } + + def checkLocalityOfReturn( + ep: EOptionP[DeclaredMethod, Property], + data: (Option[Expr[V]], Purity) + )(implicit state: State): Unit = { + import project.classHierarchy.isSubtypeOf + ep match { + case LBP(PrimitiveReturnValue | FreshReturnValue) => + case FinalP(Getter) => + if (data._2 meet state.ubPurity ne state.ubPurity) + isLocal(data._1.get, data._2) + case FinalP(ExtensibleGetter) => + if (data._1.get.isVar) { + val value = data._1.get.asVar.value.asReferenceValue + if (value.isPrecise && !isSubtypeOf(value.asReferenceType, ObjectType.Cloneable)) { + if (data._2 meet state.ubPurity ne state.ubPurity) + isLocal(data._1.get, data._2) + } else { + atMost(data._2) + } + } else { + atMost(data._2) } - - val stmt = state.code(defSite) - assert(stmt.astID == Assignment.ASTID, "defSite should be assignment") - - val rhs = stmt.asAssignment.expr - if (rhs.isConst) - return true; - - (rhs.astID: @switch) match { - case New.ASTID | NewArray.ASTID ⇒ true - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | - VirtualFunctionCall.ASTID ⇒ - val oldPurityLevel = - state.rvfCallSites.get(stmt.pc).map(_._2).getOrElse(CompileTimePure) - val data = (rhs.asFunctionCall.receiverOption, otherwise meet oldPurityLevel) - if (state.callees.isDefined) { - checkFreshnessOfReturn(stmt.pc, data, state.callees.get) - } else { - state.rvfCallSites += stmt.pc → data - reducePurityLB(otherwise) - } - true - case GetField.ASTID ⇒ - val GetField(_, declClass, name, fieldType, objRef) = rhs - project.resolveFieldReference(declClass, name, fieldType) match { - case Some(field) ⇒ - val locality = propertyStore(field, FieldLocality.key) - checkLocalityOfField(locality, (objRef, otherwise)) && - isLocalInternal( - objRef, - otherwise, - onParameter, - treatParamsAsFresh, - excludedDefSites ++ defSites - ) - case None ⇒ false - } - case _ ⇒ false + case UBP(NoFreshReturnValue) => + atMost(data._2) + case _: SomeEOptionP => + reducePurityLB(data._2) + if (data._2 meet state.ubPurity ne state.ubPurity) { + state.addRVFDependee( + ep.e, + ep.asInstanceOf[EOptionP[DeclaredMethod, ReturnValueFreshness]], + data + ) } } - - def checkLocalityOfField( - ep: EOptionP[Field, FieldLocality], - data: (Expr[V], Purity) - )(implicit state: State): Boolean = { - val isLocal = ep match { - case FinalP(LocalField | LocalFieldWithGetter) ⇒ - true - case FinalP(ExtensibleLocalField | ExtensibleLocalFieldWithGetter) ⇒ - if (data._1.isVar) { - val value = data._1.asVar.value.asReferenceValue - value.isPrecise && - !classHierarchy.isSubtypeOf(value.asReferenceType, ObjectType.Cloneable) - } else - false - case UBP(NoLocalField) ⇒ - false - case _ ⇒ - reducePurityLB(data._2) - if (data._2 meet state.ubPurity ne state.ubPurity) - state.addFieldLocalityDependee(ep.e, ep, data) - true - } - if (!isLocal) - atMost(data._2) - isLocal + } + + def checkFreshnessOfReturn( + pc: Int, + data: (Option[Expr[V]], Purity), + callees: Callees + )(implicit state: State): Unit = { + callees.callees(pc).foreach { callee => + if (callee.descriptor.returnType.isReferenceType) + checkLocalityOfReturn(propertyStore(callee, ReturnValueFreshness.key), data) } + } + + /** + * Examines a statement for its influence on the method's purity. + * This method will return false for impure statements, so evaluation can be terminated early. + */ + override def checkPurityOfStmt(stmt: Stmt[V])(implicit state: State): Boolean = + (stmt.astID: @switch) match { + // Synchronization on non-escaping local objects/arrays is pure (and useless...) + case MonitorEnter.ASTID | MonitorExit.ASTID => + val objRef = stmt.asSynchronizationStmt.objRef + isLocalInternal( + objRef, + ImpureByAnalysis, + param => ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + ) && stmt.forallSubExpressions(checkPurityOfExpr) + + // Storing into non-escaping locally initialized arrays/objects is pure + case ArrayStore.ASTID => + val arrayRef = stmt.asArrayStore.arrayRef + isLocalInternal( + arrayRef, + ImpureByAnalysis, + param => ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + ) && stmt.forallSubExpressions(checkPurityOfExpr) + case PutField.ASTID => + val objRef = stmt.asPutField.objRef + isLocalInternal( + objRef, + ImpureByAnalysis, + param => ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + ) && stmt.forallSubExpressions(checkPurityOfExpr) - def checkLocalityOfReturn( - ep: EOptionP[DeclaredMethod, Property], - data: (Option[Expr[V]], Purity) - )(implicit state: State): Unit = { - import project.classHierarchy.isSubtypeOf - ep match { - case LBP(PrimitiveReturnValue | FreshReturnValue) ⇒ - case FinalP(Getter) ⇒ - if (data._2 meet state.ubPurity ne state.ubPurity) - isLocal(data._1.get, data._2) - case FinalP(ExtensibleGetter) ⇒ - if (data._1.get.isVar) { - val value = data._1.get.asVar.value.asReferenceValue - if (value.isPrecise && !isSubtypeOf(value.asReferenceType, ObjectType.Cloneable)) { - if (data._2 meet state.ubPurity ne state.ubPurity) - isLocal(data._1.get, data._2) - } else { - atMost(data._2) - } - } else { - atMost(data._2) - } - case UBP(NoFreshReturnValue) ⇒ - atMost(data._2) - case _: SomeEOptionP ⇒ - reducePurityLB(data._2) - if (data._2 meet state.ubPurity ne state.ubPurity) { - state.addRVFDependee( - ep.e, - ep.asInstanceOf[EOptionP[DeclaredMethod, ReturnValueFreshness]], - data - ) - } - } + case _ => super.checkPurityOfStmt(stmt) } - def checkFreshnessOfReturn( - pc: Int, - data: (Option[Expr[V]], Purity), - callees: Callees - )(implicit state: State): Unit = { - callees.callees(pc).foreach { callee ⇒ - if (callee.descriptor.returnType.isReferenceType) - checkLocalityOfReturn(propertyStore(callee, ReturnValueFreshness.key), data) - } + /** + * Examines the influence of the purity property of a method on the examined method's purity. + * + * @note Adds dependendees when necessary. + */ + def checkMethodPurity( + ep: EOptionP[DeclaredMethod, Purity], + params: Seq[Expr[V]] + )(implicit state: State): Boolean = ep match { + case UBP(_: ClassifiedImpure) => + atMost(ImpureByAnalysis) + false + case eps @ LUBP(lb, ub) => + if (eps.isRefinable && ((lb meet state.ubPurity) ne state.ubPurity)) { + // On conditional, keep dependence + state.addPurityDependee(ep.e, ep, params) + reducePurityLB(lb) + } + // Contextual/external purity is handled below + atMost(ub.withoutContextual) + ub.modifiedParams.forall( + param => + isLocalInternal( + params(param), + ImpureByAnalysis, + param => ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + ) + ) + case _: SomeEOptionP => + reducePurityLB(ImpureByAnalysis) + state.addPurityDependee(ep.e, ep, params) + true + } + + /** + * Handles the effect of static data usage on the purity level. + * + * @note Modifies dependees as necessary. + */ + def checkStaticDataUsage( + ep: EOptionP[DeclaredMethod, StaticDataUsage] + )(implicit state: State): Unit = { + ep match { + case LBP(UsesNoStaticData | UsesConstantDataOnly) => + state.updateStaticDataUsage(None) + case UBP(UsesVaryingData) => + state.updateStaticDataUsage(None) + atMost(Pure) + case _ => + reducePurityLB(Pure) + state.updateStaticDataUsage(Some(ep)) } - - /** - * Examines a statement for its influence on the method's purity. - * This method will return false for impure statements, so evaluation can be terminated early. - */ - override def checkPurityOfStmt(stmt: Stmt[V])(implicit state: State): Boolean = - (stmt.astID: @switch) match { - // Synchronization on non-escaping local objects/arrays is pure (and useless...) - case MonitorEnter.ASTID | MonitorExit.ASTID ⇒ - val objRef = stmt.asSynchronizationStmt.objRef - isLocalInternal( - objRef, - ImpureByAnalysis, - param ⇒ ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true - ) && stmt.forallSubExpressions(checkPurityOfExpr) - - // Storing into non-escaping locally initialized arrays/objects is pure - case ArrayStore.ASTID ⇒ - val arrayRef = stmt.asArrayStore.arrayRef - isLocalInternal( - arrayRef, - ImpureByAnalysis, - param ⇒ ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true - ) && stmt.forallSubExpressions(checkPurityOfExpr) - case PutField.ASTID ⇒ - val objRef = stmt.asPutField.objRef - isLocalInternal( - objRef, - ImpureByAnalysis, - param ⇒ ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true - ) && stmt.forallSubExpressions(checkPurityOfExpr) - - case _ ⇒ super.checkPurityOfStmt(stmt) - } - - /** - * Examines the influence of the purity property of a method on the examined method's purity. - * - * @note Adds dependendees when necessary. - */ - def checkMethodPurity( - ep: EOptionP[DeclaredMethod, Purity], - params: Seq[Expr[V]] - )(implicit state: State): Boolean = ep match { - case UBP(_: ClassifiedImpure) ⇒ - atMost(ImpureByAnalysis) - false - case eps @ LUBP(lb, ub) ⇒ - if (eps.isRefinable && ((lb meet state.ubPurity) ne state.ubPurity)) { - // On conditional, keep dependence - state.addPurityDependee(ep.e, ep, params) - reducePurityLB(lb) - } - // Contextual/external purity is handled below - atMost(ub.withoutContextual) - ub.modifiedParams.forall(param ⇒ - isLocalInternal( - params(param), - ImpureByAnalysis, - param ⇒ ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true - )) - case _: SomeEOptionP ⇒ - reducePurityLB(ImpureByAnalysis) - state.addPurityDependee(ep.e, ep, params) - true - } - - /** - * Handles the effect of static data usage on the purity level. - * - * @note Modifies dependees as necessary. - */ - def checkStaticDataUsage(ep: EOptionP[DeclaredMethod, StaticDataUsage])(implicit state: State): Unit = { - ep match { - case LBP(UsesNoStaticData | UsesConstantDataOnly) ⇒ - state.updateStaticDataUsage(None) - case UBP(UsesVaryingData) ⇒ - state.updateStaticDataUsage(None) - atMost(Pure) - case _ ⇒ - reducePurityLB(Pure) - state.updateStaticDataUsage(Some(ep)) - } + } + + /** + * Adds the dependee necessary if a field mutability is not known yet. + */ + override def handleUnknownFieldMutability( + ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], + objRef: Option[Expr[V]] + )(implicit state: State): Unit = { + state.addFieldMutabilityDependee(ep.e, ep, objRef) + } + + /** + * Adds the dependee necessary if a type mutability is not known yet. + */ + override def handleUnknownTypeMutability( + ep: EOptionP[ObjectType, Property], + expr: Expr[V] + )(implicit state: State): Unit = { + if (ep.pk == ClassImmutability_new.key) //ClassImmutability.key) + state.addClassImmutabilityDependee( + ep.e, + ep.asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]], //ClassImmutability]], + expr + ) + else + state.addTypeImmutabilityDependee( + ep.e, + ep.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]], //TypeImmutability]], + expr + ) + } + + /** + * Add or remove the dependee when the callees property changes. + */ + override def handleCalleesUpdate( + callees: EOptionP[DeclaredMethod, Callees] + )(implicit state: State): Unit = { + state.updateCalleesDependee(callees) + if (callees.isRefinable) + reducePurityLB(ImpureByAnalysis) + } + + /* + * Adds the dependee necessary if the TACAI is not yet final. + */ + override def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: State): Unit = { + state.updateTacai(ep) + } + + /** + * Removes dependees that are known to not be needed anymore as they can not reduce the max + * purity level further. + */ + def cleanupDependees()(implicit state: State): Unit = { + if (state.ubPurity ne CompileTimePure) + state.updateStaticDataUsage(None) + + if (!state.ubPurity.isDeterministic) { + state.fieldMutabilityDependees = Map.empty + state.classImmutabilityDependees = Map.empty + state.typeImmutabilityDependees = Map.empty + state.updateStaticDataUsage(None) } - /** - * Adds the dependee necessary if a field mutability is not known yet. - */ - override def handleUnknownFieldMutability( - ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], - objRef: Option[Expr[V]] - )(implicit state: State): Unit = { - state.addFieldMutabilityDependee(ep.e, ep, objRef) - } - - /** - * Adds the dependee necessary if a type mutability is not known yet. - */ - override def handleUnknownTypeMutability( - ep: EOptionP[ObjectType, Property], - expr: Expr[V] - )(implicit state: State): Unit = { - if (ep.pk == ClassImmutability_new.key) //ClassImmutability.key) - state.addClassImmutabilityDependee( - ep.e, - ep.asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]], //ClassImmutability]], - expr - ) - else - state.addTypeImmutabilityDependee( - ep.e, - ep.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]], //TypeImmutability]], - expr - ) + 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))) } + state.fieldLocalityDependees = newFieldLocalityDependees - /** - * Add or remove the dependee when the callees property changes. - */ - override def handleCalleesUpdate( - callees: EOptionP[DeclaredMethod, Callees] - )(implicit state: State): Unit = { - state.updateCalleesDependee(callees) - if (callees.isRefinable) - reducePurityLB(ImpureByAnalysis) - } - - /* - * Adds the dependee necessary if the TACAI is not yet final. - */ - override def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: State): Unit = { - state.updateTacai(ep) - } - - /** - * Removes dependees that are known to not be needed anymore as they can not reduce the max - * purity level further. - */ - def cleanupDependees()(implicit state: State): Unit = { - if (state.ubPurity ne CompileTimePure) - state.updateStaticDataUsage(None) - - if (!state.ubPurity.isDeterministic) { - state.fieldMutabilityDependees = Map.empty - state.classImmutabilityDependees = Map.empty - state.typeImmutabilityDependees = Map.empty - state.updateStaticDataUsage(None) - } - - 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))) - } - state.fieldLocalityDependees = newFieldLocalityDependees + var newRVFCallsites: IntMap[(Option[Expr[V]], Purity)] = IntMap.empty + for ((callsite, data) <- state.rvfCallSites) { + if (data._2 meet state.ubPurity ne state.ubPurity) newRVFCallsites += ((callsite, data)) + } + 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))) + } + state.rvfDependees = newRVFDependees - var newRVFCallsites: IntMap[(Option[Expr[V]], Purity)] = IntMap.empty - for ((callsite, data) ← state.rvfCallSites) { - if (data._2 meet state.ubPurity ne state.ubPurity) newRVFCallsites += ((callsite, data)) - } - state.rvfCallSites = newRVFCallsites + 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)) + } + state.purityDependees = newPurityDependees + } + + /** + * Raises the lower bound on the purity whenever possible. + */ + def adjustLowerBound()(implicit state: State): Unit = { + if (state.calleesDependee.isDefined) + return; // Nothing to be done, lower bound is still LBImpure + + var newLowerBound = state.ubPurity + + if (state.tacai.isDefined) return; // Nothing to be done, lower bound is still LBImpure + + for ((eop, _) <- state.purityDependees.valuesIterator) { + eop match { + case LBP(lb) => newLowerBound = newLowerBound meet lb + case _ => + return; // Nothing to be done, lower bound is still LBImpure + } + } - 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))) - } - state.rvfDependees = newRVFDependees + if (state.staticDataUsage.isDefined) newLowerBound = newLowerBound meet Pure - 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)) - } - state.purityDependees = newPurityDependees + if (state.fieldMutabilityDependees.nonEmpty || + state.classImmutabilityDependees.nonEmpty || + state.typeImmutabilityDependees.nonEmpty) { + newLowerBound = newLowerBound meet SideEffectFree } - /** - * Raises the lower bound on the purity whenever possible. - */ - def adjustLowerBound()(implicit state: State): Unit = { - if (state.calleesDependee.isDefined) - return ; // Nothing to be done, lower bound is still LBImpure + for { + (_, data) <- state.fieldLocalityDependees.valuesIterator + (_, purity) <- data + } { + newLowerBound = newLowerBound meet purity + } - var newLowerBound = state.ubPurity + for { + (_, purity) <- state.rvfCallSites.valuesIterator + } { + newLowerBound = newLowerBound meet purity + } - if (state.tacai.isDefined) return ; // Nothing to be done, lower bound is still LBImpure + for { + (_, data) <- state.rvfDependees.valuesIterator + (_, purity) <- data + } { + newLowerBound = newLowerBound meet purity + } - for ((eop, _) ← state.purityDependees.valuesIterator) { - eop match { - case LBP(lb) ⇒ newLowerBound = newLowerBound meet lb - case _ ⇒ - return ; // Nothing to be done, lower bound is still LBImpure - } + state.lbPurity = newLowerBound + } + + /** + * Continuation to handle updates to properties of dependees. + * Dependees may be + * - methods / virtual methods called (for their purity) + * - fields read (for their mutability) + * - classes files for class types returned (for their mutability) + */ + def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { + val oldPurity = state.ubPurity + eps.ub.key match { + case Purity.key => + val e = eps.e.asInstanceOf[DeclaredMethod] + val dependees = state.purityDependees(e) + state.removePurityDependee(e) + dependees._2.foreach { e => + checkMethodPurity(eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]], e) } - - if (state.staticDataUsage.isDefined) newLowerBound = newLowerBound meet Pure - - if (state.fieldMutabilityDependees.nonEmpty || - state.classImmutabilityDependees.nonEmpty || - state.typeImmutabilityDependees.nonEmpty) { - newLowerBound = newLowerBound meet SideEffectFree + //case FieldMutability.key ⇒ + case ReferenceImmutability.key => + val e = eps.e.asInstanceOf[Field] + val dependees = state.fieldMutabilityDependees(e) + state.removeFieldMutabilityDependee(e) + dependees._2.foreach { e => + checkFieldMutability(eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]], e) //FieldMutability]], e) } - - for { - (_, data) ← state.fieldLocalityDependees.valuesIterator - (_, purity) ← data - } { - newLowerBound = newLowerBound meet purity + //case ClassImmutability.key ⇒ + case ClassImmutability_new.key => + val e = eps.e.asInstanceOf[ObjectType] + val dependees = state.classImmutabilityDependees(e) + state.removeClassImmutabilityDependee(e) + dependees._2.foreach { e => + checkTypeMutability( + eps.asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]], // ClassImmutability]], + e + ) } - - for { - (_, purity) ← state.rvfCallSites.valuesIterator - } { - newLowerBound = newLowerBound meet purity + //case TypeImmutability.key ⇒ + case TypeImmutability_new.key => + val e = eps.e.asInstanceOf[ObjectType] + val dependees = state.typeImmutabilityDependees(e) + state.removeTypeImmutabilityDependee(e) + dependees._2.foreach { e => + checkTypeMutability(eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]], e) //TypeImmutability]], e) } - - for { - (_, data) ← state.rvfDependees.valuesIterator - (_, purity) ← data - } { - newLowerBound = newLowerBound meet purity + case ReturnValueFreshness.key => + val e = eps.e.asInstanceOf[DeclaredMethod] + val dependees = state.rvfDependees(e) + state.removeRVFDependee(e) + dependees._2.foreach { e => + checkLocalityOfReturn( + eps.asInstanceOf[EOptionP[DeclaredMethod, ReturnValueFreshness]], + e + ) } - - state.lbPurity = newLowerBound - } - - /** - * Continuation to handle updates to properties of dependees. - * Dependees may be - * - methods / virtual methods called (for their purity) - * - fields read (for their mutability) - * - classes files for class types returned (for their mutability) - */ - def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { - val oldPurity = state.ubPurity - eps.ub.key match { - case Purity.key ⇒ - val e = eps.e.asInstanceOf[DeclaredMethod] - val dependees = state.purityDependees(e) - state.removePurityDependee(e) - dependees._2.foreach { e ⇒ - checkMethodPurity(eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]], e) - } - //case FieldMutability.key ⇒ - case ReferenceImmutability.key ⇒ - val e = eps.e.asInstanceOf[Field] - val dependees = state.fieldMutabilityDependees(e) - state.removeFieldMutabilityDependee(e) - dependees._2.foreach { e ⇒ - checkFieldMutability(eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]], e) //FieldMutability]], e) - } - //case ClassImmutability.key ⇒ - case ClassImmutability_new.key ⇒ - val e = eps.e.asInstanceOf[ObjectType] - val dependees = state.classImmutabilityDependees(e) - state.removeClassImmutabilityDependee(e) - dependees._2.foreach { e ⇒ - checkTypeMutability( - eps.asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]], // ClassImmutability]], - e - ) - } - //case TypeImmutability.key ⇒ - case TypeImmutability_new.key ⇒ - val e = eps.e.asInstanceOf[ObjectType] - val dependees = state.typeImmutabilityDependees(e) - state.removeTypeImmutabilityDependee(e) - dependees._2.foreach { e ⇒ - checkTypeMutability(eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]], e) //TypeImmutability]], e) - } - case ReturnValueFreshness.key ⇒ - val e = eps.e.asInstanceOf[DeclaredMethod] - val dependees = state.rvfDependees(e) - state.removeRVFDependee(e) - dependees._2.foreach { e ⇒ - checkLocalityOfReturn( - eps.asInstanceOf[EOptionP[DeclaredMethod, ReturnValueFreshness]], - e - ) - } - case FieldLocality.key ⇒ - val e = eps.e.asInstanceOf[Field] - val dependees = state.fieldLocalityDependees(e) - state.removeFieldLocalityDependee(e) - dependees._2.foreach { e ⇒ - checkLocalityOfField(eps.asInstanceOf[EOptionP[Field, FieldLocality]], e) - } - case Callees.key ⇒ - checkPurityOfCallees(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - state.rvfCallSites.foreach { - case (pc, data) ⇒ checkFreshnessOfReturn(pc, data, eps.ub.asInstanceOf[Callees]) - } - case StaticDataUsage.key ⇒ - checkStaticDataUsage(eps.asInstanceOf[EOptionP[DeclaredMethod, StaticDataUsage]]) - case TACAI.key ⇒ - state.updateTacai(eps.asInstanceOf[EOptionP[Method, TACAI]]) - return determineMethodPurity(eps.ub.asInstanceOf[TACAI].tac.get.cfg); + case FieldLocality.key => + val e = eps.e.asInstanceOf[Field] + val dependees = state.fieldLocalityDependees(e) + state.removeFieldLocalityDependee(e) + dependees._2.foreach { e => + checkLocalityOfField(eps.asInstanceOf[EOptionP[Field, FieldLocality]], e) } - - if (state.ubPurity eq ImpureByAnalysis) - return Result(state.definedMethod, ImpureByAnalysis); - - if (state.ubPurity ne oldPurity) - cleanupDependees() // Remove dependees that we don't need anymore. - adjustLowerBound() - - val dependees = state.dependees - if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { - Result(state.definedMethod, state.ubPurity) - } else { - InterimResult( - state.definedMethod, - state.lbPurity, - state.ubPurity, - dependees, - c - ) + case Callees.key => + checkPurityOfCallees(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + state.rvfCallSites.foreach { + case (pc, data) => checkFreshnessOfReturn(pc, data, eps.ub.asInstanceOf[Callees]) } + case StaticDataUsage.key => + checkStaticDataUsage(eps.asInstanceOf[EOptionP[DeclaredMethod, StaticDataUsage]]) + case TACAI.key => + state.updateTacai(eps.asInstanceOf[EOptionP[Method, TACAI]]) + return determineMethodPurity(eps.ub.asInstanceOf[TACAI].tac.get.cfg); } - /** - * Determines the purity of a method once TACAI is available. - */ - def determineMethodPurity( - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): ProperPropertyComputationResult = { - // Special case: The Throwable constructor is `LBSideEffectFree`, but subtype constructors - // may not be because of overridable fillInStackTrace method - if (state.method.isConstructor && state.declClass.isSubtypeOf(ObjectType.Throwable)) - project.instanceMethods(state.declClass).foreach { mdc ⇒ - if (mdc.name == "fillInStackTrace" && - mdc.method.classFile.thisType != ObjectType.Throwable) { - // "The value" is actually not used at all - hence, we can use "null" - // over here. - val selfReference = UVar(null, SelfReferenceParameter) - val fISTPurity = propertyStore(declaredMethods(mdc.method), Purity.key) - if (!checkMethodPurity(fISTPurity, Seq(selfReference))) { - // Early return for impure fillInStackTrace - return Result(state.definedMethod, state.ubPurity); - } - } - } - - // Synchronized methods have a visible side effect on the receiver - // Static synchronized methods lock the class which is potentially globally visible - if (state.method.isSynchronized) - if (state.method.isStatic) return Result(state.definedMethod, ImpureByAnalysis); - else atMost(ContextuallyPure(IntTrieSet(0))) - - val stmtCount = state.code.length - var s = 0 - while (s < stmtCount) { - if (!checkPurityOfStmt(state.code(s))) { // Early return for impure statements - assert(state.ubPurity.isInstanceOf[ClassifiedImpure]) - return Result(state.definedMethod, state.ubPurity); - } - s += 1 - } - - val callees = propertyStore(state.definedMethod, Callees.key) - if (!checkPurityOfCallees(callees)) - return Result(state.definedMethod, state.ubPurity) - - if (callees.hasUBP) - state.rvfCallSites.foreach { - case (pc, data) ⇒ - checkFreshnessOfReturn(pc, data, callees.ub) - } - - // Creating implicit exceptions is side-effect free (because of fillInStackTrace) - // but it may be ignored as domain-specific - val bbsCausingExceptions = cfg.abnormalReturnNode.predecessors - for { - bb ← bbsCausingExceptions - } { - val pc = bb.asBasicBlock.endPC - if (isSourceOfImmediateException(pc)) { - val throwingStmt = state.code(pc) - val ratedResult = rater.handleException(throwingStmt) - if (ratedResult.isDefined) atMost(ratedResult.get) - else atMost(SideEffectFree) - } - } - - if (state.ubPurity eq CompileTimePure) // Check static data usage only if necessary - checkStaticDataUsage(propertyStore(state.definedMethod, StaticDataUsage.key)) - else - cleanupDependees() // Remove dependees we already know we won't need - - val dependees = state.dependees - if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { - Result(state.definedMethod, state.ubPurity) - } else { - org.opalj.fpcf.InterimResult(state.definedMethod, state.lbPurity, state.ubPurity, dependees, c) + if (state.ubPurity eq ImpureByAnalysis) + return Result(state.definedMethod, ImpureByAnalysis); + + if (state.ubPurity ne oldPurity) + cleanupDependees() // Remove dependees that we don't need anymore. + adjustLowerBound() + + val dependees = state.dependees + if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { + Result(state.definedMethod, state.ubPurity) + } else { + InterimResult( + state.definedMethod, + state.lbPurity, + state.ubPurity, + dependees, + c + ) + } + } + + /** + * Determines the purity of a method once TACAI is available. + */ + def determineMethodPurity( + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): ProperPropertyComputationResult = { + // Special case: The Throwable constructor is `LBSideEffectFree`, but subtype constructors + // may not be because of overridable fillInStackTrace method + if (state.method.isConstructor && state.declClass.isSubtypeOf(ObjectType.Throwable)) + project.instanceMethods(state.declClass).foreach { mdc => + if (mdc.name == "fillInStackTrace" && + mdc.method.classFile.thisType != ObjectType.Throwable) { + // "The value" is actually not used at all - hence, we can use "null" + // over here. + val selfReference = UVar(null, SelfReferenceParameter) + val fISTPurity = propertyStore(declaredMethods(mdc.method), Purity.key) + if (!checkMethodPurity(fISTPurity, Seq(selfReference))) { + // Early return for impure fillInStackTrace + return Result(state.definedMethod, state.ubPurity); + } } + } + + // Synchronized methods have a visible side effect on the receiver + // Static synchronized methods lock the class which is potentially globally visible + if (state.method.isSynchronized) + if (state.method.isStatic) return Result(state.definedMethod, ImpureByAnalysis); + else atMost(ContextuallyPure(IntTrieSet(0))) + + val stmtCount = state.code.length + var s = 0 + while (s < stmtCount) { + if (!checkPurityOfStmt(state.code(s))) { // Early return for impure statements + assert(state.ubPurity.isInstanceOf[ClassifiedImpure]) + return Result(state.definedMethod, state.ubPurity); + } + s += 1 } - /** - * Determines the purity of the given method. - * - * @param definedMethod A defined method with a body. - */ - def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult = { - val method = definedMethod.definedMethod - val declClass = method.classFile.thisType - - // If this is not the method's declaration, but a non-overwritten method in a subtype, - // don't re-analyze the code - if (declClass ne definedMethod.declaringClassType) - return baseMethodPurity(definedMethod.asDefinedMethod); - - implicit val state: State = - new State(method, definedMethod, declClass) - - val tacaiO = getTACAI(method) - - if (tacaiO.isEmpty) - return InterimResult( - definedMethod, - ImpureByAnalysis, - CompileTimePure, - state.dependees, - c - ); + val callees = propertyStore(state.definedMethod, Callees.key) + if (!checkPurityOfCallees(callees)) + return Result(state.definedMethod, state.ubPurity) + + if (callees.hasUBP) + state.rvfCallSites.foreach { + case (pc, data) => + checkFreshnessOfReturn(pc, data, callees.ub) + } + + // Creating implicit exceptions is side-effect free (because of fillInStackTrace) + // but it may be ignored as domain-specific + val bbsCausingExceptions = cfg.abnormalReturnNode.predecessors + for { + bb <- bbsCausingExceptions + } { + val pc = bb.asBasicBlock.endPC + if (isSourceOfImmediateException(pc)) { + val throwingStmt = state.code(pc) + val ratedResult = rater.handleException(throwingStmt) + if (ratedResult.isDefined) atMost(ratedResult.get) + else atMost(SideEffectFree) + } + } - determineMethodPurity(tacaiO.get.cfg) + if (state.ubPurity eq CompileTimePure) // Check static data usage only if necessary + checkStaticDataUsage(propertyStore(state.definedMethod, StaticDataUsage.key)) + else + cleanupDependees() // Remove dependees we already know we won't need + + val dependees = state.dependees + if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { + Result(state.definedMethod, state.ubPurity) + } else { + org.opalj.fpcf.InterimResult( + state.definedMethod, + state.lbPurity, + state.ubPurity, + dependees, + c + ) } + } + + /** + * Determines the purity of the given method. + * + * @param definedMethod A defined method with a body. + */ + def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult = { + val method = definedMethod.definedMethod + val declClass = method.classFile.thisType + + // If this is not the method's declaration, but a non-overwritten method in a subtype, + // don't re-analyze the code + if (declClass ne definedMethod.declaringClassType) + return baseMethodPurity(definedMethod.asDefinedMethod); + + implicit val state: State = + new State(method, definedMethod, declClass) + + val tacaiO = getTACAI(method) + + if (tacaiO.isEmpty) + return InterimResult( + definedMethod, + ImpureByAnalysis, + CompileTimePure, + state.dependees, + c + ); + + determineMethodPurity(tacaiO.get.cfg) + } } object L2PurityAnalysis_new { - /** - * Domain-specific rater used to examine whether certain statements and expressions are - * domain-specific. - * If the Option is None, a rater is created from a config file option. - */ - var rater: Option[DomainSpecificRater] = None + /** + * Domain-specific rater used to examine whether certain statements and expressions are + * domain-specific. + * If the Option is None, a rater is created from a config file option. + */ + var rater: Option[DomainSpecificRater] = None - def setRater(newRater: Option[DomainSpecificRater]): Unit = { - rater = newRater - } + def setRater(newRater: Option[DomainSpecificRater]): Unit = { + rater = newRater + } } trait L2PurityAnalysisScheduler_new extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(Purity) - - override def requiredProjectInformation: ProjectInformationKeys = - Seq(DeclaredMethodsKey, ConfiguredPurityKey) - - override def uses: Set[PropertyBounds] = { - Set( - //PropertyBounds.lub(FieldMutability), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.lub(FieldImmutability), - //PropertyBounds.lub(ClassImmutability), - PropertyBounds.lub(ClassImmutability_new), - //PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(TypeImmutability_new), - PropertyBounds.lub(StaticDataUsage), - PropertyBounds.lub(ReturnValueFreshness), - PropertyBounds.ub(FieldLocality), - PropertyBounds.ub(TACAI), - PropertyBounds.ub(Callees), - PropertyBounds.lub(Purity) - ) - } + final def derivedProperty: PropertyBounds = PropertyBounds.lub(Purity) + + override def requiredProjectInformation: ProjectInformationKeys = + Seq(DeclaredMethodsKey, ConfiguredPurityKey) + + override def uses: Set[PropertyBounds] = { + Set( + //PropertyBounds.lub(FieldMutability), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.lub(FieldImmutability), + //PropertyBounds.lub(ClassImmutability), + PropertyBounds.lub(ClassImmutability_new), + //PropertyBounds.lub(TypeImmutability), + PropertyBounds.lub(TypeImmutability_new), + PropertyBounds.lub(StaticDataUsage), + PropertyBounds.lub(ReturnValueFreshness), + PropertyBounds.ub(FieldLocality), + PropertyBounds.ub(TACAI), + PropertyBounds.ub(Callees), + PropertyBounds.lub(Purity) + ) + } - final override type InitializationData = L2PurityAnalysis_new - final override def init(p: SomeProject, ps: PropertyStore): InitializationData = { - new L2PurityAnalysis_new(p) - } + final override type InitializationData = L2PurityAnalysis_new + final override def init(p: SomeProject, ps: PropertyStore): InitializationData = { + new L2PurityAnalysis_new(p) + } - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} - override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} + override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} - override def afterPhaseCompletion( - p: SomeProject, - ps: PropertyStore, - analysis: FPCFAnalysis - ): Unit = {} + override def afterPhaseCompletion( + p: SomeProject, + ps: PropertyStore, + analysis: FPCFAnalysis + ): Unit = {} } -object EagerL2PurityAnalysis_new extends L2PurityAnalysisScheduler_new with FPCFEagerAnalysisScheduler { - - override def start( - p: SomeProject, ps: PropertyStore, analysis: InitializationData - ): FPCFAnalysis = { - 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 ⇒ - dm.asDefinedMethod - } - ps.scheduleEagerComputationsForEntities(methods)( - analysis.determinePurity - ) - analysis +object EagerL2PurityAnalysis_new + extends L2PurityAnalysisScheduler_new + with FPCFEagerAnalysisScheduler { + + override def start( + p: SomeProject, + ps: PropertyStore, + analysis: InitializationData + ): FPCFAnalysis = { + 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 => + dm.asDefinedMethod } + ps.scheduleEagerComputationsForEntities(methods)( + analysis.determinePurity + ) + analysis + } - override def uses: Set[PropertyBounds] = super.uses + PropertyBounds.finalP(Callers) + override def uses: Set[PropertyBounds] = super.uses + PropertyBounds.finalP(Callers) - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } -object LazyL2PurityAnalysis_new extends L2PurityAnalysisScheduler_new with FPCFLazyAnalysisScheduler { +object LazyL2PurityAnalysis_new + extends L2PurityAnalysisScheduler_new + with FPCFLazyAnalysisScheduler { - override def register( - p: SomeProject, ps: PropertyStore, analysis: InitializationData - ): FPCFAnalysis = { - ps.registerLazyPropertyComputation(Purity.key, analysis.doDeterminePurity) - analysis - } + override def register( + p: SomeProject, + ps: PropertyStore, + analysis: InitializationData + ): FPCFAnalysis = { + ps.registerLazyPropertyComputation(Purity.key, analysis.doDeterminePurity) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } From 96092d92bda00efee0da80e96edf8f5d53a7d7d2 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 23 Jun 2020 10:28:49 +0200 Subject: [PATCH 146/327] format correction --- .../purity/L2PurityAnalysis_new.scala | 1713 ++++++++--------- 1 file changed, 850 insertions(+), 863 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala index ca38f81c69..e7acb23269 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala @@ -5,12 +5,6 @@ package fpcf package analyses package purity -import scala.annotation.switch -import scala.collection.immutable.IntMap -import net.ceedubs.ficus.Ficus._ -import org.opalj.ai -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 @@ -71,8 +65,15 @@ import org.opalj.br.fpcf.properties.ClassImmutability_new import org.opalj.br.fpcf.properties.FieldImmutability import org.opalj.br.fpcf.properties.ReferenceImmutability import org.opalj.br.fpcf.properties.TypeImmutability_new +import org.opalj.collection.immutable.EmptyIntTrieSet +import org.opalj.collection.immutable.IntTrieSet import org.opalj.tac.fpcf.properties.TACAI +import scala.annotation.switch +import scala.collection.immutable.IntMap + +import net.ceedubs.ficus.Ficus._ + /** * An inter-procedural analysis to determine a method's purity. * @@ -96,912 +97,899 @@ import org.opalj.tac.fpcf.properties.TACAI class L2PurityAnalysis_new private[analyses] (val project: SomeProject) extends AbstractPurityAnalysis_new { - /** - * Holds the state of this analysis. - * @param lbPurity The current minimum purity level for the method - * @param ubPurity The current maximum purity level for the method that will be assigned by - * checkPurityOfX methods to aggregrate the purity - * @param method The currently analyzed method - * @param definedMethod The corresponding DefinedMethod we report results for - * @param declClass The declaring class of the currently analyzed method - * @param code The code of the currently analyzed method - */ - class State( - val method: Method, - val definedMethod: DeclaredMethod, - val declClass: ObjectType, - var pcToIndex: Array[Int] = Array.empty, - var code: Array[Stmt[V]] = Array.empty, - var lbPurity: Purity = CompileTimePure, - var ubPurity: Purity = CompileTimePure - ) extends AnalysisState { - var fieldLocalityDependees - : Map[Field, (EOptionP[Field, FieldLocality], Set[(Expr[V], Purity)])] = Map.empty - - var fieldMutabilityDependees - : Map[Field, (EOptionP[Field, ReferenceImmutability], Set[Option[Expr[V]]])] = Map.empty - //FieldMutability - - var classImmutabilityDependees - : Map[ObjectType, (EOptionP[ObjectType, ClassImmutability_new], Set[Expr[V]])] = Map.empty - //ClassImmutability - var typeImmutabilityDependees - : Map[ObjectType, (EOptionP[ObjectType, TypeImmutability_new], Set[Expr[V]])] = Map.empty - //TypeImmutability - - 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]] = - (fieldLocalityDependees.valuesIterator.map(_._1) ++ - fieldMutabilityDependees.valuesIterator.map(_._1) ++ - classImmutabilityDependees.valuesIterator.map(_._1) ++ - typeImmutabilityDependees.valuesIterator.map(_._1) ++ - purityDependees.valuesIterator.map(_._1) ++ - calleesDependee ++ - rvfDependees.valuesIterator.map(_._1) ++ - staticDataUsage ++ - tacai).toTraversable - - def addFieldLocalityDependee( - f: Field, - eop: EOptionP[Field, FieldLocality], - data: (Expr[V], Purity) - ): Unit = { - if (fieldLocalityDependees.contains(f)) { - val (_, oldValues) = fieldLocalityDependees(f) - fieldLocalityDependees += ((f, (eop, oldValues + data))) - } else { - fieldLocalityDependees += ((f, (eop, Set(data)))) - } - } + /** + * Holds the state of this analysis. + * @param lbPurity The current minimum purity level for the method + * @param ubPurity The current maximum purity level for the method that will be assigned by + * checkPurityOfX methods to aggregrate the purity + * @param method The currently analyzed method + * @param definedMethod The corresponding DefinedMethod we report results for + * @param declClass The declaring class of the currently analyzed method + * @param code The code of the currently analyzed method + */ + class State( + val method: Method, + val definedMethod: DeclaredMethod, + val declClass: ObjectType, + var pcToIndex: Array[Int] = Array.empty, + var code: Array[Stmt[V]] = Array.empty, + var lbPurity: Purity = CompileTimePure, + var ubPurity: Purity = CompileTimePure + ) extends AnalysisState { + var fieldLocalityDependees: Map[Field, (EOptionP[Field, FieldLocality], Set[(Expr[V], Purity)])] = Map.empty + + var fieldMutabilityDependees: Map[Field, (EOptionP[Field, ReferenceImmutability], Set[Option[Expr[V]]])] = Map.empty + //FieldMutability + + var classImmutabilityDependees: Map[ObjectType, (EOptionP[ObjectType, ClassImmutability_new], Set[Expr[V]])] = Map.empty + //ClassImmutability + var typeImmutabilityDependees: Map[ObjectType, (EOptionP[ObjectType, TypeImmutability_new], Set[Expr[V]])] = Map.empty + //TypeImmutability + + 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]] = + (fieldLocalityDependees.valuesIterator.map(_._1) ++ + fieldMutabilityDependees.valuesIterator.map(_._1) ++ + classImmutabilityDependees.valuesIterator.map(_._1) ++ + typeImmutabilityDependees.valuesIterator.map(_._1) ++ + purityDependees.valuesIterator.map(_._1) ++ + calleesDependee ++ + rvfDependees.valuesIterator.map(_._1) ++ + staticDataUsage ++ + tacai).toTraversable + + def addFieldLocalityDependee( + f: Field, + eop: EOptionP[Field, FieldLocality], + data: (Expr[V], Purity) + ): Unit = { + if (fieldLocalityDependees.contains(f)) { + val (_, oldValues) = fieldLocalityDependees(f) + fieldLocalityDependees += ((f, (eop, oldValues + data))) + } else { + fieldLocalityDependees += ((f, (eop, Set(data)))) + } + } - def addFieldMutabilityDependee( - f: Field, - eop: EOptionP[Field, ReferenceImmutability], // FieldMutability], - owner: Option[Expr[V]] - ): Unit = { - if (fieldMutabilityDependees.contains(f)) { - val (_, oldOwners) = fieldMutabilityDependees(f) - fieldMutabilityDependees += ((f, (eop, oldOwners + owner))) - } else { - fieldMutabilityDependees += ((f, (eop, Set(owner)))) - } - } + def addFieldMutabilityDependee( + f: Field, + eop: EOptionP[Field, ReferenceImmutability], // FieldMutability], + owner: Option[Expr[V]] + ): Unit = { + if (fieldMutabilityDependees.contains(f)) { + val (_, oldOwners) = fieldMutabilityDependees(f) + fieldMutabilityDependees += ((f, (eop, oldOwners + owner))) + } else { + fieldMutabilityDependees += ((f, (eop, Set(owner)))) + } + } - def addClassImmutabilityDependee( - t: ObjectType, - eop: EOptionP[ObjectType, ClassImmutability_new], //ClassImmutability], - value: Expr[V] - ): Unit = { - if (classImmutabilityDependees.contains(t)) { - val (_, oldValues) = classImmutabilityDependees(t) - classImmutabilityDependees += ((t, (eop, oldValues + value))) - } else { - classImmutabilityDependees += ((t, (eop, Set(value)))) - } - } + def addClassImmutabilityDependee( + t: ObjectType, + eop: EOptionP[ObjectType, ClassImmutability_new], //ClassImmutability], + value: Expr[V] + ): Unit = { + if (classImmutabilityDependees.contains(t)) { + val (_, oldValues) = classImmutabilityDependees(t) + classImmutabilityDependees += ((t, (eop, oldValues + value))) + } else { + classImmutabilityDependees += ((t, (eop, Set(value)))) + } + } - def addTypeImmutabilityDependee( - t: ObjectType, - eop: EOptionP[ObjectType, TypeImmutability_new], // TypeImmutability], - value: Expr[V] - ): Unit = { - if (typeImmutabilityDependees.contains(t)) { - val (_, oldValues) = typeImmutabilityDependees(t) - typeImmutabilityDependees += ((t, (eop, oldValues + value))) - } else { - typeImmutabilityDependees += ((t, (eop, Set(value)))) - } - } + def addTypeImmutabilityDependee( + t: ObjectType, + eop: EOptionP[ObjectType, TypeImmutability_new], // TypeImmutability], + value: Expr[V] + ): Unit = { + if (typeImmutabilityDependees.contains(t)) { + val (_, oldValues) = typeImmutabilityDependees(t) + typeImmutabilityDependees += ((t, (eop, oldValues + value))) + } else { + typeImmutabilityDependees += ((t, (eop, Set(value)))) + } + } - def addPurityDependee( - dm: DeclaredMethod, - eop: EOptionP[DeclaredMethod, Purity], - params: Seq[Expr[V]] - ): Unit = { - if (purityDependees.contains(dm)) { - val (_, oldParams) = purityDependees(dm) - purityDependees += ((dm, (eop, oldParams + params))) - } else { - purityDependees += ((dm, (eop, Set(params)))) - } - } + def addPurityDependee( + dm: DeclaredMethod, + eop: EOptionP[DeclaredMethod, Purity], + params: Seq[Expr[V]] + ): Unit = { + if (purityDependees.contains(dm)) { + val (_, oldParams) = purityDependees(dm) + purityDependees += ((dm, (eop, oldParams + params))) + } else { + purityDependees += ((dm, (eop, Set(params)))) + } + } - def updateCalleesDependee(eps: EOptionP[DeclaredMethod, Callees]): Unit = { - if (eps.isFinal) calleesDependee = None - else calleesDependee = Some(eps) - if (eps.hasUBP) - callees = Some(eps.ub) - } + def updateCalleesDependee(eps: EOptionP[DeclaredMethod, Callees]): Unit = { + if (eps.isFinal) calleesDependee = None + else calleesDependee = Some(eps) + if (eps.hasUBP) + callees = Some(eps.ub) + } - def addRVFDependee( - dm: DeclaredMethod, - eop: EOptionP[DeclaredMethod, ReturnValueFreshness], - data: (Option[Expr[V]], Purity) - ): Unit = { - if (rvfDependees.contains(dm)) { - val (_, oldValues) = rvfDependees(dm) - rvfDependees += ((dm, (eop, oldValues + data))) - } else { - rvfDependees += ((dm, (eop, Set(data)))) - } - } + def addRVFDependee( + dm: DeclaredMethod, + eop: EOptionP[DeclaredMethod, ReturnValueFreshness], + data: (Option[Expr[V]], Purity) + ): Unit = { + if (rvfDependees.contains(dm)) { + val (_, oldValues) = rvfDependees(dm) + rvfDependees += ((dm, (eop, oldValues + data))) + } else { + rvfDependees += ((dm, (eop, Set(data)))) + } + } - def removeFieldLocalityDependee(f: Field): Unit = fieldLocalityDependees -= f - def removeFieldMutabilityDependee(f: Field): Unit = fieldMutabilityDependees -= f - def removeClassImmutabilityDependee(t: ObjectType): Unit = classImmutabilityDependees -= t - def removeTypeImmutabilityDependee(t: ObjectType): Unit = typeImmutabilityDependees -= t - def removePurityDependee(dm: DeclaredMethod): Unit = purityDependees -= dm - def removeRVFDependee(dm: DeclaredMethod): Unit = rvfDependees -= dm + def removeFieldLocalityDependee(f: Field): Unit = fieldLocalityDependees -= f + def removeFieldMutabilityDependee(f: Field): Unit = fieldMutabilityDependees -= f + def removeClassImmutabilityDependee(t: ObjectType): Unit = classImmutabilityDependees -= t + def removeTypeImmutabilityDependee(t: ObjectType): Unit = typeImmutabilityDependees -= t + def removePurityDependee(dm: DeclaredMethod): Unit = purityDependees -= dm + def removeRVFDependee(dm: DeclaredMethod): Unit = rvfDependees -= dm - def updateStaticDataUsage(eps: Option[EOptionP[DeclaredMethod, StaticDataUsage]]): Unit = { - staticDataUsage = eps - } + def updateStaticDataUsage(eps: Option[EOptionP[DeclaredMethod, StaticDataUsage]]): Unit = { + staticDataUsage = eps + } - def updateTacai(eps: EOptionP[Method, TACAI]): Unit = { - if (eps.isFinal) tacai = None - else tacai = Some(eps) - if (eps.hasUBP && eps.ub.tac.isDefined) { - val tac = eps.ub.tac.get - pcToIndex = tac.pcToIndex - code = tac.stmts - } + def updateTacai(eps: EOptionP[Method, TACAI]): Unit = { + if (eps.isFinal) tacai = None + else tacai = Some(eps) + if (eps.hasUBP && eps.ub.tac.isDefined) { + val tac = eps.ub.tac.get + pcToIndex = tac.pcToIndex + code = tac.stmts + } + } } - } - - type StateType = State - val raterFqn: String = project.config.as[String]( - "org.opalj.fpcf.analyses.L2PurityAnalysis.domainSpecificRater" - ) + type StateType = State - val rater: DomainSpecificRater = - L2PurityAnalysis.rater.getOrElse(resolveDomainSpecificRater(raterFqn)) - - /** - * Examines whether the given expression denotes an object/array that is local to the current - * method, i.e. the method has control over the object/array and actions on it might not - * influence purity. - * - * @param otherwise The maxPurity will be reduced to at most this level if the expression is not - * local. - */ - override def isLocal( - expr: Expr[V], - otherwise: Purity, - excludedDefSites: IntTrieSet = EmptyIntTrieSet - )(implicit state: State): Boolean = { - isLocalInternal( - expr, - otherwise, - _ => CompileTimePure, - treatParamsAsFresh = false + val raterFqn: String = project.config.as[String]( + "org.opalj.fpcf.analyses.L2PurityAnalysis_new.domainSpecificRater" ) - } - - /** - * Examines whether the given expression denotes an object/array that is local to the current - * method, i.e. the method has control over the object/array and actions on it might not - * influence purity. - * - * @note Fresh references can be treated as non-escaping as the analysis result will be impure - * if anything escapes the method via parameters, static field assignments or calls. - * - * @param otherwise The maxPurity will be reduced to at most this level if the expression is not - * local - * @param onParameter The maxPurity will be reduced to at most this level if the expression can - * be a parameter - * @param treatParamsAsFresh The value to be returned if the expression can be a parameter - */ - def isLocalInternal( - expr: Expr[V], - otherwise: Purity, - onParameter: Int => Purity, - 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.isConst) - return true; - - if (!expr.isVar) { - // The expression could refer to further expressions in a non-flat representation. - // In that case it could be, e.g., a GetStatic. In that case the reference is not - // locally created and/or initialized. To avoid special handling, we just fallback to - // false here as the analysis is intended to be used on flat representations anyway. - atMost(otherwise) - return false; - } - // Primitive values are always local (required for parameters of contextually pure calls) - // TODO (value is null for the self reference of a throwable constructor...) - if (expr.asVar.value != null && - (expr.asVar.value.computationalType ne ComputationalTypeReference)) - return true; - - val defSites = expr.asVar.definedBy -- excludedDefSites - val isLocal = - defSites.forall( - isLocalDefsite( - _, - otherwise, - onParameter, - treatParamsAsFresh, - defSites, - excludedDefSites + val rater: DomainSpecificRater = + L2PurityAnalysis.rater.getOrElse(resolveDomainSpecificRater(raterFqn)) + + /** + * Examines whether the given expression denotes an object/array that is local to the current + * method, i.e. the method has control over the object/array and actions on it might not + * influence purity. + * + * @param otherwise The maxPurity will be reduced to at most this level if the expression is not + * local. + */ + override def isLocal( + expr: Expr[V], + otherwise: Purity, + excludedDefSites: IntTrieSet = EmptyIntTrieSet + )(implicit state: State): Boolean = { + isLocalInternal( + expr, + otherwise, + _ ⇒ CompileTimePure, + treatParamsAsFresh = false ) - ) - if (!isLocal) atMost(otherwise) - isLocal - } - - /** - * Examines whether the given defsite denotes an object/array that is local to the current - * method, i.e. the method has control over the object/array and actions on it might not - * influence purity. - * - * @param otherwise The maxPurity will be reduced to at most this level if the defsite is not - * local - * @param onParameter The maxPurity will be reduced to at most this level if the defsite is a - * parameter - * @param treatParamsAsFresh The value to be returned if the defsite is a parameter - */ - def isLocalDefsite( - defSite: Int, - otherwise: Purity, - onParameter: Int => Purity, - treatParamsAsFresh: Boolean, - defSites: IntTrieSet, - excludedDefSites: IntTrieSet - )(implicit state: State): Boolean = { - if (isImmediateVMException(defSite)) - return true; // VMLevelValues are freshly created - - if (ai.isMethodExternalExceptionOrigin(defSite)) - return false; // Method external exceptions are not freshly created - - if (defSite == OriginOfThis) { - if (!state.method.isConstructor) { - atMost(onParameter(0)) - } - return treatParamsAsFresh | state.method.isConstructor; } - if (defSite < 0) { - atMost(onParameter(-defSite - 1)) - return treatParamsAsFresh; - } - - val stmt = state.code(defSite) - assert(stmt.astID == Assignment.ASTID, "defSite should be assignment") - - val rhs = stmt.asAssignment.expr - if (rhs.isConst) - return true; - - (rhs.astID: @switch) match { - case New.ASTID | NewArray.ASTID => true - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => - val oldPurityLevel = - state.rvfCallSites.get(stmt.pc).map(_._2).getOrElse(CompileTimePure) - val data = (rhs.asFunctionCall.receiverOption, otherwise meet oldPurityLevel) - if (state.callees.isDefined) { - checkFreshnessOfReturn(stmt.pc, data, state.callees.get) - } else { - state.rvfCallSites += stmt.pc -> data - reducePurityLB(otherwise) + /** + * Examines whether the given expression denotes an object/array that is local to the current + * method, i.e. the method has control over the object/array and actions on it might not + * influence purity. + * + * @note Fresh references can be treated as non-escaping as the analysis result will be impure + * if anything escapes the method via parameters, static field assignments or calls. + * + * @param otherwise The maxPurity will be reduced to at most this level if the expression is not + * local + * @param onParameter The maxPurity will be reduced to at most this level if the expression can + * be a parameter + * @param treatParamsAsFresh The value to be returned if the expression can be a parameter + */ + def isLocalInternal( + expr: Expr[V], + otherwise: Purity, + onParameter: Int ⇒ Purity, + 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.isConst) + return true; + + if (!expr.isVar) { + // The expression could refer to further expressions in a non-flat representation. + // In that case it could be, e.g., a GetStatic. In that case the reference is not + // locally created and/or initialized. To avoid special handling, we just fallback to + // false here as the analysis is intended to be used on flat representations anyway. + atMost(otherwise) + return false; } - true - case GetField.ASTID => - val GetField(_, declClass, name, fieldType, objRef) = rhs - project.resolveFieldReference(declClass, name, fieldType) match { - case Some(field) => - val locality = propertyStore(field, FieldLocality.key) - checkLocalityOfField(locality, (objRef, otherwise)) && - isLocalInternal( - objRef, - otherwise, - onParameter, - treatParamsAsFresh, - excludedDefSites ++ defSites + + // Primitive values are always local (required for parameters of contextually pure calls) + // TODO (value is null for the self reference of a throwable constructor...) + if (expr.asVar.value != null && + (expr.asVar.value.computationalType ne ComputationalTypeReference)) + return true; + + val defSites = expr.asVar.definedBy -- excludedDefSites + val isLocal = + defSites.forall( + isLocalDefsite( + _, + otherwise, + onParameter, + treatParamsAsFresh, + defSites, + excludedDefSites + ) ) - case None => false - } - case _ => false - } - } - - def checkLocalityOfField( - ep: EOptionP[Field, FieldLocality], - data: (Expr[V], Purity) - )(implicit state: State): Boolean = { - val isLocal = ep match { - case FinalP(LocalField | LocalFieldWithGetter) => - true - case FinalP(ExtensibleLocalField | ExtensibleLocalFieldWithGetter) => - if (data._1.isVar) { - val value = data._1.asVar.value.asReferenceValue - value.isPrecise && - !classHierarchy.isSubtypeOf(value.asReferenceType, ObjectType.Cloneable) - } else - false - case UBP(NoLocalField) => - false - case _ => - reducePurityLB(data._2) - if (data._2 meet state.ubPurity ne state.ubPurity) - state.addFieldLocalityDependee(ep.e, ep, data) - true + if (!isLocal) atMost(otherwise) + isLocal } - if (!isLocal) - atMost(data._2) - isLocal - } - - def checkLocalityOfReturn( - ep: EOptionP[DeclaredMethod, Property], - data: (Option[Expr[V]], Purity) - )(implicit state: State): Unit = { - import project.classHierarchy.isSubtypeOf - ep match { - case LBP(PrimitiveReturnValue | FreshReturnValue) => - case FinalP(Getter) => - if (data._2 meet state.ubPurity ne state.ubPurity) - isLocal(data._1.get, data._2) - case FinalP(ExtensibleGetter) => - if (data._1.get.isVar) { - val value = data._1.get.asVar.value.asReferenceValue - if (value.isPrecise && !isSubtypeOf(value.asReferenceType, ObjectType.Cloneable)) { - if (data._2 meet state.ubPurity ne state.ubPurity) - isLocal(data._1.get, data._2) - } else { - atMost(data._2) - } - } else { - atMost(data._2) + + /** + * Examines whether the given defsite denotes an object/array that is local to the current + * method, i.e. the method has control over the object/array and actions on it might not + * influence purity. + * + * @param otherwise The maxPurity will be reduced to at most this level if the defsite is not + * local + * @param onParameter The maxPurity will be reduced to at most this level if the defsite is a + * parameter + * @param treatParamsAsFresh The value to be returned if the defsite is a parameter + */ + def isLocalDefsite( + defSite: Int, + otherwise: Purity, + onParameter: Int ⇒ Purity, + treatParamsAsFresh: Boolean, + defSites: IntTrieSet, + excludedDefSites: IntTrieSet + )(implicit state: State): Boolean = { + if (isImmediateVMException(defSite)) + return true; // VMLevelValues are freshly created + + if (ai.isMethodExternalExceptionOrigin(defSite)) + return false; // Method external exceptions are not freshly created + + if (defSite == OriginOfThis) { + if (!state.method.isConstructor) { + atMost(onParameter(0)) + } + return treatParamsAsFresh | state.method.isConstructor; } - case UBP(NoFreshReturnValue) => - atMost(data._2) - case _: SomeEOptionP => - reducePurityLB(data._2) - if (data._2 meet state.ubPurity ne state.ubPurity) { - state.addRVFDependee( - ep.e, - ep.asInstanceOf[EOptionP[DeclaredMethod, ReturnValueFreshness]], - data - ) + + if (defSite < 0) { + atMost(onParameter(-defSite - 1)) + return treatParamsAsFresh; + } + + val stmt = state.code(defSite) + assert(stmt.astID == Assignment.ASTID, "defSite should be assignment") + + val rhs = stmt.asAssignment.expr + if (rhs.isConst) + return true; + + (rhs.astID: @switch) match { + case New.ASTID | NewArray.ASTID ⇒ true + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + val oldPurityLevel = + state.rvfCallSites.get(stmt.pc).map(_._2).getOrElse(CompileTimePure) + val data = (rhs.asFunctionCall.receiverOption, otherwise meet oldPurityLevel) + if (state.callees.isDefined) { + checkFreshnessOfReturn(stmt.pc, data, state.callees.get) + } else { + state.rvfCallSites += stmt.pc -> data + reducePurityLB(otherwise) + } + true + case GetField.ASTID ⇒ + val GetField(_, declClass, name, fieldType, objRef) = rhs + project.resolveFieldReference(declClass, name, fieldType) match { + case Some(field) ⇒ + val locality = propertyStore(field, FieldLocality.key) + checkLocalityOfField(locality, (objRef, otherwise)) && + isLocalInternal( + objRef, + otherwise, + onParameter, + treatParamsAsFresh, + excludedDefSites ++ defSites + ) + case None ⇒ false + } + case _ ⇒ false } } - } - - def checkFreshnessOfReturn( - pc: Int, - data: (Option[Expr[V]], Purity), - callees: Callees - )(implicit state: State): Unit = { - callees.callees(pc).foreach { callee => - if (callee.descriptor.returnType.isReferenceType) - checkLocalityOfReturn(propertyStore(callee, ReturnValueFreshness.key), data) - } - } - /** - * Examines a statement for its influence on the method's purity. - * This method will return false for impure statements, so evaluation can be terminated early. - */ - override def checkPurityOfStmt(stmt: Stmt[V])(implicit state: State): Boolean = - (stmt.astID: @switch) match { - // Synchronization on non-escaping local objects/arrays is pure (and useless...) - case MonitorEnter.ASTID | MonitorExit.ASTID => - val objRef = stmt.asSynchronizationStmt.objRef - isLocalInternal( - objRef, - ImpureByAnalysis, - param => ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true - ) && stmt.forallSubExpressions(checkPurityOfExpr) - - // Storing into non-escaping locally initialized arrays/objects is pure - case ArrayStore.ASTID => - val arrayRef = stmt.asArrayStore.arrayRef - isLocalInternal( - arrayRef, - ImpureByAnalysis, - param => ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true - ) && stmt.forallSubExpressions(checkPurityOfExpr) - case PutField.ASTID => - val objRef = stmt.asPutField.objRef - isLocalInternal( - objRef, - ImpureByAnalysis, - param => ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true - ) && stmt.forallSubExpressions(checkPurityOfExpr) + def checkLocalityOfField( + ep: EOptionP[Field, FieldLocality], + data: (Expr[V], Purity) + )(implicit state: State): Boolean = { + val isLocal = ep match { + case FinalP(LocalField | LocalFieldWithGetter) ⇒ + true + case FinalP(ExtensibleLocalField | ExtensibleLocalFieldWithGetter) ⇒ + if (data._1.isVar) { + val value = data._1.asVar.value.asReferenceValue + value.isPrecise && + !classHierarchy.isSubtypeOf(value.asReferenceType, ObjectType.Cloneable) + } else + false + case UBP(NoLocalField) ⇒ + false + case _ ⇒ + reducePurityLB(data._2) + if (data._2 meet state.ubPurity ne state.ubPurity) + state.addFieldLocalityDependee(ep.e, ep, data) + true + } + if (!isLocal) + atMost(data._2) + isLocal + } - case _ => super.checkPurityOfStmt(stmt) + def checkLocalityOfReturn( + ep: EOptionP[DeclaredMethod, Property], + data: (Option[Expr[V]], Purity) + )(implicit state: State): Unit = { + import project.classHierarchy.isSubtypeOf + ep match { + case LBP(PrimitiveReturnValue | FreshReturnValue) ⇒ + case FinalP(Getter) ⇒ + if (data._2 meet state.ubPurity ne state.ubPurity) + isLocal(data._1.get, data._2) + case FinalP(ExtensibleGetter) ⇒ + if (data._1.get.isVar) { + val value = data._1.get.asVar.value.asReferenceValue + if (value.isPrecise && !isSubtypeOf(value.asReferenceType, ObjectType.Cloneable)) { + if (data._2 meet state.ubPurity ne state.ubPurity) + isLocal(data._1.get, data._2) + } else { + atMost(data._2) + } + } else { + atMost(data._2) + } + case UBP(NoFreshReturnValue) ⇒ + atMost(data._2) + case _: SomeEOptionP ⇒ + reducePurityLB(data._2) + if (data._2 meet state.ubPurity ne state.ubPurity) { + state.addRVFDependee( + ep.e, + ep.asInstanceOf[EOptionP[DeclaredMethod, ReturnValueFreshness]], + data + ) + } + } } - /** - * Examines the influence of the purity property of a method on the examined method's purity. - * - * @note Adds dependendees when necessary. - */ - def checkMethodPurity( - ep: EOptionP[DeclaredMethod, Purity], - params: Seq[Expr[V]] - )(implicit state: State): Boolean = ep match { - case UBP(_: ClassifiedImpure) => - atMost(ImpureByAnalysis) - false - case eps @ LUBP(lb, ub) => - if (eps.isRefinable && ((lb meet state.ubPurity) ne state.ubPurity)) { - // On conditional, keep dependence - state.addPurityDependee(ep.e, ep, params) - reducePurityLB(lb) - } - // Contextual/external purity is handled below - atMost(ub.withoutContextual) - ub.modifiedParams.forall( - param => - isLocalInternal( - params(param), - ImpureByAnalysis, - param => ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true - ) - ) - case _: SomeEOptionP => - reducePurityLB(ImpureByAnalysis) - state.addPurityDependee(ep.e, ep, params) - true - } - - /** - * Handles the effect of static data usage on the purity level. - * - * @note Modifies dependees as necessary. - */ - def checkStaticDataUsage( - ep: EOptionP[DeclaredMethod, StaticDataUsage] - )(implicit state: State): Unit = { - ep match { - case LBP(UsesNoStaticData | UsesConstantDataOnly) => - state.updateStaticDataUsage(None) - case UBP(UsesVaryingData) => - state.updateStaticDataUsage(None) - atMost(Pure) - case _ => - reducePurityLB(Pure) - state.updateStaticDataUsage(Some(ep)) + def checkFreshnessOfReturn( + pc: Int, + data: (Option[Expr[V]], Purity), + callees: Callees + )(implicit state: State): Unit = { + callees.callees(pc).foreach { callee ⇒ + if (callee.descriptor.returnType.isReferenceType) + checkLocalityOfReturn(propertyStore(callee, ReturnValueFreshness.key), data) + } } - } - /** - * Adds the dependee necessary if a field mutability is not known yet. - */ - override def handleUnknownFieldMutability( - ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], - objRef: Option[Expr[V]] - )(implicit state: State): Unit = { - state.addFieldMutabilityDependee(ep.e, ep, objRef) - } - - /** - * Adds the dependee necessary if a type mutability is not known yet. - */ - override def handleUnknownTypeMutability( - ep: EOptionP[ObjectType, Property], - expr: Expr[V] - )(implicit state: State): Unit = { - if (ep.pk == ClassImmutability_new.key) //ClassImmutability.key) - state.addClassImmutabilityDependee( - ep.e, - ep.asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]], //ClassImmutability]], - expr - ) - else - state.addTypeImmutabilityDependee( - ep.e, - ep.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]], //TypeImmutability]], - expr - ) - } - - /** - * Add or remove the dependee when the callees property changes. - */ - override def handleCalleesUpdate( - callees: EOptionP[DeclaredMethod, Callees] - )(implicit state: State): Unit = { - state.updateCalleesDependee(callees) - if (callees.isRefinable) - reducePurityLB(ImpureByAnalysis) - } - - /* - * Adds the dependee necessary if the TACAI is not yet final. - */ - override def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: State): Unit = { - state.updateTacai(ep) - } + /** + * Examines a statement for its influence on the method's purity. + * This method will return false for impure statements, so evaluation can be terminated early. + */ + override def checkPurityOfStmt(stmt: Stmt[V])(implicit state: State): Boolean = + (stmt.astID: @switch) match { + // Synchronization on non-escaping local objects/arrays is pure (and useless...) + case MonitorEnter.ASTID | MonitorExit.ASTID ⇒ + val objRef = stmt.asSynchronizationStmt.objRef + isLocalInternal( + objRef, + ImpureByAnalysis, + param ⇒ ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + ) && stmt.forallSubExpressions(checkPurityOfExpr) + + // Storing into non-escaping locally initialized arrays/objects is pure + case ArrayStore.ASTID ⇒ + val arrayRef = stmt.asArrayStore.arrayRef + isLocalInternal( + arrayRef, + ImpureByAnalysis, + param ⇒ ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + ) && stmt.forallSubExpressions(checkPurityOfExpr) + case PutField.ASTID ⇒ + val objRef = stmt.asPutField.objRef + isLocalInternal( + objRef, + ImpureByAnalysis, + param ⇒ ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + ) && stmt.forallSubExpressions(checkPurityOfExpr) + + case _ ⇒ super.checkPurityOfStmt(stmt) + } - /** - * Removes dependees that are known to not be needed anymore as they can not reduce the max - * purity level further. - */ - def cleanupDependees()(implicit state: State): Unit = { - if (state.ubPurity ne CompileTimePure) - state.updateStaticDataUsage(None) - - if (!state.ubPurity.isDeterministic) { - state.fieldMutabilityDependees = Map.empty - state.classImmutabilityDependees = Map.empty - state.typeImmutabilityDependees = Map.empty - state.updateStaticDataUsage(None) + /** + * Examines the influence of the purity property of a method on the examined method's purity. + * + * @note Adds dependendees when necessary. + */ + def checkMethodPurity( + ep: EOptionP[DeclaredMethod, Purity], + params: Seq[Expr[V]] + )(implicit state: State): Boolean = ep match { + case UBP(_: ClassifiedImpure) ⇒ + atMost(ImpureByAnalysis) + false + case eps @ LUBP(lb, ub) ⇒ + if (eps.isRefinable && ((lb meet state.ubPurity) ne state.ubPurity)) { + // On conditional, keep dependence + state.addPurityDependee(ep.e, ep, params) + reducePurityLB(lb) + } + // Contextual/external purity is handled below + atMost(ub.withoutContextual) + ub.modifiedParams.forall( + param ⇒ + isLocalInternal( + params(param), + ImpureByAnalysis, + param ⇒ ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + ) + ) + case _: SomeEOptionP ⇒ + reducePurityLB(ImpureByAnalysis) + state.addPurityDependee(ep.e, ep, params) + true } - 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))) + /** + * Handles the effect of static data usage on the purity level. + * + * @note Modifies dependees as necessary. + */ + def checkStaticDataUsage( + ep: EOptionP[DeclaredMethod, StaticDataUsage] + )(implicit state: State): Unit = { + ep match { + case LBP(UsesNoStaticData | UsesConstantDataOnly) ⇒ + state.updateStaticDataUsage(None) + case UBP(UsesVaryingData) ⇒ + state.updateStaticDataUsage(None) + atMost(Pure) + case _ ⇒ + reducePurityLB(Pure) + state.updateStaticDataUsage(Some(ep)) + } } - state.fieldLocalityDependees = newFieldLocalityDependees - var newRVFCallsites: IntMap[(Option[Expr[V]], Purity)] = IntMap.empty - for ((callsite, data) <- state.rvfCallSites) { - if (data._2 meet state.ubPurity ne state.ubPurity) newRVFCallsites += ((callsite, data)) + /** + * Adds the dependee necessary if a field mutability is not known yet. + */ + override def handleUnknownFieldMutability( + ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], + objRef: Option[Expr[V]] + )(implicit state: State): Unit = { + state.addFieldMutabilityDependee(ep.e, ep, objRef) } - 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))) + + /** + * Adds the dependee necessary if a type mutability is not known yet. + */ + override def handleUnknownTypeMutability( + ep: EOptionP[ObjectType, Property], + expr: Expr[V] + )(implicit state: State): Unit = { + if (ep.pk == ClassImmutability_new.key) //ClassImmutability.key) + state.addClassImmutabilityDependee( + ep.e, + ep.asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]], //ClassImmutability]], + expr + ) + else + state.addTypeImmutabilityDependee( + ep.e, + ep.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]], //TypeImmutability]], + expr + ) } - 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)) + /** + * Add or remove the dependee when the callees property changes. + */ + override def handleCalleesUpdate( + callees: EOptionP[DeclaredMethod, Callees] + )(implicit state: State): Unit = { + state.updateCalleesDependee(callees) + if (callees.isRefinable) + reducePurityLB(ImpureByAnalysis) } - state.purityDependees = newPurityDependees - } - /** - * Raises the lower bound on the purity whenever possible. + /* + * Adds the dependee necessary if the TACAI is not yet final. */ - def adjustLowerBound()(implicit state: State): Unit = { - if (state.calleesDependee.isDefined) - return; // Nothing to be done, lower bound is still LBImpure - - var newLowerBound = state.ubPurity + override def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: State): Unit = { + state.updateTacai(ep) + } - if (state.tacai.isDefined) return; // Nothing to be done, lower bound is still LBImpure + /** + * Removes dependees that are known to not be needed anymore as they can not reduce the max + * purity level further. + */ + def cleanupDependees()(implicit state: State): Unit = { + if (state.ubPurity ne CompileTimePure) + state.updateStaticDataUsage(None) + + if (!state.ubPurity.isDeterministic) { + state.fieldMutabilityDependees = Map.empty + state.classImmutabilityDependees = Map.empty + state.typeImmutabilityDependees = Map.empty + state.updateStaticDataUsage(None) + } - for ((eop, _) <- state.purityDependees.valuesIterator) { - eop match { - case LBP(lb) => newLowerBound = newLowerBound meet lb - case _ => - return; // Nothing to be done, lower bound is still LBImpure - } - } + 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))) + } + state.fieldLocalityDependees = newFieldLocalityDependees - if (state.staticDataUsage.isDefined) newLowerBound = newLowerBound meet Pure + var newRVFCallsites: IntMap[(Option[Expr[V]], Purity)] = IntMap.empty + for ((callsite, data) ← state.rvfCallSites) { + if (data._2 meet state.ubPurity ne state.ubPurity) newRVFCallsites += ((callsite, data)) + } + state.rvfCallSites = newRVFCallsites - if (state.fieldMutabilityDependees.nonEmpty || - state.classImmutabilityDependees.nonEmpty || - state.typeImmutabilityDependees.nonEmpty) { - newLowerBound = newLowerBound meet SideEffectFree - } + 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))) + } + state.rvfDependees = newRVFDependees - for { - (_, data) <- state.fieldLocalityDependees.valuesIterator - (_, purity) <- data - } { - newLowerBound = newLowerBound meet purity + 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)) + } + state.purityDependees = newPurityDependees } - for { - (_, purity) <- state.rvfCallSites.valuesIterator - } { - newLowerBound = newLowerBound meet purity - } + /** + * Raises the lower bound on the purity whenever possible. + */ + def adjustLowerBound()(implicit state: State): Unit = { + if (state.calleesDependee.isDefined) + return ; // Nothing to be done, lower bound is still LBImpure - for { - (_, data) <- state.rvfDependees.valuesIterator - (_, purity) <- data - } { - newLowerBound = newLowerBound meet purity - } + var newLowerBound = state.ubPurity - state.lbPurity = newLowerBound - } + if (state.tacai.isDefined) return ; // Nothing to be done, lower bound is still LBImpure - /** - * Continuation to handle updates to properties of dependees. - * Dependees may be - * - methods / virtual methods called (for their purity) - * - fields read (for their mutability) - * - classes files for class types returned (for their mutability) - */ - def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { - val oldPurity = state.ubPurity - eps.ub.key match { - case Purity.key => - val e = eps.e.asInstanceOf[DeclaredMethod] - val dependees = state.purityDependees(e) - state.removePurityDependee(e) - dependees._2.foreach { e => - checkMethodPurity(eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]], e) + for ((eop, _) ← state.purityDependees.valuesIterator) { + eop match { + case LBP(lb) ⇒ newLowerBound = newLowerBound meet lb + case _ ⇒ + return ; // Nothing to be done, lower bound is still LBImpure + } } - //case FieldMutability.key ⇒ - case ReferenceImmutability.key => - val e = eps.e.asInstanceOf[Field] - val dependees = state.fieldMutabilityDependees(e) - state.removeFieldMutabilityDependee(e) - dependees._2.foreach { e => - checkFieldMutability(eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]], e) //FieldMutability]], e) + + if (state.staticDataUsage.isDefined) newLowerBound = newLowerBound meet Pure + + if (state.fieldMutabilityDependees.nonEmpty || + state.classImmutabilityDependees.nonEmpty || + state.typeImmutabilityDependees.nonEmpty) { + newLowerBound = newLowerBound meet SideEffectFree } - //case ClassImmutability.key ⇒ - case ClassImmutability_new.key => - val e = eps.e.asInstanceOf[ObjectType] - val dependees = state.classImmutabilityDependees(e) - state.removeClassImmutabilityDependee(e) - dependees._2.foreach { e => - checkTypeMutability( - eps.asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]], // ClassImmutability]], - e - ) + + for { + (_, data) ← state.fieldLocalityDependees.valuesIterator + (_, purity) ← data + } { + newLowerBound = newLowerBound meet purity } - //case TypeImmutability.key ⇒ - case TypeImmutability_new.key => - val e = eps.e.asInstanceOf[ObjectType] - val dependees = state.typeImmutabilityDependees(e) - state.removeTypeImmutabilityDependee(e) - dependees._2.foreach { e => - checkTypeMutability(eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]], e) //TypeImmutability]], e) + + for { + (_, purity) ← state.rvfCallSites.valuesIterator + } { + newLowerBound = newLowerBound meet purity } - case ReturnValueFreshness.key => - val e = eps.e.asInstanceOf[DeclaredMethod] - val dependees = state.rvfDependees(e) - state.removeRVFDependee(e) - dependees._2.foreach { e => - checkLocalityOfReturn( - eps.asInstanceOf[EOptionP[DeclaredMethod, ReturnValueFreshness]], - e - ) + + for { + (_, data) ← state.rvfDependees.valuesIterator + (_, purity) ← data + } { + newLowerBound = newLowerBound meet purity } - case FieldLocality.key => - val e = eps.e.asInstanceOf[Field] - val dependees = state.fieldLocalityDependees(e) - state.removeFieldLocalityDependee(e) - dependees._2.foreach { e => - checkLocalityOfField(eps.asInstanceOf[EOptionP[Field, FieldLocality]], e) + + state.lbPurity = newLowerBound + } + + /** + * Continuation to handle updates to properties of dependees. + * Dependees may be + * - methods / virtual methods called (for their purity) + * - fields read (for their mutability) + * - classes files for class types returned (for their mutability) + */ + def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { + val oldPurity = state.ubPurity + eps.ub.key match { + case Purity.key ⇒ + val e = eps.e.asInstanceOf[DeclaredMethod] + val dependees = state.purityDependees(e) + state.removePurityDependee(e) + dependees._2.foreach { e ⇒ + checkMethodPurity(eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]], e) + } + //case FieldMutability.key ⇒ + case ReferenceImmutability.key ⇒ + val e = eps.e.asInstanceOf[Field] + val dependees = state.fieldMutabilityDependees(e) + state.removeFieldMutabilityDependee(e) + dependees._2.foreach { e ⇒ + checkFieldMutability(eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]], e) //FieldMutability]], e) + } + //case ClassImmutability.key ⇒ + case ClassImmutability_new.key ⇒ + val e = eps.e.asInstanceOf[ObjectType] + val dependees = state.classImmutabilityDependees(e) + state.removeClassImmutabilityDependee(e) + dependees._2.foreach { e ⇒ + checkTypeMutability( + eps.asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]], // ClassImmutability]], + e + ) + } + //case TypeImmutability.key ⇒ + case TypeImmutability_new.key ⇒ + val e = eps.e.asInstanceOf[ObjectType] + val dependees = state.typeImmutabilityDependees(e) + state.removeTypeImmutabilityDependee(e) + dependees._2.foreach { e ⇒ + checkTypeMutability(eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]], e) //TypeImmutability]], e) + } + case ReturnValueFreshness.key ⇒ + val e = eps.e.asInstanceOf[DeclaredMethod] + val dependees = state.rvfDependees(e) + state.removeRVFDependee(e) + dependees._2.foreach { e ⇒ + checkLocalityOfReturn( + eps.asInstanceOf[EOptionP[DeclaredMethod, ReturnValueFreshness]], + e + ) + } + case FieldLocality.key ⇒ + val e = eps.e.asInstanceOf[Field] + val dependees = state.fieldLocalityDependees(e) + state.removeFieldLocalityDependee(e) + dependees._2.foreach { e ⇒ + checkLocalityOfField(eps.asInstanceOf[EOptionP[Field, FieldLocality]], e) + } + case Callees.key ⇒ + checkPurityOfCallees(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + state.rvfCallSites.foreach { + case (pc, data) ⇒ checkFreshnessOfReturn(pc, data, eps.ub.asInstanceOf[Callees]) + } + case StaticDataUsage.key ⇒ + checkStaticDataUsage(eps.asInstanceOf[EOptionP[DeclaredMethod, StaticDataUsage]]) + case TACAI.key ⇒ + state.updateTacai(eps.asInstanceOf[EOptionP[Method, TACAI]]) + return determineMethodPurity(eps.ub.asInstanceOf[TACAI].tac.get.cfg); } - case Callees.key => - checkPurityOfCallees(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - state.rvfCallSites.foreach { - case (pc, data) => checkFreshnessOfReturn(pc, data, eps.ub.asInstanceOf[Callees]) + + if (state.ubPurity eq ImpureByAnalysis) + return Result(state.definedMethod, ImpureByAnalysis); + + if (state.ubPurity ne oldPurity) + cleanupDependees() // Remove dependees that we don't need anymore. + adjustLowerBound() + + val dependees = state.dependees + if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { + Result(state.definedMethod, state.ubPurity) + } else { + InterimResult( + state.definedMethod, + state.lbPurity, + state.ubPurity, + dependees, + c + ) } - case StaticDataUsage.key => - checkStaticDataUsage(eps.asInstanceOf[EOptionP[DeclaredMethod, StaticDataUsage]]) - case TACAI.key => - state.updateTacai(eps.asInstanceOf[EOptionP[Method, TACAI]]) - return determineMethodPurity(eps.ub.asInstanceOf[TACAI].tac.get.cfg); } - if (state.ubPurity eq ImpureByAnalysis) - return Result(state.definedMethod, ImpureByAnalysis); - - if (state.ubPurity ne oldPurity) - cleanupDependees() // Remove dependees that we don't need anymore. - adjustLowerBound() - - val dependees = state.dependees - if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { - Result(state.definedMethod, state.ubPurity) - } else { - InterimResult( - state.definedMethod, - state.lbPurity, - state.ubPurity, - dependees, - c - ) - } - } + /** + * Determines the purity of a method once TACAI is available. + */ + def determineMethodPurity( + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): ProperPropertyComputationResult = { + // Special case: The Throwable constructor is `LBSideEffectFree`, but subtype constructors + // may not be because of overridable fillInStackTrace method + if (state.method.isConstructor && state.declClass.isSubtypeOf(ObjectType.Throwable)) + project.instanceMethods(state.declClass).foreach { mdc ⇒ + if (mdc.name == "fillInStackTrace" && + mdc.method.classFile.thisType != ObjectType.Throwable) { + // "The value" is actually not used at all - hence, we can use "null" + // over here. + val selfReference = UVar(null, SelfReferenceParameter) + val fISTPurity = propertyStore(declaredMethods(mdc.method), Purity.key) + if (!checkMethodPurity(fISTPurity, Seq(selfReference))) { + // Early return for impure fillInStackTrace + return Result(state.definedMethod, state.ubPurity); + } + } + } + + // Synchronized methods have a visible side effect on the receiver + // Static synchronized methods lock the class which is potentially globally visible + if (state.method.isSynchronized) + if (state.method.isStatic) return Result(state.definedMethod, ImpureByAnalysis); + else atMost(ContextuallyPure(IntTrieSet(0))) + + val stmtCount = state.code.length + var s = 0 + while (s < stmtCount) { + if (!checkPurityOfStmt(state.code(s))) { // Early return for impure statements + assert(state.ubPurity.isInstanceOf[ClassifiedImpure]) + return Result(state.definedMethod, state.ubPurity); + } + s += 1 + } - /** - * Determines the purity of a method once TACAI is available. - */ - def determineMethodPurity( - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): ProperPropertyComputationResult = { - // Special case: The Throwable constructor is `LBSideEffectFree`, but subtype constructors - // may not be because of overridable fillInStackTrace method - if (state.method.isConstructor && state.declClass.isSubtypeOf(ObjectType.Throwable)) - project.instanceMethods(state.declClass).foreach { mdc => - if (mdc.name == "fillInStackTrace" && - mdc.method.classFile.thisType != ObjectType.Throwable) { - // "The value" is actually not used at all - hence, we can use "null" - // over here. - val selfReference = UVar(null, SelfReferenceParameter) - val fISTPurity = propertyStore(declaredMethods(mdc.method), Purity.key) - if (!checkMethodPurity(fISTPurity, Seq(selfReference))) { - // Early return for impure fillInStackTrace - return Result(state.definedMethod, state.ubPurity); - } + val callees = propertyStore(state.definedMethod, Callees.key) + if (!checkPurityOfCallees(callees)) + return Result(state.definedMethod, state.ubPurity) + + if (callees.hasUBP) + state.rvfCallSites.foreach { + case (pc, data) ⇒ + checkFreshnessOfReturn(pc, data, callees.ub) + } + + // Creating implicit exceptions is side-effect free (because of fillInStackTrace) + // but it may be ignored as domain-specific + val bbsCausingExceptions = cfg.abnormalReturnNode.predecessors + for { + bb ← bbsCausingExceptions + } { + val pc = bb.asBasicBlock.endPC + if (isSourceOfImmediateException(pc)) { + val throwingStmt = state.code(pc) + val ratedResult = rater.handleException(throwingStmt) + if (ratedResult.isDefined) atMost(ratedResult.get) + else atMost(SideEffectFree) + } } - } - - // Synchronized methods have a visible side effect on the receiver - // Static synchronized methods lock the class which is potentially globally visible - if (state.method.isSynchronized) - if (state.method.isStatic) return Result(state.definedMethod, ImpureByAnalysis); - else atMost(ContextuallyPure(IntTrieSet(0))) - - val stmtCount = state.code.length - var s = 0 - while (s < stmtCount) { - if (!checkPurityOfStmt(state.code(s))) { // Early return for impure statements - assert(state.ubPurity.isInstanceOf[ClassifiedImpure]) - return Result(state.definedMethod, state.ubPurity); - } - s += 1 - } - val callees = propertyStore(state.definedMethod, Callees.key) - if (!checkPurityOfCallees(callees)) - return Result(state.definedMethod, state.ubPurity) - - if (callees.hasUBP) - state.rvfCallSites.foreach { - case (pc, data) => - checkFreshnessOfReturn(pc, data, callees.ub) - } - - // Creating implicit exceptions is side-effect free (because of fillInStackTrace) - // but it may be ignored as domain-specific - val bbsCausingExceptions = cfg.abnormalReturnNode.predecessors - for { - bb <- bbsCausingExceptions - } { - val pc = bb.asBasicBlock.endPC - if (isSourceOfImmediateException(pc)) { - val throwingStmt = state.code(pc) - val ratedResult = rater.handleException(throwingStmt) - if (ratedResult.isDefined) atMost(ratedResult.get) - else atMost(SideEffectFree) - } - } + if (state.ubPurity eq CompileTimePure) // Check static data usage only if necessary + checkStaticDataUsage(propertyStore(state.definedMethod, StaticDataUsage.key)) + else + cleanupDependees() // Remove dependees we already know we won't need - if (state.ubPurity eq CompileTimePure) // Check static data usage only if necessary - checkStaticDataUsage(propertyStore(state.definedMethod, StaticDataUsage.key)) - else - cleanupDependees() // Remove dependees we already know we won't need - - val dependees = state.dependees - if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { - Result(state.definedMethod, state.ubPurity) - } else { - org.opalj.fpcf.InterimResult( - state.definedMethod, - state.lbPurity, - state.ubPurity, - dependees, - c - ) + val dependees = state.dependees + if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { + Result(state.definedMethod, state.ubPurity) + } else { + org.opalj.fpcf.InterimResult( + state.definedMethod, + state.lbPurity, + state.ubPurity, + dependees, + c + ) + } } - } - /** - * Determines the purity of the given method. - * - * @param definedMethod A defined method with a body. - */ - def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult = { - val method = definedMethod.definedMethod - val declClass = method.classFile.thisType - - // If this is not the method's declaration, but a non-overwritten method in a subtype, - // don't re-analyze the code - if (declClass ne definedMethod.declaringClassType) - return baseMethodPurity(definedMethod.asDefinedMethod); - - implicit val state: State = - new State(method, definedMethod, declClass) - - val tacaiO = getTACAI(method) - - if (tacaiO.isEmpty) - return InterimResult( - definedMethod, - ImpureByAnalysis, - CompileTimePure, - state.dependees, - c - ); - - determineMethodPurity(tacaiO.get.cfg) - } + /** + * Determines the purity of the given method. + * + * @param definedMethod A defined method with a body. + */ + def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult = { + val method = definedMethod.definedMethod + val declClass = method.classFile.thisType + + // If this is not the method's declaration, but a non-overwritten method in a subtype, + // don't re-analyze the code + if (declClass ne definedMethod.declaringClassType) + return baseMethodPurity(definedMethod.asDefinedMethod); + + implicit val state: State = + new State(method, definedMethod, declClass) + + val tacaiO = getTACAI(method) + + if (tacaiO.isEmpty) + return InterimResult( + definedMethod, + ImpureByAnalysis, + CompileTimePure, + state.dependees, + c + ); + + determineMethodPurity(tacaiO.get.cfg) + } } object L2PurityAnalysis_new { - /** - * Domain-specific rater used to examine whether certain statements and expressions are - * domain-specific. - * If the Option is None, a rater is created from a config file option. - */ - var rater: Option[DomainSpecificRater] = None + /** + * Domain-specific rater used to examine whether certain statements and expressions are + * domain-specific. + * If the Option is None, a rater is created from a config file option. + */ + var rater: Option[DomainSpecificRater] = None - def setRater(newRater: Option[DomainSpecificRater]): Unit = { - rater = newRater - } + def setRater(newRater: Option[DomainSpecificRater]): Unit = { + rater = newRater + } } trait L2PurityAnalysisScheduler_new extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(Purity) - - override def requiredProjectInformation: ProjectInformationKeys = - Seq(DeclaredMethodsKey, ConfiguredPurityKey) - - override def uses: Set[PropertyBounds] = { - Set( - //PropertyBounds.lub(FieldMutability), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.lub(FieldImmutability), - //PropertyBounds.lub(ClassImmutability), - PropertyBounds.lub(ClassImmutability_new), - //PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(TypeImmutability_new), - PropertyBounds.lub(StaticDataUsage), - PropertyBounds.lub(ReturnValueFreshness), - PropertyBounds.ub(FieldLocality), - PropertyBounds.ub(TACAI), - PropertyBounds.ub(Callees), - PropertyBounds.lub(Purity) - ) - } + final def derivedProperty: PropertyBounds = PropertyBounds.lub(Purity) + + override def requiredProjectInformation: ProjectInformationKeys = + Seq(DeclaredMethodsKey, ConfiguredPurityKey) + + override def uses: Set[PropertyBounds] = { + Set( + //PropertyBounds.lub(FieldMutability), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.lub(FieldImmutability), + //PropertyBounds.lub(ClassImmutability), + PropertyBounds.lub(ClassImmutability_new), + //PropertyBounds.lub(TypeImmutability), + PropertyBounds.lub(TypeImmutability_new), + PropertyBounds.lub(StaticDataUsage), + PropertyBounds.lub(ReturnValueFreshness), + PropertyBounds.ub(FieldLocality), + PropertyBounds.ub(TACAI), + PropertyBounds.ub(Callees), + PropertyBounds.lub(Purity) + ) + } - final override type InitializationData = L2PurityAnalysis_new - final override def init(p: SomeProject, ps: PropertyStore): InitializationData = { - new L2PurityAnalysis_new(p) - } + final override type InitializationData = L2PurityAnalysis_new + final override def init(p: SomeProject, ps: PropertyStore): InitializationData = { + new L2PurityAnalysis_new(p) + } - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} - override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} + override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} - override def afterPhaseCompletion( - p: SomeProject, - ps: PropertyStore, - analysis: FPCFAnalysis - ): Unit = {} + override def afterPhaseCompletion( + p: SomeProject, + ps: PropertyStore, + analysis: FPCFAnalysis + ): Unit = {} } @@ -1009,44 +997,43 @@ object EagerL2PurityAnalysis_new extends L2PurityAnalysisScheduler_new with FPCFEagerAnalysisScheduler { - override def start( - p: SomeProject, - ps: PropertyStore, - analysis: InitializationData - ): FPCFAnalysis = { - 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 => - dm.asDefinedMethod + override def start( + p: SomeProject, + ps: PropertyStore, + analysis: InitializationData + ): FPCFAnalysis = { + 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 ⇒ + dm.asDefinedMethod + } + ps.scheduleEagerComputationsForEntities(methods)( + analysis.determinePurity + ) + analysis } - ps.scheduleEagerComputationsForEntities(methods)( - analysis.determinePurity - ) - analysis - } - override def uses: Set[PropertyBounds] = super.uses + PropertyBounds.finalP(Callers) + override def uses: Set[PropertyBounds] = super.uses + PropertyBounds.finalP(Callers) - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } object LazyL2PurityAnalysis_new extends L2PurityAnalysisScheduler_new with FPCFLazyAnalysisScheduler { - override def register( - p: SomeProject, - ps: PropertyStore, - analysis: InitializationData - ): FPCFAnalysis = { - ps.registerLazyPropertyComputation(Purity.key, analysis.doDeterminePurity) - analysis - } + override def register( + p: SomeProject, + ps: PropertyStore, + analysis: InitializationData + ): FPCFAnalysis = { + ps.registerLazyPropertyComputation(Purity.key, analysis.doDeterminePurity) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } From be179c0f309ea475fd874f64437a629e258bea42 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 23 Jun 2020 10:36:47 +0200 Subject: [PATCH 147/327] adapt the imports in demos to the new analysis location --- .../ClassImmutabilityAnalysisDemo.scala | 8 +- ...AnalysisDemo_performanceMeasurements.scala | 8 +- ...mutabilityAnalysisDemo_withNewPurity.scala | 9 +- .../FieldImmutabilityAnalysisDemo.scala | 45 +-- ...AnalysisDemo_performanceMeasurements.scala | 8 +- ...mutabilityAnalysisDemo_withNewPurity.scala | 8 +- .../ImmutabilityAnalysisDemo_new.scala | 292 +++++++++--------- .../ReferenceImmutabilityAnalysisDemo.scala | 160 +++++----- .../TypeImmutabilityAnalysisDemo.scala | 8 +- ...AnalysisDemo_performanceMeasurements.scala | 8 +- ...mutabilityAnalysisDemo_withNewPurity.scala | 8 +- 11 files changed, 281 insertions(+), 281 deletions(-) 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 index b285c858e2..59682429b8 100644 --- 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 @@ -9,17 +9,19 @@ import java.util.Calendar import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.util.PerformanceEvaluation.memory //import org.opalj.br.ObjectType -import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new 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.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis -import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis @@ -30,8 +32,6 @@ import org.opalj.br.fpcf.properties.ShallowImmutableClass import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala index 6474e8ed9c..d81c37d01d 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.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.analyses.LazyL0CompileTimeConstancyAnalysis -import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds; diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala index 4de458d8a8..551680d778 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala @@ -9,14 +9,11 @@ import java.util.Calendar import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new - -import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new 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.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.DeepImmutableClass @@ -26,10 +23,12 @@ import org.opalj.br.fpcf.properties.ShallowImmutableClass import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds 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 index 2773724891..3c988d7107 100644 --- 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 @@ -1,17 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.analyses -import java.io.BufferedWriter -import java.io.FileWriter -import java.io.File import java.net.URL -import java.util.Calendar 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.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis @@ -24,12 +19,13 @@ import org.opalj.br.fpcf.properties.ShallowImmutableField import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.memory import org.opalj.util.PerformanceEvaluation.time @@ -141,21 +137,26 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { | needs : ${memoryConsumption / 1024 / 1024} MBytes |""".stripMargin ) - val calendar = Calendar.getInstance() - val file = new File( - s"C:/MA/results/fieldImm_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) + // val calendar = Calendar.getInstance() - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() + /** + * val file = new File( + * s"C:/MA/results/fieldImm_${calendar.get(Calendar.YEAR)}_" + + * s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_" + + * s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_" + + * s"${calendar.get(Calendar.MILLISECOND)}.txt" + * ) * + */ + // val bw = new BufferedWriter(new FileWriter(file)) + //bw.write(sb.toString()) + //bw.close() - s""" - | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes - |""".stripMargin + /** + * s""" + * | took : $analysisTime seconds + * | needs : ${memoryConsumption / 1024 / 1024} MBytes + * |""".stripMargin * + */ + sb.toString() } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala index 7d55bc9dab..0d32cb996d 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala @@ -11,18 +11,18 @@ 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.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala index 0231beb03a..751a28b7af 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala @@ -12,7 +12,6 @@ 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.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis @@ -23,11 +22,12 @@ import org.opalj.br.fpcf.properties.ShallowImmutableField import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala index 36c8c4aab5..dcea0228fe 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala @@ -8,7 +8,6 @@ import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis @@ -26,10 +25,11 @@ import org.opalj.fpcf.Entity import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyKind import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.EagerLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.Nanoseconds import org.opalj.util.PerformanceEvaluation.time @@ -44,152 +44,152 @@ import org.opalj.util.gc */ object ImmutabilityAnalysisDemo_new extends ProjectAnalysisApplication { - override def title: String = "determines the immutability of objects and types" - - override def description: String = "determines the immutability of objects and types" - - private[this] var setupTime = Nanoseconds.None - private[this] var analysisTime = Nanoseconds.None - private[this] var performanceData: Map[Nanoseconds, List[Nanoseconds]] = Map.empty - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - var r: () ⇒ String = null - - def handleResults(t: Nanoseconds, ts: Seq[Nanoseconds]) = { - performanceData += ((t, List(setupTime, analysisTime))) - performanceData = performanceData.filter((t_ts) ⇒ ts.contains(t_ts._1)) - } - - List(1).foreach { parallelismLevel ⇒ - performanceData = Map.empty - gc() - - println(s"\nRunning analysis with $parallelismLevel thread(s):") - r = time[() ⇒ String](10, 50, 15, analyze(project, parallelismLevel))(handleResults) - println( - s"Results with $parallelismLevel threads:\n"+ - performanceData.values - .map(v ⇒ v.map(_.toSeconds.toString(false))) - .map( - v ⇒ List("setup\t", "analysis\t").zip(v).map(e ⇒ e._1 + e._2).mkString("", "\n", "\n") - ) - .mkString("\n") + override def title: String = "determines the immutability of objects and types" + + override def description: String = "determines the immutability of objects and types" + + private[this] var setupTime = Nanoseconds.None + private[this] var analysisTime = Nanoseconds.None + private[this] var performanceData: Map[Nanoseconds, List[Nanoseconds]] = Map.empty + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + var r: () => String = null + + def handleResults(t: Nanoseconds, ts: Seq[Nanoseconds]) = { + performanceData += ((t, List(setupTime, analysisTime))) + performanceData = performanceData.filter((t_ts) => ts.contains(t_ts._1)) + } + + List(1).foreach { parallelismLevel => + performanceData = Map.empty + gc() + + println(s"\nRunning analysis with $parallelismLevel thread(s):") + r = time[() => String](10, 50, 15, analyze(project, parallelismLevel))(handleResults) + println( + s"Results with $parallelismLevel threads:\n" + + performanceData.values + .map(v => v.map(_.toSeconds.toString(false))) + .map( + v => List("setup\t", "analysis\t").zip(v).map(e => e._1 + e._2).mkString("", "\n", "\n") ) + .mkString("\n") + ) - gc() - } - BasicReport(r()) + gc() + } + BasicReport(r()) + } + + def analyze(theProject: Project[URL], parallelismLevel: Int): () => String = { + var result = "Results:\n" + val project = Project.recreate(theProject) // We need an empty project(!) + project.get(RTACallGraphKey) + + // The following measurements (t) are done such that the results are comparable with the + // reactive async approach developed by P. Haller and Simon Gries. + PropertyStoreKey.parallelismLevel = parallelismLevel + //PropertyStoreKey + val propertyStore = project.get(PropertyStoreKey) + + time { + propertyStore.setupPhase( + Set[PropertyKind]( + FieldMutability.key, + ClassImmutability.key, + TypeImmutability.key, + FieldPrematurelyRead.key, + Purity.key, + FieldImmutability.key, + ReferenceImmutability.key, + ClassImmutability_new.key, + TypeImmutability_new.key + ) + ) + //LazyL0FieldMutabilityAnalysis.register(project, propertyStore, null) // (project, propertyStore) + //EagerClassImmutabilityAnalysis.start(project, propertyStore, project.allClassFiles) + //EagerTypeImmutabilityAnalysis.start(project, propertyStore, null) //project.allClassFiles) + + LazyClassImmutabilityAnalysis.register(project, propertyStore, project.allClassFiles) + LazyL2FieldMutabilityAnalysis.register(project, propertyStore, null) + LazyTypeImmutabilityAnalysis.register(project, propertyStore, null) + LazyUnsoundPrematurelyReadFieldsAnalysis.register(project, propertyStore, null) + LazyL2PurityAnalysis.register(project, propertyStore, null) + + EagerL0ReferenceImmutabilityAnalysis.start(project, propertyStore, null) + EagerL0FieldImmutabilityAnalysis.start(project, propertyStore, null) + EagerLxClassImmutabilityAnalysis_new.start(project, propertyStore, project.allClassFiles) + EagerLxTypeImmutabilityAnalysis_new.start(project, propertyStore, null) + //propertyStore.suppressError = true + //propertyStore.waitOnPhaseCompletion() + } { r => + analysisTime = r } - def analyze(theProject: Project[URL], parallelismLevel: Int): () ⇒ String = { - var result = "Results:\n" - val project = Project.recreate(theProject) // We need an empty project(!) - project.get(RTACallGraphKey) - - // The following measurements (t) are done such that the results are comparable with the - // reactive async approach developed by P. Haller and Simon Gries. - PropertyStoreKey.parallelismLevel = parallelismLevel - //PropertyStoreKey - val propertyStore = project.get(PropertyStoreKey) - - time { - propertyStore.setupPhase( - Set[PropertyKind]( - FieldMutability.key, - ClassImmutability.key, - TypeImmutability.key, - FieldPrematurelyRead.key, - Purity.key, - FieldImmutability.key, - ReferenceImmutability.key, - ClassImmutability_new.key, - TypeImmutability_new.key - ) + result += s"\t- analysis time: ${analysisTime.toSeconds}\n" + + () => { + val immutableReferences = + propertyStore.entities(ReferenceImmutability.key) + val immutableClasses = + propertyStore + .entities(ClassImmutability_new.key) + .filter(eps => !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) + .toBuffer + .groupBy((eps: EPS[_ <: Entity, _ <: Property]) => eps.ub) + .map { kv => + ( + kv._1, + kv._2.toList.sortWith { (a, b) => + val cfA = a.e.asInstanceOf[ClassFile] + val cfB = b.e.asInstanceOf[ClassFile] + cfA.thisType.toJava < cfB.thisType.toJava + } ) - //LazyL0FieldMutabilityAnalysis.register(project, propertyStore, null) // (project, propertyStore) - //EagerClassImmutabilityAnalysis.start(project, propertyStore, project.allClassFiles) - //EagerTypeImmutabilityAnalysis.start(project, propertyStore, null) //project.allClassFiles) - - LazyClassImmutabilityAnalysis.register(project, propertyStore, project.allClassFiles) - LazyL2FieldMutabilityAnalysis.register(project, propertyStore, null) - LazyTypeImmutabilityAnalysis.register(project, propertyStore, null) - LazyUnsoundPrematurelyReadFieldsAnalysis.register(project, propertyStore, null) - LazyL2PurityAnalysis.register(project, propertyStore, null) - - EagerL0ReferenceImmutabilityAnalysis.start(project, propertyStore, null) - EagerL0FieldImmutabilityAnalysis.start(project, propertyStore, null) - EagerLxClassImmutabilityAnalysis_new.start(project, propertyStore, project.allClassFiles) - EagerLxTypeImmutabilityAnalysis_new.start(project, propertyStore, null) - //propertyStore.suppressError = true - //propertyStore.waitOnPhaseCompletion() - } { r ⇒ - analysisTime = r - } - - result += s"\t- analysis time: ${analysisTime.toSeconds}\n" - - () ⇒ { - val immutableReferences = - propertyStore.entities(ReferenceImmutability.key) - val immutableClasses = - propertyStore - .entities(ClassImmutability_new.key) - .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) - .toBuffer - .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) - .map { kv ⇒ - ( - kv._1, - kv._2.toList.sortWith { (a, b) ⇒ - val cfA = a.e.asInstanceOf[ClassFile] - val cfB = b.e.asInstanceOf[ClassFile] - cfA.thisType.toJava < cfB.thisType.toJava - } - ) - } - - val immutableClassesPerCategory = - immutableClasses - .map(kv ⇒ "\t\t"+kv._1+": "+kv._2.size) - .toBuffer - .sorted - .mkString("\n") - - val immutableTypes = - propertyStore - .entities(TypeImmutability_new.key) - .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) - .toBuffer - .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) - .map(kv ⇒ (kv._1, kv._2.size)) - val immutableTypesPerCategory = - immutableTypes.map(kv ⇒ "\t\t"+kv._1+": "+kv._2).toBuffer.sorted.mkString("\n") - - val immutableClassesInfo = - immutableClasses.values.flatten - .filter { ep ⇒ - !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration - } - .map { eps ⇒ - eps.e.asInstanceOf[ClassFile].thisType.toJava+ - " => "+eps.ub+ - " => "+propertyStore(eps.e, TypeImmutability_new.key).ub - } - .mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") - - "immutable References: "+immutableReferences.size+"\n" - "\t- details:\n"+ - immutableClassesInfo+ - "\nSummary (w.r.t classes):\n"+ - "\tObject Immutability:\n"+ - immutableClassesPerCategory+"\n"+ - "\tType Immutability:\n"+ - immutableTypesPerCategory+"\n"+ - "\n"+propertyStore.toString(false) - } + } + + val immutableClassesPerCategory = + immutableClasses + .map(kv => "\t\t" + kv._1 + ": " + kv._2.size) + .toBuffer + .sorted + .mkString("\n") + + val immutableTypes = + propertyStore + .entities(TypeImmutability_new.key) + .filter(eps => !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) + .toBuffer + .groupBy((eps: EPS[_ <: Entity, _ <: Property]) => eps.ub) + .map(kv => (kv._1, kv._2.size)) + val immutableTypesPerCategory = + immutableTypes.map(kv => "\t\t" + kv._1 + ": " + kv._2).toBuffer.sorted.mkString("\n") + + val immutableClassesInfo = + immutableClasses.values.flatten + .filter { ep => + !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration + } + .map { eps => + eps.e.asInstanceOf[ClassFile].thisType.toJava + + " => " + eps.ub + + " => " + propertyStore(eps.e, TypeImmutability_new.key).ub + } + .mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") + + "immutable References: " + immutableReferences.size + "\n" + "\t- details:\n" + + immutableClassesInfo + + "\nSummary (w.r.t classes):\n" + + "\tObject Immutability:\n" + + immutableClassesPerCategory + "\n" + + "\tType Immutability:\n" + + immutableTypesPerCategory + "\n" + + "\n" + propertyStore.toString(false) } + } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index a9432a6662..66898bf3e9 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -26,7 +26,7 @@ import org.opalj.fpcf.SomeEPS import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new + import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis /** @@ -36,82 +36,82 @@ import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmuta */ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } + 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 memoryConsumption: Long = 0 - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - memory { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + def analyze(project: Project[URL]): String = { + var memoryConsumption: Long = 0 + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + memory { + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyFieldLocalityAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion() + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyStaticDataUsageAnalysis, + LazyFieldLocalityAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion() - } { t => - analysisTime = t.toSeconds - } - } { mu => - memoryConsumption = mu - } - var sb: StringBuilder = new StringBuilder() - sb = sb.append("Mutable References: \n") - val mutableReferences = propertyStore.finalEntities(MutableReference).toList - sb = sb.append( - mutableReferences.mkString(", \n") - ) + } { t ⇒ + analysisTime = t.toSeconds + } + } { mu ⇒ + memoryConsumption = mu + } + var sb: StringBuilder = new StringBuilder() + sb = sb.append("Mutable References: \n") + val mutableReferences = propertyStore.finalEntities(MutableReference).toList + sb = sb.append( + mutableReferences.mkString(", \n") + ) - sb = sb.append("\n Lazy Initialized Reference: \n") - val lazyInitializedReferences = propertyStore - .finalEntities(LazyInitializedReference) - .toList - sb = sb.append( - lazyInitializedReferences.mkString(", \n") - ) + sb = sb.append("\n Lazy Initialized Reference: \n") + val lazyInitializedReferences = propertyStore + .finalEntities(LazyInitializedReference) + .toList + sb = sb.append( + lazyInitializedReferences.mkString(", \n") + ) - val immutableReferencesTrue = propertyStore.entities({ eps: SomeEPS => - eps.ub match { - case ImmutableReference(true) => true - case _ => false - } - }) - val immutableReferencesFalse = propertyStore.entities({ eps: SomeEPS => - eps.ub match { - case ImmutableReference(false) => true - case _ => false - } - }) + val immutableReferencesTrue = propertyStore.entities({ eps: SomeEPS ⇒ + eps.ub match { + case ImmutableReference(true) ⇒ true + case _ ⇒ false + } + }) + val immutableReferencesFalse = propertyStore.entities({ eps: SomeEPS ⇒ + eps.ub match { + case ImmutableReference(false) ⇒ true + case _ ⇒ false + } + }) - sb = sb.append( - s""" + sb = sb.append( + s""" | imm ref true: |${immutableReferencesTrue.mkString(", \n")} | @@ -119,10 +119,10 @@ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { | imm ref false: | ${immutableReferencesFalse.mkString(", \n")} |""".stripMargin - ) + ) - sb.append( - s""" + sb.append( + s""" | mutable References: ${mutableReferences.size} | lazy initialized References: ${lazyInitializedReferences.size} | immutable References: ${} @@ -131,9 +131,9 @@ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { | needed: ${memoryConsumption / 1024 / 1024} MBytes | |""".stripMargin - ) + ) - /* val calendar = Calendar.getInstance() + /* val calendar = Calendar.getInstance() val file = new File( s"C:/MA/results/refImm_${calendar.get(Calendar.YEAR)}_" + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_" + @@ -143,10 +143,10 @@ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val bw = new BufferedWriter(new FileWriter(file)) bw.write(sb.toString()) bw.close() **/ - //s""" - // | took : $analysisTime seconds - // | needs : ${memoryConsumption / 1024 / 1024} MBytes - // |""".stripMargin - sb.toString() - } + //s""" + // | took : $analysisTime seconds + // | needs : ${memoryConsumption / 1024 / 1024} MBytes + // |""".stripMargin + sb.toString() + } } 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 index b59da12e81..1d08313e77 100644 --- 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 @@ -14,7 +14,6 @@ import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis -import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis @@ -24,16 +23,17 @@ import org.opalj.br.fpcf.properties.MutableType_new import org.opalj.br.fpcf.properties.ShallowImmutableType import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.util.PerformanceEvaluation.memory /** diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala index ec94f7dfc1..e2810a06f7 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.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.analyses.LazyL0CompileTimeConstancyAnalysis -import org.opalj.br.fpcf.analyses.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala index 834e2b7c03..52446c864d 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala @@ -13,7 +13,6 @@ 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.LazyL0FieldImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.DeepImmutableType @@ -22,14 +21,15 @@ import org.opalj.br.fpcf.properties.MutableType_new import org.opalj.br.fpcf.properties.ShallowImmutableType import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyLxClassImmutabilityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds -import org.opalj.tac.fpcf.analyses.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** From 5f314db226e840d2946da5a6dfee309a89f76784 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 23 Jun 2020 15:08:09 +0200 Subject: [PATCH 148/327] format + ref conf correction --- .../ImmutabilityAnalysisDemo_new.scala | 284 ++--- .../properties/ReferenceImmutability.scala | 66 +- OPAL/tac/src/main/resources/reference.conf | 10 +- .../purity/AbstractPurityAnalysis_new.scala | 1118 ++++++++--------- 4 files changed, 743 insertions(+), 735 deletions(-) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala index dcea0228fe..2141fd5af5 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala @@ -44,152 +44,152 @@ import org.opalj.util.gc */ object ImmutabilityAnalysisDemo_new extends ProjectAnalysisApplication { - override def title: String = "determines the immutability of objects and types" - - override def description: String = "determines the immutability of objects and types" - - private[this] var setupTime = Nanoseconds.None - private[this] var analysisTime = Nanoseconds.None - private[this] var performanceData: Map[Nanoseconds, List[Nanoseconds]] = Map.empty - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () => Boolean - ): BasicReport = { - var r: () => String = null - - def handleResults(t: Nanoseconds, ts: Seq[Nanoseconds]) = { - performanceData += ((t, List(setupTime, analysisTime))) - performanceData = performanceData.filter((t_ts) => ts.contains(t_ts._1)) - } - - List(1).foreach { parallelismLevel => - performanceData = Map.empty - gc() - - println(s"\nRunning analysis with $parallelismLevel thread(s):") - r = time[() => String](10, 50, 15, analyze(project, parallelismLevel))(handleResults) - println( - s"Results with $parallelismLevel threads:\n" + - performanceData.values - .map(v => v.map(_.toSeconds.toString(false))) - .map( - v => List("setup\t", "analysis\t").zip(v).map(e => e._1 + e._2).mkString("", "\n", "\n") + override def title: String = "determines the immutability of objects and types" + + override def description: String = "determines the immutability of objects and types" + + private[this] var setupTime = Nanoseconds.None + private[this] var analysisTime = Nanoseconds.None + private[this] var performanceData: Map[Nanoseconds, List[Nanoseconds]] = Map.empty + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + var r: () ⇒ String = null + + def handleResults(t: Nanoseconds, ts: Seq[Nanoseconds]) = { + performanceData += ((t, List(setupTime, analysisTime))) + performanceData = performanceData.filter((t_ts) ⇒ ts.contains(t_ts._1)) + } + + List(1).foreach { parallelismLevel ⇒ + performanceData = Map.empty + gc() + + println(s"\nRunning analysis with $parallelismLevel thread(s):") + r = time[() ⇒ String](10, 50, 15, analyze(project, parallelismLevel))(handleResults) + println( + s"Results with $parallelismLevel threads:\n"+ + performanceData.values + .map(v ⇒ v.map(_.toSeconds.toString(false))) + .map( + v ⇒ List("setup\t", "analysis\t").zip(v).map(e ⇒ e._1 + e._2).mkString("", "\n", "\n") + ) + .mkString("\n") ) - .mkString("\n") - ) - gc() - } - BasicReport(r()) - } - - def analyze(theProject: Project[URL], parallelismLevel: Int): () => String = { - var result = "Results:\n" - val project = Project.recreate(theProject) // We need an empty project(!) - project.get(RTACallGraphKey) - - // The following measurements (t) are done such that the results are comparable with the - // reactive async approach developed by P. Haller and Simon Gries. - PropertyStoreKey.parallelismLevel = parallelismLevel - //PropertyStoreKey - val propertyStore = project.get(PropertyStoreKey) - - time { - propertyStore.setupPhase( - Set[PropertyKind]( - FieldMutability.key, - ClassImmutability.key, - TypeImmutability.key, - FieldPrematurelyRead.key, - Purity.key, - FieldImmutability.key, - ReferenceImmutability.key, - ClassImmutability_new.key, - TypeImmutability_new.key - ) - ) - //LazyL0FieldMutabilityAnalysis.register(project, propertyStore, null) // (project, propertyStore) - //EagerClassImmutabilityAnalysis.start(project, propertyStore, project.allClassFiles) - //EagerTypeImmutabilityAnalysis.start(project, propertyStore, null) //project.allClassFiles) - - LazyClassImmutabilityAnalysis.register(project, propertyStore, project.allClassFiles) - LazyL2FieldMutabilityAnalysis.register(project, propertyStore, null) - LazyTypeImmutabilityAnalysis.register(project, propertyStore, null) - LazyUnsoundPrematurelyReadFieldsAnalysis.register(project, propertyStore, null) - LazyL2PurityAnalysis.register(project, propertyStore, null) - - EagerL0ReferenceImmutabilityAnalysis.start(project, propertyStore, null) - EagerL0FieldImmutabilityAnalysis.start(project, propertyStore, null) - EagerLxClassImmutabilityAnalysis_new.start(project, propertyStore, project.allClassFiles) - EagerLxTypeImmutabilityAnalysis_new.start(project, propertyStore, null) - //propertyStore.suppressError = true - //propertyStore.waitOnPhaseCompletion() - } { r => - analysisTime = r + gc() + } + BasicReport(r()) } - result += s"\t- analysis time: ${analysisTime.toSeconds}\n" - - () => { - val immutableReferences = - propertyStore.entities(ReferenceImmutability.key) - val immutableClasses = - propertyStore - .entities(ClassImmutability_new.key) - .filter(eps => !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) - .toBuffer - .groupBy((eps: EPS[_ <: Entity, _ <: Property]) => eps.ub) - .map { kv => - ( - kv._1, - kv._2.toList.sortWith { (a, b) => - val cfA = a.e.asInstanceOf[ClassFile] - val cfB = b.e.asInstanceOf[ClassFile] - cfA.thisType.toJava < cfB.thisType.toJava - } + def analyze(theProject: Project[URL], parallelismLevel: Int): () ⇒ String = { + var result = "Results:\n" + val project = Project.recreate(theProject) // We need an empty project(!) + project.get(RTACallGraphKey) + + // The following measurements (t) are done such that the results are comparable with the + // reactive async approach developed by P. Haller and Simon Gries. + PropertyStoreKey.parallelismLevel = parallelismLevel + //PropertyStoreKey + val propertyStore = project.get(PropertyStoreKey) + + time { + propertyStore.setupPhase( + Set[PropertyKind]( + FieldMutability.key, + ClassImmutability.key, + TypeImmutability.key, + FieldPrematurelyRead.key, + Purity.key, + FieldImmutability.key, + ReferenceImmutability.key, + ClassImmutability_new.key, + TypeImmutability_new.key + ) ) - } - - val immutableClassesPerCategory = - immutableClasses - .map(kv => "\t\t" + kv._1 + ": " + kv._2.size) - .toBuffer - .sorted - .mkString("\n") - - val immutableTypes = - propertyStore - .entities(TypeImmutability_new.key) - .filter(eps => !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) - .toBuffer - .groupBy((eps: EPS[_ <: Entity, _ <: Property]) => eps.ub) - .map(kv => (kv._1, kv._2.size)) - val immutableTypesPerCategory = - immutableTypes.map(kv => "\t\t" + kv._1 + ": " + kv._2).toBuffer.sorted.mkString("\n") - - val immutableClassesInfo = - immutableClasses.values.flatten - .filter { ep => - !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration - } - .map { eps => - eps.e.asInstanceOf[ClassFile].thisType.toJava + - " => " + eps.ub + - " => " + propertyStore(eps.e, TypeImmutability_new.key).ub - } - .mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") - - "immutable References: " + immutableReferences.size + "\n" - "\t- details:\n" + - immutableClassesInfo + - "\nSummary (w.r.t classes):\n" + - "\tObject Immutability:\n" + - immutableClassesPerCategory + "\n" + - "\tType Immutability:\n" + - immutableTypesPerCategory + "\n" + - "\n" + propertyStore.toString(false) + //LazyL0FieldMutabilityAnalysis.register(project, propertyStore, null) // (project, propertyStore) + //EagerClassImmutabilityAnalysis.start(project, propertyStore, project.allClassFiles) + //EagerTypeImmutabilityAnalysis.start(project, propertyStore, null) //project.allClassFiles) + + LazyClassImmutabilityAnalysis.register(project, propertyStore, project.allClassFiles) + LazyL2FieldMutabilityAnalysis.register(project, propertyStore, null) + LazyTypeImmutabilityAnalysis.register(project, propertyStore, null) + LazyUnsoundPrematurelyReadFieldsAnalysis.register(project, propertyStore, null) + LazyL2PurityAnalysis.register(project, propertyStore, null) + + EagerL0ReferenceImmutabilityAnalysis.start(project, propertyStore, null) + EagerL0FieldImmutabilityAnalysis.start(project, propertyStore, null) + EagerLxClassImmutabilityAnalysis_new.start(project, propertyStore, project.allClassFiles) + EagerLxTypeImmutabilityAnalysis_new.start(project, propertyStore, null) + //propertyStore.suppressError = true + //propertyStore.waitOnPhaseCompletion() + } { r ⇒ + analysisTime = r + } + + result += s"\t- analysis time: ${analysisTime.toSeconds}\n" + + () ⇒ { + val immutableReferences = + propertyStore.entities(ReferenceImmutability.key) + val immutableClasses = + propertyStore + .entities(ClassImmutability_new.key) + .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) + .toBuffer + .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) + .map { kv ⇒ + ( + kv._1, + kv._2.toList.sortWith { (a, b) ⇒ + val cfA = a.e.asInstanceOf[ClassFile] + val cfB = b.e.asInstanceOf[ClassFile] + cfA.thisType.toJava < cfB.thisType.toJava + } + ) + } + + val immutableClassesPerCategory = + immutableClasses + .map(kv ⇒ "\t\t"+kv._1+": "+kv._2.size) + .toBuffer + .sorted + .mkString("\n") + + val immutableTypes = + propertyStore + .entities(TypeImmutability_new.key) + .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) + .toBuffer + .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) + .map(kv ⇒ (kv._1, kv._2.size)) + val immutableTypesPerCategory = + immutableTypes.map(kv ⇒ "\t\t"+kv._1+": "+kv._2).toBuffer.sorted.mkString("\n") + + val immutableClassesInfo = + immutableClasses.values.flatten + .filter { ep ⇒ + !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration + } + .map { eps ⇒ + eps.e.asInstanceOf[ClassFile].thisType.toJava+ + " => "+eps.ub+ + " => "+propertyStore(eps.e, TypeImmutability_new.key).ub + } + .mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") + + "immutable References: "+immutableReferences.size+"\n" + "\t- details:\n"+ + immutableClassesInfo+ + "\nSummary (w.r.t classes):\n"+ + "\tObject Immutability:\n"+ + immutableClassesPerCategory+"\n"+ + "\tType Immutability:\n"+ + immutableTypesPerCategory+"\n"+ + "\n"+propertyStore.toString(false) + } } - } } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala index 930728dac9..f8b1140745 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -9,7 +9,7 @@ import org.opalj.fpcf.PropertyMetaInformation sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - type Self = ReferenceImmutability + type Self = ReferenceImmutability } @@ -30,57 +30,57 @@ sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaIn sealed trait ReferenceImmutability extends OrderedProperty with ReferenceImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key + final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key } object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { - var notEscapes: Boolean = false + var notEscapes: Boolean = false - final val PropertyKeyName = "opalj.ReferenceImmutability" + final val PropertyKeyName = "opalj.ReferenceImmutability" - final val key: PropertyKey[ReferenceImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableReference - ) - } + final val key: PropertyKey[ReferenceImmutability] = { + PropertyKey.create( + PropertyKeyName, + MutableReference + ) + } } case class ImmutableReference(notEscapes: Boolean) extends ReferenceImmutability { - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: ReferenceImmutability): ReferenceImmutability = - if (this == that) - this - else - that + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + def meet(that: ReferenceImmutability): ReferenceImmutability = + if (this == that) + this + else + that } case object LazyInitializedReference extends ReferenceImmutability { - def meet(other: ReferenceImmutability): properties.ReferenceImmutability = { - if (other == MutableReference) { - other - } else { - this + def meet(other: ReferenceImmutability): properties.ReferenceImmutability = { + if (other == MutableReference) { + other + } else { + this + } } - } - override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { - other match { - case ImmutableReference(_) => - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - case _ => + override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { + other match { + case ImmutableReference(_) ⇒ + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + case _ ⇒ + } } - } } case object MutableReference extends ReferenceImmutability { - def meet(other: ReferenceImmutability): this.type = this + def meet(other: ReferenceImmutability): this.type = this - override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { - if (other != MutableReference) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other => $this") + override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { + if (other != MutableReference) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other => $this") + } } - } } diff --git a/OPAL/tac/src/main/resources/reference.conf b/OPAL/tac/src/main/resources/reference.conf index b1ff40bcfa..ba7f4d0aa4 100644 --- a/OPAL/tac/src/main/resources/reference.conf +++ b/OPAL/tac/src/main/resources/reference.conf @@ -48,7 +48,12 @@ org.opalj { description = "Determines a method's purity.", eagerFactory = "org.opalj.tac.fpcf.analyses.purity.EagerL2PurityAnalysis", lazyFactory = "org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis" - } + }, + "L2PurityAnalysis_new" { + description = "Determines a method's purity.", + eagerFactory = "org.opalj.fpcf.analyses.EagerL2PurityAnalysis_new", + lazyFactory = "org.opalj.fpcf.analyses.LazyL2PurityAnalysis_new"t + } } }, analyses { @@ -85,6 +90,9 @@ org.opalj { L2PurityAnalysis { domainSpecificRater = "org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater" }, + L2PurityAnalysis_new { + domainSpecificRater = "org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater" + }, ConfiguredPurity { purities = [ # Native methods diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala index e7fbc27b28..1ab0870f58 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala @@ -115,271 +115,271 @@ import scala.annotation.switch */ trait AbstractPurityAnalysis_new extends FPCFAnalysis { - /** The type of the TAC domain. */ - type V = DUVar[ValueInformation] - - /** - * The state of the analysis. - * Analyses are expected to extend this trait with the information they need. - * - * lbPurity - The current minimum possible purity level for the method - * ubPurity - The current maximum purity level for the method - * method - The currently analyzed method - * declClass - The declaring class of the currently analyzed method - * code - The code of the currently analyzed method - */ - trait AnalysisState { - var lbPurity: Purity - var ubPurity: Purity - val method: Method - val definedMethod: DeclaredMethod - val declClass: ObjectType - var pcToIndex: Array[Int] - var code: Array[Stmt[V]] - } - - type StateType <: AnalysisState - - protected[this] def raterFqn: String - - val rater: DomainSpecificRater - - implicit protected[this] val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) - - val configuredPurity: ConfiguredPurity = project.get(ConfiguredPurityKey) - - /** - * Reduces the maxPurity of the current method to at most the given purity level. - */ - def reducePurityLB(newLevel: Purity)(implicit state: StateType): Unit = { - state.lbPurity = state.lbPurity meet newLevel - } - - /** - * Reduces the minPurity and maxPurity of the current method to at most the given purity level. - */ - def atMost(newLevel: Purity)(implicit state: StateType): Unit = { - state.lbPurity = state.lbPurity meet newLevel - state.ubPurity = state.ubPurity meet newLevel - } - - /** - * Examines whether the given expression denotes an object/array that is local to the current - * method, i.e. the method has control over the object/array and actions on it might not - * influence purity. - * - * @param otherwise The maxPurity will be reduced to at most this level if the expression is not - * local. - */ - def isLocal( - expr: Expr[V], - otherwise: Purity, - excludedDefSites: IntTrieSet = EmptyIntTrieSet - )(implicit state: StateType): Boolean - - /** - * Checks whether the statement, which is the origin of an exception, directly created the - * exception or if the VM instantiated the exception. Here, we are only concerned about the - * exceptions thrown by the instructions not about exceptions that are transitively thrown; - * e.g. if a method is called. - * TODO We need this method because currently, for exceptions that terminate the method, no - * definitions are recorded. Once this is done, use that information instead to determine - * whether it may be an immediate exception or not. - */ - def isSourceOfImmediateException(origin: ValueOrigin)(implicit state: StateType): Boolean = { - - def evaluationMayCauseVMLevelException(expr: Expr[V]): Boolean = { - (expr.astID: @switch) match { - - case NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => - val rcvr = expr.asInstanceFunctionCall.receiver - !rcvr.isVar || rcvr.asVar.value.asReferenceValue.isNull.isYesOrUnknown - - case StaticFunctionCall.ASTID => false - - case _ => true - } + /** The type of the TAC domain. */ + type V = DUVar[ValueInformation] + + /** + * The state of the analysis. + * Analyses are expected to extend this trait with the information they need. + * + * lbPurity - The current minimum possible purity level for the method + * ubPurity - The current maximum purity level for the method + * method - The currently analyzed method + * declClass - The declaring class of the currently analyzed method + * code - The code of the currently analyzed method + */ + trait AnalysisState { + var lbPurity: Purity + var ubPurity: Purity + val method: Method + val definedMethod: DeclaredMethod + val declClass: ObjectType + var pcToIndex: Array[Int] + var code: Array[Stmt[V]] } - val stmt = state.code(origin) - (stmt.astID: @switch) match { - case StaticMethodCall.ASTID => false // We are looking for implicit exceptions only + type StateType <: AnalysisState - case Throw.ASTID => - stmt.asThrow.exception.asVar.value.asReferenceValue.isNull.isNotNo + protected[this] def raterFqn: String - case NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID => - val rcvr = stmt.asInstanceMethodCall.receiver - !rcvr.isVar || rcvr.asVar.value.asReferenceValue.isNull.isNotNo + val rater: DomainSpecificRater - case Assignment.ASTID => evaluationMayCauseVMLevelException(stmt.asAssignment.expr) + implicit protected[this] val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) - case ExprStmt.ASTID => evaluationMayCauseVMLevelException(stmt.asExprStmt.expr) + val configuredPurity: ConfiguredPurity = project.get(ConfiguredPurityKey) - case _ => true + /** + * Reduces the maxPurity of the current method to at most the given purity level. + */ + def reducePurityLB(newLevel: Purity)(implicit state: StateType): Unit = { + state.lbPurity = state.lbPurity meet newLevel } - } - - /** - * Examines whether a call constitutes a domain-specific action using the domain-specific rater. - * If it is, the maxPurity will be reduced to at most the domain-specific purity returned by the - * domain-specific rater. - */ - def isDomainSpecificCall( - call: Call[V], - receiver: Option[Expr[V]] - )(implicit state: StateType): Boolean = { - implicit val code: Array[Stmt[V]] = state.code - val ratedResult = rater.handleCall(call, receiver) - if (ratedResult.isDefined) - atMost(ratedResult.get) - ratedResult.isDefined - } - - /** - * Examines a statement for its influence on the method's purity. - * This method will return false for impure statements, so evaluation can be terminated early. - */ - def checkPurityOfStmt(stmt: Stmt[V])(implicit state: StateType): Boolean = { - val isStmtNotImpure = (stmt.astID: @switch) match { - // For method calls, purity will be checked later - case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID => - true - - // We don't handle unresolved Invokedynamics - // - either OPAL removes it or we forget about it - case InvokedynamicMethodCall.ASTID => - atMost(ImpureByAnalysis) - false - - // Returning objects/arrays is pure, if the returned object/array is locally initialized - // and non-escaping or the object is immutable - case ReturnValue.ASTID => - checkPurityOfReturn(stmt.asReturnValue.expr) - true - case Throw.ASTID => - checkPurityOfReturn(stmt.asThrow.exception) - true - - // Synchronization on non-escaping locally initialized objects/arrays is pure (and - // useless...) - case MonitorEnter.ASTID | MonitorExit.ASTID => - isLocal(stmt.asSynchronizationStmt.objRef, ImpureByAnalysis) - - // Storing into non-escaping locally initialized objects/arrays is pure - case ArrayStore.ASTID => isLocal(stmt.asArrayStore.arrayRef, ImpureByAnalysis) - case PutField.ASTID => isLocal(stmt.asPutField.objRef, ImpureByAnalysis) - - case PutStatic.ASTID => - // Note that a putstatic is not necessarily pure/sideeffect free, even if it - // is executed within a static initializer to initialize a field of - // `the` class; it is possible that the initialization triggers the - // initialization of another class which reads the value of this static field. - // See - // https://stackoverflow.com/questions/6416408/static-circular-dependency-in-java - // for an in-depth discussion. - // (Howevever, if we would check for cycles, we could determine that it is pure, - // but this is not considered to be too useful...) - atMost(ImpureByAnalysis) - false - - // Creating implicit exceptions is side-effect free (because of fillInStackTrace) - // but it may be ignored as domain-specific - case CaughtException.ASTID => - for { - origin <- stmt.asCaughtException.origins - if isImmediateVMException(origin) - } { - val baseOrigin = state.code(ai.underlyingPC(origin)) - val ratedResult = rater.handleException(baseOrigin) - if (ratedResult.isDefined) atMost(ratedResult.get) - else atMost(SideEffectFree) + + /** + * Reduces the minPurity and maxPurity of the current method to at most the given purity level. + */ + def atMost(newLevel: Purity)(implicit state: StateType): Unit = { + state.lbPurity = state.lbPurity meet newLevel + state.ubPurity = state.ubPurity meet newLevel + } + + /** + * Examines whether the given expression denotes an object/array that is local to the current + * method, i.e. the method has control over the object/array and actions on it might not + * influence purity. + * + * @param otherwise The maxPurity will be reduced to at most this level if the expression is not + * local. + */ + def isLocal( + expr: Expr[V], + otherwise: Purity, + excludedDefSites: IntTrieSet = EmptyIntTrieSet + )(implicit state: StateType): Boolean + + /** + * Checks whether the statement, which is the origin of an exception, directly created the + * exception or if the VM instantiated the exception. Here, we are only concerned about the + * exceptions thrown by the instructions not about exceptions that are transitively thrown; + * e.g. if a method is called. + * TODO We need this method because currently, for exceptions that terminate the method, no + * definitions are recorded. Once this is done, use that information instead to determine + * whether it may be an immediate exception or not. + */ + def isSourceOfImmediateException(origin: ValueOrigin)(implicit state: StateType): Boolean = { + + def evaluationMayCauseVMLevelException(expr: Expr[V]): Boolean = { + (expr.astID: @switch) match { + + case NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + val rcvr = expr.asInstanceFunctionCall.receiver + !rcvr.isVar || rcvr.asVar.value.asReferenceValue.isNull.isYesOrUnknown + + case StaticFunctionCall.ASTID ⇒ false + + case _ ⇒ true + } + } + + val stmt = state.code(origin) + (stmt.astID: @switch) match { + case StaticMethodCall.ASTID ⇒ false // We are looking for implicit exceptions only + + case Throw.ASTID ⇒ + stmt.asThrow.exception.asVar.value.asReferenceValue.isNull.isNotNo + + case NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + val rcvr = stmt.asInstanceMethodCall.receiver + !rcvr.isVar || rcvr.asVar.value.asReferenceValue.isNull.isNotNo + + case Assignment.ASTID ⇒ evaluationMayCauseVMLevelException(stmt.asAssignment.expr) + + case ExprStmt.ASTID ⇒ evaluationMayCauseVMLevelException(stmt.asExprStmt.expr) + + case _ ⇒ true } - true - - // Reference comparisons may have different results for structurally equal values - case If.ASTID => - val If(_, left, _, right, _) = stmt - if (left.cTpe eq ComputationalTypeReference) - if (!(isLocal(left, CompileTimePure) || isLocal(right, CompileTimePure))) - atMost(SideEffectFree) - true - - // The following statements do not further influence purity - case Goto.ASTID | JSR.ASTID | Ret.ASTID | Switch.ASTID | Assignment.ASTID | Return.ASTID | - Nop.ASTID | ExprStmt.ASTID | Checkcast.ASTID => - true } - isStmtNotImpure && stmt.forallSubExpressions(checkPurityOfExpr) - } - - /** - * Examines an expression for its influence on the method's purity. - * This method will return false for impure expressions, so evaluation can be terminated early. - */ - def checkPurityOfExpr(expr: Expr[V])(implicit state: StateType): Boolean = { - val isExprNotImpure = (expr.astID: @switch) match { - // For function calls, purity will be checked later - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => - true - - // Field/array loads are pure if the field is (effectively) final or the object/array is - // local and non-escaping - case GetStatic.ASTID => + /** + * Examines whether a call constitutes a domain-specific action using the domain-specific rater. + * If it is, the maxPurity will be reduced to at most the domain-specific purity returned by the + * domain-specific rater. + */ + def isDomainSpecificCall( + call: Call[V], + receiver: Option[Expr[V]] + )(implicit state: StateType): Boolean = { implicit val code: Array[Stmt[V]] = state.code - val ratedResult = rater.handleGetStatic(expr.asGetStatic) - if (ratedResult.isDefined) atMost(ratedResult.get) - else checkPurityOfFieldRef(expr.asGetStatic) - true - case GetField.ASTID => - checkPurityOfFieldRef(expr.asGetField) - true - case ArrayLoad.ASTID => - if (state.ubPurity.isDeterministic) - isLocal(expr.asArrayLoad.arrayRef, SideEffectFree) - true - - // We don't handle unresolved Invokedynamic - // - either OPAL removes it or we forget about it - case InvokedynamicFunctionCall.ASTID => - atMost(ImpureByAnalysis) - false - - // The following expressions do not further influence purity, potential exceptions are - // handled explicitly - case New.ASTID | NewArray.ASTID | InstanceOf.ASTID | Compare.ASTID | Param.ASTID | - MethodTypeConst.ASTID | MethodHandleConst.ASTID | IntConst.ASTID | LongConst.ASTID | - FloatConst.ASTID | DoubleConst.ASTID | StringConst.ASTID | ClassConst.ASTID | - NullExpr.ASTID | BinaryExpr.ASTID | PrefixExpr.ASTID | PrimitiveTypecastExpr.ASTID | - ArrayLength.ASTID | Var.ASTID => - true + val ratedResult = rater.handleCall(call, receiver) + if (ratedResult.isDefined) + atMost(ratedResult.get) + ratedResult.isDefined + } + + /** + * Examines a statement for its influence on the method's purity. + * This method will return false for impure statements, so evaluation can be terminated early. + */ + def checkPurityOfStmt(stmt: Stmt[V])(implicit state: StateType): Boolean = { + val isStmtNotImpure = (stmt.astID: @switch) match { + // For method calls, purity will be checked later + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + true + + // We don't handle unresolved Invokedynamics + // - either OPAL removes it or we forget about it + case InvokedynamicMethodCall.ASTID ⇒ + atMost(ImpureByAnalysis) + false + + // Returning objects/arrays is pure, if the returned object/array is locally initialized + // and non-escaping or the object is immutable + case ReturnValue.ASTID ⇒ + checkPurityOfReturn(stmt.asReturnValue.expr) + true + case Throw.ASTID ⇒ + checkPurityOfReturn(stmt.asThrow.exception) + true + + // Synchronization on non-escaping locally initialized objects/arrays is pure (and + // useless...) + case MonitorEnter.ASTID | MonitorExit.ASTID ⇒ + isLocal(stmt.asSynchronizationStmt.objRef, ImpureByAnalysis) + + // Storing into non-escaping locally initialized objects/arrays is pure + case ArrayStore.ASTID ⇒ isLocal(stmt.asArrayStore.arrayRef, ImpureByAnalysis) + case PutField.ASTID ⇒ isLocal(stmt.asPutField.objRef, ImpureByAnalysis) + + case PutStatic.ASTID ⇒ + // Note that a putstatic is not necessarily pure/sideeffect free, even if it + // is executed within a static initializer to initialize a field of + // `the` class; it is possible that the initialization triggers the + // initialization of another class which reads the value of this static field. + // See + // https://stackoverflow.com/questions/6416408/static-circular-dependency-in-java + // for an in-depth discussion. + // (Howevever, if we would check for cycles, we could determine that it is pure, + // but this is not considered to be too useful...) + atMost(ImpureByAnalysis) + false + + // Creating implicit exceptions is side-effect free (because of fillInStackTrace) + // but it may be ignored as domain-specific + case CaughtException.ASTID ⇒ + for { + origin ← stmt.asCaughtException.origins + if isImmediateVMException(origin) + } { + val baseOrigin = state.code(ai.underlyingPC(origin)) + val ratedResult = rater.handleException(baseOrigin) + if (ratedResult.isDefined) atMost(ratedResult.get) + else atMost(SideEffectFree) + } + true + + // Reference comparisons may have different results for structurally equal values + case If.ASTID ⇒ + val If(_, left, _, right, _) = stmt + if (left.cTpe eq ComputationalTypeReference) + if (!(isLocal(left, CompileTimePure) || isLocal(right, CompileTimePure))) + atMost(SideEffectFree) + true + + // The following statements do not further influence purity + case Goto.ASTID | JSR.ASTID | Ret.ASTID | Switch.ASTID | Assignment.ASTID | Return.ASTID | + Nop.ASTID | ExprStmt.ASTID | Checkcast.ASTID ⇒ + true + } + + isStmtNotImpure && stmt.forallSubExpressions(checkPurityOfExpr) + } + /** + * Examines an expression for its influence on the method's purity. + * This method will return false for impure expressions, so evaluation can be terminated early. + */ + def checkPurityOfExpr(expr: Expr[V])(implicit state: StateType): Boolean = { + val isExprNotImpure = (expr.astID: @switch) match { + // For function calls, purity will be checked later + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + true + + // Field/array loads are pure if the field is (effectively) final or the object/array is + // local and non-escaping + case GetStatic.ASTID ⇒ + implicit val code: Array[Stmt[V]] = state.code + val ratedResult = rater.handleGetStatic(expr.asGetStatic) + if (ratedResult.isDefined) atMost(ratedResult.get) + else checkPurityOfFieldRef(expr.asGetStatic) + true + case GetField.ASTID ⇒ + checkPurityOfFieldRef(expr.asGetField) + true + case ArrayLoad.ASTID ⇒ + if (state.ubPurity.isDeterministic) + isLocal(expr.asArrayLoad.arrayRef, SideEffectFree) + true + + // We don't handle unresolved Invokedynamic + // - either OPAL removes it or we forget about it + case InvokedynamicFunctionCall.ASTID ⇒ + atMost(ImpureByAnalysis) + false + + // The following expressions do not further influence purity, potential exceptions are + // handled explicitly + case New.ASTID | NewArray.ASTID | InstanceOf.ASTID | Compare.ASTID | Param.ASTID | + MethodTypeConst.ASTID | MethodHandleConst.ASTID | IntConst.ASTID | LongConst.ASTID | + FloatConst.ASTID | DoubleConst.ASTID | StringConst.ASTID | ClassConst.ASTID | + NullExpr.ASTID | BinaryExpr.ASTID | PrefixExpr.ASTID | PrimitiveTypecastExpr.ASTID | + ArrayLength.ASTID | Var.ASTID ⇒ + true + + } + + isExprNotImpure && expr.forallSubExpressions(checkPurityOfExpr) } - isExprNotImpure && expr.forallSubExpressions(checkPurityOfExpr) - } - - def checkPurityOfMethod( - callee: DeclaredMethod, - params: Seq[Expr[V]] - )(implicit state: StateType): Boolean = { - if (callee.hasSingleDefinedMethod && (callee.definedMethod eq state.method)) { - true - } else { - val calleePurity = propertyStore(callee, Purity.key) - checkMethodPurity(calleePurity, params) + def checkPurityOfMethod( + callee: DeclaredMethod, + params: Seq[Expr[V]] + )(implicit state: StateType): Boolean = { + if (callee.hasSingleDefinedMethod && (callee.definedMethod eq state.method)) { + true + } else { + val calleePurity = propertyStore(callee, Purity.key) + checkMethodPurity(calleePurity, params) + } } - } - - def getCall(stmt: Stmt[V])(implicit state: StateType): Call[V] = stmt.astID match { - case StaticMethodCall.ASTID => stmt.asStaticMethodCall - case NonVirtualMethodCall.ASTID => stmt.asNonVirtualMethodCall - case VirtualMethodCall.ASTID => stmt.asVirtualMethodCall - case Assignment.ASTID => stmt.asAssignment.expr.asFunctionCall - case ExprStmt.ASTID => stmt.asExprStmt.expr.asFunctionCall - case CaughtException.ASTID => - /* + + def getCall(stmt: Stmt[V])(implicit state: StateType): Call[V] = stmt.astID match { + case StaticMethodCall.ASTID ⇒ stmt.asStaticMethodCall + case NonVirtualMethodCall.ASTID ⇒ stmt.asNonVirtualMethodCall + case VirtualMethodCall.ASTID ⇒ stmt.asVirtualMethodCall + case Assignment.ASTID ⇒ stmt.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID ⇒ stmt.asExprStmt.expr.asFunctionCall + case CaughtException.ASTID ⇒ + /* * There is no caught exception instruction in bytecode, so it might be the case, that * in the three-address code, there is a CaughtException stmt right before the call * with the same pc. Therefore, we have to get the call stmt after the current stmt. @@ -397,336 +397,336 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { * 12: pc=52 caught java.lang.Exception ... * 13: pc=52 java.lang.Exception.printStackTrace() */ - getCall(state.code(state.pcToIndex(stmt.pc) + 1)) - case _ => - throw new IllegalStateException(s"unexpected stmt $stmt") - } - - /** - * Examines the influence of the purity property of a method on the examined method's purity. - * - * @note Adds dependendies when necessary. - */ - def checkMethodPurity( - ep: EOptionP[DeclaredMethod, Purity], - params: Seq[Expr[V]] = Seq.empty - )(implicit state: StateType): Boolean - - /** - * Examines whether a field read influences a method's purity. - * Reading values from fields that are not (effectively) final may cause nondeterministic - * behavior, so the method can only be side-effect free. - */ - def checkPurityOfFieldRef( - fieldRef: FieldRead[V] - )(implicit state: StateType): Unit = { - // Don't do dependee checks if already non-deterministic - if (state.ubPurity.isDeterministic) { - fieldRef.asFieldRead.resolveField match { - case Some(field) if field.isStatic => - checkFieldMutability(propertyStore(field, ReferenceImmutability.key), None) //FieldMutability.key), None) - case Some(field) => - checkFieldMutability( - propertyStore(field, ReferenceImmutability.key), - Some(fieldRef.asGetField.objRef) //FieldMutability.key), Some(fieldRef.asGetField.objRef) - ) - case _ => // Unknown field - if (fieldRef.isGetField) isLocal(fieldRef.asGetField.objRef, SideEffectFree) - else atMost(SideEffectFree) - } + getCall(state.code(state.pcToIndex(stmt.pc) + 1)) + case _ ⇒ + throw new IllegalStateException(s"unexpected stmt $stmt") } - } - - /** - * Examines the influence that a given field mutability has on the method's purity. - */ - def checkFieldMutability( - ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], - objRef: Option[Expr[V]] - )(implicit state: StateType): Unit = ep match { - - //case LBP(ImmutableReference(_)) ⇒ - //case LBP(LazyInitializedReference) ⇒ - case LBP(ImmutableReference(_) | LazyInitializedReference) => - println("====>>>> EP: " + ep) //_: FinalField) ⇒ // Final fields don't impede purity - case FinalP(MutableReference) => - println("====>>>> EP: " + ep) //_: FinalEP[Field, ReferenceImmutability] ⇒ //FieldMutability] ⇒ // Mutable field - if (objRef.isDefined) { - if (state.ubPurity.isDeterministic) - isLocal(objRef.get, SideEffectFree) - } else atMost(SideEffectFree) - case _ => - reducePurityLB(SideEffectFree) - if (state.ubPurity.isDeterministic) - handleUnknownFieldMutability(ep, objRef) - - } - - /** - * Handles what to do when the mutability of a field is not yet known. - * Analyses must implement this method with the behavior they need, e.g. registering dependees. - */ - def handleUnknownFieldMutability( - ep: EOptionP[Field, ReferenceImmutability], //FieldMutability], - objRef: Option[Expr[V]] - )(implicit state: StateType): Unit - - /** - * Examines the effect of returning a value on the method's purity. - * Returning a reference to a mutable object or array may cause nondeterministic behavior - * as the object/array may be modified between invocations of the method, so the method can - * only be side-effect free. E.g., a given parameter which references a mutable object is - * returned (and not otherwise accessed). - */ - def checkPurityOfReturn(returnValue: Expr[V])(implicit state: StateType): Unit = { - if (returnValue.cTpe != ComputationalTypeReference) - return; // Only non-primitive return values influence purity. - - if (!state.ubPurity.isDeterministic) - return; // If the method can't be pure, the return value is not important. - - if (!returnValue.isVar) { - // The expression could refer to further expressions in a non-flat representation. To - // avoid special handling, we just fallback to SideEffectFreeWithoutAllocations here if - // the return value is not local as the analysis is intended to be used on flat - // representations anyway. - isLocal(returnValue, SideEffectFree) - return; + + /** + * Examines the influence of the purity property of a method on the examined method's purity. + * + * @note Adds dependendies when necessary. + */ + def checkMethodPurity( + ep: EOptionP[DeclaredMethod, Purity], + params: Seq[Expr[V]] = Seq.empty + )(implicit state: StateType): Boolean + + /** + * Examines whether a field read influences a method's purity. + * Reading values from fields that are not (effectively) final may cause nondeterministic + * behavior, so the method can only be side-effect free. + */ + def checkPurityOfFieldRef( + fieldRef: FieldRead[V] + )(implicit state: StateType): Unit = { + // Don't do dependee checks if already non-deterministic + if (state.ubPurity.isDeterministic) { + fieldRef.asFieldRead.resolveField match { + case Some(field) if field.isStatic ⇒ + checkFieldMutability(propertyStore(field, ReferenceImmutability.key), None) //FieldMutability.key), None) + case Some(field) ⇒ + checkFieldMutability( + propertyStore(field, ReferenceImmutability.key), + Some(fieldRef.asGetField.objRef) //FieldMutability.key), Some(fieldRef.asGetField.objRef) + ) + case _ ⇒ // Unknown field + if (fieldRef.isGetField) isLocal(fieldRef.asGetField.objRef, SideEffectFree) + else atMost(SideEffectFree) + } + } } - val value = returnValue.asVar.value.asReferenceValue - if (value.isNull.isYes) - return; // Null is immutable + /** + * Examines the influence that a given field mutability has on the method's purity. + */ + def checkFieldMutability( + ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], + objRef: Option[Expr[V]] + )(implicit state: StateType): Unit = ep match { + + //case LBP(ImmutableReference(_)) ⇒ + //case LBP(LazyInitializedReference) ⇒ + case LBP(ImmutableReference(_) | LazyInitializedReference) ⇒ + println("====>>>> EP: "+ep) //_: FinalField) ⇒ // Final fields don't impede purity + case FinalP(MutableReference) ⇒ + println("====>>>> EP: "+ep) //_: FinalEP[Field, ReferenceImmutability] ⇒ //FieldMutability] ⇒ // Mutable field + if (objRef.isDefined) { + if (state.ubPurity.isDeterministic) + isLocal(objRef.get, SideEffectFree) + } else atMost(SideEffectFree) + case _ ⇒ + reducePurityLB(SideEffectFree) + if (state.ubPurity.isDeterministic) + handleUnknownFieldMutability(ep, objRef) - if (value.upperTypeBound.exists(_.isArrayType)) { - // Arrays are always mutable - isLocal(returnValue, SideEffectFree) - return; } - if (value.isPrecise) { // Precise class known, use ClassImmutability - val returnType = value.upperTypeBound.head - - val classImmutability = - propertyStore( - returnType, - ClassImmutability_new.key // ClassImmutability.key - ).asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]] // ClassImmutability]] - checkTypeMutability(classImmutability, returnValue) - - } else { // Precise class unknown, use TypeImmutability - // IMPROVE Use ObjectType once we attach the respective information to ObjectTypes - val returnTypes = value.upperTypeBound - - returnTypes.forall { returnType => - val typeImmutability = - propertyStore( - returnType, - TypeImmutability_new.key //.key - ).asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] //TypeImmutability]] - checkTypeMutability(typeImmutability, returnValue) - } - } - } - - /** - * Examines the effect that the mutability of a returned value's type has on the method's - * purity. - */ - def checkTypeMutability( - ep: EOptionP[ObjectType, Property], - returnValue: Expr[V] - )(implicit state: StateType): Boolean = ep match { - // Returning immutable object is pure - case LBP(DeepImmutableType | DeepImmutableClass) => - true // ImmutableType | ImmutableObject) ⇒ true7 - case _: FinalEP[ObjectType, Property] => - atMost(Pure) // Can not be compile time pure if mutable object is returned - if (state.ubPurity.isDeterministic) - isLocal(returnValue, SideEffectFree) - false // Return early if we are already side-effect free - case _ => - reducePurityLB(SideEffectFree) - if (state.ubPurity.isDeterministic) - handleUnknownTypeMutability(ep, returnValue) - true - } - - /** - * Handles what to do when the mutability of a type is not yet known. - * Analyses must implement this method with the behavior they need, e.g. registering dependees. - */ - def handleUnknownTypeMutability( - ep: EOptionP[ObjectType, Property], - expr: Expr[V] - )(implicit state: StateType): Unit - - /** - * Examines the effect that the purity of all potential callees has on the purity of the method. - */ - def checkPurityOfCallees( - calleesEOptP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: StateType - ): Boolean = { - handleCalleesUpdate(calleesEOptP) - calleesEOptP match { - case UBPS(p: Callees, isFinal) => - if (!isFinal) reducePurityLB(ImpureByAnalysis) - - val hasIncompleteCallSites = - p.incompleteCallSites.exists { pc => - val index = state.pcToIndex(pc) - if (index < 0) - false // call will not be executed - else { - val call = getCall(state.code(state.pcToIndex(pc))) - !isDomainSpecificCall(call, call.receiverOption) - } - } + /** + * Handles what to do when the mutability of a field is not yet known. + * Analyses must implement this method with the behavior they need, e.g. registering dependees. + */ + def handleUnknownFieldMutability( + ep: EOptionP[Field, ReferenceImmutability], //FieldMutability], + objRef: Option[Expr[V]] + )(implicit state: StateType): Unit + + /** + * Examines the effect of returning a value on the method's purity. + * Returning a reference to a mutable object or array may cause nondeterministic behavior + * as the object/array may be modified between invocations of the method, so the method can + * only be side-effect free. E.g., a given parameter which references a mutable object is + * returned (and not otherwise accessed). + */ + def checkPurityOfReturn(returnValue: Expr[V])(implicit state: StateType): Unit = { + if (returnValue.cTpe != ComputationalTypeReference) + return ; // Only non-primitive return values influence purity. + + if (!state.ubPurity.isDeterministic) + return ; // If the method can't be pure, the return value is not important. + + if (!returnValue.isVar) { + // The expression could refer to further expressions in a non-flat representation. To + // avoid special handling, we just fallback to SideEffectFreeWithoutAllocations here if + // the return value is not local as the analysis is intended to be used on flat + // representations anyway. + isLocal(returnValue, SideEffectFree) + return ; + } + + val value = returnValue.asVar.value.asReferenceValue + if (value.isNull.isYes) + return ; // Null is immutable - if (hasIncompleteCallSites) { - atMost(ImpureByAnalysis) - return false; + if (value.upperTypeBound.exists(_.isArrayType)) { + // Arrays are always mutable + isLocal(returnValue, SideEffectFree) + return ; } - val noDirectCalleeIsImpure = p.directCallSites().forall { - case (pc, callees) => - val index = state.pcToIndex(pc) - if (index < 0) - true // call will not be executed - else { - val call = getCall(state.code(index)) - isDomainSpecificCall(call, call.receiverOption) || - callees.forall { callee => - checkPurityOfMethod( - callee, - call.receiverOption.orNull +: call.params - ) - } + if (value.isPrecise) { // Precise class known, use ClassImmutability + val returnType = value.upperTypeBound.head + + val classImmutability = + propertyStore( + returnType, + ClassImmutability_new.key // ClassImmutability.key + ).asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]] // ClassImmutability]] + checkTypeMutability(classImmutability, returnValue) + + } else { // Precise class unknown, use TypeImmutability + // IMPROVE Use ObjectType once we attach the respective information to ObjectTypes + val returnTypes = value.upperTypeBound + + returnTypes.forall { returnType ⇒ + val typeImmutability = + propertyStore( + returnType, + TypeImmutability_new.key //.key + ).asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] //TypeImmutability]] + checkTypeMutability(typeImmutability, returnValue) } } + } + + /** + * Examines the effect that the mutability of a returned value's type has on the method's + * purity. + */ + def checkTypeMutability( + ep: EOptionP[ObjectType, Property], + returnValue: Expr[V] + )(implicit state: StateType): Boolean = ep match { + // Returning immutable object is pure + case LBP(DeepImmutableType | DeepImmutableClass) ⇒ + true // ImmutableType | ImmutableObject) ⇒ true7 + case _: FinalEP[ObjectType, Property] ⇒ + atMost(Pure) // Can not be compile time pure if mutable object is returned + if (state.ubPurity.isDeterministic) + isLocal(returnValue, SideEffectFree) + false // Return early if we are already side-effect free + case _ ⇒ + reducePurityLB(SideEffectFree) + if (state.ubPurity.isDeterministic) + handleUnknownTypeMutability(ep, returnValue) + true + } - if (!noDirectCalleeIsImpure) - return false; - - val noIndirectCalleeIsImpure = p.indirectCallSites().forall { - case (pc, callees) => - val index = state.pcToIndex(pc) - if (index < 0) - true // call will not be executed - else { - val call = getCall(state.code(index)) - isDomainSpecificCall(call, call.receiverOption) || - callees.forall { callee => - checkPurityOfMethod( - callee, - p.indirectCallReceiver(pc, callee) - .map(receiver => uVarForDefSites(receiver, state.pcToIndex)) - .orNull +: - p.indirectCallParameters(pc, callee).map { paramO => - paramO.map(uVarForDefSites(_, state.pcToIndex)).orNull + /** + * Handles what to do when the mutability of a type is not yet known. + * Analyses must implement this method with the behavior they need, e.g. registering dependees. + */ + def handleUnknownTypeMutability( + ep: EOptionP[ObjectType, Property], + expr: Expr[V] + )(implicit state: StateType): Unit + + /** + * Examines the effect that the purity of all potential callees has on the purity of the method. + */ + def checkPurityOfCallees( + calleesEOptP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: StateType + ): Boolean = { + handleCalleesUpdate(calleesEOptP) + calleesEOptP match { + case UBPS(p: Callees, isFinal) ⇒ + if (!isFinal) reducePurityLB(ImpureByAnalysis) + + val hasIncompleteCallSites = + p.incompleteCallSites.exists { pc ⇒ + val index = state.pcToIndex(pc) + if (index < 0) + false // call will not be executed + else { + val call = getCall(state.code(state.pcToIndex(pc))) + !isDomainSpecificCall(call, call.receiverOption) + } } - ) - } - } + + if (hasIncompleteCallSites) { + atMost(ImpureByAnalysis) + return false; + } + + val noDirectCalleeIsImpure = p.directCallSites().forall { + case (pc, callees) ⇒ + val index = state.pcToIndex(pc) + if (index < 0) + true // call will not be executed + else { + val call = getCall(state.code(index)) + isDomainSpecificCall(call, call.receiverOption) || + callees.forall { callee ⇒ + checkPurityOfMethod( + callee, + call.receiverOption.orNull +: call.params + ) + } + } + } + + if (!noDirectCalleeIsImpure) + return false; + + val noIndirectCalleeIsImpure = p.indirectCallSites().forall { + case (pc, callees) ⇒ + val index = state.pcToIndex(pc) + if (index < 0) + true // call will not be executed + else { + val call = getCall(state.code(index)) + isDomainSpecificCall(call, call.receiverOption) || + callees.forall { callee ⇒ + checkPurityOfMethod( + callee, + p.indirectCallReceiver(pc, callee) + .map(receiver ⇒ uVarForDefSites(receiver, state.pcToIndex)) + .orNull +: + p.indirectCallParameters(pc, callee).map { paramO ⇒ + paramO.map(uVarForDefSites(_, state.pcToIndex)).orNull + } + ) + } + } + } + + noIndirectCalleeIsImpure + + case _ ⇒ + reducePurityLB(ImpureByAnalysis) + true } + } - noIndirectCalleeIsImpure + /** + * Handles what to do when the set of potential callees changes. + * Analyses must implement this method with the behavior they need, e.g. registering dependees. + */ + def handleCalleesUpdate( + callees: EOptionP[DeclaredMethod, Callees] + )(implicit state: StateType): Unit + + /** + * Handles what to do if the TACAI is not yet final. + */ + def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: StateType): Unit + + /** + * Retrieves and commits the methods purity as calculated for its declaring class type for the + * current DefinedMethod that represents the non-overwritten method in a subtype. + */ + def baseMethodPurity(dm: DefinedMethod): ProperPropertyComputationResult = { + + 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) + case epk ⇒ + InterimResult(dm, ImpureByAnalysis, CompileTimePure, Seq(epk), c) + } - case _ => - reducePurityLB(ImpureByAnalysis) - true - } - } - - /** - * Handles what to do when the set of potential callees changes. - * Analyses must implement this method with the behavior they need, e.g. registering dependees. - */ - def handleCalleesUpdate( - callees: EOptionP[DeclaredMethod, Callees] - )(implicit state: StateType): Unit - - /** - * Handles what to do if the TACAI is not yet final. - */ - def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: StateType): Unit - - /** - * Retrieves and commits the methods purity as calculated for its declaring class type for the - * current DefinedMethod that represents the non-overwritten method in a subtype. - */ - def baseMethodPurity(dm: DefinedMethod): ProperPropertyComputationResult = { - - 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) - case epk => - InterimResult(dm, ImpureByAnalysis, CompileTimePure, Seq(epk), c) + c(propertyStore(declaredMethods(dm.definedMethod), Purity.key)) } - c(propertyStore(declaredMethods(dm.definedMethod), Purity.key)) - } - - /** - * Determines the purity of the given method. - * - * @param definedMethod A defined method with a body. - */ - def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult - - /** Called when the analysis is scheduled lazily. */ - def doDeterminePurity(e: Entity): ProperPropertyComputationResult = { - e match { - case dm: DefinedMethod if dm.definedMethod.body.isDefined => - determinePurity(dm) - case dm: DeclaredMethod => Result(dm, ImpureByLackOfInformation) - case _ => - throw new IllegalArgumentException(s"$e is not a declared method") + /** + * Determines the purity of the given method. + * + * @param definedMethod A defined method with a body. + */ + def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult + + /** Called when the analysis is scheduled lazily. */ + def doDeterminePurity(e: Entity): ProperPropertyComputationResult = { + e match { + case dm: DefinedMethod if dm.definedMethod.body.isDefined ⇒ + determinePurity(dm) + case dm: DeclaredMethod ⇒ Result(dm, ImpureByLackOfInformation) + case _ ⇒ + throw new IllegalArgumentException(s"$e is not a declared method") + } } - } - - /** - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - def getTACAI( - method: Method - )(implicit state: StateType): Option[TACode[TACMethodParameter, V]] = { - propertyStore(method, TACAI.key) match { - case finalEP: FinalEP[Method, TACAI] => - handleTACAI(finalEP) - finalEP.ub.tac - case eps @ InterimUBP(ub: TACAI) => - reducePurityLB(ImpureByAnalysis) - handleTACAI(eps) - ub.tac - case epk => - reducePurityLB(ImpureByAnalysis) - handleTACAI(epk) - None + + /** + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + def getTACAI( + method: Method + )(implicit state: StateType): Option[TACode[TACMethodParameter, V]] = { + propertyStore(method, TACAI.key) match { + case finalEP: FinalEP[Method, TACAI] ⇒ + handleTACAI(finalEP) + finalEP.ub.tac + case eps @ InterimUBP(ub: TACAI) ⇒ + reducePurityLB(ImpureByAnalysis) + handleTACAI(eps) + ub.tac + case epk ⇒ + reducePurityLB(ImpureByAnalysis) + handleTACAI(epk) + None + } } - } - - def resolveDomainSpecificRater(fqn: String): DomainSpecificRater = { - import scala.reflect.runtime.universe.runtimeMirror - val mirror = runtimeMirror(getClass.getClassLoader) - try { - val module = mirror.staticModule(fqn) - mirror.reflectModule(module).instance.asInstanceOf[DomainSpecificRater] - } catch { - case ex @ (_: ScalaReflectionException | _: ClassCastException) => - OPALLogger.error( - "analysis configuration", - "resolve of domain specific rater failed, change " + - s"org.opalj.fpcf.${this.getClass.getName}.domainSpecificRater in " + - "ai/reference.conf to an existing DomainSpecificRater implementation", - ex - )(GlobalLogContext) - new BaseDomainSpecificRater // Provide a safe default if resolution failed + + def resolveDomainSpecificRater(fqn: String): DomainSpecificRater = { + import scala.reflect.runtime.universe.runtimeMirror + val mirror = runtimeMirror(getClass.getClassLoader) + try { + val module = mirror.staticModule(fqn) + mirror.reflectModule(module).instance.asInstanceOf[DomainSpecificRater] + } catch { + case ex @ (_: ScalaReflectionException | _: ClassCastException) ⇒ + OPALLogger.error( + "analysis configuration", + "resolve of domain specific rater failed, change "+ + s"org.opalj.fpcf.${this.getClass.getName}.domainSpecificRater in "+ + "ai/reference.conf to an existing DomainSpecificRater implementation", + ex + )(GlobalLogContext) + new BaseDomainSpecificRater // Provide a safe default if resolution failed + } } - } } From 2791a367b3bb8def34c519b7bfc4c496ef5721b6 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 23 Jun 2020 16:39:20 +0200 Subject: [PATCH 149/327] Test --- .../reference/L0ReferenceImmutabilityAnalysis.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index 73e1ba2c89..fead9b74f0 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -521,7 +521,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) val r = isNonDeterministic(newEP) - //if (!r) state.referenceImmutability = LazyInitializedReference + if (!r) state.referenceImmutability = LazyInitializedReference println("continuation purity result: "+r) isNotFinal = r println("cont. is not final: "+isNotFinal) From 2387fdb18735c88d89108b9a45a44535ed1565e7 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 23 Jun 2020 17:34:47 +0200 Subject: [PATCH 150/327] revert Test --- .../L0ReferenceImmutabilityAnalysis.scala | 1206 ++++++++--------- 1 file changed, 603 insertions(+), 603 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index fead9b74f0..a1d1c78820 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -73,333 +73,333 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec with AbstractReferenceImmutabilityAnalysis with FPCFAnalysis { - def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field ⇒ determineReferenceImmutability(field) - case _ ⇒ - val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } - - /** - * Analyzes the mutability of private non-final fields. - * - * 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 determineReferenceImmutability( - field: Field - ): ProperPropertyComputationResult = { - println( - "start determine ref imm analysis=====================================================================================================" - ) - println("field: "+field.name) - implicit val state: State = State(field) - // Fields are not final if they are read prematurely! - if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { - println("field is prematurely read") - return Result(field, MutableReference) - }; //Result(field, NonFinalFieldByAnalysis); - println("01") - state.referenceImmutability = ImmutableReference(true) //EffectivelyFinalField - - val thisType = field.classFile.thisType - - if ((field.isPublic || field.isPackagePrivate || field.isProtected)) { - println("field: "+field+" is public, pack priv or protected") - if (!field.isFinal) - return Result(field, MutableReference) - else - state.notEscapes = false - }; //Result(field, NonFinalFieldByLackOfInformation) - println("02") - // 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) { - if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - } - project.classesPerPackage(thisType.packageName) - } else { - Set(field.classFile) - } - println("03") - val classesHavingAccess: Iterator[ClassFile] = - if (field.isProtected) { - if (typeExtensibility(thisType).isYesOrUnknown && !state.field.isFinal) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - } - 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 - } - println("04") - // If there are native methods, we give up - if (classesHavingAccess.exists(_.methods.exists(_.isNative)) && !state.field.isFinal) { - println("native") - return Result(field, MutableReference) - }; //return Result(field, NonFinalFieldByLackOfInformation); - println("05") - - println("1-----------------------------------------------------------------------") - for { - (method, pcs) ← fieldAccessInformation.readAccesses(field) - taCode ← getTACAI(method, pcs) - } { - pcs.foreach(pc ⇒ { - val index = taCode.pcToIndex(pc) - if (index > 0) { - taCode.stmts(index) match { - case Assignment(pc3, targetVar, GetField(pc4, cl, name, _, value)) ⇒ - if (name == field.name) { - state.notEscapes = false - } - case _ ⇒ - } - } else state.notEscapes = false - }) + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field => determineReferenceImmutability(field) + case _ => + val m = entity.getClass.getSimpleName + " is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } + + /** + * Analyzes the mutability of private non-final fields. + * + * 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 determineReferenceImmutability( + field: Field + ): ProperPropertyComputationResult = { + println( + "start determine ref imm analysis=====================================================================================================" + ) + println("field: " + field.name) + implicit val state: State = State(field) + // Fields are not final if they are read prematurely! + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { + println("field is prematurely read") + return Result(field, MutableReference) + }; //Result(field, NonFinalFieldByAnalysis); + println("01") + state.referenceImmutability = ImmutableReference(true) //EffectivelyFinalField + + val thisType = field.classFile.thisType + + if ((field.isPublic || field.isPackagePrivate || field.isProtected)) { + println("field: " + field + " is public, pack priv or protected") + if (!field.isFinal) + return Result(field, MutableReference) + else + state.notEscapes = false + }; //Result(field, NonFinalFieldByLackOfInformation) + println("02") + // 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) { + if (!closedPackages.isClosed(thisType.packageName)) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } + println("03") + val classesHavingAccess: Iterator[ClassFile] = + if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown && !state.field.isFinal) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); } - println("0------------------") - for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode ← getTACAI(method, pcs) - } { - println("2-----------------------------------------------------------------------") - println("method: "+method) - for (pc ← pcs) { - println("pc: "+pc) - val index = taCode.pcToIndex(pc) - if (index > 0) { - val stmt = taCode.stmts(index) - println("stmt: "+stmt) - stmt match { - case PutField(_, _, _, _, _, value) ⇒ - value match { - case v @ UVar(defSites, value2) ⇒ // SObjectValue(t,_,_,_)) => - //if (!v.defSites.filter(_ < 1).isEmpty) - //escape var - { - v.defSites.foreach( - i ⇒ { - println("def site: "+i) - if (i > 0) { - val stmt2 = taCode.stmts(i) - println("stmt2: "+stmt2) - stmt2 match { - case Assignment(pcAssignment, dv @ DVar(useSites, value), expr) ⇒ - //useSites - dv.useSites.foreach( - x ⇒ { - val innerstmt = taCode.stmts(x) - innerstmt match { - case NonVirtualMethodCall( - pc, - bdeclaringClass, - isInterface, - name, - descriptor, - receiver, - params - ) ⇒ { - params.foreach(expr ⇒ { - expr match { - case vrs @ UVar(defSites, value) ⇒ { //: SObjectValue(tpe,isNull,isPrecise) - //val isStaticIndex = if (method.isStatic) 0 else -1 - vrs.defSites.foreach( - dfste ⇒ { - if (dfste < 0) //(0 + isStaticIndex)) - state.notEscapes = false - else { - val stmtDefSite = taCode.stmts(dfste) - println("stmt def site: "+stmtDefSite) - stmtDefSite match { - case Assignment( - pcA, - targetVar, - GetField( - pc, - declaringClass, - name, - declaredFieldType, - objRef - ) - ) ⇒ { - val fs = project.allFields.filter( - { f ⇒ - f.name.equals(name) && f.fieldType.equals( - declaredFieldType - ) && f.classFile.equals( - state.field.classFile - ) - } - ) - fs.foreach(f ⇒ { - val result = - propertyStore(f, FieldImmutability.key) - result match { - case FinalP(DeepImmutableField) ⇒ - case _ ⇒ state.notEscapes = false - } - }) - } - case _ ⇒ - } - } - - } - ) - } - } - }) - } - case _ ⇒ - } - /** - * case NonVirtualMethodCall(pc,org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses.Generic_class1, - * isInterface=false, - * void (java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object), - * UVar(defSites={1},value=SObjectValue(type=org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses.Generic_class1,isNull=No,isPrecise=true)),(UVar(defSites={-5},value=SObjectValue(type=org.opalj.fpcf.fixt - * ures.immutability.classes.multinested_genericClasses.Generic_class1,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-4},value=SObjectValue(type=org.opalj.fpcf.fixtures.immutability.classes.mu - * ltinested_genericClasses.TrivialMutableClass,isNull=Unknown,isPrecise=false)))) => * - */ - - } - ) - if (expr.isNew || expr.isConst) { - if (expr.isNew) { - - /** - * val NEW = expr.asNew - * - * val oType = NEW.tpe - * println("otype: "+oType) - * - * if (oType == ObjectType.Object) - * state.notEscapes = false - * else { - * val result = propertyStore(oType, TypeImmutability_new.key) - * result match { - * case FinalP(DependentImmutableType) ⇒ { - * println("depenent type") - * state.notEscapes = false - * } - * case fp @ FinalP(_) ⇒ println("etc. final: "+fp) - * case ep @ _ ⇒ { - * println("type imm not yet computed") - * state.typeDependees += ep - * } - * } - * }* - */ - - } - /** - * propertyStore( - * definitionSites(method, pc), - * EscapeProperty.key - * ) match { - * case FinalP(NoEscape) ⇒ // nothing to do - * case FinalP(AtMost(EscapeViaParameter)) ⇒ // introduced via this - * case _ ⇒ - * state.notEscapes = false - * } - * } else { - * state.notEscapes = false* - */ //TODO - } - case _ ⇒ state.notEscapes = false + 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 + } + println("04") + // If there are native methods, we give up + if (classesHavingAccess.exists(_.methods.exists(_.isNative)) && !state.field.isFinal) { + println("native") + return Result(field, MutableReference) + }; //return Result(field, NonFinalFieldByLackOfInformation); + println("05") + + println("1-----------------------------------------------------------------------") + for { + (method, pcs) <- fieldAccessInformation.readAccesses(field) + taCode <- getTACAI(method, pcs) + } { + pcs.foreach(pc => { + val index = taCode.pcToIndex(pc) + if (index > 0) { + taCode.stmts(index) match { + case Assignment(pc3, targetVar, GetField(pc4, cl, name, _, value)) => + if (name == field.name) { + state.notEscapes = false + } + case _ => + } + } else state.notEscapes = false + }) + } + println("0------------------") + for { + (method, pcs) <- fieldAccessInformation.writeAccesses(field) + taCode <- getTACAI(method, pcs) + } { + println("2-----------------------------------------------------------------------") + println("method: " + method) + for (pc <- pcs) { + println("pc: " + pc) + val index = taCode.pcToIndex(pc) + if (index > 0) { + val stmt = taCode.stmts(index) + println("stmt: " + stmt) + stmt match { + case PutField(_, _, _, _, _, value) => + value match { + case v @ UVar(defSites, value2) => // SObjectValue(t,_,_,_)) => + //if (!v.defSites.filter(_ < 1).isEmpty) + //escape var + { + v.defSites.foreach( + i => { + println("def site: " + i) + if (i > 0) { + val stmt2 = taCode.stmts(i) + println("stmt2: " + stmt2) + stmt2 match { + case Assignment(pcAssignment, dv @ DVar(useSites, value), expr) => + //useSites + dv.useSites.foreach( + x => { + val innerstmt = taCode.stmts(x) + innerstmt match { + case NonVirtualMethodCall( + pc, + bdeclaringClass, + isInterface, + name, + descriptor, + receiver, + params + ) => { + params.foreach(expr => { + expr match { + case vrs @ UVar(defSites, value) => { //: SObjectValue(tpe,isNull,isPrecise) + //val isStaticIndex = if (method.isStatic) 0 else -1 + vrs.defSites.foreach( + dfste => { + if (dfste < 0) //(0 + isStaticIndex)) + state.notEscapes = false + else { + val stmtDefSite = taCode.stmts(dfste) + println("stmt def site: " + stmtDefSite) + stmtDefSite match { + case Assignment( + pcA, + targetVar, + GetField( + pc, + declaringClass, + name, + declaredFieldType, + objRef + ) + ) => { + val fs = project.allFields.filter( + { f => + f.name.equals(name) && f.fieldType.equals( + declaredFieldType + ) && f.classFile.equals( + state.field.classFile + ) + } + ) + fs.foreach(f => { + val result = + propertyStore(f, FieldImmutability.key) + result match { + case FinalP(DeepImmutableField) => + case _ => state.notEscapes = false + } + }) } - } else { - //constructor ?? - state.notEscapes = false + case _ => + } } - } - ) + + } + ) + } + } + }) } - case _ ⇒ state.notEscapes = false - } - case _ ⇒ state.notEscapes = false - } - } else state.notEscapes = false - } + case _ => + } + /** + * case NonVirtualMethodCall(pc,org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses.Generic_class1, + * isInterface=false, + * void (java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object), + * UVar(defSites={1},value=SObjectValue(type=org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses.Generic_class1,isNull=No,isPrecise=true)),(UVar(defSites={-5},value=SObjectValue(type=org.opalj.fpcf.fixt + * ures.immutability.classes.multinested_genericClasses.Generic_class1,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-4},value=SObjectValue(type=org.opalj.fpcf.fixtures.immutability.classes.mu + * ltinested_genericClasses.TrivialMutableClass,isNull=Unknown,isPrecise=false)))) => * + */ - if (methodUpdatesField(method, taCode, pcs) && !state.field.isFinal) { - println("method does updates field") - return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnfalysis); - } else println("method does not updates fields") - println("st.ref imm 1: "+state.referenceImmutability+", reference: "+state.field) - } - println("st.ref imm 2: "+state.referenceImmutability+", reference: "+state.field) + } + ) + if (expr.isNew || expr.isConst) { + if (expr.isNew) { + + /** + * val NEW = expr.asNew + * + * val oType = NEW.tpe + * println("otype: "+oType) + * + * if (oType == ObjectType.Object) + * state.notEscapes = false + * else { + * val result = propertyStore(oType, TypeImmutability_new.key) + * result match { + * case FinalP(DependentImmutableType) ⇒ { + * println("depenent type") + * state.notEscapes = false + * } + * case fp @ FinalP(_) ⇒ println("etc. final: "+fp) + * case ep @ _ ⇒ { + * println("type imm not yet computed") + * state.typeDependees += ep + * } + * } + * }* + */ - if (state.lazyInitInvocation.isDefined) { - //val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - //TODO //handleCalls(calleesEOP) - } - println("st.ref imm 3: "+state.referenceImmutability+", reference: "+state.field) - println("finish; go to create results") - createResult() + } + /** + * propertyStore( + * definitionSites(method, pc), + * EscapeProperty.key + * ) match { + * case FinalP(NoEscape) ⇒ // nothing to do + * case FinalP(AtMost(EscapeViaParameter)) ⇒ // introduced via this + * case _ ⇒ + * state.notEscapes = false + * } + * } else { + * state.notEscapes = false* + */ //TODO + } + case _ => state.notEscapes = false + } + } else { + //constructor ?? + state.notEscapes = false + } + } + ) + } + case _ => state.notEscapes = false + } + case _ => state.notEscapes = false + } + } else state.notEscapes = false + } + + if (methodUpdatesField(method, taCode, pcs) && !state.field.isFinal) { + println("method does updates field") + return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnfalysis); + } else println("method does not updates fields") + println("st.ref imm 1: " + state.referenceImmutability + ", reference: " + state.field) } + println("st.ref imm 2: " + state.referenceImmutability + ", reference: " + state.field) - def handleCalls( - calleesEOP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: State - ): Boolean = { - println("cEOP: "+calleesEOP) - calleesEOP match { - case FinalP(callees) ⇒ - state.calleesDependee = None - handleCallees(callees) - case InterimUBP(callees) ⇒ - state.calleesDependee = Some(calleesEOP) - handleCallees(callees) - case r @ _ ⇒ - state.calleesDependee = Some(calleesEOP) - println("false: ; + result "+r) - false - } + if (state.lazyInitInvocation.isDefined) { + //val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + //TODO //handleCalls(calleesEOP) } - - def handleCallees(callees: Callees)(implicit state: State): Boolean = { - val pc = state.lazyInitInvocation.get._2 - if (callees.isIncompleteCallSite(pc)) { - println("is incomplete call site") - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else { - val targets = callees.callees(pc).toTraversable - if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { - val nonDeterministicTargets = - targets.filter(target ⇒ isNonDeterministic(propertyStore(target, Purity.key))) - println( - "target non deterministic: "+nonDeterministicTargets - .mkString(", ") - ) - nonDeterministicTargets.foreach(t ⇒ println(propertyStore(t, Purity.key))) - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else false - } + println("st.ref imm 3: " + state.referenceImmutability + ", reference: " + state.field) + println("finish; go to create results") + createResult() + } + + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: State + ): Boolean = { + println("cEOP: " + calleesEOP) + calleesEOP match { + case FinalP(callees) => + state.calleesDependee = None + handleCallees(callees) + case InterimUBP(callees) => + state.calleesDependee = Some(calleesEOP) + handleCallees(callees) + case r @ _ => + state.calleesDependee = Some(calleesEOP) + println("false: ; + result " + r) + false } + } + + def handleCallees(callees: Callees)(implicit state: State): Boolean = { + val pc = state.lazyInitInvocation.get._2 + if (callees.isIncompleteCallSite(pc)) { + println("is incomplete call site") + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else { + val targets = callees.callees(pc).toTraversable + if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { + val nonDeterministicTargets = + targets.filter(target => isNonDeterministic(propertyStore(target, Purity.key))) + println( + "target non deterministic: " + nonDeterministicTargets + .mkString(", ") + ) + nonDeterministicTargets.foreach(t => println(propertyStore(t, Purity.key))) + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else false + } + } - /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. - */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field * The code below can be used to identify such value, but is not yet adapted to using the * TACAI property */ - /* + /* var constantVal: Option[Any] = None var allInitializeConstant = true @@ -457,294 +457,294 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } constantVal */ - } - - /** - * Prepares the PropertyComputation result, either as IntermediateResult if there are still - * dependees or as Result otherwise. - */ - def createResult()(implicit state: State): ProperPropertyComputationResult = { - println("create results: ") - println("current ref imm: "+state.referenceImmutability) - if (state.hasDependees && (state.referenceImmutability ne MutableReference)) { //NonFinalFieldByAnalysis)) - println("has still dependendees") - println("state dependees: "+state.dependees) - InterimResult( - state.field, - MutableReference, //NonFinalFieldByAnalysis, - state.referenceImmutability, - state.dependees, - c - ) - } else { - println("end") - println("state.reference immutability: "+state.referenceImmutability) - println("state: "+state.notEscapes) - if (state.field.isFinal) - Result(state.field, ImmutableReference(state.notEscapes)) - else - state.referenceImmutability match { - case ImmutableReference(_) ⇒ Result(state.field, ImmutableReference(state.notEscapes)) - case _ ⇒ Result(state.field, state.referenceImmutability) - } + } + + /** + * Prepares the PropertyComputation result, either as IntermediateResult if there are still + * dependees or as Result otherwise. + */ + def createResult()(implicit state: State): ProperPropertyComputationResult = { + println("create results: ") + println("current ref imm: " + state.referenceImmutability) + if (state.hasDependees && (state.referenceImmutability ne MutableReference)) { //NonFinalFieldByAnalysis)) + println("has still dependendees") + println("state dependees: " + state.dependees) + InterimResult( + state.field, + MutableReference, //NonFinalFieldByAnalysis, + state.referenceImmutability, + state.dependees, + c + ) + } else { + println("end") + println("state.reference immutability: " + state.referenceImmutability) + println("state: " + state.notEscapes) + if (state.field.isFinal) + Result(state.field, ImmutableReference(state.notEscapes)) + else + state.referenceImmutability match { + case ImmutableReference(_) => Result(state.field, ImmutableReference(state.notEscapes)) + case _ => 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 = { - println("continuation") - var isNotFinal = false - eps.pk match { - case EscapeProperty.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - val escapes = handleEscapeProperty(newEP) - if (escapes) - state.notEscapes = false - isNotFinal = escapes - 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 ⇒ - isNotFinal = handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key ⇒ - isNotFinal = isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] - state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) - val r = isNonDeterministic(newEP) - if (!r) state.referenceImmutability = LazyInitializedReference - println("continuation purity result: "+r) - isNotFinal = r - println("cont. is not final: "+isNotFinal) - case ReferenceImmutability.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] - state.referenceImmutabilityDependees = - state.referenceImmutabilityDependees.filter(_.e ne newEP.e) - isNotFinal = !isImmutableReference(newEP) - case TypeImmutability_new.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] - state.typeDependees = state.typeDependees.filter(_.e ne newEP.e) - newEP match { - case FinalP(DependentImmutableType) ⇒ state.notEscapes = false - case FinalP(_) ⇒ - case ep @ _ ⇒ state.typeDependees += ep - } + } + + /** + * 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 = { + println("continuation") + var isNotFinal = false + eps.pk match { + case EscapeProperty.key => + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + val escapes = handleEscapeProperty(newEP) + if (escapes) + state.notEscapes = false + isNotFinal = escapes + 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 => + isNotFinal = handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key => + isNotFinal = isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key => + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + val r = isNonDeterministic(newEP) + //if (!r) state.referenceImmutability = LazyInitializedReference + println("continuation purity result: " + r) + isNotFinal = r + println("cont. is not final: " + isNotFinal) + case ReferenceImmutability.key => + val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + isNotFinal = !isImmutableReference(newEP) + case TypeImmutability_new.key => + val newEP = eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] + state.typeDependees = state.typeDependees.filter(_.e ne newEP.e) + newEP match { + case FinalP(DependentImmutableType) => state.notEscapes = false + case FinalP(_) => + case ep @ _ => state.typeDependees += ep } - - if (isNotFinal && !state.field.isFinal) { - Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) - } else - 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 = { - println("method updates field?") - val field = state.field - val stmts = taCode.stmts - for (pc ← pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID ⇒ - if (method.isInitializer) { - if (field.isStatic) { - if (method.isConstructor) - return true; - } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - 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.referenceImmutability == LazyInitializedReference) //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; - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - //TODO Lazy Initialization here - /** - * val liResult = propertyStore(field, ReferenceImmutabilityLazyInitialization.key) - * - * liResult match { - * case FinalP(NoLazyInitialization) => return true; - * case FinalP(NotThreadSafeLazyInitialization) => return true; - * case FinalP(LazyInitialization) => - * state.referenceImmutability = LazyInitializedReference - * case _ => return true; - * } - */ - // A field written outside an initializer must be lazily - // initialized or it is non-final - - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - )) { - println("is not lazy initialized") - println("method: "+method) - return true - }; - println("is lazy initialized") - state.referenceImmutability = LazyInitializedReference - //LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - - // 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. - return true; - } - - } - case _ ⇒ throw new RuntimeException("unexpected field access"); - } + if (isNotFinal && !state.field.isFinal) { + Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) + } else + 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 = { + println("method updates field?") + val field = state.field + val stmts = taCode.stmts + for (pc <- pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID => + if (method.isInitializer) { + if (field.isStatic) { + if (method.isConstructor) + return true; } else { - // nothing to do as the put field is dead + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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.referenceImmutability == LazyInitializedReference) //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; + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + //TODO Lazy Initialization here + /** + * val liResult = propertyStore(field, ReferenceImmutabilityLazyInitialization.key) + * + * liResult match { + * case FinalP(NoLazyInitialization) => return true; + * case FinalP(NotThreadSafeLazyInitialization) => return true; + * case FinalP(LazyInitialization) => + * state.referenceImmutability = LazyInitializedReference + * case _ => return true; + * } + */ + // A field written outside an initializer must be lazily + // initialized or it is non-final + + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )) { + println("is not lazy initialized") + println("method: " + method) + return true + }; + println("is lazy initialized") + state.referenceImmutability = LazyInitializedReference + //LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + + // 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. + return true; + } + + } + case _ => throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead } - false + } } - - /** - * 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 eps: InterimEP[Method, TACAI] ⇒ - * state.tacDependees += method → ((eps, pcs)) - * eps.ub.tac - * case epk ⇒ - * state.tacDependees += method → ((epk, pcs)) - * None - * } - * } - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - /** - * 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 = { - println("ref: "+ref) - println("defined by: "+ref.definedBy) - - ref.definedBy.forall { defSite ⇒ - println("0") - println("field: "+state.field) - println("defsite: "+defSite) - if (defSite < 0) true // Must be locally created - else { - println("1") - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - println("escape property: "+escape) - handleEscapeProperty(escape) - } - } + false + } + + /** + * 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 eps: InterimEP[Method, TACAI] ⇒ + * state.tacDependees += method → ((eps, pcs)) + * eps.ub.tac + * case epk ⇒ + * state.tacDependees += method → ((epk, pcs)) + * None + * } + * } + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + /** + * 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 = { + println("ref: " + ref) + println("defined by: " + ref.definedBy) + + ref.definedBy.forall { defSite => + println("0") + println("field: " + state.field) + println("defsite: " + defSite) + if (defSite < 0) true // Must be locally created + else { + println("1") + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + println("escape property: " + escape) + handleEscapeProperty(escape) } + } } + } + + /** + * 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 = { + ep match { + case FinalP(NoEscape | EscapeInCallee | EscapeViaReturn) => + false - /** - * 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 = { - 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 - } + 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 } + } } trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.ub(EscapeProperty), - PropertyBounds.lub(TypeImmutability_new) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.ub(EscapeProperty), + PropertyBounds.lub(TypeImmutability_new) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) } @@ -755,16 +755,16 @@ object EagerL0ReferenceImmutabilityAnalysis extends L0ReferenceImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allProjectClassFiles.flatMap(_.fields) - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + val fields = p.allProjectClassFiles.flatMap(_.fields) + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -774,18 +774,18 @@ object LazyL0ReferenceImmutabilityAnalysis extends L0ReferenceImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - ReferenceImmutability.key, - analysis.determineReferenceImmutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + ReferenceImmutability.key, + analysis.determineReferenceImmutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } From ed032f5bca3a5721d044b0db97128454e88293ed Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 24 Jun 2020 09:46:34 +0200 Subject: [PATCH 151/327] inserting printlns for debugging purpose --- .../tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala index e7acb23269..c7384a7f4c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala @@ -736,7 +736,9 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * - classes files for class types returned (for their mutability) */ def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { + println("enter continuation") val oldPurity = state.ubPurity + println("eps: "+eps) eps.ub.key match { case Purity.key ⇒ val e = eps.e.asInstanceOf[DeclaredMethod] @@ -810,6 +812,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) val dependees = state.dependees if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { + println("dependees empty lb=ub; result : "+state.ubPurity) Result(state.definedMethod, state.ubPurity) } else { InterimResult( @@ -913,7 +916,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult = { val method = definedMethod.definedMethod val declClass = method.classFile.thisType - + println("determine purity of method: "+method+", with class: "+declClass) // If this is not the method's declaration, but a non-overwritten method in a subtype, // don't re-analyze the code if (declClass ne definedMethod.declaringClassType) From de498a997775c1ddf64f075867eb817592d677ad Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 24 Jun 2020 10:08:56 +0200 Subject: [PATCH 152/327] inserting linebreak; trying to solve formatting issue --- .../opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala index c7384a7f4c..0f3abc6836 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala @@ -917,6 +917,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) val method = definedMethod.definedMethod val declClass = method.classFile.thisType println("determine purity of method: "+method+", with class: "+declClass) + // If this is not the method's declaration, but a non-overwritten method in a subtype, // don't re-analyze the code if (declClass ne definedMethod.declaringClassType) From 11fb3a0b3a4cd040bd84f5e0ee28f20a1fc17a52 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 24 Jun 2020 11:09:41 +0200 Subject: [PATCH 153/327] optimized running analyses --- .../fpcf/ReferenceImmutabilityTests_withNewPurity.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala index 661786cbf1..40eb9e07c9 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala @@ -11,7 +11,6 @@ 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.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis @@ -52,15 +51,12 @@ class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { LazyLxClassImmutabilityAnalysis_new, LazyLxTypeImmutabilityAnalysis_new, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyInterProceduralEscapeAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis, - LazyL2FieldMutabilityAnalysis, LazyL2PurityAnalysis_new, - LazyInterProceduralEscapeAnalysis, - LazyLxTypeImmutabilityAnalysis_new + LazyInterProceduralEscapeAnalysis ) ) as.propertyStore.shutdown() From 35e75f0815e8c8ce6cf3a1843b69cc6ce58d2738 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 24 Jun 2020 11:11:56 +0200 Subject: [PATCH 154/327] inserted printlns for debugging purpose --- ...mutabilityAnalysisLazyInitialization.scala | 2 + .../L0ReferenceImmutabilityAnalysis.scala | 1207 +++++++++-------- 2 files changed, 606 insertions(+), 603 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index 6f71e6b3dd..0ea63fea92 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -478,6 +478,8 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization // More than one definition site for the value might lead to differences between // invocations, but not if this method has no parameters and is deterministic // (in this case, the definition reaching the write will always be the same) + val result = propertyStore(declaredMethods(method), Purity.key) + println("purity result: "+result) method.descriptor.parametersCount == 0 && !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index a1d1c78820..0f1f7f7651 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -73,333 +73,334 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec with AbstractReferenceImmutabilityAnalysis with FPCFAnalysis { - def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field => determineReferenceImmutability(field) - case _ => - val m = entity.getClass.getSimpleName + " is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } - - /** - * Analyzes the mutability of private non-final fields. - * - * 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 determineReferenceImmutability( - field: Field - ): ProperPropertyComputationResult = { - println( - "start determine ref imm analysis=====================================================================================================" - ) - println("field: " + field.name) - implicit val state: State = State(field) - // Fields are not final if they are read prematurely! - if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { - println("field is prematurely read") - return Result(field, MutableReference) - }; //Result(field, NonFinalFieldByAnalysis); - println("01") - state.referenceImmutability = ImmutableReference(true) //EffectivelyFinalField - - val thisType = field.classFile.thisType - - if ((field.isPublic || field.isPackagePrivate || field.isProtected)) { - println("field: " + field + " is public, pack priv or protected") - if (!field.isFinal) - return Result(field, MutableReference) - else - state.notEscapes = false - }; //Result(field, NonFinalFieldByLackOfInformation) - println("02") - // 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) { - if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - } - project.classesPerPackage(thisType.packageName) - } else { - Set(field.classFile) - } - println("03") - val classesHavingAccess: Iterator[ClassFile] = - if (field.isProtected) { - if (typeExtensibility(thisType).isYesOrUnknown && !state.field.isFinal) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - } - 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 - } - println("04") - // If there are native methods, we give up - if (classesHavingAccess.exists(_.methods.exists(_.isNative)) && !state.field.isFinal) { - println("native") - return Result(field, MutableReference) - }; //return Result(field, NonFinalFieldByLackOfInformation); - println("05") - - println("1-----------------------------------------------------------------------") - for { - (method, pcs) <- fieldAccessInformation.readAccesses(field) - taCode <- getTACAI(method, pcs) - } { - pcs.foreach(pc => { - val index = taCode.pcToIndex(pc) - if (index > 0) { - taCode.stmts(index) match { - case Assignment(pc3, targetVar, GetField(pc4, cl, name, _, value)) => - if (name == field.name) { - state.notEscapes = false - } - case _ => - } - } else state.notEscapes = false - }) + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { + case field: Field ⇒ determineReferenceImmutability(field) + case _ ⇒ + val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) } - println("0------------------") - for { - (method, pcs) <- fieldAccessInformation.writeAccesses(field) - taCode <- getTACAI(method, pcs) - } { - println("2-----------------------------------------------------------------------") - println("method: " + method) - for (pc <- pcs) { - println("pc: " + pc) - val index = taCode.pcToIndex(pc) - if (index > 0) { - val stmt = taCode.stmts(index) - println("stmt: " + stmt) - stmt match { - case PutField(_, _, _, _, _, value) => - value match { - case v @ UVar(defSites, value2) => // SObjectValue(t,_,_,_)) => - //if (!v.defSites.filter(_ < 1).isEmpty) - //escape var - { - v.defSites.foreach( - i => { - println("def site: " + i) - if (i > 0) { - val stmt2 = taCode.stmts(i) - println("stmt2: " + stmt2) - stmt2 match { - case Assignment(pcAssignment, dv @ DVar(useSites, value), expr) => - //useSites - dv.useSites.foreach( - x => { - val innerstmt = taCode.stmts(x) - innerstmt match { - case NonVirtualMethodCall( - pc, - bdeclaringClass, - isInterface, - name, - descriptor, - receiver, - params - ) => { - params.foreach(expr => { - expr match { - case vrs @ UVar(defSites, value) => { //: SObjectValue(tpe,isNull,isPrecise) - //val isStaticIndex = if (method.isStatic) 0 else -1 - vrs.defSites.foreach( - dfste => { - if (dfste < 0) //(0 + isStaticIndex)) - state.notEscapes = false - else { - val stmtDefSite = taCode.stmts(dfste) - println("stmt def site: " + stmtDefSite) - stmtDefSite match { - case Assignment( - pcA, - targetVar, - GetField( - pc, - declaringClass, - name, - declaredFieldType, - objRef - ) - ) => { - val fs = project.allFields.filter( - { f => - f.name.equals(name) && f.fieldType.equals( - declaredFieldType - ) && f.classFile.equals( - state.field.classFile - ) - } - ) - fs.foreach(f => { - val result = - propertyStore(f, FieldImmutability.key) - result match { - case FinalP(DeepImmutableField) => - case _ => state.notEscapes = false - } - }) + + /** + * Analyzes the mutability of private non-final fields. + * + * 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 determineReferenceImmutability( + field: Field + ): ProperPropertyComputationResult = { + println( + "start determine ref imm analysis=====================================================================================================" + ) + println("field: "+field.name) + implicit val state: State = State(field) + // Fields are not final if they are read prematurely! + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { + println("field is prematurely read") + return Result(field, MutableReference) + }; //Result(field, NonFinalFieldByAnalysis); + println("01") + state.referenceImmutability = ImmutableReference(true) //EffectivelyFinalField + + val thisType = field.classFile.thisType + + if ((field.isPublic || field.isPackagePrivate || field.isProtected)) { + println("field: "+field+" is public, pack priv or protected") + if (!field.isFinal) + return Result(field, MutableReference) + else + state.notEscapes = false + }; //Result(field, NonFinalFieldByLackOfInformation) + println("02") + // 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) { + if (!closedPackages.isClosed(thisType.packageName)) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) + } + println("03") + val classesHavingAccess: Iterator[ClassFile] = + if (field.isProtected) { + if (typeExtensibility(thisType).isYesOrUnknown && !state.field.isFinal) { + return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + } + 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 + } + println("04") + // If there are native methods, we give up + if (classesHavingAccess.exists(_.methods.exists(_.isNative)) && !state.field.isFinal) { + println("native") + return Result(field, MutableReference) + }; //return Result(field, NonFinalFieldByLackOfInformation); + println("05") + + println("1-----------------------------------------------------------------------") + for { + (method, pcs) ← fieldAccessInformation.readAccesses(field) + taCode ← getTACAI(method, pcs) + } { + pcs.foreach(pc ⇒ { + val index = taCode.pcToIndex(pc) + if (index > 0) { + taCode.stmts(index) match { + case Assignment(pc3, targetVar, GetField(pc4, cl, name, _, value)) ⇒ + if (name == field.name) { + state.notEscapes = false + } + case _ ⇒ + } + } else state.notEscapes = false + }) + } + println("0------------------") + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode ← getTACAI(method, pcs) + } { + println("2-----------------------------------------------------------------------") + println("method: "+method) + for (pc ← pcs) { + println("pc: "+pc) + val index = taCode.pcToIndex(pc) + if (index > 0) { + val stmt = taCode.stmts(index) + println("stmt: "+stmt) + stmt match { + case PutField(_, _, _, _, _, value) ⇒ + value match { + case v @ UVar(defSites, value2) ⇒ // SObjectValue(t,_,_,_)) => + //if (!v.defSites.filter(_ < 1).isEmpty) + //escape var + { + v.defSites.foreach( + i ⇒ { + println("def site: "+i) + if (i > 0) { + val stmt2 = taCode.stmts(i) + println("stmt2: "+stmt2) + stmt2 match { + case Assignment(pcAssignment, dv @ DVar(useSites, value), expr) ⇒ + //useSites + dv.useSites.foreach( + x ⇒ { + val innerstmt = taCode.stmts(x) + innerstmt match { + case NonVirtualMethodCall( + pc, + bdeclaringClass, + isInterface, + name, + descriptor, + receiver, + params + ) ⇒ { + params.foreach(expr ⇒ { + expr match { + case vrs @ UVar(defSites, value) ⇒ { //: SObjectValue(tpe,isNull,isPrecise) + //val isStaticIndex = if (method.isStatic) 0 else -1 + vrs.defSites.foreach( + dfste ⇒ { + if (dfste < 0) //(0 + isStaticIndex)) + state.notEscapes = false + else { + val stmtDefSite = taCode.stmts(dfste) + println("stmt def site: "+stmtDefSite) + stmtDefSite match { + case Assignment( + pcA, + targetVar, + GetField( + pc, + declaringClass, + name, + declaredFieldType, + objRef + ) + ) ⇒ { + val fs = project.allFields.filter( + { f ⇒ + f.name.equals(name) && f.fieldType.equals( + declaredFieldType + ) && f.classFile.equals( + state.field.classFile + ) + } + ) + fs.foreach(f ⇒ { + val result = + propertyStore(f, FieldImmutability.key) + result match { + case FinalP(DeepImmutableField) ⇒ + case _ ⇒ state.notEscapes = false + } + }) + } + case _ ⇒ + } + } + + } + ) + } + } + }) + } + case _ ⇒ + } + /** + * case NonVirtualMethodCall(pc,org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses.Generic_class1, + * isInterface=false, + * void (java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object), + * UVar(defSites={1},value=SObjectValue(type=org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses.Generic_class1,isNull=No,isPrecise=true)),(UVar(defSites={-5},value=SObjectValue(type=org.opalj.fpcf.fixt + * ures.immutability.classes.multinested_genericClasses.Generic_class1,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-4},value=SObjectValue(type=org.opalj.fpcf.fixtures.immutability.classes.mu + * ltinested_genericClasses.TrivialMutableClass,isNull=Unknown,isPrecise=false)))) => * + */ + + } + ) + if (expr.isNew || expr.isConst) { + if (expr.isNew) { + + /** + * val NEW = expr.asNew + * + * val oType = NEW.tpe + * println("otype: "+oType) + * + * if (oType == ObjectType.Object) + * state.notEscapes = false + * else { + * val result = propertyStore(oType, TypeImmutability_new.key) + * result match { + * case FinalP(DependentImmutableType) ⇒ { + * println("depenent type") + * state.notEscapes = false + * } + * case fp @ FinalP(_) ⇒ println("etc. final: "+fp) + * case ep @ _ ⇒ { + * println("type imm not yet computed") + * state.typeDependees += ep + * } + * } + * }* + */ + + } + /** + * propertyStore( + * definitionSites(method, pc), + * EscapeProperty.key + * ) match { + * case FinalP(NoEscape) ⇒ // nothing to do + * case FinalP(AtMost(EscapeViaParameter)) ⇒ // introduced via this + * case _ ⇒ + * state.notEscapes = false + * } + * } else { + * state.notEscapes = false* + */ //TODO + } + case _ ⇒ state.notEscapes = false } - case _ => - } + } else { + //constructor ?? + state.notEscapes = false } - - } - ) - } - } - }) + } + ) } - case _ => - } - /** - * case NonVirtualMethodCall(pc,org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses.Generic_class1, - * isInterface=false, - * void (java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object), - * UVar(defSites={1},value=SObjectValue(type=org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses.Generic_class1,isNull=No,isPrecise=true)),(UVar(defSites={-5},value=SObjectValue(type=org.opalj.fpcf.fixt - * ures.immutability.classes.multinested_genericClasses.Generic_class1,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-4},value=SObjectValue(type=org.opalj.fpcf.fixtures.immutability.classes.mu - * ltinested_genericClasses.TrivialMutableClass,isNull=Unknown,isPrecise=false)))) => * - */ + case _ ⇒ state.notEscapes = false + } + case _ ⇒ state.notEscapes = false + } + } else state.notEscapes = false + } - } - ) - if (expr.isNew || expr.isConst) { - if (expr.isNew) { - - /** - * val NEW = expr.asNew - * - * val oType = NEW.tpe - * println("otype: "+oType) - * - * if (oType == ObjectType.Object) - * state.notEscapes = false - * else { - * val result = propertyStore(oType, TypeImmutability_new.key) - * result match { - * case FinalP(DependentImmutableType) ⇒ { - * println("depenent type") - * state.notEscapes = false - * } - * case fp @ FinalP(_) ⇒ println("etc. final: "+fp) - * case ep @ _ ⇒ { - * println("type imm not yet computed") - * state.typeDependees += ep - * } - * } - * }* - */ + if (methodUpdatesField(method, taCode, pcs) && !state.field.isFinal) { + println("method does updates field") + return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnfalysis); + } else println("method does not updates fields") + println("st.ref imm 1: "+state.referenceImmutability+", reference: "+state.field) + } + println("st.ref imm 2: "+state.referenceImmutability+", reference: "+state.field) - } - /** - * propertyStore( - * definitionSites(method, pc), - * EscapeProperty.key - * ) match { - * case FinalP(NoEscape) ⇒ // nothing to do - * case FinalP(AtMost(EscapeViaParameter)) ⇒ // introduced via this - * case _ ⇒ - * state.notEscapes = false - * } - * } else { - * state.notEscapes = false* - */ //TODO - } - case _ => state.notEscapes = false - } - } else { - //constructor ?? - state.notEscapes = false - } - } - ) - } - case _ => state.notEscapes = false - } - case _ => state.notEscapes = false - } - } else state.notEscapes = false - } - - if (methodUpdatesField(method, taCode, pcs) && !state.field.isFinal) { - println("method does updates field") - return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnfalysis); - } else println("method does not updates fields") - println("st.ref imm 1: " + state.referenceImmutability + ", reference: " + state.field) + if (state.lazyInitInvocation.isDefined) { + //val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + //TODO //handleCalls(calleesEOP) + } + println("st.ref imm 3: "+state.referenceImmutability+", reference: "+state.field) + println("finish; go to create results") + createResult() } - println("st.ref imm 2: " + state.referenceImmutability + ", reference: " + state.field) - if (state.lazyInitInvocation.isDefined) { - //val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - //TODO //handleCalls(calleesEOP) - } - println("st.ref imm 3: " + state.referenceImmutability + ", reference: " + state.field) - println("finish; go to create results") - createResult() - } - - def handleCalls( - calleesEOP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: State - ): Boolean = { - println("cEOP: " + calleesEOP) - calleesEOP match { - case FinalP(callees) => - state.calleesDependee = None - handleCallees(callees) - case InterimUBP(callees) => - state.calleesDependee = Some(calleesEOP) - handleCallees(callees) - case r @ _ => - state.calleesDependee = Some(calleesEOP) - println("false: ; + result " + r) - false + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees] + )( + implicit + state: State + ): Boolean = { + println("cEOP: "+calleesEOP) + calleesEOP match { + case FinalP(callees) ⇒ + state.calleesDependee = None + handleCallees(callees) + case InterimUBP(callees) ⇒ + state.calleesDependee = Some(calleesEOP) + handleCallees(callees) + case r @ _ ⇒ + state.calleesDependee = Some(calleesEOP) + println("false: ; + result "+r) + false + } } - } - - def handleCallees(callees: Callees)(implicit state: State): Boolean = { - val pc = state.lazyInitInvocation.get._2 - if (callees.isIncompleteCallSite(pc)) { - println("is incomplete call site") - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else { - val targets = callees.callees(pc).toTraversable - if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { - val nonDeterministicTargets = - targets.filter(target => isNonDeterministic(propertyStore(target, Purity.key))) - println( - "target non deterministic: " + nonDeterministicTargets - .mkString(", ") - ) - nonDeterministicTargets.foreach(t => println(propertyStore(t, Purity.key))) - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else false + + def handleCallees(callees: Callees)(implicit state: State): Boolean = { + val pc = state.lazyInitInvocation.get._2 + if (callees.isIncompleteCallSite(pc)) { + println("is incomplete call site") + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else { + val targets = callees.callees(pc).toTraversable + targets.foreach(t ⇒ println("targets: "+propertyStore(t, Purity.key))) + if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { + val nonDeterministicTargets = + targets.filter(target ⇒ isNonDeterministic(propertyStore(target, Purity.key))) + println( + "target non deterministic: "+nonDeterministicTargets + .mkString(", ") + ) + nonDeterministicTargets.foreach(t ⇒ println(propertyStore(t, Purity.key))) + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + true + } else false + } } - } - /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. - */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + /** + * Returns the value the field will have after initialization or None if there may be multiple + * values. + */ + def getDefaultValue()(implicit state: State): Option[Any] = { + Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field + /* TODO Some lazy initialized fields use a different value to mark an uninitialized field * The code below can be used to identify such value, but is not yet adapted to using the * TACAI property */ - /* + /* var constantVal: Option[Any] = None var allInitializeConstant = true @@ -457,294 +458,294 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } constantVal */ - } - - /** - * Prepares the PropertyComputation result, either as IntermediateResult if there are still - * dependees or as Result otherwise. - */ - def createResult()(implicit state: State): ProperPropertyComputationResult = { - println("create results: ") - println("current ref imm: " + state.referenceImmutability) - if (state.hasDependees && (state.referenceImmutability ne MutableReference)) { //NonFinalFieldByAnalysis)) - println("has still dependendees") - println("state dependees: " + state.dependees) - InterimResult( - state.field, - MutableReference, //NonFinalFieldByAnalysis, - state.referenceImmutability, - state.dependees, - c - ) - } else { - println("end") - println("state.reference immutability: " + state.referenceImmutability) - println("state: " + state.notEscapes) - if (state.field.isFinal) - Result(state.field, ImmutableReference(state.notEscapes)) - else - state.referenceImmutability match { - case ImmutableReference(_) => Result(state.field, ImmutableReference(state.notEscapes)) - case _ => 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 = { - println("continuation") - var isNotFinal = false - eps.pk match { - case EscapeProperty.key => - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - val escapes = handleEscapeProperty(newEP) - if (escapes) - state.notEscapes = false - isNotFinal = escapes - 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 => - isNotFinal = handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key => - isNotFinal = isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key => - val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] - state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) - val r = isNonDeterministic(newEP) - //if (!r) state.referenceImmutability = LazyInitializedReference - println("continuation purity result: " + r) - isNotFinal = r - println("cont. is not final: " + isNotFinal) - case ReferenceImmutability.key => - val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] - state.referenceImmutabilityDependees = - state.referenceImmutabilityDependees.filter(_.e ne newEP.e) - isNotFinal = !isImmutableReference(newEP) - case TypeImmutability_new.key => - val newEP = eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] - state.typeDependees = state.typeDependees.filter(_.e ne newEP.e) - newEP match { - case FinalP(DependentImmutableType) => state.notEscapes = false - case FinalP(_) => - case ep @ _ => state.typeDependees += ep - } } - if (isNotFinal && !state.field.isFinal) { - Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) - } else - 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 = { - println("method updates field?") - val field = state.field - val stmts = taCode.stmts - for (pc <- pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID => - if (method.isInitializer) { - if (field.isStatic) { - if (method.isConstructor) - return true; - } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - 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.referenceImmutability == LazyInitializedReference) //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; - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - //TODO Lazy Initialization here - /** - * val liResult = propertyStore(field, ReferenceImmutabilityLazyInitialization.key) - * - * liResult match { - * case FinalP(NoLazyInitialization) => return true; - * case FinalP(NotThreadSafeLazyInitialization) => return true; - * case FinalP(LazyInitialization) => - * state.referenceImmutability = LazyInitializedReference - * case _ => return true; - * } - */ - // A field written outside an initializer must be lazily - // initialized or it is non-final - - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - )) { - println("is not lazy initialized") - println("method: " + method) - return true - }; - println("is lazy initialized") - state.referenceImmutability = LazyInitializedReference - //LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - - // 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. - return true; - } - - } - case _ => throw new RuntimeException("unexpected field access"); - } + /** + * Prepares the PropertyComputation result, either as IntermediateResult if there are still + * dependees or as Result otherwise. + */ + def createResult()(implicit state: State): ProperPropertyComputationResult = { + println("create results: ") + println("current ref imm: "+state.referenceImmutability) + if (state.hasDependees && (state.referenceImmutability ne MutableReference)) { //NonFinalFieldByAnalysis)) + println("has still dependendees") + println("create result; interim result; state dependees: "+state.dependees) + InterimResult( + state.field, + MutableReference, //NonFinalFieldByAnalysis, + state.referenceImmutability, + state.dependees, + c + ) } else { - // nothing to do as the put field is dead + println("end") + println("state.reference immutability: "+state.referenceImmutability) + println("state: "+state.notEscapes) + if (state.field.isFinal) + Result(state.field, ImmutableReference(state.notEscapes)) + else + state.referenceImmutability match { + case ImmutableReference(_) ⇒ Result(state.field, ImmutableReference(state.notEscapes)) + case _ ⇒ Result(state.field, state.referenceImmutability) + } } - } } - false - } - - /** - * 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 eps: InterimEP[Method, TACAI] ⇒ - * state.tacDependees += method → ((eps, pcs)) - * eps.ub.tac - * case epk ⇒ - * state.tacDependees += method → ((epk, pcs)) - * None - * } - * } - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - /** - * 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 = { - println("ref: " + ref) - println("defined by: " + ref.definedBy) - - ref.definedBy.forall { defSite => - println("0") - println("field: " + state.field) - println("defsite: " + defSite) - if (defSite < 0) true // Must be locally created - else { - println("1") - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - println("escape property: " + escape) - handleEscapeProperty(escape) + + /** + * 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 = { + println("continuation") + var isNotFinal = false + eps.pk match { + case EscapeProperty.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] + state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + val escapes = handleEscapeProperty(newEP) + if (escapes) + state.notEscapes = false + isNotFinal = escapes + 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 ⇒ + isNotFinal = handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + case FieldPrematurelyRead.key ⇒ + isNotFinal = isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) + case Purity.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + val r = isNonDeterministic(newEP) + //if (!r) state.referenceImmutability = LazyInitializedReference + println("continuation purity result: "+r) + isNotFinal = r + println("cont. is not final: "+isNotFinal) + case ReferenceImmutability.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + state.referenceImmutabilityDependees = + state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + isNotFinal = !isImmutableReference(newEP) + case TypeImmutability_new.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] + state.typeDependees = state.typeDependees.filter(_.e ne newEP.e) + newEP match { + case FinalP(DependentImmutableType) ⇒ state.notEscapes = false + case FinalP(_) ⇒ + case ep @ _ ⇒ state.typeDependees += ep + } } - } - } - } - - /** - * 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 = { - ep match { - case FinalP(NoEscape | EscapeInCallee | EscapeViaReturn) => - false - case FinalP(AtMost(_)) => - true + if (isNotFinal && !state.field.isFinal) { + Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) + } else + createResult() + } - case _: FinalEP[DefinitionSite, EscapeProperty] => - true // Escape state is worse than via return + /** + * 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 = { + println("method updates field?") + val field = state.field + val stmts = taCode.stmts + for (pc ← pcs) { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = stmts(index) + + if (stmt.pc == pc) { + stmt.astID match { + case PutStatic.ASTID | PutField.ASTID ⇒ + if (method.isInitializer) { + if (field.isStatic) { + if (method.isConstructor) + return true; + } else { + val receiverDefs = stmt.asPutField.objRef.asVar.definedBy + 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.referenceImmutability == LazyInitializedReference) //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; + val defaultValue = getDefaultValue() + if (defaultValue.isEmpty) + return true; + + //TODO Lazy Initialization here + /** + * val liResult = propertyStore(field, ReferenceImmutabilityLazyInitialization.key) + * + * liResult match { + * case FinalP(NoLazyInitialization) => return true; + * case FinalP(NotThreadSafeLazyInitialization) => return true; + * case FinalP(LazyInitialization) => + * state.referenceImmutability = LazyInitializedReference + * case _ => return true; + * } + */ + // A field written outside an initializer must be lazily + // initialized or it is non-final + + if (!isLazyInitialization( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex + )) { + println("is not lazy initialized") + println("method: "+method) + return true + }; + println("is lazy initialized") + state.referenceImmutability = LazyInitializedReference + //LazyInitializedField + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { + + // 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. + return true; + } - case InterimUBP(NoEscape | EscapeInCallee | EscapeViaReturn) => - state.escapeDependees += ep + } + case _ ⇒ throw new RuntimeException("unexpected field access"); + } + } else { + // nothing to do as the put field is dead + } + } + } false + } - case InterimUBP(AtMost(_)) => - true - - case _: InterimEP[DefinitionSite, EscapeProperty] => - true // Escape state is worse than via return + /** + * 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 eps: InterimEP[Method, TACAI] ⇒ + * state.tacDependees += method → ((eps, pcs)) + * eps.ub.tac + * case epk ⇒ + * state.tacDependees += method → ((epk, pcs)) + * None + * } + * } + * Returns the TACode for a method if available, registering dependencies as necessary. + */ + /** + * 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 = { + println("ref: "+ref) + println("defined by: "+ref.definedBy) + + ref.definedBy.forall { defSite ⇒ + println("0") + println("field: "+state.field) + println("defsite: "+defSite) + if (defSite < 0) true // Must be locally created + else { + println("1") + val definition = stmts(defSite).asAssignment + // Must either be null or freshly allocated + if (definition.expr.isNullExpr) false + else if (!definition.expr.isNew) true + else { + val escape = + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + println("escape property: "+escape) + handleEscapeProperty(escape) + } + } + } + } - case _ => - state.escapeDependees += ep - false + /** + * 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 = { + 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 + } } - } } trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.ub(EscapeProperty), - PropertyBounds.lub(TypeImmutability_new) - ) + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.lub(Purity), + PropertyBounds.lub(FieldPrematurelyRead), + PropertyBounds.ub(TACAI), + PropertyBounds.lub(ReferenceImmutability), + PropertyBounds.ub(EscapeProperty), + PropertyBounds.lub(TypeImmutability_new) + ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) } @@ -755,16 +756,16 @@ object EagerL0ReferenceImmutabilityAnalysis extends L0ReferenceImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allProjectClassFiles.flatMap(_.fields) - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) - analysis - } + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + val fields = p.allProjectClassFiles.flatMap(_.fields) + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) + analysis + } - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** @@ -774,18 +775,18 @@ object LazyL0ReferenceImmutabilityAnalysis extends L0ReferenceImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityAnalysis(p) - ps.registerLazyPropertyComputation( - ReferenceImmutability.key, - analysis.determineReferenceImmutability - ) - analysis - } + final override def register( + p: SomeProject, + ps: PropertyStore, + unused: Null + ): FPCFAnalysis = { + val analysis = new L0ReferenceImmutabilityAnalysis(p) + ps.registerLazyPropertyComputation( + ReferenceImmutability.key, + analysis.determineReferenceImmutability + ) + analysis + } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } From 2a9a21c4f1e090a97429daf3b28dbc8bfc398d38 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 25 Jun 2020 16:16:01 +0200 Subject: [PATCH 155/327] first attempt for double checked locking. Works with the provided tests. Should with more tests be checked for soundness. And be cleaned up. And the analysis could be enhanced by considerung other dcl cases than the mentioned one. --- .../DoubleCheckedLockingClass1.java | 9 +- .../DoubleCheckedLockingClass2.java | 23 ++ .../DoubleCheckedLockingClass3.java | 20 ++ .../DoubleCheckedLockingClass4.java | 19 ++ .../DoubleCheckedLockingClass5.java | 20 ++ .../DoubleCheckedLockingClass6.java | 24 ++ .../DoubleCheckedLockingClass7.java | 24 ++ .../DoubleCheckedLockingClass8.java | 23 ++ .../SimpleLazyInstantiation.java | 4 +- .../SimpleLazyIntInstantiation.java | 4 +- .../SimpleLazyObjectsInstantiation.java | 4 +- ...mutabilityAnalysisLazyInitialization.scala | 258 ++++++++++++++++-- .../L0ReferenceImmutabilityAnalysis.scala | 25 +- 13 files changed, 413 insertions(+), 44 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass3.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass4.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass5.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass7.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass8.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java index 0276ce053d..2a952f58ea 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java @@ -1,15 +1,16 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.LazyInitializationAnnotation; public class DoubleCheckedLockingClass1{ - @LazyInitializationAnnotation("") - private static DoubleCheckedLockingClass1 instance; - public static DoubleCheckedLockingClass1 getInstance() { + @LazyInitializedReferenceAnnotation("") + private DoubleCheckedLockingClass1 instance; + public DoubleCheckedLockingClass1 getInstance() { if(instance==null){ - synchronized(DoubleCheckedLockingClass1.class) { + synchronized(instance) { if(instance==null){ instance = new DoubleCheckedLockingClass1(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java new file mode 100644 index 0000000000..639f1a0586 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java @@ -0,0 +1,23 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class DoubleCheckedLockingClass2 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass2 instance; + private DoubleCheckedLockingClass2 instance2 = new DoubleCheckedLockingClass2(); + public DoubleCheckedLockingClass2 getInstance() { + if(instance==null){ + synchronized(instance2) { + if(instance==null){ + instance = new DoubleCheckedLockingClass2(); + } + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass3.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass3.java new file mode 100644 index 0000000000..f1bcea58b8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass3.java @@ -0,0 +1,20 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class DoubleCheckedLockingClass3 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass3 instance; + public DoubleCheckedLockingClass3 getInstance() { + if(instance==null){ + synchronized(DoubleCheckedLockingClass3.class) { + instance = new DoubleCheckedLockingClass3(); + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass4.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass4.java new file mode 100644 index 0000000000..9f573435c9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass4.java @@ -0,0 +1,19 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class DoubleCheckedLockingClass4 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass4 instance; + public DoubleCheckedLockingClass4 getInstance() { + synchronized(DoubleCheckedLockingClass4.class) { + if(instance==null){ + instance = new DoubleCheckedLockingClass4(); + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass5.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass5.java new file mode 100644 index 0000000000..5fbe1a3bb1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass5.java @@ -0,0 +1,20 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class DoubleCheckedLockingClass5 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass5 instance; + public DoubleCheckedLockingClass5 getInstance() { + if(instance==null){ + if(instance==null){ + instance = new DoubleCheckedLockingClass5(); + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java new file mode 100644 index 0000000000..4ad50d13f8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java @@ -0,0 +1,24 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class DoubleCheckedLockingClass6 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass6 instance; + public DoubleCheckedLockingClass6 getInstance() { + if(instance==null){ + synchronized(instance) { + if(instance==null){ + instance = new DoubleCheckedLockingClass6(); + } + instance = new DoubleCheckedLockingClass6(); + } + + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass7.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass7.java new file mode 100644 index 0000000000..5fe1ae8338 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass7.java @@ -0,0 +1,24 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class DoubleCheckedLockingClass7 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass7 instance; + public DoubleCheckedLockingClass7 getInstance() { + if(instance==null){ + synchronized(instance) { + if(instance==null){ + instance = new DoubleCheckedLockingClass7(); + } + + } + instance = new DoubleCheckedLockingClass7(); + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass8.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass8.java new file mode 100644 index 0000000000..315c9f2b94 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass8.java @@ -0,0 +1,23 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class DoubleCheckedLockingClass8 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass8 instance; + public DoubleCheckedLockingClass8 getInstance() { + if(instance==null){ + synchronized(instance) { + if(instance==null){ + instance = new DoubleCheckedLockingClass8(); + } + } + } + instance = new DoubleCheckedLockingClass8(); + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java index bbc089942c..b1ee5cf034 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java @@ -1,9 +1,9 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; -import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.NotThreadSafeLazyInitializationAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; public class SimpleLazyInstantiation{ - @NotThreadSafeLazyInitializationAnnotation("") + @MutableReferenceAnnotation("") private static SimpleLazyInstantiation instance; public static SimpleLazyInstantiation init() { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java index 6be5b03d14..0fe13875d7 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java @@ -1,9 +1,9 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; -import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.LazyInitializationAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceAnnotation; public class SimpleLazyIntInstantiation{ - @LazyInitializationAnnotation("") + @LazyInitializedReferenceAnnotation("") private int i = 0; public int hashcode() { if(i==0) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java index b0b9e87c3a..1bfe387607 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java @@ -1,9 +1,9 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; -import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.NotThreadSafeLazyInitializationAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; public class SimpleLazyObjectsInstantiation{ - @NotThreadSafeLazyInitializationAnnotation("") + @MutableReferenceAnnotation("") private static SimpleLazyObjectsInstantiation instance; public static SimpleLazyObjectsInstantiation getInstance() { if(instance==null) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index 0ea63fea92..57740b63f7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -20,16 +20,22 @@ import org.opalj.collection.immutable.IntTrieSet import org.opalj.tac.SelfReferenceParameter import org.opalj.tac.Assignment import org.opalj.tac.CaughtException +import org.opalj.tac.DVar import org.opalj.tac.Expr import org.opalj.tac.FieldWriteAccessStmt import org.opalj.tac.GetField import org.opalj.tac.GetStatic import org.opalj.tac.If +import org.opalj.tac.MonitorEnter +import org.opalj.tac.MonitorExit import org.opalj.tac.NonVirtualFunctionCall import org.opalj.tac.ReturnValue import org.opalj.tac.StaticFunctionCall import org.opalj.tac.Stmt +import org.opalj.tac.TACMethodParameter import org.opalj.tac.TACStmts +import org.opalj.tac.TACode +import org.opalj.tac.UVar import org.opalj.tac.VirtualFunctionCall import scala.annotation.switch @@ -75,11 +81,20 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the // first statement that is executed after the if-Statement if the field's value was not the // default value - val (guardIndex, guardedIndex, readIndex) = - findGuard(writeIndex, defaultValue, code, cfg) match { - case Some((guard, guarded, read)) ⇒ (guard, guarded, read) - case None ⇒ return false; - } + val (guardIndex, guardedIndex, readIndex) = { + val findGuardResult = findGuard(writeIndex, defaultValue, code, cfg, 1) + println("find guard result: "+findGuardResult.mkString(", ")) + if (findGuardResult.size > 0) + findGuardResult.head + else return false; + /** + * findGuardResult match { + * case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + * case None ⇒ return false; + * } * + */ + } + println("0004") // Detect only simple patterns where the lazily initialized value is returned immediately if (!checkImmediateReturn(write, writeIndex, readIndex, code)) @@ -97,6 +112,133 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization true } + def isDoubleCheckedLocking( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int], + tacCai: TACode[TACMethodParameter, V] + )(implicit state: State): Boolean = { + //val write = code(writeIndex).asFieldWriteAccessStmt + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) + return false; // Reads outside the (single) lazy initialization method + + // There must be a guarding if-Statement + // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the + // first statement that is executed after the if-Statement if the field's value was not the + // default value + val guardResults: List[(Int, Int, Int)] = findGuard(writeIndex, defaultValue, code, cfg, 2) + println("find guard result 2: "+guardResults.mkString(", ")) + + //if(!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) + // return false; + println("--------------------------------------------------------------------------------------------dcl") + + val monitorResult: (Option[Int], Option[Int]) = findMonitor(writeIndex, defaultValue, code, cfg, tacCai) + println("monitorResult: "+monitorResult) + /** + * guardResults.foreach(x ⇒ { + * if (!checkWrites(write, writeIndex, x._1, x._2, method, code, cfg)) { + * println("check writes false") + * return false + * }; + * // Field reads (except for the guard) may only be executed if the field's value is not the + * // default value + * if (!checkReads(reads, x._3, x._2, writeIndex, cfg, pcToIndex)) { + * println("check reads false") + * return false + * }; + * }) * + */ + monitorResult match { + case (Some(enter), Some(exit)) ⇒ { + + //val outer = + guardResults.exists(x ⇒ x._1 < enter && exit < x._2) && + guardResults.exists(x ⇒ enter < x._1 && x._2 < exit) +/*** val inner = + * return outer && inner;* + */ + } + case _ ⇒ return false; + } + true + + } + + def findMonitor( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + tacCode: TACode[TACMethodParameter, V] + )(implicit state: State): (Option[Int], Option[Int]) = { + + var result: (Option[Int], Option[Int]) = (None, None) + val startBB = cfg.bb(fieldWrite) + var MonitorExitqueuedBBs: Set[CFGNode] = startBB.successors + var worklistMonitorExit = getSuccessors(startBB, Set.empty) + + //find monitorexit + while (!worklistMonitorExit.isEmpty) { + val curBB = worklistMonitorExit.head + worklistMonitorExit = worklistMonitorExit.tail + //val startPC = curBB.startPC + val endPC = curBB.endPC + val cfStmt = code(endPC) + cfStmt match { + case MonitorExit(pc, v @ UVar(defSites, value)) //if(v.value.computationalType == state.field.fieldType) + ⇒ + if (v.defSites.filter(i ⇒ { + tacCode.stmts(i) match { + case Assignment(pc1, DVar(value1, defSites1), GetField(pc2, t, name, classType, UVar(value2, defSites2))) ⇒ + classType == state.field.fieldType && name == state.field.name + case _ ⇒ false + } + }).size == v.defSites.size) { + result = ((result._1), Some(tacCode.pcToIndex(pc))) + } + case _ ⇒ + val successors = getSuccessors(curBB, MonitorExitqueuedBBs) + worklistMonitorExit ++= successors + MonitorExitqueuedBBs ++= successors + } + } + + var monitorEnterqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklistMonitorEnter = getPredecessors(startBB, Set.empty) + //find monitorenter + while (!worklistMonitorEnter.isEmpty) { + val curBB = worklistMonitorEnter.head + worklistMonitorEnter = worklistMonitorEnter.tail + //val startPC = curBB.startPC + val endPC = curBB.endPC + val cfStmt = code(endPC) + cfStmt match { + case MonitorEnter(pc, v @ UVar(defSites, value)) //if(v.value.computationalType == state.field.fieldType) + ⇒ + if (v.defSites.filter(i ⇒ { + tacCode.stmts(i) match { + case Assignment(pc1, DVar(value1, defSites1), GetField(pc2, t, name, classType, UVar(value2, defSites2))) ⇒ + classType == state.field.fieldType + case _ ⇒ false + } + }).size == v.defSites.size) { + result = (Some(tacCode.pcToIndex(pc)), result._2) + } + + case _ ⇒ + val predecessor = getPredecessors(curBB, monitorEnterqueuedBBs) + worklistMonitorEnter ++= predecessor + monitorEnterqueuedBBs ++= predecessor + } + } + 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 @@ -106,77 +248,121 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization fieldWrite: Int, defaultValue: Any, code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Option[(Int, Int, Int)] = { + cfg: CFG[Stmt[V], TACStmts[V]], + amount: Int + )(implicit state: State): List[(Int, Int, Int)] = { + println("start find guard") val startBB = cfg.bb(fieldWrite).asBasicBlock - + println("start bb: "+startBB) var enqueuedBBs: Set[CFGNode] = startBB.predecessors var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - var result: Option[(Int, Int)] = None - + var result: List[(Int, Int)] = List.empty + var i: Int = 0 while (worklist.nonEmpty) { val curBB = worklist.head worklist = worklist.tail - + i = i + 1 + println(i.toString()+" curBB: "+curBB) + println("worklist: "+worklist.mkString(", ")) val startPC = curBB.startPC val endPC = curBB.endPC - + println( + s""" + |startPc: $startPC + |endPC: $endPC + |""".stripMargin + ) val cfStmt = code(endPC) + println("cfStmt: "+cfStmt) (cfStmt.astID: @switch) match { + /** + * case MonitorEnter.ASTID ⇒ + * val monitorEnter = cfStmt.asMonitorEnter + * println("monitor-enter: "+monitorEnter) + */ case If.ASTID ⇒ val ifStmt = cfStmt.asIf + println("if stmt condition: "+ifStmt.condition) ifStmt.condition match { case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; + println("EQ") + if (result.size >= amount) { + println("result: "+result.mkString(", ")) + if (result.head._1 != endPC || result.last._2 != endPC + 1) + return List.empty; } else { - result = Some((endPC, endPC + 1)) + result = { (endPC, endPC + 1) } :: result } case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None; + println("NE") + println(result.mkString(", ")) + if (result.size >= amount) { + println("result: "+result.mkString(", ")) + if (result.head._1 != endPC || result.last._2 != ifStmt.targetStmt) { + println( + s"""head 1 : ${result.head._1} + |endPc: $endPC + |last 2 : ${result.last._2} + |ifstmt target stmt : ${ifStmt.targetStmt} + |""".stripMargin + ) + return List.empty; + } } else { - result = Some((endPC, ifStmt.targetStmt)) + println("else") + result = (endPC, ifStmt.targetStmt) :: result } // Otherwise, we have to ensure that a guard is present for all predecessors case _ ⇒ - if (startPC == 0) return None; + println("_") + if (startPC == 0) { + println("reached the end") + return List.empty; + } val predecessors = getPredecessors(curBB, enqueuedBBs) worklist ++= predecessors enqueuedBBs ++= predecessors + } // Otherwise, we have to ensure that a guard is present for all predecessors case _ ⇒ - if (startPC == 0) return None; + if (startPC == 0) return List.empty; val predecessors = getPredecessors(curBB, enqueuedBBs) worklist ++= predecessors enqueuedBBs ++= predecessors } - } + if (result.size < amount) { + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + println("worklist end: "+worklist.mkString(", ")) + } - if (result.isDefined) { + } + println("result: "+result.mkString(", ")) + if (result.size >= amount) { // 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.get._1).asIf + val ifStmt = code(result.head._1).asIf val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr val definitions = expr.asVar.definedBy val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ - use == result.get._1 || use == result.get._2 + use == result.head._1 || use == result.last._2 } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard + if (definitions.size == 1 && fieldReadUsedCorrectly) { + return result.map(x ⇒ (x._1, x._2, definitions.head)); + //return (result.head._1, result.last._2, definitions.head) :: Nil + }; // Found proper guard } - None + List.empty } /** @@ -275,6 +461,17 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization result.toList } + def getSuccessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { + val result = node.successors.iterator flatMap ({ + currentNode ⇒ + if (currentNode.isBasicBlock) + if (visited.contains(currentNode)) None + else Some(currentNode.asBasicBlock) + else getSuccessors(currentNode, visited) + }) + result.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 @@ -434,7 +631,8 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization defSites.size == 1 && head >= 0 && isDefaultConst(code(head).asAssignment.expr) } else { expr.isIntConst && defaultValue == expr.asIntConst.value || - expr.isFloatConst && defaultValue == expr.asFloatConst.value + expr.isFloatConst && defaultValue == expr.asFloatConst.value || + defaultValue == null //TODO ?? } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index 0f1f7f7651..15ddeb765c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -5,6 +5,7 @@ import org.opalj.br.ClassFile import org.opalj.br.DeclaredMethod import org.opalj.br.Field import org.opalj.br.FloatType +import org.opalj.br.IntegerType import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.PCs @@ -181,7 +182,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec stmt match { case PutField(_, _, _, _, _, value) ⇒ value match { - case v @ UVar(defSites, value2) ⇒ // SObjectValue(t,_,_,_)) => + case v @ UVar(defSites, value2) ⇒ //SObjectValue(t,_,_,_) => //if (!v.defSites.filter(_ < 1).isEmpty) //escape var { @@ -395,7 +396,15 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * values. */ def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) + Some( + state.field.fieldType match { + case FloatType ⇒ 0.0f + case IntegerType ⇒ 0 + case ObjectType(_) ⇒ null + } + ) + + //TODO ?? Some(if (state.field.fieldType eq FloatType) 0.0f else 0) /* TODO Some lazy initialized fields use a different value to mark an uninitialized field * The code below can be used to identify such value, but is not yet adapted to using the @@ -606,8 +615,16 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec */ // A field written outside an initializer must be lazily // initialized or it is non-final - - if (!isLazyInitialization( + val b = isDoubleCheckedLocking( + index, + defaultValue.get, + method, + taCode.stmts, + taCode.cfg, + taCode.pcToIndex, + taCode + ) + if (!b && !isLazyInitialization( index, defaultValue.get, method, From fa0e345662807d8e8f51918c8d121aa7f5305103 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 25 Jun 2020 16:57:10 +0200 Subject: [PATCH 156/327] inserting printlns for debugging purposes --- .../L0FieldImmutabilityAnalysis.scala | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index 76ae813639..0f7847d09c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -245,6 +245,15 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } def createResult(state: State): ProperPropertyComputationResult = { + println( + s"""reaching create result + |field: ${state.field} + | class: ${state.field.classFile.thisType} + | ref imm: ${state.referenceImmutability} + | type imm: ${state.typeImmutability} + | ref not esc: ${state.referenceNotEscapes} + | depend imm: ${state.dependentImmutability} + |""".stripMargin) state.referenceImmutability match { case Some(false) | None ⇒ Result(field, MutableField) @@ -261,7 +270,14 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) Result(field, DependentImmutableField) case Some(DependentImmutabilityKind.onlyDeepImmutable) ⇒ Result(field, DeepImmutableField) - case _ ⇒ Result(field, ShallowImmutableField) + case _ ⇒ { + println( + s"""default case in field immutability + | state.dependentimmutability: ${state.dependentImmutability} + | + |""".stripMargin) + Result(field, ShallowImmutableField) + } } } } From 685f28eff9b206d7b07af8582de2ad9404db4e69 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 26 Jun 2020 15:52:29 +0200 Subject: [PATCH 157/327] adjusting correct rater --- .../purity/L2PurityAnalysis_new.scala | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala index 0f3abc6836..ac346187aa 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala @@ -59,7 +59,6 @@ import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.analyses.ConfiguredPurityKey import org.opalj.br.fpcf.properties.cg.Callees import org.opalj.br.fpcf.properties.cg.Callers -import org.opalj.br.fpcf.properties.cg.NoCallers import org.opalj.ai.isImmediateVMException import org.opalj.br.fpcf.properties.ClassImmutability_new import org.opalj.br.fpcf.properties.FieldImmutability @@ -71,8 +70,8 @@ import org.opalj.tac.fpcf.properties.TACAI import scala.annotation.switch import scala.collection.immutable.IntMap - import net.ceedubs.ficus.Ficus._ +import org.opalj.br.fpcf.properties.cg.NoCallers /** * An inter-procedural analysis to determine a method's purity. @@ -263,7 +262,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) ) val rater: DomainSpecificRater = - L2PurityAnalysis.rater.getOrElse(resolveDomainSpecificRater(raterFqn)) + L2PurityAnalysis_new.rater.getOrElse(resolveDomainSpecificRater(raterFqn)) /** * Examines whether the given expression denotes an object/array that is local to the current @@ -997,20 +996,15 @@ trait L2PurityAnalysisScheduler_new extends FPCFAnalysisScheduler { } -object EagerL2PurityAnalysis_new - extends L2PurityAnalysisScheduler_new - with FPCFEagerAnalysisScheduler { +object EagerL2PurityAnalysis_new extends L2PurityAnalysisScheduler_new with FPCFEagerAnalysisScheduler { override def start( - p: SomeProject, - ps: PropertyStore, - analysis: InitializationData + p: SomeProject, ps: PropertyStore, analysis: InitializationData ): FPCFAnalysis = { 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)( @@ -1026,14 +1020,10 @@ object EagerL2PurityAnalysis_new override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } -object LazyL2PurityAnalysis_new - extends L2PurityAnalysisScheduler_new - with FPCFLazyAnalysisScheduler { +object LazyL2PurityAnalysis_new extends L2PurityAnalysisScheduler_new with FPCFLazyAnalysisScheduler { override def register( - p: SomeProject, - ps: PropertyStore, - analysis: InitializationData + p: SomeProject, ps: PropertyStore, analysis: InitializationData ): FPCFAnalysis = { ps.registerLazyPropertyComputation(Purity.key, analysis.doDeterminePurity) analysis From ddc59ef52afda33ad73c288d6bb8045fdd6bb196 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 30 Jun 2020 10:49:28 +0200 Subject: [PATCH 158/327] revise dcl recognition with domtree, and tests --- .../DoubleCheckedLockingClass1.java | 2 +- .../DoubleCheckedLockingClass2.java | 6 +- .../DoubleCheckedLockingClass6.java | 2 +- .../DoubleCheckedLockingClass7.java | 2 +- .../DoubleCheckedLockingClass8.java | 2 +- ...mutabilityAnalysisLazyInitialization.scala | 182 +++++++++++++----- 6 files changed, 141 insertions(+), 55 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java index 2a952f58ea..85dc44dda5 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java @@ -10,7 +10,7 @@ public class DoubleCheckedLockingClass1{ private DoubleCheckedLockingClass1 instance; public DoubleCheckedLockingClass1 getInstance() { if(instance==null){ - synchronized(instance) { + synchronized(this) { if(instance==null){ instance = new DoubleCheckedLockingClass1(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java index 639f1a0586..79d94dbfdf 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java @@ -1,16 +1,16 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceAnnotation; public class DoubleCheckedLockingClass2 { - @MutableReferenceAnnotation("") + @LazyInitializedReferenceAnnotation("") private DoubleCheckedLockingClass2 instance; private DoubleCheckedLockingClass2 instance2 = new DoubleCheckedLockingClass2(); public DoubleCheckedLockingClass2 getInstance() { if(instance==null){ - synchronized(instance2) { + synchronized(this) { if(instance==null){ instance = new DoubleCheckedLockingClass2(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java index 4ad50d13f8..f2cbc3783f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java @@ -9,7 +9,7 @@ public class DoubleCheckedLockingClass6 { private DoubleCheckedLockingClass6 instance; public DoubleCheckedLockingClass6 getInstance() { if(instance==null){ - synchronized(instance) { + synchronized(this) { if(instance==null){ instance = new DoubleCheckedLockingClass6(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass7.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass7.java index 5fe1ae8338..2bd6da16cd 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass7.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass7.java @@ -9,7 +9,7 @@ public class DoubleCheckedLockingClass7 { private DoubleCheckedLockingClass7 instance; public DoubleCheckedLockingClass7 getInstance() { if(instance==null){ - synchronized(instance) { + synchronized(this) { if(instance==null){ instance = new DoubleCheckedLockingClass7(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass8.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass8.java index 315c9f2b94..7747635e2d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass8.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass8.java @@ -9,7 +9,7 @@ public class DoubleCheckedLockingClass8 { private DoubleCheckedLockingClass8 instance; public DoubleCheckedLockingClass8 getInstance() { if(instance==null){ - synchronized(instance) { + synchronized(this) { if(instance==null){ instance = new DoubleCheckedLockingClass8(); } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index 57740b63f7..ad70f151c8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -20,7 +20,6 @@ import org.opalj.collection.immutable.IntTrieSet import org.opalj.tac.SelfReferenceParameter import org.opalj.tac.Assignment import org.opalj.tac.CaughtException -import org.opalj.tac.DVar import org.opalj.tac.Expr import org.opalj.tac.FieldWriteAccessStmt import org.opalj.tac.GetField @@ -83,9 +82,9 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization // default value val (guardIndex, guardedIndex, readIndex) = { val findGuardResult = findGuard(writeIndex, defaultValue, code, cfg, 1) - println("find guard result: "+findGuardResult.mkString(", ")) - if (findGuardResult.size > 0) - findGuardResult.head + println("find guard result: "+findGuardResult._1.mkString(", ")) + if (findGuardResult._1.size > 0) + findGuardResult._1.head else return false; /** * findGuardResult match { @@ -130,14 +129,14 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the // first statement that is executed after the if-Statement if the field's value was not the // default value - val guardResults: List[(Int, Int, Int)] = findGuard(writeIndex, defaultValue, code, cfg, 2) - println("find guard result 2: "+guardResults.mkString(", ")) + val guardResults: (List[(Int, Int, Int)], (Option[(CFGNode, CFGNode)])) = findGuard(writeIndex, defaultValue, code, cfg, 2) + println("find guard result 2: "+guardResults._1.mkString(", ")) //if(!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) // return false; println("--------------------------------------------------------------------------------------------dcl") - val monitorResult: (Option[Int], Option[Int]) = findMonitor(writeIndex, defaultValue, code, cfg, tacCai) + val monitorResult = findMonitor(writeIndex, defaultValue, code, cfg, tacCai) println("monitorResult: "+monitorResult) /** * guardResults.foreach(x ⇒ { @@ -153,18 +152,49 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization * }; * }) * */ - monitorResult match { - case (Some(enter), Some(exit)) ⇒ { - - //val outer = - guardResults.exists(x ⇒ x._1 < enter && exit < x._2) && - guardResults.exists(x ⇒ enter < x._1 && x._2 < exit) -/*** val inner = - * return outer && inner;* - */ - } - case _ ⇒ return false; + val domTree = cfg.dominatorTree + + (guardResults._2, monitorResult._2) match { + case (Some((guard1: CFGNode, guard2: CFGNode)), Some(monitor: CFGNode)) ⇒ + println( + s"""domtree: $domTree + | g1 dom m: ${domTree.strictlyDominates(guard1.nodeId, monitor.nodeId)} + | m dom g2: ${domTree.strictlyDominates(monitor.nodeId, guard2.nodeId)} + | g1 dom g2: ${domTree.strictlyDominates(guard1.nodeId, guard2.nodeId)} + | g2 dom g1: ${domTree.strictlyDominates(guard2.nodeId, guard1.nodeId)} + | g2 dom m: ${domTree.strictlyDominates(guard2.nodeId, monitor.nodeId)} + | m dom g1: ${domTree.strictlyDominates(monitor.nodeId, guard1.nodeId)} + | + | m: $monitor + | g2: $guard2 + | m==g2: ${monitor == guard2} + | + |""".stripMargin + ) + + if ((guard1 == monitor || domTree.strictlyDominates(guard1.nodeId, monitor.nodeId)) && + (monitor == guard2 || domTree.strictlyDominates(monitor.nodeId, guard2.nodeId))) + println("dominates!!!") + else { + println("not dominates!!!!") + return false; + } + case _ ⇒ println("not dominates!!!"); return false; } + /** + * monitorResult._1 match { + * case (Some(enter), Some(exit)) ⇒ { + * + * //val outer = + * guardResults._1.exists(x ⇒ x._1 < enter && exit < x._2) && + * guardResults._1.exists(x ⇒ enter < x._1 && x._2 < exit) + * /*** val inner = + * return outer && inner;* + * */ + * } + * case _ ⇒ return false; + * }* + */ true } @@ -175,13 +205,43 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]], tacCode: TACode[TACMethodParameter, V] - )(implicit state: State): (Option[Int], Option[Int]) = { + ): ((Option[Int], Option[Int]), Option[CFGNode]) = { //(implicit state: State) var result: (Option[Int], Option[Int]) = (None, None) + var dclBBs: List[CFGNode] = List.empty val startBB = cfg.bb(fieldWrite) var MonitorExitqueuedBBs: Set[CFGNode] = startBB.successors var worklistMonitorExit = getSuccessors(startBB, Set.empty) + /** + * def checkMonitor(pc: PC, v: UVar[ValueInformation], curBB: CFGNode): Boolean = { + * println("check monitor") + * v.defSites.filter(i ⇒ { + * if (i > 0) + * tacCode.stmts(i) match { + * case Assignment(pc1, DVar(value1, defSites1), GetField(pc2, t, name, classType, UVar(value2, defSites2))) ⇒ + * println("classType: "+classType) + * println("classFile.thisType: "+state.field.classFile.thisType) + * println( + * s""" + * |classFile comparison result: ${classType == state.field.classFile.thisType} + * + * |""". + * stripMargin + * ) + * classType == + * state.field. + * classFile.thisType + * //&& name == state.field.name + * case _ ⇒ false + * } + * else // (i == -1) + * true + * + * }).size == v.defSites.size + * } * + */ + //find monitorexit while (!worklistMonitorExit.isEmpty) { val curBB = worklistMonitorExit.head @@ -192,17 +252,13 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization cfStmt match { case MonitorExit(pc, v @ UVar(defSites, value)) //if(v.value.computationalType == state.field.fieldType) ⇒ - if (v.defSites.filter(i ⇒ { - tacCode.stmts(i) match { - case Assignment(pc1, DVar(value1, defSites1), GetField(pc2, t, name, classType, UVar(value2, defSites2))) ⇒ - classType == state.field.fieldType && name == state.field.name - case _ ⇒ false - } - }).size == v.defSites.size) { - result = ((result._1), Some(tacCode.pcToIndex(pc))) - } + //if(checkMonitor(pc, v, curBB)) { + + result = ((result._1), Some(tacCode.pcToIndex(pc))) + //} case _ ⇒ val successors = getSuccessors(curBB, MonitorExitqueuedBBs) + println("successors: "+successors.mkString(", ")) worklistMonitorExit ++= successors MonitorExitqueuedBBs ++= successors } @@ -210,33 +266,53 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization var monitorEnterqueuedBBs: Set[CFGNode] = startBB.predecessors var worklistMonitorEnter = getPredecessors(startBB, Set.empty) + println("worklist monitorenter: "+worklistMonitorEnter.toList.mkString(", ")) //find monitorenter while (!worklistMonitorEnter.isEmpty) { val curBB = worklistMonitorEnter.head + println("curBB: "+curBB) worklistMonitorEnter = worklistMonitorEnter.tail //val startPC = curBB.startPC - val endPC = curBB.endPC - val cfStmt = code(endPC) + //val endPC = curBB.endPC + val startPC = curBB.startPC + val cfStmt = code(startPC) //(endPC) + println("cfg should be monitor enter: "+cfStmt) cfStmt match { - case MonitorEnter(pc, v @ UVar(defSites, value)) //if(v.value.computationalType == state.field.fieldType) + case me @ MonitorEnter(pc, v @ UVar(defSites, value)) //if(v.value.computationalType == state.field.fieldType) ⇒ - if (v.defSites.filter(i ⇒ { - tacCode.stmts(i) match { - case Assignment(pc1, DVar(value1, defSites1), GetField(pc2, t, name, classType, UVar(value2, defSites2))) ⇒ - classType == state.field.fieldType - case _ ⇒ false - } - }).size == v.defSites.size) { - result = (Some(tacCode.pcToIndex(pc)), result._2) - } + + /** + * if (v.defSites.filter(i ⇒ { + * tacCode.stmts(i) match { + * case Assignment(pc1, DVar(value1, defSites1), GetField(pc2, t, name, classType, UVar(value2, defSites2))) ⇒ + * classType == state.field.fieldType + * case _ ⇒ false + * } + * }).size == v.defSites.size) { + * result = (Some(tacCode.pcToIndex(pc)), result._2) + * } * + */ + + //if(checkMonitor(pc, v, curBB)) { + println("Monitor Enter:-----------------------------------------------------------------------------") + println("me") + result = (Some(tacCode.pcToIndex(pc)), (result._2)) + dclBBs = curBB :: dclBBs + //} case _ ⇒ val predecessor = getPredecessors(curBB, monitorEnterqueuedBBs) + println("predecessor: "+predecessor.mkString(", ")) worklistMonitorEnter ++= predecessor monitorEnterqueuedBBs ++= predecessor } } - result + val bbs = { + if (dclBBs.size == 1) + Some(dclBBs.head) + else None + } + (result, bbs) } /** @@ -250,9 +326,12 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]], amount: Int - )(implicit state: State): List[(Int, Int, Int)] = { + )(implicit state: State): (List[(Int, Int, Int)], (Option[(CFGNode, CFGNode)])) = { println("start find guard") val startBB = cfg.bb(fieldWrite).asBasicBlock + + var dclBBs: List[CFGNode] = List.empty + println("start bb: "+startBB) var enqueuedBBs: Set[CFGNode] = startBB.predecessors var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) @@ -290,8 +369,9 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization if (result.size >= amount) { println("result: "+result.mkString(", ")) if (result.head._1 != endPC || result.last._2 != endPC + 1) - return List.empty; + return (List.empty, None); } else { + dclBBs = curBB :: dclBBs result = { (endPC, endPC + 1) } :: result } @@ -308,10 +388,11 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization |ifstmt target stmt : ${ifStmt.targetStmt} |""".stripMargin ) - return List.empty; + return (List.empty, None); } } else { println("else") + dclBBs = curBB :: dclBBs result = (endPC, ifStmt.targetStmt) :: result } @@ -320,7 +401,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization println("_") if (startPC == 0) { println("reached the end") - return List.empty; + return (List.empty, None); } val predecessors = getPredecessors(curBB, enqueuedBBs) @@ -331,7 +412,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization // Otherwise, we have to ensure that a guard is present for all predecessors case _ ⇒ - if (startPC == 0) return List.empty; + if (startPC == 0) return (List.empty, None); val predecessors = getPredecessors(curBB, enqueuedBBs) worklist ++= predecessors @@ -357,12 +438,17 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization use == result.head._1 || use == result.last._2 } if (definitions.size == 1 && fieldReadUsedCorrectly) { - return result.map(x ⇒ (x._1, x._2, definitions.head)); + val BBs = + if (dclBBs.size == 2) + Some((dclBBs.head, dclBBs.last)) + else None + + return (result.map(x ⇒ (x._1, x._2, definitions.head)), BBs); //return (result.head._1, result.last._2, definitions.head) :: Nil }; // Found proper guard } - List.empty + (List.empty, None) } /** From f23bfb6f1037ae8359261eea2de9f5c115bd6541 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 30 Jun 2020 10:53:06 +0200 Subject: [PATCH 159/327] inserting printlns for debugging purposes --- .../scala/org/opalj/fpcf/PurityTests.scala | 2 +- .../fpcf/ReferenceImmutabilityTests.scala | 17 +++++- ...renceImmutabilityTests_withNewPurity.scala | 10 +++- .../L0ReferenceImmutabilityAnalysis.scala | 1 + .../purity/AbstractPurityAnalysis.scala | 23 ++++---- .../purity/AbstractPurityAnalysis_new.scala | 33 ++++++------ .../analyses/purity/L2PurityAnalysis.scala | 50 +++++++++++++++-- .../purity/L2PurityAnalysis_new.scala | 53 ++++++++++++++++--- 8 files changed, 148 insertions(+), 41 deletions(-) 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..a31688f92e 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 @@ -44,7 +44,7 @@ class PurityTests extends PropertiesTest { } override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/purity") + List("org/opalj/fpcf/fixtures/immutability/reference/sandbox") } describe("the org.opalj.fpcf.analyses.L0PurityAnalysis is executed") { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index abe27a4069..7bb33c068f 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -6,10 +6,16 @@ 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.LazyClassImmutabilityAnalysis +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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis @@ -42,11 +48,18 @@ class ReferenceImmutabilityTests extends PropertiesTest { val as = executeAnalyses( Set( EagerL0ReferenceImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, LazyInterProceduralEscapeAnalysis, - LazyLxTypeImmutabilityAnalysis_new + LazyLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyL2FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala index 40eb9e07c9..0df7b7e300 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala @@ -6,11 +6,14 @@ 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.LazyClassImmutabilityAnalysis 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.LazyUnsoundPrematurelyReadFieldsAnalysis 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.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis @@ -52,11 +55,14 @@ class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { LazyLxTypeImmutabilityAnalysis_new, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyStaticDataUsageAnalysis, + LazyL2PurityAnalysis_new, LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis, - LazyL2PurityAnalysis_new, - LazyInterProceduralEscapeAnalysis + LazyL1FieldMutabilityAnalysis, + LazyClassImmutabilityAnalysis, + LazyTypeImmutabilityAnalysis ) ) as.propertyStore.shutdown() diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index 15ddeb765c..4bf4402be7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -529,6 +529,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec isNotFinal = isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) case Purity.key ⇒ val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] + println("purity continuation result: "+newEP) state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) val r = isNonDeterministic(newEP) //if (!r) state.referenceImmutability = LazyInitializedReference 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..81233b9534 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 @@ -389,17 +389,20 @@ trait AbstractPurityAnalysis extends FPCFAnalysis { def checkFieldMutability( ep: EOptionP[Field, FieldMutability], 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 - if (objRef.isDefined) { + )(implicit state: StateType): Unit = { + println("pa2 ep of checkFieldMutability: "+ep) + ep match { + case LBP(_: FinalField) ⇒ // Final fields don't impede purity + case _: FinalEP[Field, FieldMutability] ⇒ // Mutable field + if (objRef.isDefined) { + if (state.ubPurity.isDeterministic) + isLocal(objRef.get, SideEffectFree) + } else atMost(SideEffectFree) + case _ ⇒ + reducePurityLB(SideEffectFree) if (state.ubPurity.isDeterministic) - isLocal(objRef.get, SideEffectFree) - } else atMost(SideEffectFree) - case _ ⇒ - reducePurityLB(SideEffectFree) - if (state.ubPurity.isDeterministic) - handleUnknownFieldMutability(ep, objRef) + handleUnknownFieldMutability(ep, objRef) + } } /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala index 1ab0870f58..98b9b87fd7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala @@ -443,23 +443,26 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { def checkFieldMutability( ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], objRef: Option[Expr[V]] - )(implicit state: StateType): Unit = ep match { - - //case LBP(ImmutableReference(_)) ⇒ - //case LBP(LazyInitializedReference) ⇒ - case LBP(ImmutableReference(_) | LazyInitializedReference) ⇒ - println("====>>>> EP: "+ep) //_: FinalField) ⇒ // Final fields don't impede purity - case FinalP(MutableReference) ⇒ - println("====>>>> EP: "+ep) //_: FinalEP[Field, ReferenceImmutability] ⇒ //FieldMutability] ⇒ // Mutable field - if (objRef.isDefined) { + )(implicit state: StateType): Unit = { + println("pa2 ep of checkFieldMutability: "+ep) + ep match { + + //case LBP(ImmutableReference(_)) ⇒ + //case LBP(LazyInitializedReference) ⇒ + case LBP(ImmutableReference(_) | LazyInitializedReference) ⇒ + println("pa2 ====>>>> EP: "+ep) //_: FinalField) ⇒ // Final fields don't impede purity + case FinalP(MutableReference) ⇒ + println("pa2 ====>>>> EP: "+ep) //_: FinalEP[Field, ReferenceImmutability] ⇒ //FieldMutability] ⇒ // Mutable field + if (objRef.isDefined) { + if (state.ubPurity.isDeterministic) + isLocal(objRef.get, SideEffectFree) + } else atMost(SideEffectFree) + case _ ⇒ + reducePurityLB(SideEffectFree) if (state.ubPurity.isDeterministic) - isLocal(objRef.get, SideEffectFree) - } else atMost(SideEffectFree) - case _ ⇒ - reducePurityLB(SideEffectFree) - if (state.ubPurity.isDeterministic) - handleUnknownFieldMutability(ep, objRef) + handleUnknownFieldMutability(ep, objRef) + } } /** 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..0fcd383867 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 @@ -729,7 +729,9 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst * - classes files for class types returned (for their mutability) */ def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { + println("pa1 enter continuation") val oldPurity = state.ubPurity + println("pa1 eps: "+eps) eps.ub.key match { case Purity.key ⇒ val e = eps.e.asInstanceOf[DeclaredMethod] @@ -791,8 +793,10 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst return determineMethodPurity(eps.ub.asInstanceOf[TACAI].tac.get.cfg); } - if (state.ubPurity eq ImpureByAnalysis) - return Result(state.definedMethod, ImpureByAnalysis); + if (state.ubPurity eq ImpureByAnalysis) { + println("pa1 upPurity eq ImpureByAnalysis") + return Result(state.definedMethod, ImpureByAnalysis) + }; if (state.ubPurity ne oldPurity) cleanupDependees() // Remove dependees that we don't need anymore. @@ -800,8 +804,19 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst val dependees = state.dependees if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { + println("pa1 dependees empty lb=ub; result : "+state.ubPurity) Result(state.definedMethod, state.ubPurity) } else { + println( + s""" + |pa1 + | interim result + | lbPurity: ${state.lbPurity} + | ubPurity. ${state.ubPurity} + | dependees: ${dependees.mkString(", ")} + | + |""".stripMargin + ) InterimResult( state.definedMethod, state.lbPurity, @@ -883,8 +898,26 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst val dependees = state.dependees if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { + println( + s""" + |pa1 + |return result: + |defined method: ${state.definedMethod} + | ubPurity: ${state.ubPurity} + |""".stripMargin + ) Result(state.definedMethod, state.ubPurity) } else { + println( + s""" + |pa1 + | interim result + | lbPurity: ${state.lbPurity} + | ubPurity. ${state.ubPurity} + | dependees: ${dependees.mkString(", ")} + | + |""".stripMargin + ) org.opalj.fpcf.InterimResult(state.definedMethod, state.lbPurity, state.ubPurity, dependees, c) } } @@ -908,14 +941,23 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst val tacaiO = getTACAI(method) - if (tacaiO.isEmpty) + if (tacaiO.isEmpty) { + println( + s""" + |pa1 + |interim result + |tacai0 is empty + | + |""".stripMargin + ) return InterimResult( definedMethod, ImpureByAnalysis, CompileTimePure, state.dependees, c - ); + ) + }; determineMethodPurity(tacaiO.get.cfg) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala index ac346187aa..1265872bd9 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala @@ -735,9 +735,9 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * - classes files for class types returned (for their mutability) */ def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { - println("enter continuation") + println("pa2 enter continuation") val oldPurity = state.ubPurity - println("eps: "+eps) + println("pa2 eps: "+eps) eps.ub.key match { case Purity.key ⇒ val e = eps.e.asInstanceOf[DeclaredMethod] @@ -802,8 +802,10 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) return determineMethodPurity(eps.ub.asInstanceOf[TACAI].tac.get.cfg); } - if (state.ubPurity eq ImpureByAnalysis) - return Result(state.definedMethod, ImpureByAnalysis); + if (state.ubPurity eq ImpureByAnalysis) { + println("pa2 upPurity eq ImpureByAnalysis") + return Result(state.definedMethod, ImpureByAnalysis) + }; if (state.ubPurity ne oldPurity) cleanupDependees() // Remove dependees that we don't need anymore. @@ -811,9 +813,19 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) val dependees = state.dependees if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { - println("dependees empty lb=ub; result : "+state.ubPurity) + println("pa2 dependees empty lb=ub; result : "+state.ubPurity) Result(state.definedMethod, state.ubPurity) } else { + println( + s""" + |pa2 + | interim result + | lbPurity: ${state.lbPurity} + | ubPurity. ${state.ubPurity} + | dependees: ${dependees.mkString(", ")} + | + |""".stripMargin + ) InterimResult( state.definedMethod, state.lbPurity, @@ -895,8 +907,26 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) val dependees = state.dependees if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { + println( + s""" + |pa2 + |return result: + |defined method: ${state.definedMethod} + | ubPurity: ${state.ubPurity} + |""".stripMargin + ) Result(state.definedMethod, state.ubPurity) } else { + println( + s""" + |pa2 + | interim result + | lbPurity: ${state.lbPurity} + | ubPurity. ${state.ubPurity} + | dependees: ${dependees.mkString(", ")} + | + |""".stripMargin + ) org.opalj.fpcf.InterimResult( state.definedMethod, state.lbPurity, @@ -915,7 +945,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult = { val method = definedMethod.definedMethod val declClass = method.classFile.thisType - println("determine purity of method: "+method+", with class: "+declClass) + println("pa2 determine purity of method: "+method+", with class: "+declClass) // If this is not the method's declaration, but a non-overwritten method in a subtype, // don't re-analyze the code @@ -927,7 +957,15 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) val tacaiO = getTACAI(method) - if (tacaiO.isEmpty) + if (tacaiO.isEmpty) { + println( + s""" + |pa2 + |interim result + |tacai0 is empty + | + |""".stripMargin + ) return InterimResult( definedMethod, ImpureByAnalysis, @@ -935,6 +973,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) state.dependees, c ); + } determineMethodPurity(tacaiO.get.cfg) } From 0e4a33540b8dd5dc2090cc9ff84b4ee3efb864e3 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 1 Jul 2020 12:31:40 +0200 Subject: [PATCH 160/327] inserting printlns for debugging purposes --- .../purity/AbstractPurityAnalysis.scala | 2 +- .../purity/L2PurityAnalysis_new.scala | 90 +++++++++++-------- 2 files changed, 55 insertions(+), 37 deletions(-) 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 81233b9534..31235c0505 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 @@ -390,7 +390,7 @@ trait AbstractPurityAnalysis extends FPCFAnalysis { ep: EOptionP[Field, FieldMutability], objRef: Option[Expr[V]] )(implicit state: StateType): Unit = { - println("pa2 ep of checkFieldMutability: "+ep) + println("pa1 ep of checkFieldMutability: "+ep) ep match { case LBP(_: FinalField) ⇒ // Final fields don't impede purity case _: FinalEP[Field, FieldMutability] ⇒ // Mutable field diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala index 1265872bd9..fbf111a6ca 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala @@ -497,7 +497,8 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * Examines a statement for its influence on the method's purity. * This method will return false for impure statements, so evaluation can be terminated early. */ - override def checkPurityOfStmt(stmt: Stmt[V])(implicit state: State): Boolean = + override def checkPurityOfStmt(stmt: Stmt[V])(implicit state: State): Boolean = { + println("check purity of statemt") (stmt.astID: @switch) match { // Synchronization on non-escaping local objects/arrays is pure (and useless...) case MonitorEnter.ASTID | MonitorExit.ASTID ⇒ @@ -529,6 +530,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) case _ ⇒ super.checkPurityOfStmt(stmt) } + } /** * Examines the influence of the purity property of a method on the examined method's purity. @@ -538,32 +540,36 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) def checkMethodPurity( ep: EOptionP[DeclaredMethod, Purity], params: Seq[Expr[V]] - )(implicit state: State): Boolean = ep match { - case UBP(_: ClassifiedImpure) ⇒ - atMost(ImpureByAnalysis) - false - case eps @ LUBP(lb, ub) ⇒ - if (eps.isRefinable && ((lb meet state.ubPurity) ne state.ubPurity)) { - // On conditional, keep dependence - state.addPurityDependee(ep.e, ep, params) - reducePurityLB(lb) - } - // Contextual/external purity is handled below - atMost(ub.withoutContextual) - ub.modifiedParams.forall( - param ⇒ - isLocalInternal( - params(param), - ImpureByAnalysis, - param ⇒ ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true + )(implicit state: State): Boolean = + { + println("pa2 check metho purity. ep: "+ep) + ep match { + case UBP(_: ClassifiedImpure) ⇒ + atMost(ImpureByAnalysis) + false + case eps @ LUBP(lb, ub) ⇒ + if (eps.isRefinable && ((lb meet state.ubPurity) ne state.ubPurity)) { + // On conditional, keep dependence + state.addPurityDependee(ep.e, ep, params) + reducePurityLB(lb) + } + // Contextual/external purity is handled below + atMost(ub.withoutContextual) + ub.modifiedParams.forall( + param ⇒ + isLocalInternal( + params(param), + ImpureByAnalysis, + param ⇒ ContextuallyPure(IntTrieSet(param)), + treatParamsAsFresh = true + ) ) - ) - case _: SomeEOptionP ⇒ - reducePurityLB(ImpureByAnalysis) - state.addPurityDependee(ep.e, ep, params) - true - } + case _: SomeEOptionP ⇒ + reducePurityLB(ImpureByAnalysis) + state.addPurityDependee(ep.e, ep, params) + true + } + } /** * Handles the effect of static data usage on the purity level. @@ -573,6 +579,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) def checkStaticDataUsage( ep: EOptionP[DeclaredMethod, StaticDataUsage] )(implicit state: State): Unit = { + println("checkstatic data usage") ep match { case LBP(UsesNoStaticData | UsesConstantDataOnly) ⇒ state.updateStaticDataUsage(None) @@ -592,6 +599,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], objRef: Option[Expr[V]] )(implicit state: State): Unit = { + println("ep2 handle unknown field mutability") state.addFieldMutabilityDependee(ep.e, ep, objRef) } @@ -602,6 +610,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) ep: EOptionP[ObjectType, Property], expr: Expr[V] )(implicit state: State): Unit = { + println("ep2 handle unknown type mutability") if (ep.pk == ClassImmutability_new.key) //ClassImmutability.key) state.addClassImmutabilityDependee( ep.e, @@ -622,6 +631,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) override def handleCalleesUpdate( callees: EOptionP[DeclaredMethod, Callees] )(implicit state: State): Unit = { + println("handle callees update") state.updateCalleesDependee(callees) if (callees.isRefinable) reducePurityLB(ImpureByAnalysis) @@ -631,6 +641,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * Adds the dependee necessary if the TACAI is not yet final. */ override def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: State): Unit = { + println("handle tacai") state.updateTacai(ep) } @@ -639,6 +650,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * purity level further. */ def cleanupDependees()(implicit state: State): Unit = { + println("ep2 cleanup dependees") if (state.ubPurity ne CompileTimePure) state.updateStaticDataUsage(None) @@ -681,6 +693,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * Raises the lower bound on the purity whenever possible. */ def adjustLowerBound()(implicit state: State): Unit = { + println("adjust lower bound") if (state.calleesDependee.isDefined) return ; // Nothing to be done, lower bound is still LBImpure @@ -842,6 +855,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) def determineMethodPurity( cfg: CFG[Stmt[V], TACStmts[V]] )(implicit state: State): ProperPropertyComputationResult = { + println("pa2 determine method purity of method: "+state.method) // Special case: The Throwable constructor is `LBSideEffectFree`, but subtype constructors // may not be because of overridable fillInStackTrace method if (state.method.isConstructor && state.declClass.isSubtypeOf(ObjectType.Throwable)) @@ -858,13 +872,17 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) } } } - + println("pa2_1") // Synchronized methods have a visible side effect on the receiver // Static synchronized methods lock the class which is potentially globally visible - if (state.method.isSynchronized) - if (state.method.isStatic) return Result(state.definedMethod, ImpureByAnalysis); - else atMost(ContextuallyPure(IntTrieSet(0))) - + if (state.method.isSynchronized) { + println("pa2 methods is synchronized") + if (state.method.isStatic) { + println("pa2 method is additionally static and thus impure") + return Result(state.definedMethod, ImpureByAnalysis); + } else atMost(ContextuallyPure(IntTrieSet(0))) + } + println("pa2_2") val stmtCount = state.code.length var s = 0 while (s < stmtCount) { @@ -874,17 +892,17 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) } s += 1 } - + println("pa2_3") val callees = propertyStore(state.definedMethod, Callees.key) if (!checkPurityOfCallees(callees)) return Result(state.definedMethod, state.ubPurity) - + println("pa2_4") if (callees.hasUBP) state.rvfCallSites.foreach { case (pc, data) ⇒ checkFreshnessOfReturn(pc, data, callees.ub) } - + println("pa2_5") // Creating implicit exceptions is side-effect free (because of fillInStackTrace) // but it may be ignored as domain-specific val bbsCausingExceptions = cfg.abnormalReturnNode.predecessors @@ -899,12 +917,12 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) else atMost(SideEffectFree) } } - + println("pa2_6") if (state.ubPurity eq CompileTimePure) // Check static data usage only if necessary checkStaticDataUsage(propertyStore(state.definedMethod, StaticDataUsage.key)) else cleanupDependees() // Remove dependees we already know we won't need - + println("pa2_7") val dependees = state.dependees if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { println( From 7c01ad07df3af8df982a7ac060c742561c694b10 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 1 Jul 2020 13:34:50 +0200 Subject: [PATCH 161/327] inserting printlns for debugging purposes --- .../opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala index fbf111a6ca..29b9904832 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala @@ -498,7 +498,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * This method will return false for impure statements, so evaluation can be terminated early. */ override def checkPurityOfStmt(stmt: Stmt[V])(implicit state: State): Boolean = { - println("check purity of statemt") + println("check purity of statemt" + stmt) (stmt.astID: @switch) match { // Synchronization on non-escaping local objects/arrays is pure (and useless...) case MonitorEnter.ASTID | MonitorExit.ASTID ⇒ From 612dcc656ff6d174bcbd82ff29c0048459f8239b Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 1 Jul 2020 14:10:48 +0200 Subject: [PATCH 162/327] inserting printlns for debugging purposes --- .../purity/AbstractPurityAnalysis_new.scala | 14 +++++++++----- .../analyses/purity/L2PurityAnalysis_new.scala | 8 ++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala index 98b9b87fd7..7e7d499bb7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala @@ -573,7 +573,9 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { implicit state: StateType ): Boolean = { + println("check purity of callees") handleCalleesUpdate(calleesEOptP) + println("callees eopt: " + calleesEOptP) calleesEOptP match { case UBPS(p: Callees, isFinal) ⇒ if (!isFinal) reducePurityLB(ImpureByAnalysis) @@ -588,7 +590,7 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { !isDomainSpecificCall(call, call.receiverOption) } } - + println("has icomplete callsite: "+ hasIncompleteCallSites) if (hasIncompleteCallSites) { atMost(ImpureByAnalysis) return false; @@ -610,9 +612,10 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { } } } - - if (!noDirectCalleeIsImpure) - return false; + println("no direct calleeisImpure: "+ noDirectCalleeIsImpure) + if (!noDirectCalleeIsImpure) { + return false + }; val noIndirectCalleeIsImpure = p.indirectCallSites().forall { case (pc, callees) ⇒ @@ -635,10 +638,11 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { } } } - + println("noIndirectCalleeIsImpure: " + noIndirectCalleeIsImpure) noIndirectCalleeIsImpure case _ ⇒ + println("else case 5") reducePurityLB(ImpureByAnalysis) true } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala index 29b9904832..13b0b297ef 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala @@ -498,7 +498,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * This method will return false for impure statements, so evaluation can be terminated early. */ override def checkPurityOfStmt(stmt: Stmt[V])(implicit state: State): Boolean = { - println("check purity of statemt" + stmt) + println("check purity of statemt"+stmt) (stmt.astID: @switch) match { // Synchronization on non-escaping local objects/arrays is pure (and useless...) case MonitorEnter.ASTID | MonitorExit.ASTID ⇒ @@ -632,9 +632,12 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) callees: EOptionP[DeclaredMethod, Callees] )(implicit state: State): Unit = { println("handle callees update") + println("callees: " + callees) state.updateCalleesDependee(callees) - if (callees.isRefinable) + if (callees.isRefinable) { + println("callee is refinable") reducePurityLB(ImpureByAnalysis) + } } /* @@ -894,6 +897,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) } println("pa2_3") val callees = propertyStore(state.definedMethod, Callees.key) + println("callees: " + callees) if (!checkPurityOfCallees(callees)) return Result(state.definedMethod, state.ubPurity) println("pa2_4") From 8994dee3c98c04d00b2919c4b06c136a453738f7 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 1 Jul 2020 16:12:57 +0200 Subject: [PATCH 163/327] inserting printlns for debugging purposes --- .../analyses/purity/AbstractPurityAnalysis_new.scala | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala index 7e7d499bb7..8f33c5fdc2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala @@ -575,7 +575,7 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { ): Boolean = { println("check purity of callees") handleCalleesUpdate(calleesEOptP) - println("callees eopt: " + calleesEOptP) + println("callees eopt: "+calleesEOptP) calleesEOptP match { case UBPS(p: Callees, isFinal) ⇒ if (!isFinal) reducePurityLB(ImpureByAnalysis) @@ -583,14 +583,17 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { val hasIncompleteCallSites = p.incompleteCallSites.exists { pc ⇒ val index = state.pcToIndex(pc) + println("index: " + index) if (index < 0) false // call will not be executed else { val call = getCall(state.code(state.pcToIndex(pc))) + println("getCall: "+ call) + println("isDomainspecificCall: " + isDomainSpecificCall(call, call.receiverOption)) !isDomainSpecificCall(call, call.receiverOption) } } - println("has icomplete callsite: "+ hasIncompleteCallSites) + println("has icomplete callsite: "+hasIncompleteCallSites) if (hasIncompleteCallSites) { atMost(ImpureByAnalysis) return false; @@ -612,7 +615,7 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { } } } - println("no direct calleeisImpure: "+ noDirectCalleeIsImpure) + println("no direct calleeisImpure: "+noDirectCalleeIsImpure) if (!noDirectCalleeIsImpure) { return false }; @@ -638,7 +641,7 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { } } } - println("noIndirectCalleeIsImpure: " + noIndirectCalleeIsImpure) + println("noIndirectCalleeIsImpure: "+noIndirectCalleeIsImpure) noIndirectCalleeIsImpure case _ ⇒ From 9399d395f60807e331d4654b1aea79459f224790 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 1 Jul 2020 16:40:32 +0200 Subject: [PATCH 164/327] set purity analysis rater --- .../scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala | 4 ++++ .../fpcf/ReferenceImmutabilityTests_withNewPurity.scala | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index 7bb33c068f..d65ff7522c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -18,7 +18,9 @@ import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater /** * @author Tobias Peter Roth @@ -44,6 +46,8 @@ class ReferenceImmutabilityTests extends PropertiesTest { validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) } + L2PurityAnalysis.setRater(Some(SystemOutLoggingAllExceptionRater)) + describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { val as = executeAnalyses( Set( diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala index 0df7b7e300..d44f8a99e4 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala @@ -20,7 +20,10 @@ import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis +import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater /** * @author Tobias Peter Roth @@ -30,7 +33,7 @@ class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") + List("org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization") ///reference_immutability_lazy_initialization/sandbox") } override def init(p: Project[URL]): Unit = { @@ -46,6 +49,8 @@ class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) } + L2PurityAnalysis_new.setRater(Some(SystemOutLoggingAllExceptionRater)) + describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { val as = executeAnalyses( Set( From 8998b043dc249a3f7aac5fe63289e1bca70432ce Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 1 Jul 2020 16:49:47 +0200 Subject: [PATCH 165/327] import issue --- .../opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala index d44f8a99e4..2b47f3c8bb 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala @@ -20,7 +20,6 @@ import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater From 2f29b599160016a42861b3533b0ae132a5857b3e Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Wed, 1 Jul 2020 17:17:18 +0200 Subject: [PATCH 166/327] run all imm tests --- .../opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala index 2b47f3c8bb..94b4f10a87 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala @@ -32,7 +32,7 @@ class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization") ///reference_immutability_lazy_initialization/sandbox") + List("org/opalj/fpcf/fixtures/immutability") // /reference_immutability_lazy_initialization") ///reference_immutability_lazy_initialization/sandbox") } override def init(p: Project[URL]): Unit = { From f8d993401f1be40b3cd940dfe59a46dfb8816cc0 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 11:44:46 +0200 Subject: [PATCH 167/327] inserting printlns for debugging purposes in rta call graph analysis --- .../tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala index fdada7307e..454ef2b399 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala @@ -123,6 +123,9 @@ class RTACallGraphAnalysis private[analyses] ( )) if (mResult.isEmpty) { + if(caller.name=="init" && caller.declaringClassType.toString.contains("DeterministicCall")){ + println("rta error is empty") + } unknownLibraryCall( caller, call.name, @@ -134,6 +137,9 @@ class RTACallGraphAnalysis private[analyses] ( calleesAndCallers ) } else if (isMethodOverridable(mResult.value).isYesOrUnknown) { + if(caller.name=="init" && caller.declaringClassType.toString.contains("DeterministicCall")){ + println("rta error is not empty") + } calleesAndCallers.addIncompleteCallSite(pc) } } From 919d5b30a068720ba5dfe83c982a6d56b21b2e94 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 12:53:30 +0200 Subject: [PATCH 168/327] inserting printlns for debugging purposes, revise --- .../tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala index 454ef2b399..d5f456d9f4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala @@ -123,8 +123,9 @@ class RTACallGraphAnalysis private[analyses] ( )) if (mResult.isEmpty) { - if(caller.name=="init" && caller.declaringClassType.toString.contains("DeterministicCall")){ + if(caller.declaringClassType.toString.toLowerCase().contains("deterministiccall")){ println("rta error is empty") + println("caller: " +caller) } unknownLibraryCall( caller, @@ -137,8 +138,10 @@ class RTACallGraphAnalysis private[analyses] ( calleesAndCallers ) } else if (isMethodOverridable(mResult.value).isYesOrUnknown) { - if(caller.name=="init" && caller.declaringClassType.toString.contains("DeterministicCall")){ + if(caller.declaringClassType.toString.toLowerCase().contains("deterministiccall")){ + println("rta error is not empty") + println("caller: " +caller) } calleesAndCallers.addIncompleteCallSite(pc) } From ad8a0544b88d0503eb5b95379dd0d7fed7df2e9b Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 13:31:58 +0200 Subject: [PATCH 169/327] inserting printlns for debugging purposes --- .../fpcf/analyses/cg/AbstractCallGraphAnalysis.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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..028c13b22d 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 @@ -138,18 +138,24 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { handleVirtualCall(state.method, call, call.pc, calls)(state) case Assignment(_, _, idc: InvokedynamicFunctionCall[V]) ⇒ + if(state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) + println(s"unresolved invokedynamics in call graph construction: $idc") calls.addIncompleteCallSite(idc.pc) logOnce( Warn("analysis - call graph construction", s"unresolved invokedynamic: $idc") ) case ExprStmt(_, idc: InvokedynamicFunctionCall[V]) ⇒ + if(state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) + println("analysis - call graph construction", s"unresolved invokedynamic: $idc") calls.addIncompleteCallSite(idc.pc) logOnce( Warn("analysis - call graph construction", s"unresolved invokedynamic: $idc") ) case idc: InvokedynamicMethodCall[_] ⇒ + if(state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) + println("analysis - call graph construction", s"unresolved invokedynamic: $idc") calls.addIncompleteCallSite(idc.pc) logOnce( Warn("analysis - call graph construction", s"unresolved invokedynamic: $idc") @@ -213,7 +219,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { packageName: String, pc: Int, calleesAndCallers: DirectCalls - ): Unit = { + )(implicit state: State): Unit = { val declaringClassType = callDeclaringClass.mostPreciseObjectType val runtimeType = runtimeReceiverType.mostPreciseObjectType @@ -234,6 +240,8 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { } } + if(state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) + println(s"unknown library call") calleesAndCallers.addIncompleteCallSite(pc) } From 8c21add17ecba6c4543749875c4e3d10eb31b708 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 13:41:16 +0200 Subject: [PATCH 170/327] inserting printlns for debugging purposes, revise --- .../fpcf/analyses/cg/AbstractCallGraphAnalysis.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 028c13b22d..38b742ca49 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 @@ -82,7 +82,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { )(implicit state: State): Unit protected final def processMethod( - state: State, calls: DirectCalls + implicit state: State, calls: DirectCalls ): ProperPropertyComputationResult = { val tac = state.tac @@ -147,7 +147,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { case ExprStmt(_, idc: InvokedynamicFunctionCall[V]) ⇒ if(state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) - println("analysis - call graph construction", s"unresolved invokedynamic: $idc") + println(s"analysis - call graph construction unresolved invokedynamic: $idc") calls.addIncompleteCallSite(idc.pc) logOnce( Warn("analysis - call graph construction", s"unresolved invokedynamic: $idc") @@ -155,10 +155,10 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { case idc: InvokedynamicMethodCall[_] ⇒ if(state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) - println("analysis - call graph construction", s"unresolved invokedynamic: $idc") + println(s"analysis - call graph construction unresolved invokedynamic: $idc") calls.addIncompleteCallSite(idc.pc) logOnce( - Warn("analysis - call graph construction", s"unresolved invokedynamic: $idc") + Warn(s"analysis - call graph construction unresolved invokedynamic: $idc") ) case _ ⇒ //nothing to do @@ -191,7 +191,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { pc: Int, target: org.opalj.Result[Method], calleesAndCallers: DirectCalls - ): Unit = { + )(implicit state:State) : Unit = { if (target.hasValue) { val tgtDM = declaredMethods(target.value) calleesAndCallers.addCall(caller, tgtDM, pc) From 670692e88df9b4a73a7d2c7a94e101a507661d7e Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 13:47:36 +0200 Subject: [PATCH 171/327] inserting printlns for debugging purposes, revise --- .../tac/fpcf/analyses/cg/AbstractCallGraphAnalysis.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 38b742ca49..3fd52a51bd 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 @@ -79,7 +79,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { specializedDeclaringClassType: ReferenceType, potentialTargets: ForeachRefIterator[ObjectType], calleesAndCallers: DirectCalls - )(implicit state: State): Unit + ): Unit protected final def processMethod( implicit state: State, calls: DirectCalls @@ -191,7 +191,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { pc: Int, target: org.opalj.Result[Method], calleesAndCallers: DirectCalls - )(implicit state:State) : Unit = { + ): Unit = { if (target.hasValue) { val tgtDM = declaredMethods(target.value) calleesAndCallers.addCall(caller, tgtDM, pc) @@ -219,7 +219,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { packageName: String, pc: Int, calleesAndCallers: DirectCalls - )(implicit state: State): Unit = { + ): Unit = { val declaringClassType = callDeclaringClass.mostPreciseObjectType val runtimeType = runtimeReceiverType.mostPreciseObjectType @@ -240,7 +240,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { } } - if(state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) + if(callDeclaringClass.toString.toLowerCase.contains("deterministiccall")) println(s"unknown library call") calleesAndCallers.addIncompleteCallSite(pc) } From 681dabf4150d79c6bf4facc7a3da1ef28146602f Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 13:56:42 +0200 Subject: [PATCH 172/327] inserting printlns for debugging purposes,revise --- .../tac/fpcf/analyses/cg/AbstractCallGraphAnalysis.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 3fd52a51bd..8961b8df70 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 @@ -79,10 +79,10 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { specializedDeclaringClassType: ReferenceType, potentialTargets: ForeachRefIterator[ObjectType], calleesAndCallers: DirectCalls - ): Unit + )(implicit state: State): Unit protected final def processMethod( - implicit state: State, calls: DirectCalls + state: State, calls: DirectCalls ): ProperPropertyComputationResult = { val tac = state.tac @@ -158,7 +158,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { println(s"analysis - call graph construction unresolved invokedynamic: $idc") calls.addIncompleteCallSite(idc.pc) logOnce( - Warn(s"analysis - call graph construction unresolved invokedynamic: $idc") + Warn("analysis - call graph construction", s"unresolved invokedynamic: $idc") ) case _ ⇒ //nothing to do From 6de092f4c160c5cbd34e34c52f3a5c8f1c32e0c2 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 14:18:29 +0200 Subject: [PATCH 173/327] inserting printlns for debugging purposes --- .../opalj/tac/fpcf/analyses/cg/AbstractCallGraphAnalysis.scala | 1 + 1 file changed, 1 insertion(+) 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 8961b8df70..15c53848d1 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 @@ -241,6 +241,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { } if(callDeclaringClass.toString.toLowerCase.contains("deterministiccall")) + println("caller: " + caller) println(s"unknown library call") calleesAndCallers.addIncompleteCallSite(pc) } From f03a58f5139b854f7a3f1277db1efed4e6e22d79 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 14:39:46 +0200 Subject: [PATCH 174/327] inserting exception for debugging purposes --- .../opalj/tac/fpcf/analyses/cg/AbstractCallGraphAnalysis.scala | 1 + 1 file changed, 1 insertion(+) 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 15c53848d1..d8e4cffbe9 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 @@ -241,6 +241,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { } if(callDeclaringClass.toString.toLowerCase.contains("deterministiccall")) + new Exception().printStackTrace() println("caller: " + caller) println(s"unknown library call") calleesAndCallers.addIncompleteCallSite(pc) From 60b3f52a168f1aa4141f94ac712def72b5995e29 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 15:23:34 +0200 Subject: [PATCH 175/327] compiling tests on github before run --- .github/workflows/scala.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/scala.yml b/.github/workflows/scala.yml index 19d693f6ca..8fd4069e8f 100644 --- a/.github/workflows/scala.yml +++ b/.github/workflows/scala.yml @@ -32,5 +32,7 @@ jobs: needs: build steps: + - name: compile Tests + run: sbt -J-Xmx20G test:compile - name: Run Tests run: sbt -J-Xmx20G test From b28ffbf77aa8793fc3ac9b340585573f8029c6f3 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 15:36:01 +0200 Subject: [PATCH 176/327] inserting printlns java version for debugging purposes --- .../opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala index 94b4f10a87..8c88a8d030 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala @@ -32,10 +32,11 @@ class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") // /reference_immutability_lazy_initialization") ///reference_immutability_lazy_initialization/sandbox") + List("org/opalj/fpcf/fixtures/immutability") ///reference_immutability_lazy_initialization/sandbox2") } override def init(p: Project[URL]): Unit = { + println("Java version: "+System.getProperty("java.version")) p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) } From 3f0a35ab38cb489b51567790b05ea632832f3110 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 15:44:46 +0200 Subject: [PATCH 177/327] inserting printlns for debugging purposes --- .../analyses/cg/AbstractCallGraphAnalysis.scala | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) 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 d8e4cffbe9..b9c6031411 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 @@ -138,15 +138,15 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { handleVirtualCall(state.method, call, call.pc, calls)(state) case Assignment(_, _, idc: InvokedynamicFunctionCall[V]) ⇒ - if(state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) - println(s"unresolved invokedynamics in call graph construction: $idc") + if (state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) + println(s"unresolved invokedynamics in call graph construction: $idc") calls.addIncompleteCallSite(idc.pc) logOnce( Warn("analysis - call graph construction", s"unresolved invokedynamic: $idc") ) case ExprStmt(_, idc: InvokedynamicFunctionCall[V]) ⇒ - if(state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) + if (state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) println(s"analysis - call graph construction unresolved invokedynamic: $idc") calls.addIncompleteCallSite(idc.pc) logOnce( @@ -154,7 +154,7 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { ) case idc: InvokedynamicMethodCall[_] ⇒ - if(state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) + if (state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) println(s"analysis - call graph construction unresolved invokedynamic: $idc") calls.addIncompleteCallSite(idc.pc) logOnce( @@ -240,10 +240,11 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { } } - if(callDeclaringClass.toString.toLowerCase.contains("deterministiccall")) - new Exception().printStackTrace() - println("caller: " + caller) + if (callDeclaringClass.toString.toLowerCase.contains(".deterministiccall")) { + new Exception().printStackTrace(System.out) + println("caller: "+caller) println(s"unknown library call") + } calleesAndCallers.addIncompleteCallSite(pc) } From 3f5e20de4828f524effcd2fe0410f79578a31e60 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 15:49:36 +0200 Subject: [PATCH 178/327] inserting println target --- .../analyses/cg/rta/RTACallGraphAnalysis.scala | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala index d5f456d9f4..0cc1627715 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala @@ -89,7 +89,10 @@ class RTACallGraphAnalysis private[analyses] ( call.name, call.descriptor ) - + if (caller.name.toLowerCase.contains("init") && caller.declaringClassType.toString.toLowerCase().contains("deterministiccall")) { + println("caller:"+caller) + println("target: "+tgtR) + } handleCall( caller, call.name, @@ -123,9 +126,9 @@ class RTACallGraphAnalysis private[analyses] ( )) if (mResult.isEmpty) { - if(caller.declaringClassType.toString.toLowerCase().contains("deterministiccall")){ - println("rta error is empty") - println("caller: " +caller) + if (caller.declaringClassType.toString.toLowerCase().contains("deterministiccall")) { + println("rta error is empty") + println("caller: "+caller) } unknownLibraryCall( caller, @@ -138,10 +141,10 @@ class RTACallGraphAnalysis private[analyses] ( calleesAndCallers ) } else if (isMethodOverridable(mResult.value).isYesOrUnknown) { - if(caller.declaringClassType.toString.toLowerCase().contains("deterministiccall")){ + if (caller.declaringClassType.toString.toLowerCase().contains("deterministiccall")) { println("rta error is not empty") - println("caller: " +caller) + println("caller: "+caller) } calleesAndCallers.addIncompleteCallSite(pc) } From 0fc4cf99f88743691b34a805fb3a222ce3f20f3f Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 15:53:35 +0200 Subject: [PATCH 179/327] println return all methods --- .../opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala index 0cc1627715..824b4ce288 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala @@ -92,6 +92,7 @@ class RTACallGraphAnalysis private[analyses] ( if (caller.name.toLowerCase.contains("init") && caller.declaringClassType.toString.toLowerCase().contains("deterministiccall")) { println("caller:"+caller) println("target: "+tgtR) + println(project.classFile(caller.declaringClassType).get.methods.mkString(", ")) } handleCall( caller, From 0a9cdfb6a9c1c9dfd1d02c8fa591a4d6c1bcfcb2 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Fri, 3 Jul 2020 16:09:49 +0200 Subject: [PATCH 180/327] inserting printlns for printing body of init for debugging purposes --- .../opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala index 824b4ce288..99aed3e91e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala @@ -92,7 +92,9 @@ class RTACallGraphAnalysis private[analyses] ( if (caller.name.toLowerCase.contains("init") && caller.declaringClassType.toString.toLowerCase().contains("deterministiccall")) { println("caller:"+caller) println("target: "+tgtR) + println("callstmt: "+ call.toString) println(project.classFile(caller.declaringClassType).get.methods.mkString(", ")) + println("method body:" + project.classFile(caller.declaringClassType).get.findMethod("init").head.body.get.instructions.toList.mkString(", ")) } handleCall( caller, From efa1e12e5bd76d27af27257443e1f0321d2104bf Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 9 Jul 2020 10:20:37 +0200 Subject: [PATCH 181/327] removing printlns from call graph analysis --- .../analyses/cg/AbstractCallGraphAnalysis.scala | 11 ----------- .../analyses/cg/rta/RTACallGraphAnalysis.scala | 16 ---------------- 2 files changed, 27 deletions(-) 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 b9c6031411..8ad9d4a7b3 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 @@ -138,24 +138,18 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { handleVirtualCall(state.method, call, call.pc, calls)(state) case Assignment(_, _, idc: InvokedynamicFunctionCall[V]) ⇒ - if (state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) - println(s"unresolved invokedynamics in call graph construction: $idc") calls.addIncompleteCallSite(idc.pc) logOnce( Warn("analysis - call graph construction", s"unresolved invokedynamic: $idc") ) case ExprStmt(_, idc: InvokedynamicFunctionCall[V]) ⇒ - if (state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) - println(s"analysis - call graph construction unresolved invokedynamic: $idc") calls.addIncompleteCallSite(idc.pc) logOnce( Warn("analysis - call graph construction", s"unresolved invokedynamic: $idc") ) case idc: InvokedynamicMethodCall[_] ⇒ - if (state.method.declaringClassType.toString.toLowerCase().contains("deterministiccall")) - println(s"analysis - call graph construction unresolved invokedynamic: $idc") calls.addIncompleteCallSite(idc.pc) logOnce( Warn("analysis - call graph construction", s"unresolved invokedynamic: $idc") @@ -240,11 +234,6 @@ trait AbstractCallGraphAnalysis extends ReachableMethodAnalysis { } } - if (callDeclaringClass.toString.toLowerCase.contains(".deterministiccall")) { - new Exception().printStackTrace(System.out) - println("caller: "+caller) - println(s"unknown library call") - } calleesAndCallers.addIncompleteCallSite(pc) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala index 99aed3e91e..ca5e16ee6a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala @@ -89,13 +89,6 @@ class RTACallGraphAnalysis private[analyses] ( call.name, call.descriptor ) - if (caller.name.toLowerCase.contains("init") && caller.declaringClassType.toString.toLowerCase().contains("deterministiccall")) { - println("caller:"+caller) - println("target: "+tgtR) - println("callstmt: "+ call.toString) - println(project.classFile(caller.declaringClassType).get.methods.mkString(", ")) - println("method body:" + project.classFile(caller.declaringClassType).get.findMethod("init").head.body.get.instructions.toList.mkString(", ")) - } handleCall( caller, call.name, @@ -129,10 +122,6 @@ class RTACallGraphAnalysis private[analyses] ( )) if (mResult.isEmpty) { - if (caller.declaringClassType.toString.toLowerCase().contains("deterministiccall")) { - println("rta error is empty") - println("caller: "+caller) - } unknownLibraryCall( caller, call.name, @@ -144,11 +133,6 @@ class RTACallGraphAnalysis private[analyses] ( calleesAndCallers ) } else if (isMethodOverridable(mResult.value).isYesOrUnknown) { - if (caller.declaringClassType.toString.toLowerCase().contains("deterministiccall")) { - - println("rta error is not empty") - println("caller: "+caller) - } calleesAndCallers.addIncompleteCallSite(pc) } } From 477101cf6f6e68d2214e32b027204524793c1c0b Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 9 Jul 2020 10:23:24 +0200 Subject: [PATCH 182/327] extending reference immutability properties for different lazy initialization cases --- .../properties/ReferenceImmutability.scala | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala index f8b1140745..b68913cdf4 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -56,7 +56,37 @@ case class ImmutableReference(notEscapes: Boolean) extends ReferenceImmutability that } -case object LazyInitializedReference extends ReferenceImmutability { +case object LazyInitializedThreadSafeReference extends ReferenceImmutability { + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = other match { + case ImmutableReference(_) ⇒ + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + case _ ⇒ + } + def meet(other: ReferenceImmutability): ReferenceImmutability = + if (other == MutableReference || other == LazyInitializedNotThreadSafeOrNotDeterministicReference || + other == LazyInitializedNotThreadSafeButDeterministicReference) { + other + } else { + this + } +} + +case object LazyInitializedNotThreadSafeButDeterministicReference extends ReferenceImmutability { + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = other match { + case ImmutableReference(_) | LazyInitializedThreadSafeReference ⇒ + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); + case _ ⇒ + } + + def meet(other: ReferenceImmutability): ReferenceImmutability = { + if (other == MutableReference || other == LazyInitializedNotThreadSafeOrNotDeterministicReference) { + other + } else { + this + } + } +} +case object LazyInitializedNotThreadSafeOrNotDeterministicReference extends ReferenceImmutability { def meet(other: ReferenceImmutability): properties.ReferenceImmutability = { if (other == MutableReference) { other @@ -64,15 +94,14 @@ case object LazyInitializedReference extends ReferenceImmutability { this } } - override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { other match { - case ImmutableReference(_) ⇒ + case ImmutableReference(_) | LazyInitializedNotThreadSafeButDeterministicReference | + LazyInitializedThreadSafeReference ⇒ throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); case _ ⇒ } } - } case object MutableReference extends ReferenceImmutability { From 39d8e0221b555c9f74b9ff34120c3450461599fa Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 9 Jul 2020 10:25:21 +0200 Subject: [PATCH 183/327] removing printlns from old purity analysis --- .../purity/AbstractPurityAnalysis.scala | 1 - .../analyses/purity/L2PurityAnalysis.scala | 40 ------------------- 2 files changed, 41 deletions(-) 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 31235c0505..8e464d51d2 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 @@ -390,7 +390,6 @@ trait AbstractPurityAnalysis extends FPCFAnalysis { ep: EOptionP[Field, FieldMutability], objRef: Option[Expr[V]] )(implicit state: StateType): Unit = { - println("pa1 ep of checkFieldMutability: "+ep) ep match { case LBP(_: FinalField) ⇒ // Final fields don't impede purity case _: FinalEP[Field, FieldMutability] ⇒ // Mutable field 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 0fcd383867..162f66709c 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 @@ -729,9 +729,7 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst * - classes files for class types returned (for their mutability) */ def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { - println("pa1 enter continuation") val oldPurity = state.ubPurity - println("pa1 eps: "+eps) eps.ub.key match { case Purity.key ⇒ val e = eps.e.asInstanceOf[DeclaredMethod] @@ -794,7 +792,6 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst } if (state.ubPurity eq ImpureByAnalysis) { - println("pa1 upPurity eq ImpureByAnalysis") return Result(state.definedMethod, ImpureByAnalysis) }; @@ -804,19 +801,8 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst val dependees = state.dependees if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { - println("pa1 dependees empty lb=ub; result : "+state.ubPurity) Result(state.definedMethod, state.ubPurity) } else { - println( - s""" - |pa1 - | interim result - | lbPurity: ${state.lbPurity} - | ubPurity. ${state.ubPurity} - | dependees: ${dependees.mkString(", ")} - | - |""".stripMargin - ) InterimResult( state.definedMethod, state.lbPurity, @@ -898,26 +884,8 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst val dependees = state.dependees if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { - println( - s""" - |pa1 - |return result: - |defined method: ${state.definedMethod} - | ubPurity: ${state.ubPurity} - |""".stripMargin - ) Result(state.definedMethod, state.ubPurity) } else { - println( - s""" - |pa1 - | interim result - | lbPurity: ${state.lbPurity} - | ubPurity. ${state.ubPurity} - | dependees: ${dependees.mkString(", ")} - | - |""".stripMargin - ) org.opalj.fpcf.InterimResult(state.definedMethod, state.lbPurity, state.ubPurity, dependees, c) } } @@ -942,14 +910,6 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst val tacaiO = getTACAI(method) if (tacaiO.isEmpty) { - println( - s""" - |pa1 - |interim result - |tacai0 is empty - | - |""".stripMargin - ) return InterimResult( definedMethod, ImpureByAnalysis, From 1a54a31ba14913febeda3945f579d21e6aeb563c Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 9 Jul 2020 10:27:19 +0200 Subject: [PATCH 184/327] removing printlns from the new purity analysis and extending the analysis with the new lazy initialization reference immutability properties --- .../purity/AbstractPurityAnalysis_new.scala | 22 ++----- .../purity/L2PurityAnalysis_new.scala | 64 +------------------ 2 files changed, 8 insertions(+), 78 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala index 8f33c5fdc2..409f3bd12a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala @@ -22,7 +22,9 @@ import org.opalj.br.fpcf.properties.DeepImmutableType import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.ImpureByAnalysis import org.opalj.br.fpcf.properties.ImpureByLackOfInformation -import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.Pure import org.opalj.br.fpcf.properties.Purity @@ -444,15 +446,14 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], objRef: Option[Expr[V]] )(implicit state: StateType): Unit = { - println("pa2 ep of checkFieldMutability: "+ep) ep match { //case LBP(ImmutableReference(_)) ⇒ //case LBP(LazyInitializedReference) ⇒ - case LBP(ImmutableReference(_) | LazyInitializedReference) ⇒ - println("pa2 ====>>>> EP: "+ep) //_: FinalField) ⇒ // Final fields don't impede purity - case FinalP(MutableReference) ⇒ - println("pa2 ====>>>> EP: "+ep) //_: FinalEP[Field, ReferenceImmutability] ⇒ //FieldMutability] ⇒ // Mutable field + case LBP(ImmutableReference(_) | LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ + //_: FinalField) ⇒ // Final fields don't impede purity + case FinalP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference) ⇒ + //_: FinalEP[Field, ReferenceImmutability] ⇒ //FieldMutability] ⇒ // Mutable field if (objRef.isDefined) { if (state.ubPurity.isDeterministic) isLocal(objRef.get, SideEffectFree) @@ -573,9 +574,7 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { implicit state: StateType ): Boolean = { - println("check purity of callees") handleCalleesUpdate(calleesEOptP) - println("callees eopt: "+calleesEOptP) calleesEOptP match { case UBPS(p: Callees, isFinal) ⇒ if (!isFinal) reducePurityLB(ImpureByAnalysis) @@ -583,17 +582,13 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { val hasIncompleteCallSites = p.incompleteCallSites.exists { pc ⇒ val index = state.pcToIndex(pc) - println("index: " + index) if (index < 0) false // call will not be executed else { val call = getCall(state.code(state.pcToIndex(pc))) - println("getCall: "+ call) - println("isDomainspecificCall: " + isDomainSpecificCall(call, call.receiverOption)) !isDomainSpecificCall(call, call.receiverOption) } } - println("has icomplete callsite: "+hasIncompleteCallSites) if (hasIncompleteCallSites) { atMost(ImpureByAnalysis) return false; @@ -615,7 +610,6 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { } } } - println("no direct calleeisImpure: "+noDirectCalleeIsImpure) if (!noDirectCalleeIsImpure) { return false }; @@ -641,11 +635,9 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { } } } - println("noIndirectCalleeIsImpure: "+noIndirectCalleeIsImpure) noIndirectCalleeIsImpure case _ ⇒ - println("else case 5") reducePurityLB(ImpureByAnalysis) true } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala index 13b0b297ef..a97441f95f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala @@ -498,7 +498,6 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * This method will return false for impure statements, so evaluation can be terminated early. */ override def checkPurityOfStmt(stmt: Stmt[V])(implicit state: State): Boolean = { - println("check purity of statemt"+stmt) (stmt.astID: @switch) match { // Synchronization on non-escaping local objects/arrays is pure (and useless...) case MonitorEnter.ASTID | MonitorExit.ASTID ⇒ @@ -542,7 +541,6 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) params: Seq[Expr[V]] )(implicit state: State): Boolean = { - println("pa2 check metho purity. ep: "+ep) ep match { case UBP(_: ClassifiedImpure) ⇒ atMost(ImpureByAnalysis) @@ -579,7 +577,6 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) def checkStaticDataUsage( ep: EOptionP[DeclaredMethod, StaticDataUsage] )(implicit state: State): Unit = { - println("checkstatic data usage") ep match { case LBP(UsesNoStaticData | UsesConstantDataOnly) ⇒ state.updateStaticDataUsage(None) @@ -599,7 +596,6 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], objRef: Option[Expr[V]] )(implicit state: State): Unit = { - println("ep2 handle unknown field mutability") state.addFieldMutabilityDependee(ep.e, ep, objRef) } @@ -610,7 +606,6 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) ep: EOptionP[ObjectType, Property], expr: Expr[V] )(implicit state: State): Unit = { - println("ep2 handle unknown type mutability") if (ep.pk == ClassImmutability_new.key) //ClassImmutability.key) state.addClassImmutabilityDependee( ep.e, @@ -631,11 +626,8 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) override def handleCalleesUpdate( callees: EOptionP[DeclaredMethod, Callees] )(implicit state: State): Unit = { - println("handle callees update") - println("callees: " + callees) state.updateCalleesDependee(callees) if (callees.isRefinable) { - println("callee is refinable") reducePurityLB(ImpureByAnalysis) } } @@ -644,7 +636,6 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * Adds the dependee necessary if the TACAI is not yet final. */ override def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: State): Unit = { - println("handle tacai") state.updateTacai(ep) } @@ -653,7 +644,6 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * purity level further. */ def cleanupDependees()(implicit state: State): Unit = { - println("ep2 cleanup dependees") if (state.ubPurity ne CompileTimePure) state.updateStaticDataUsage(None) @@ -696,7 +686,6 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * Raises the lower bound on the purity whenever possible. */ def adjustLowerBound()(implicit state: State): Unit = { - println("adjust lower bound") if (state.calleesDependee.isDefined) return ; // Nothing to be done, lower bound is still LBImpure @@ -751,9 +740,7 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) * - classes files for class types returned (for their mutability) */ def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { - println("pa2 enter continuation") val oldPurity = state.ubPurity - println("pa2 eps: "+eps) eps.ub.key match { case Purity.key ⇒ val e = eps.e.asInstanceOf[DeclaredMethod] @@ -819,7 +806,6 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) } if (state.ubPurity eq ImpureByAnalysis) { - println("pa2 upPurity eq ImpureByAnalysis") return Result(state.definedMethod, ImpureByAnalysis) }; @@ -829,19 +815,8 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) val dependees = state.dependees if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { - println("pa2 dependees empty lb=ub; result : "+state.ubPurity) Result(state.definedMethod, state.ubPurity) } else { - println( - s""" - |pa2 - | interim result - | lbPurity: ${state.lbPurity} - | ubPurity. ${state.ubPurity} - | dependees: ${dependees.mkString(", ")} - | - |""".stripMargin - ) InterimResult( state.definedMethod, state.lbPurity, @@ -858,7 +833,6 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) def determineMethodPurity( cfg: CFG[Stmt[V], TACStmts[V]] )(implicit state: State): ProperPropertyComputationResult = { - println("pa2 determine method purity of method: "+state.method) // Special case: The Throwable constructor is `LBSideEffectFree`, but subtype constructors // may not be because of overridable fillInStackTrace method if (state.method.isConstructor && state.declClass.isSubtypeOf(ObjectType.Throwable)) @@ -875,17 +849,14 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) } } } - println("pa2_1") // Synchronized methods have a visible side effect on the receiver // Static synchronized methods lock the class which is potentially globally visible if (state.method.isSynchronized) { - println("pa2 methods is synchronized") if (state.method.isStatic) { - println("pa2 method is additionally static and thus impure") + //("pa2 method is additionally static and thus impure") return Result(state.definedMethod, ImpureByAnalysis); } else atMost(ContextuallyPure(IntTrieSet(0))) } - println("pa2_2") val stmtCount = state.code.length var s = 0 while (s < stmtCount) { @@ -895,18 +866,14 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) } s += 1 } - println("pa2_3") val callees = propertyStore(state.definedMethod, Callees.key) - println("callees: " + callees) if (!checkPurityOfCallees(callees)) return Result(state.definedMethod, state.ubPurity) - println("pa2_4") if (callees.hasUBP) state.rvfCallSites.foreach { case (pc, data) ⇒ checkFreshnessOfReturn(pc, data, callees.ub) } - println("pa2_5") // Creating implicit exceptions is side-effect free (because of fillInStackTrace) // but it may be ignored as domain-specific val bbsCausingExceptions = cfg.abnormalReturnNode.predecessors @@ -921,34 +888,14 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) else atMost(SideEffectFree) } } - println("pa2_6") if (state.ubPurity eq CompileTimePure) // Check static data usage only if necessary checkStaticDataUsage(propertyStore(state.definedMethod, StaticDataUsage.key)) else cleanupDependees() // Remove dependees we already know we won't need - println("pa2_7") val dependees = state.dependees if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { - println( - s""" - |pa2 - |return result: - |defined method: ${state.definedMethod} - | ubPurity: ${state.ubPurity} - |""".stripMargin - ) Result(state.definedMethod, state.ubPurity) } else { - println( - s""" - |pa2 - | interim result - | lbPurity: ${state.lbPurity} - | ubPurity. ${state.ubPurity} - | dependees: ${dependees.mkString(", ")} - | - |""".stripMargin - ) org.opalj.fpcf.InterimResult( state.definedMethod, state.lbPurity, @@ -967,7 +914,6 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult = { val method = definedMethod.definedMethod val declClass = method.classFile.thisType - println("pa2 determine purity of method: "+method+", with class: "+declClass) // If this is not the method's declaration, but a non-overwritten method in a subtype, // don't re-analyze the code @@ -980,14 +926,6 @@ class L2PurityAnalysis_new private[analyses] (val project: SomeProject) val tacaiO = getTACAI(method) if (tacaiO.isEmpty) { - println( - s""" - |pa2 - |interim result - |tacai0 is empty - | - |""".stripMargin - ) return InterimResult( definedMethod, ImpureByAnalysis, From 7261eda4dd915ec682070c94675c1907cd01fa23 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 9 Jul 2020 10:32:19 +0200 Subject: [PATCH 185/327] removing old first lazy initialization analysis try --- ...bilityLazyInitializationAnalysisDemo.scala | 133 -- ...AnalysisDemo_performanceMeasurements.scala | 117 -- .../LazyInitializationAnnotation.java | 29 - ...hreadSafeLazyInitializationAnnotation.java | 36 - ...eImmutabilityLazyInitializationTests.scala | 87 -- .../ReferenceImmutabilityTests_sandbox.scala | 63 - ...mmutabilityLazyInitializationMatcher.scala | 62 - ...erenceImmutabilityLazyInitialization.scala | 50 - ...mutabilityLazyInitializationAnalysis.scala | 1342 ----------------- 9 files changed, 1919 deletions(-) delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala delete mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_sandbox.scala delete mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala delete mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutabilityLazyInitialization.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0ReferenceImmutabilityLazyInitializationAnalysis.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala deleted file mode 100644 index f0d97dcd47..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo.scala +++ /dev/null @@ -1,133 +0,0 @@ -/* 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.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.LazyClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis -import org.opalj.br.fpcf.properties.LazyInitialization -import org.opalj.br.fpcf.properties.NoLazyInitialization -import org.opalj.br.fpcf.properties.NotThreadSafeLazyInitialization -import org.opalj.fpcf.PropertyStore -import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis -import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis -import org.opalj.util.PerformanceEvaluation.time -import org.opalj.util.Seconds - -/** - * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. - * - * @author Tobias Peter Roth - */ -object ReferenceImmutabilityLazyInitializationAnalysisDemo extends ProjectAnalysisApplication { - - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(project: Project[URL]): String = { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityLazyInitializationAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyStaticDataUsageAnalysis, - LazyTypeImmutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyFieldLocalityAnalysis, - LazyReturnValueFreshnessAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - /*def printResult(string: String, property: Property)( - implicit - propertyStore: PropertyStore - ): Unit = {} **/ - val sb: StringBuilder = new StringBuilder() - - sb.append("Not initialized References: \n") - sb.append( - propertyStore - .finalEntities(NoLazyInitialization) - .toList - .map(x ⇒ x.toString+"\n") - .toString() - ) - sb.append("\nNot threadsafe Lazy Initialization: \n") - sb.append( - propertyStore - .finalEntities(NotThreadSafeLazyInitialization) - .toList - .map(x ⇒ x.toString+"\n") - .toString() - ) - sb.append("\nLazy Initialization: \n") - sb.append( - propertyStore - .finalEntities(LazyInitialization) - .toList - .map(x ⇒ x.toString+"\n") - .toString() - ) - - /** - * "Not lazy initialized References: " + propertyStore - * .finalEntities(NoLazyInitialization) - * .toList - * .toString() + "\n" + - * "Not thread safe lazy initialization: " + propertyStore - * .finalEntities(NotThreadSafeLazyInitialization) - * .toList - * .toString() + "\n" + - * "Lazy Initialization: " + propertyStore - * .finalEntities(LazyInitialization) - * .toList - * .toString()* - */ - val dateString: String = Calendar.getInstance().get(Calendar.MILLISECOND).toString - val file = new File("C:/MA/results/refDCLImm"+dateString+".txt") - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() - - " took : "+analysisTime+" seconds" - s"took $analysisTime seconds " - } -} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala deleted file mode 100644 index cafd2bb0e1..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements.scala +++ /dev/null @@ -1,117 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.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.LazyClassImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis -import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis -import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis -import org.opalj.fpcf.PropertyStore -import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis -import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis -import org.opalj.util.PerformanceEvaluation.time -import org.opalj.util.Seconds - -/** - * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. - * - * @author Tobias Peter Roth - */ -object ReferenceImmutabilityLazyInitializationAnalysisDemo_performanceMeasurements - extends ProjectAnalysisApplication { - - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(theProject: Project[URL]): String = { - var times: List[Seconds] = Nil: List[Seconds] - for (i ← 0 until 10) { - val project = Project.recreate(theProject) - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityLazyInitializationAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyStaticDataUsageAnalysis, - LazyTypeImmutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyFieldLocalityAnalysis, - LazyReturnValueFreshnessAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - times = analysisTime :: times - } - /*def printResult(string: String, property: Property)( - implicit - propertyStore: PropertyStore - ): Unit = {} **/ - /** - * val notInitializedReferencesString = propertyStore - * .finalEntities(NoLazyInitialization) - * .toList - * .toString() - * val notThreadSafeLazyInitializationString = propertyStore - * .finalEntities(NotThreadSafeLazyInitialization) - * .toList - * .toString() - * val lazyInitializationString = propertyStore - * .finalEntities(LazyInitialization) - * .toList - * .toString() - * println(s"Not initialized References $notInitializedReferencesString") - * println(s"Not threadsafe Lazy Initialization: $notThreadSafeLazyInitializationString") - * println(s"Lazy Initialization String: $lazyInitializationString") - */ - /** - * "Not lazy initialized References: " + propertyStore - * .finalEntities(NoLazyInitialization) - * .toList - * .toString() + "\n" + - * "Not thread safe lazy initialization: " + propertyStore - * .finalEntities(NotThreadSafeLazyInitialization) - * .toList - * .toString() + "\n" + - * "Lazy Initialization: " + propertyStore - * .finalEntities(LazyInitialization) - * .toList - * .toString()* - */ - times.foreach(s ⇒ println(s+" seconds")) - val aver = times.fold(new Seconds(0))((x: Seconds, y: Seconds) ⇒ x + y).timeSpan / times.size - f"took: $aver seconds on average" - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java deleted file mode 100644 index cd5dbd96cb..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/LazyInitializationAnnotation.java +++ /dev/null @@ -1,29 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.reference_immutability_lazy_initialization; - -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Annotation to state that the annotated field is lazy initialized - * - * @author Tobias Peter Roth - */ -@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = LazyInitializationMatcher.class) -@Documented -@Retention(RetentionPolicy.CLASS) -public @interface LazyInitializationAnnotation { - - /** - * A short reasoning of this property. - */ - String value();// default = "N/A"; - - Class[] analyses() default {L0ReferenceImmutabilityLazyInitializationAnalysis.class}; - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java deleted file mode 100644 index 4154597d3a..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NotThreadSafeLazyInitializationAnnotation.java +++ /dev/null @@ -1,36 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.reference_immutability_lazy_initialization; - -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Annotation to state that the annotated reference is mutable - * - * @author Tobias Peter Roth - */ -@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = NotThreadSafeInitializationMatcher.class) -@Documented -@Retention(RetentionPolicy.CLASS) -public @interface NotThreadSafeLazyInitializationAnnotation { - - - /** - * True if the field is non-final because it is read prematurely. - * Tests may ignore @NonFinal annotations if the FieldPrematurelyRead property for the field - * did not identify the premature read. - */ - boolean prematurelyRead() default false; - /** - * A short reasoning of this property. - */ - String value() default "N/A"; - - Class[] analyses() default {L0ReferenceImmutabilityLazyInitializationAnalysis.class}; - -} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala deleted file mode 100644 index 7fa6b66dbc..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityLazyInitializationTests.scala +++ /dev/null @@ -1,87 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf - -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.LazyUnsoundPrematurelyReadFieldsAnalysis -import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.EagerL0ReferenceImmutabilityLazyInitializationAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis - -/** - * @author Tobias Peter Roth - */ -class ReferenceImmutabilityLazyInitializationTests extends PropertiesTest { - - override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures") - } - - 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("ReferenceImmutabilityLazyInitialization") - ) - } - - describe("the org.opalj.fpcf.analyses.ReferenceImmutabilityLazyInitialization is executed") { - val as = executeAnalyses( - Set( - EagerL0ReferenceImmutabilityLazyInitializationAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis - ) - ) - as.propertyStore.shutdown() - validateProperties( - as, - fieldsWithAnnotations(as.project), - Set("ReferenceImmutabilityLazyInitialization") - ) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ - -} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_sandbox.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_sandbox.scala deleted file mode 100644 index 683f6b22e4..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_sandbox.scala +++ /dev/null @@ -1,63 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf - -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.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.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new - -/** - * @author Tobias Peter Roth - */ -class ReferenceImmutabilityTests_sandbox extends PropertiesTest { - - override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/reference_immutability_sandbox") - } - - 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("ReferenceImmutability")) - } - - describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { - val as = executeAnalyses( - Set( - EagerL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyInterProceduralEscapeAnalysis, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala deleted file mode 100644 index 8ab9e6e7f3..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/ReferenceImmutabilityLazyInitializationMatcher.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.reference_immutability_lazy_initialization - -import org.opalj.br.AnnotationLike -import org.opalj.br.ObjectType -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties.LazyInitialization -import org.opalj.br.fpcf.properties.NoLazyInitialization -import org.opalj.br.fpcf.properties.NotThreadSafeLazyInitialization -import org.opalj.br.fpcf.properties.ReferenceImmutabilityLazyInitialization -import org.opalj.fpcf.Entity -import org.opalj.fpcf.Property -import org.opalj.fpcf.properties.AbstractPropertyMatcher - -/** - * @author Tobias Peter Roth - */ -class ReferenceImmutabilityLazyInitializationMatcher( - val property: ReferenceImmutabilityLazyInitialization -) 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 NoLazyInitializationMatcher - extends ReferenceImmutabilityLazyInitializationMatcher(NoLazyInitialization) - -class NotThreadSafeInitializationMatcher - extends ReferenceImmutabilityLazyInitializationMatcher(NotThreadSafeLazyInitialization) - -class LazyInitializationMatcher - extends ReferenceImmutabilityLazyInitializationMatcher(LazyInitialization) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutabilityLazyInitialization.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutabilityLazyInitialization.scala deleted file mode 100644 index 1f0a796ec7..0000000000 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutabilityLazyInitialization.scala +++ /dev/null @@ -1,50 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.br.fpcf.properties - -import org.opalj.fpcf.Property -import org.opalj.fpcf.PropertyKey -import org.opalj.fpcf.PropertyMetaInformation - -sealed trait ReferenceImmutabilityLazyInstantiationPropertyMetaInformation - extends PropertyMetaInformation { - - type Self = ReferenceImmutabilityLazyInitialization - -} - -/** - * Describes if the reference of a org.opalj.br.Field was lazyliy initialized. - * - * [[NoLazyInitialization]] The reference is not lazily initialized - * - * [[NotThreadSafeLazyInitialization]] The reference is lazily initialized, but not threadsafe - * - * [[LazyInitialization]] The reference is lazy initialized - * - * @author Tobias Peter Roth - */ -sealed trait ReferenceImmutabilityLazyInitialization - extends Property - with ReferenceImmutabilityLazyInstantiationPropertyMetaInformation { - final def key: PropertyKey[ReferenceImmutabilityLazyInitialization] = - ReferenceImmutabilityLazyInitialization.key -} - -object ReferenceImmutabilityLazyInitialization - extends ReferenceImmutabilityLazyInstantiationPropertyMetaInformation { - - final val PropertyKeyName = "opalj.ReferenceImmutabilityLazyInitialization" - - final val key: PropertyKey[ReferenceImmutabilityLazyInitialization] = { - PropertyKey.create( - PropertyKeyName, - NoLazyInitialization - ) - } -} - -case object NoLazyInitialization extends ReferenceImmutabilityLazyInitialization - -case object NotThreadSafeLazyInitialization extends ReferenceImmutabilityLazyInitialization - -case object LazyInitialization extends ReferenceImmutabilityLazyInitialization diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0ReferenceImmutabilityLazyInitializationAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0ReferenceImmutabilityLazyInitializationAnalysis.scala deleted file mode 100644 index 69c6da4f1a..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0ReferenceImmutabilityLazyInitializationAnalysis.scala +++ /dev/null @@ -1,1342 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses - -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.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.FieldAccessInformationKey -import org.opalj.br.analyses.SomeProject -import org.opalj.br.analyses.cg.ClosedPackagesKey -import org.opalj.br.analyses.cg.TypeExtensibilityKey -import org.opalj.br.cfg.BasicBlock -import org.opalj.br.cfg.CFG -import org.opalj.br.cfg.CFGNode -import org.opalj.br.fpcf.FPCFAnalysisScheduler -import org.opalj.br.fpcf.properties.EscapeProperty -import org.opalj.br.fpcf.properties.FieldPrematurelyRead -import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.properties.cg.Callees -import org.opalj.ai.isImmediateVMException -import org.opalj.ai.pcOfImmediateVMException -import org.opalj.ai.pcOfMethodExternalException -import org.opalj.br.ComputationalTypeFloat -import org.opalj.br.ComputationalTypeInt -import org.opalj.br.DeclaredMethod -import org.opalj.br.Field -import org.opalj.br.FloatType -import org.opalj.br.Method -import org.opalj.br.ObjectType -import org.opalj.br.PC -import org.opalj.br.PCs -import org.opalj.br.ReferenceType -import org.opalj.br.fpcf.properties.AtMost -import org.opalj.br.fpcf.properties.EscapeInCallee -import org.opalj.br.fpcf.properties.EscapeViaReturn -import org.opalj.br.fpcf.properties.ImmutableReference -import org.opalj.br.fpcf.properties.LazyInitialization -import org.opalj.br.fpcf.properties.MutableReference -import org.opalj.br.fpcf.properties.NoEscape -import org.opalj.br.fpcf.properties.NoLazyInitialization -import org.opalj.br.fpcf.properties.NotPrematurelyReadField -import org.opalj.br.fpcf.properties.NotThreadSafeLazyInitialization -import org.opalj.br.fpcf.properties.PrematurelyReadField -import org.opalj.br.fpcf.properties.ReferenceImmutability -import org.opalj.br.fpcf.properties.ReferenceImmutabilityLazyInitialization -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.LBP -import org.opalj.fpcf.ProperPropertyComputationResult -import org.opalj.fpcf.Property -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyComputationResult -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.SomeEPS -import org.opalj.fpcf.UBP -import org.opalj.fpcf.Result -import org.opalj.tac.Assignment -import org.opalj.tac.CaughtException -import org.opalj.tac.DUVar -import org.opalj.tac.Expr -import org.opalj.tac.FieldWriteAccessStmt -import org.opalj.tac.GetField -import org.opalj.tac.GetStatic -import org.opalj.tac.If -import org.opalj.tac.NonVirtualFunctionCall -import org.opalj.tac.PutField -import org.opalj.tac.PutStatic -import org.opalj.tac.ReturnValue -import org.opalj.tac.StaticFunctionCall -import org.opalj.tac.Stmt -import org.opalj.tac.TACMethodParameter -import org.opalj.tac.TACStmts -import org.opalj.tac.TACode -import org.opalj.tac.VirtualFunctionCall -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.tac.SelfReferenceParameter - -import scala.collection.mutable - -/** - * - * Implementation is used from the old L2FieldMutability implementation - * but the lattice is mapped to the new reference immutability lattice. - * - * @note Requires that the 3-address code's expressions are not deeply nested. - * - * @author Dominik Helm - * @author Florian Kübler - * @author Michael Eichberg - * @author Tobias Peter Roth - */ -class L0ReferenceImmutabilityLazyInitializationAnalysis private[analyses] (val project: SomeProject) - extends FPCFAnalysis { - - case class State( - field: Field, - var referenceImmutability: ReferenceImmutabilityLazyInitialization = NoLazyInitialization, - 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, - var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, - var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty, - var isThreadSafeType: Boolean = true - ) { - def hasDependees: Boolean = { - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || - escapeDependees.nonEmpty || tacDependees.nonEmpty - } - - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) - } - } - - 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) - - def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field ⇒ determineReferenceImmutabilityLazyInitialization(field) - case _ ⇒ - val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } - - /** - * Analyzes the mutability of private non-final fields. - * - * 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 determineReferenceImmutabilityLazyInitialization( - field: Field - ): ProperPropertyComputationResult = { - implicit val state: State = State(field) - // Fields are not final if they are read prematurely! - //if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) - // return Result(field, MutableReference); //Result(field, NonFinalFieldByAnalysis); - //if (field.isFinal) - // return createResult(); - - //state.referenceImmutability = ImmutableReference //EffectivelyFinalField - - //val thisType = field.classFile.thisType - - //if (field.isPublic) - // return Result(field, MutableReference); //Result(field, NonFinalFieldByLackOfInformation) - - // 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) { - * if (!closedPackages.isClosed(thisType.packageName)) { - * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - * } - * project.classesPerPackage(thisType.packageName) - * } else { - * Set(field.classFile) - * } - * - * val classesHavingAccess: Iterator[ClassFile] = - * if (field.isProtected) { - * if (typeExtensibility(thisType).isYesOrUnknown) { - * return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - * } - * 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))) - /// return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); - - // 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 - // of the class after the reference to the class and (an indirect) reference to the - // field has become available. Consider the following example: - // class X implements Y{ - // - // private Object o; - // - // public Object getO() { return o; } - // - // private static X instance; - // static { - // instance = new X(); - // Z.register(instance); - // // when we reach this point o is now (via getO) publically accessible and - // // X is properly initialized! - // o = new Object(); // o is mutated... - // } - // } - - /** - * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) - None - } - } - - for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode ← getTACAI(method, pcs) - } { - //if ( - checkMethod(method, taCode, pcs) //) { - // return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnalysis); - //} - - } - - ///if (state.lazyInitInvocation.isDefined) { - /// val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - /// handleCalls(calleesEOP) - ///} - - createResult() - } - - /** - * def handleCalls( - * calleesEOP: EOptionP[DeclaredMethod, Callees] - * )( - * implicit - * state: State - * ): Boolean = { - * calleesEOP match { - * case FinalP(callees) => - * state.calleesDependee = None - * handleCallees(callees) - * case InterimUBP(callees) => - * state.calleesDependee = Some(calleesEOP) - * handleCallees(callees) - * case _ => - * state.calleesDependee = Some(calleesEOP) - * false - * } - * }* - */ - /** - * - * def handleCallees(callees: Callees)(implicit state: State): Boolean = { - * val pc = state.lazyInitInvocation.get._2 - * if (callees.isIncompleteCallSite(pc)) { - * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - * true - * } else { - * val targets = callees.callees(pc).toTraversable - * if (targets.exists(target => isNonDeterministic(propertyStore(target, Purity.key)))) { - * state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - * true - * } else false - * } - * }* - */ - /** - * Returns the value the field will have after initialization or None if there may be multiple - * values. - */ - def getDefaultValue()(implicit state: State): Option[Any] = { - Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field - * The code below can be used to identify such value, but is not yet adapted to using the - * TACAI property */ - /* - var constantVal: Option[Any] = None - var allInitializeConstant = true - val field = state.field - var constructors: Set[Method] = - if(field.isStatic) Set.empty else field.classFile.constructors.toSet - val writesIterator = fieldAccessInformation.writeAccesses(field).iterator - while (writesIterator.hasNext && allInitializeConstant) { - val (method, pc) = writesIterator.next() - constructors -= method - val code = tacai(method).stmts - val index = pcToIndex(pc) - val stmt = code(index) - if (stmt.astID == PutStatic.ASTID || - stmt.asPutField.objRef.asVar.definedBy == SelfReferenceParameter) { - val write = stmt.asFieldWriteAccessStmt - if (write.resolveField(p).contains(state.field)) { - val defs = write.value.asVar.definedBy - if (defs.size == 1 && defs.head >= 0) { - val defSite = code(defs.head).asAssignment.expr - val const = if (defSite.isIntConst) - Some(defSite.asIntConst.value) - else if (defSite.isFloatConst) - Some(defSite.asFloatConst.value) - else None - if (const.isDefined) { - if (constantVal.isDefined) { - if (constantVal != const) { - allInitializeConstant = false - constantVal = None - } - } else constantVal = const - } else { - allInitializeConstant = false - constantVal = None - } - } - } - } - } - for (constructor ← constructors) { - // TODO iterate all statements - val NonVirtualMethodCall(_, declClass, _, name, _, rcvr, _) = stmt - // Consider calls to other constructors as initializations as either - // the called constructor will initialize the field or delegate to yet - // another constructor - if (declClass != state.field.classFile.thisType || name != "" || - rcvr.asVar.definedBy != SelfReferenceParameter) { - if (constantVal.isDefined) allInitializeConstant = false - else constantVal = Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - } - } - constantVal */ - } - - /** - * 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 NoLazyInitialization)) //NonFinalFieldByAnalysis)) - InterimResult( - state.field, - NoLazyInitialization, //MutableReference, //NonFinalFieldByAnalysis, - state.referenceImmutability, - state.dependees, - c - ) - else { - if (state.referenceImmutability == LazyInitialization && !state.isThreadSafeType) - Result(state.field, NotThreadSafeLazyInitialization) - 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 = { - val isNotFinal: Boolean = eps.pk match { - case EscapeProperty.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - 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)) - checkMethod(method, newEP.ub.tac.get, pcs) - //case Callees.key => - // handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - case FieldPrematurelyRead.key ⇒ - isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) - case Purity.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] - state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) - isNonDeterministic(newEP) - case ReferenceImmutabilityLazyInitialization.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] - state.referenceImmutabilityDependees = - state.referenceImmutabilityDependees.filter(_.e ne newEP.e) - !isImmutableReference(newEP) - } - - if (isNotFinal) { - Result(state.field, NoLazyInitialization) //Result(state.field, NonFinalFieldByAnalysis) - } else - createResult() - } - - /** - * Checks whether a field write may be a lazy initialization. - * - * We only consider simple cases of lazy initialization where the single field write is guarded - * so it is executed only if the field still has its default value and where the written value - * is guaranteed to be the same even if the write is executed multiple times (may happen because - * of the initializing method being executed more than once on concurrent threads). - * - * Also, all field reads must be used only for a guard or their uses have to be guarded - * themselves. - */ - def isLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int], - pcs: PCs - )(implicit state: State): Boolean = { - //xx println("PS: "+propertyStore(declaredMethods(method), Purity.key)) - val write = code(writeIndex).asFieldWriteAccessStmt - //xx println("1") - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - // Only handle lazy initialization of ints and floats as they are guaranteed to be - // written atomically - ////return false; - state.isThreadSafeType = false - } - //xx println("2") - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return false; // Reads outside the (single) lazy initialization method - } - //xx println("3") - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val (guardIndex, guardedIndex, readIndex) = - findGuard(writeIndex, defaultValue, code, cfg) match { - case Some((guard, guarded, read)) ⇒ (guard, guarded, read) - case None ⇒ return false; - } - //xx println("4") - // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) //return false; - // possibly double checked locking - { - //TODO - if (isDoubleCheckedLocking(method, pcs)) { - state.isThreadSafeType = true; - return true; - } else - return false; - } - //xx println("5") - // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - return false; - //xx println("6") - // Field reads (except for the guard) may only be executed if the field's value is not the - // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - return false; - //xx println("7") - true - } - - 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 eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) - None - } - } - - def isDoubleCheckedLocking(method: Method, pcs: PCs)(implicit state: State): Boolean = { - val fieldAccessInformation = project.get(FieldAccessInformationKey) - var guards: List[(Int, Int)] = Nil - var assignments: List[Int] = Nil - var monitorEnter: Option[(Int, Option[ObjectType])] = None - var monitorExit: Option[(Int, Option[ObjectType])] = None - var result = true - val accessingPcs = fieldAccessInformation.allWriteAccesses - .filter(p ⇒ p._1 == state.field) - .head - ._2 - .filter(p ⇒ p._1 == method) - .head - ._2 - - if (state.field.fieldType.isObjectType && method.asMethod.isStatic) { - val tacCode = getTACAI(method, pcs) - tacCode match { - case Some(tac) ⇒ { - val hm = new mutable.HashMap[Int, Assignment[V]]() - //index for tac-code - var i: Int = -1 - if (tac != null && tac.instructions != null) - if (tac != null && tac.instructions != null) - tac.instructions.foreach(instr ⇒ { - i = i + 1 - if (instr.isIfStmt) { - var currentFieldsClassType: ObjectType = null - if (state.field.fieldType.isObjectType) - currentFieldsClassType = state.field.fieldType.asObjectType // method.classFile.thisType - var ifLeftType: ReferenceType = null - if (instr.asIf.left.isVar && instr.asIf.left.asVar.value.isReferenceValue) // && instr.asIf.left.asVar.value.asReferenceValue) - try { - ifLeftType = instr.asIf.left.asVar.value.asReferenceValue.asReferenceType - } catch { - case _: Throwable ⇒ - } - - if ( //is non null check - instr.asIf.condition == NE && - // has same type as field - currentFieldsClassType != null && - ifLeftType != null && - ifLeftType.equals(currentFieldsClassType) //guards field? - ) { - // => guards the value - guards = (i, instr.asIf.target) :: guards - } - } - if (instr.isAssignment) { - hm += (i -> instr.asAssignment) - if (accessingPcs.contains(instr.pc)) - assignments = instr.pc.toInt :: assignments - } - - if (instr.isPutStatic && accessingPcs.contains(instr.pc)) { - assignments = i :: assignments - } - - if (instr.isMonitorEnter) { - val defB = instr.asMonitorEnter.objRef.asVar.definedBy.head - var objTypeOfMonitorEnter: Option[ObjectType] = None - try { - objTypeOfMonitorEnter = - Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) - } catch { - case _: Throwable ⇒ - } - monitorEnter = Some((i, objTypeOfMonitorEnter)) - //hm.foreach(x ⇒ println(x)) - } - - if (instr.isMonitorExit) { - val defB = instr.asMonitorExit.objRef.asVar.definedBy.head - var objTypeOfMonitorExit: Option[ObjectType] = None - try { - objTypeOfMonitorExit = - Some(hm(defB).asAssignment.expr.asClassConst.value.asObjectType) - } catch { - case _: Throwable ⇒ - } - monitorExit = Some((i, objTypeOfMonitorExit)) - } - }) - } - case _ ⇒ result = false - } - assignments.foreach(a ⇒ guards.foreach(g ⇒ result = result && a > g._1 && a < g._2)) - result = result && assignments.size >= 1 - result = result && monitorEnter != None && monitorExit != None - monitorEnter match { - case Some((n, Some(ot: ObjectType))) ⇒ - result = result && - ot == state.field.fieldType.asObjectType && - //outer guard(s) - guards.filter(x ⇒ n > x._1).size >= 1 && - //inner guard(s) - guards.filter(x ⇒ n < x._1).size >= 1 - case None ⇒ result = false - case _ ⇒ result = false - } - monitorExit match { - case Some((n, Some(ot: ObjectType))) if (ot == state.field.fieldType.asObjectType) ⇒ - case None ⇒ result = false - case _ ⇒ result = false - } - result - } else - false - } - - def checkWrites( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - //xx println("51") - val definitions = write.value.asVar.definedBy - //xx println("52") - val isDeterministic = - if (definitions.size == 1) { - // The value written must be computed deterministically - //xx println("53") - checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) - } else { - // More than one definition site for the value might lead to differences between - // invocations, but not if this method has no parameters and is deterministic - // (in this case, the definition reaching the write will always be the same) - //xx println("54") - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - //xx println("55") - // The field write must be guarded correctly - val r1 = isDeterministic - val r2 = checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - //xx println("isDeterministic: "+r1) - //xx println("checkWriteIsGuarded: "+r2) - //r1 && r2 //TODO check - if (!(r1 && r2) && !state.isThreadSafeType) { - //state.isThreadSafeType = false - true - } else - r1 && r2 - } - - /** - * Checks if the field write for a lazy initialization is immediately followed by a return of - * the written value (possibly loaded by another field read). - */ - def checkImmediateReturn( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 - - var load = -1 - - while (index < code.length) { - val stmt = code(index) - stmt.astID match { - case Assignment.ASTID ⇒ - if (isReadOfCurrentField(stmt.asAssignment.expr)) - load = index - else - return false; // No field read or a field read of a different field - case ReturnValue.ASTID ⇒ - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) - return true; // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) - return true; // return of field value loaded by field read - else - return false; // return of different value - case _ ⇒ return false; // neither a field read nor a return - } - index += 1 - } - false - } - - def lazyInitializerIsDeterministic( - method: Method, - code: Array[Stmt[V]] - )(implicit state: State): Boolean = { - val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( - propertyStore(declaredMethods(method), Purity.key) - )) - result - } - - /** - * Analyzes field writes for a single method, returning false if the field may still be - * effectively final and true otherwise. - */ - def checkMethod( - method: Method, - taCode: TACode[TACMethodParameter, V], - pcs: PCs - )(implicit state: State): Boolean = { - val field = state.field - val stmts = taCode.stmts - for (pc ← pcs) { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = stmts(index) - if (stmt.pc == pc) { - stmt.astID match { - case PutStatic.ASTID | PutField.ASTID ⇒ - if (method.isInitializer) { - - if (field.isStatic) { - if (method.isConstructor) - return true; - } else { - val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - 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.referenceImmutability == NotThreadSafeLazyInitialization || state.referenceImmutability == LazyInitialization) //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; - - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; - - // A field written outside an initializer must be lazily - // initialized or it is non-final - if (!isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex, - pcs - )) - return true; - state.referenceImmutability = LazyInitialization //LazyInitializedField - } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - // 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. - return true; - } - } - case _ ⇒ throw new RuntimeException("unexpected field access"); - } - } else { - // nothing to do as the put field is dead - } - } - } - false - } - - /** - * 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 eps: InterimEP[Method, TACAI] ⇒ - * state.tacDependees += method → ((eps, pcs)) - * eps.ub.tac - * case epk ⇒ - * state.tacDependees += method → ((epk, pcs)) - * None - * } - * } - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - /** - * 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 // Must be locally created - else { - val definition = stmts(defSite).asAssignment - // Must either be null or freshly allocated - if (definition.expr.isNullExpr) false - else if (!definition.expr.isNew) true - else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - handleEscapeProperty(escape) - } - } - } - } - - /** - * 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 = 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 - } - - /** - * Checks if the field write is only executed if the field's value was still the default value. - * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization - * and the field write. - */ - def checkWriteIsGuarded( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val startBB = cfg.bb(writeIndex).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt ⇒ - stmt.astID == CaughtException.ASTID - } flatMap { exception ⇒ - exception.asCaughtException.origins.map { origin: Int ⇒ - if (isImmediateVMException(origin)) - pcOfImmediateVMException(origin) - else - pcOfMethodExternalException(origin) - }.iterator - } - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - if (startPC == 0 || startPC == guardedIndex) - return false; // Reached method start or wrong branch of guarding if-Statement - - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; - - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) - if (!lazyInitializerIsDeterministic(method, code)) - return false; - - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - true - } - - /** - * 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]] - )(implicit state: State): Boolean = { - //xx println("begin checkwriteisdeterministic") - def isConstant(uvar: Expr[V]): Boolean = { - val defSites = uvar.asVar.definedBy - - def isConstantDef(index: Int) = { - //xx println("index: "+index) - if (index < 0) false - else if (code(defSites.head).asAssignment.expr.isConst) true - else { - val expr = code(index).asAssignment.expr - //xx println("expr: "+expr) - //println("resolveField(p): " + expr.asFieldRead.resolveField(p)) - expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ ⇒ // Unknown field - false - }) - } - } - - defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) - } - - val value = origin.expr - - //xx println("value.astID: "+value.astID) - - val isNonConstDeterministic = value.astID match { - case GetStatic.ASTID | GetField.ASTID ⇒ - //xx println("a") - value.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - //xx println("b") - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ ⇒ // Unknown field - //xx println("c") - false - } - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ - //xx println("d") - // 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.exists(!isConstant(_))) { - //xx println("e") - false - } else { - //xx println("f") - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true - } - case _ ⇒ - //xx println("g") - // 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, code) - } - - value.isConst || isNonConstDeterministic - } - - /** - * Checks that all non-dead field reads that are not used for the guarding if-Statement of a - * lazy initialization are only executed if the field did not have its default value or after - * the (single) field write. - */ - def checkReads( - reads: Seq[(Method, PCs)], - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - ): Boolean = { - // There is only a single method with reads aside from initializers (checked by - // isLazilyInitialized), so we have to check only reads from that one method. - reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ - val index = pcToIndex(readPC) - index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) - } - } - - /** - * Checks that a field read is only executed if the field did not have its default value or - * after the (single) field write. - */ - def checkRead( - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Boolean = { - val startBB = cfg.bb(readIndex).asBasicBlock - val writeBB = cfg.bb(writeIndex) - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - - if (startPC == 0) - return false; // Reached the start of the method but not the guard or field write - - if ((curBB eq writeBB) && writeIndex > readIndex) - return false; // In the basic block of the write, but before the write - - if (startPC != guardedIndex && // Did not reach the guard - (curBB ne writeBB) /* Not the basic block of the write */ ) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - true - } - - /** - * Gets all predecessor BasicBlocks of a CFGNode. - */ - def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode ⇒ - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) - } - result.toList - } - - /** - * 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. - */ - def findGuard( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Option[(Int, Int, Int)] = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int)] = None - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID ⇒ - val ifStmt = cfStmt.asIf - ifStmt.condition match { - case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1)) - } - - case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None; - } else { - result = Some((endPC, ifStmt.targetStmt)) - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - var fieldReadUses: IntTrieSet = IntTrieSet.empty - if (definitions.head >= 0 && code(definitions.head).isAssignment) - fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head)); // Found proper guard - } - - None - } - - /** - * 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])(implicit state: State): Boolean = { - val field = expr.astID match { - case GetField.ASTID ⇒ - val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) None - else expr.asGetField.resolveField(project) - case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) - case _ ⇒ None - } - field.contains(state.field) - } - - /** - * 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]] - )(implicit state: State): Boolean = { - - /** - * Checks if an expression is an IntConst or FloatConst with the corresponding default value. - */ - def isDefaultConst(expr: Expr[V]): Boolean = { - //xx println("Expression: "+expr) - if (expr.isNullExpr) - true //-- - else 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 || - expr.isFloatConst && defaultValue == expr.asFloatConst.value - } - } - - /** - * Checks whether the non-constant expression of the if-Statement is a read of the current - * field. - */ - def isGuardInternal(expr: V): Boolean = { - expr.definedBy forall { index ⇒ - if (index < 0) false // If the value is from a parameter, this can not be the guard - else isReadOfCurrentField(code(index).asAssignment.expr) - } - } - - if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { - isGuardInternal(ifStmt.rightExpr.asVar) - } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { - isGuardInternal(ifStmt.leftExpr.asVar) - } else //TODO check - //false - { - - //TODO if expression = Nullexpr - //xx println("----------------------------------------<<<<") - state.isThreadSafeType = false - true //TODO check - } - } - - /** - * Checks if 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) ⇒ true - case eps ⇒ - state.prematurelyReadDependee = Some(eps) - false - } - - /** - * Checkes 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. - */ - 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 - } - - /** - * Checkes if 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, ReferenceImmutability] - )(implicit state: State): Boolean = eop match { - case FinalEP(e, ImmutableReference(_)) ⇒ true - case FinalEP(e, MutableReference) ⇒ false - // - case LBP(ImmutableReference(_)) ⇒ true - case UBP(MutableReference) ⇒ false - - /** - * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ - * true - * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * - */ - case _ ⇒ - state.referenceImmutabilityDependees += eop - true - } -} - -trait L0ReferenceImmutabilityLazyInitializationAnalysisScheduler extends FPCFAnalysisScheduler { - - final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.lub(Purity), - PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), - PropertyBounds.lub(ReferenceImmutabilityLazyInitialization), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.ub(EscapeProperty) - ) - - final def derivedProperty: PropertyBounds = - PropertyBounds.lub(ReferenceImmutabilityLazyInitialization) - -} - -/** - * Executor for the field mutability analysis. - */ -object EagerL0ReferenceImmutabilityLazyInitializationAnalysis - extends L0ReferenceImmutabilityLazyInitializationAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { - - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) - val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)( - analysis.determineReferenceImmutabilityLazyInitialization - ) - analysis - } - - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty -} - -/** - * Executor for the lazy field mutability analysis. - */ -object LazyL0ReferenceImmutabilityLazyInitializationAnalysis - extends L0ReferenceImmutabilityLazyInitializationAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { - - final override def register( - p: SomeProject, - ps: PropertyStore, - unused: Null - ): FPCFAnalysis = { - val analysis = new L0ReferenceImmutabilityLazyInitializationAnalysis(p) - ps.registerLazyPropertyComputation( - ReferenceImmutabilityLazyInitialization.key, - analysis.determineReferenceImmutabilityLazyInitialization - ) - analysis - } - - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) -} From 9df1c1f13010a47b865c16ba070a3f7258885824 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 9 Jul 2020 10:36:33 +0200 Subject: [PATCH 186/327] Reference Imm Analysis without printlns and extended for lazy initalization recognition with new properties, not yet revised --- ...bstractReferenceImmutabilityAnalysis.scala | 16 +- ...mutabilityAnalysisLazyInitialization.scala | 566 +++++++++--------- .../L0ReferenceImmutabilityAnalysis.scala | 328 ++++------ 3 files changed, 421 insertions(+), 489 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala index a2be91ce97..545780357d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala @@ -40,9 +40,6 @@ import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.properties.TACAI import org.opalj.value.ValueInformation -/*** - * - */ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { type V = DUVar[ValueInformation] @@ -107,11 +104,10 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { eop: EOptionP[DeclaredMethod, Purity] )(implicit state: State): Boolean = eop match { case LBP(p: Purity) if p.isDeterministic ⇒ - println("XXX: false is non deterministic"); false + false case EUBP(e, p: Purity) if !p.isDeterministic ⇒ - println("e: "+e+",XXX: true is non deterministic"); true + true case _ ⇒ - println("XXX: else"); state.purityDependees += eop false } @@ -122,7 +118,6 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { )(implicit state: State): Boolean = { val propertyStoreResult = propertyStore(declaredMethods(method), Purity.key) - println("property store purity result: "+propertyStoreResult) val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( propertyStoreResult )) @@ -138,15 +133,8 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { )(implicit state: State): Boolean = eop match { case FinalEP(e, ImmutableReference(_)) ⇒ true case FinalEP(e, MutableReference) ⇒ false - // case LBP(ImmutableReference(_)) ⇒ true case UBP(MutableReference) ⇒ false - - /** - * case LBP(_: ImmutableReference) ⇒ //FinalField) ⇒ - * true - * case UBP(_: MutableReference) ⇒ false // NonFinalField) ⇒ false * - */ case _ ⇒ state.referenceImmutabilityDependees += eop true diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index ad70f151c8..4152b4b2e7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -9,17 +9,23 @@ import org.opalj.ai.pcOfMethodExternalException import org.opalj.br.ComputationalTypeFloat import org.opalj.br.ComputationalTypeInt import org.opalj.br.Method +import org.opalj.br.PC import org.opalj.br.PCs import org.opalj.br.cfg.BasicBlock import org.opalj.br.cfg.CFG import org.opalj.br.cfg.CFGNode import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference +import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.Purity import org.opalj.br.fpcf.properties.ReferenceImmutability import org.opalj.collection.immutable.IntTrieSet import org.opalj.tac.SelfReferenceParameter import org.opalj.tac.Assignment import org.opalj.tac.CaughtException +import org.opalj.tac.ClassConst +import org.opalj.tac.DVar import org.opalj.tac.Expr import org.opalj.tac.FieldWriteAccessStmt import org.opalj.tac.GetField @@ -36,6 +42,7 @@ import org.opalj.tac.TACStmts import org.opalj.tac.TACode import org.opalj.tac.UVar import org.opalj.tac.VirtualFunctionCall +import org.opalj.value.ValueInformation import scala.annotation.switch @@ -63,28 +70,35 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization pcToIndex: Array[Int] )(implicit state: State): Boolean = { val write = code(writeIndex).asFieldWriteAccessStmt - println("0001") + //println("0001") if (state.field.fieldType.computationalType != ComputationalTypeInt && state.field.fieldType.computationalType != ComputationalTypeFloat) { // Only handle lazy initialization of ints and floats as they are guaranteed to be // written atomically return false; } - println("0002") + //println("0002") + //-------------------------------------------------------- + //TODO reasoning if there is another way to do this + val writes = fieldAccessInformation.writeAccesses(state.field) + //println("writes: "+writes) + if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) + return false; // more than one write in the method + //---------------------------------------------------------------- val reads = fieldAccessInformation.readAccesses(state.field) if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { return false; // Reads outside the (single) lazy initialization method } - println("0003") + //println("0003") // There must be a guarding if-Statement // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the // first statement that is executed after the if-Statement if the field's value was not the // default value val (guardIndex, guardedIndex, readIndex) = { - val findGuardResult = findGuard(writeIndex, defaultValue, code, cfg, 1) - println("find guard result: "+findGuardResult._1.mkString(", ")) - if (findGuardResult._1.size > 0) - findGuardResult._1.head + val findGuardResult = findGuard(writeIndex, defaultValue, code, cfg) + //println("find guard result: "+findGuardResult) + if (findGuardResult.isDefined) + (findGuardResult.get._1, findGuardResult.get._2, findGuardResult.get._3) else return false; /** * findGuardResult match { @@ -94,24 +108,24 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization */ } - println("0004") + //println("0004") // Detect only simple patterns where the lazily initialized value is returned immediately if (!checkImmediateReturn(write, writeIndex, readIndex, code)) return false; - println("0005") + //println("0005") // The value written must be computed deterministically and the writes guarded correctly if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) return false; - println("0006") + //println("0006") // Field reads (except for the guard) may only be executed if the field's value is not the // default value if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) return false; - println("0007") + //println("0007") true } - def isDoubleCheckedLocking( + def isThreadSafeLazyInitialisation( writeIndex: Int, defaultValue: Any, method: Method, @@ -119,83 +133,79 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization cfg: CFG[Stmt[V], TACStmts[V]], pcToIndex: Array[Int], tacCai: TACode[TACMethodParameter, V] - )(implicit state: State): Boolean = { - //val write = code(writeIndex).asFieldWriteAccessStmt - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) - return false; // Reads outside the (single) lazy initialization method + )(implicit state: State): ReferenceImmutability = { + var result: ReferenceImmutability = LazyInitializedThreadSafeReference + val write = code(writeIndex).asFieldWriteAccessStmt + val writeBB = cfg.bb(writeIndex).asBasicBlock + val domTree = cfg.dominatorTree - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val guardResults: (List[(Int, Int, Int)], (Option[(CFGNode, CFGNode)])) = findGuard(writeIndex, defaultValue, code, cfg, 2) - println("find guard result 2: "+guardResults._1.mkString(", ")) + val (guardIndex, guardedIndex, readIndex, afterGuardRecognizedTheDefaultValueIndex) = { + val findGuardResult: (Option[(Int, Int, Int, CFGNode, Int)]) = findGuard(writeIndex, defaultValue, code, cfg) + if (findGuardResult.isDefined) + (findGuardResult.get._1, findGuardResult.get._2, findGuardResult.get._3, findGuardResult.get._5) + else return MutableReference; + } + val guardedBB = cfg.bb(afterGuardRecognizedTheDefaultValueIndex) - //if(!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - // return false; - println("--------------------------------------------------------------------------------------------dcl") + val monitorResult: ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = + findMonitor(writeIndex, defaultValue, code, cfg, tacCai) - val monitorResult = findMonitor(writeIndex, defaultValue, code, cfg, tacCai) - println("monitorResult: "+monitorResult) - /** - * guardResults.foreach(x ⇒ { - * if (!checkWrites(write, writeIndex, x._1, x._2, method, code, cfg)) { - * println("check writes false") - * return false - * }; - * // Field reads (except for the guard) may only be executed if the field's value is not the - * // default value - * if (!checkReads(reads, x._3, x._2, writeIndex, cfg, pcToIndex)) { - * println("check reads false") - * return false - * }; - * }) * - */ - val domTree = cfg.dominatorTree + val reads = fieldAccessInformation.readAccesses(state.field) + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return MutableReference + } + val writes = fieldAccessInformation.writeAccesses(state.field) + if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { + return MutableReference + }; + if (method.returnType == state.field.fieldType && + !checkThatTheValueOfTheFieldIsReturned(write, writeIndex, readIndex, code)) { + return MutableReference; + } - (guardResults._2, monitorResult._2) match { - case (Some((guard1: CFGNode, guard2: CFGNode)), Some(monitor: CFGNode)) ⇒ - println( - s"""domtree: $domTree - | g1 dom m: ${domTree.strictlyDominates(guard1.nodeId, monitor.nodeId)} - | m dom g2: ${domTree.strictlyDominates(monitor.nodeId, guard2.nodeId)} - | g1 dom g2: ${domTree.strictlyDominates(guard1.nodeId, guard2.nodeId)} - | g2 dom g1: ${domTree.strictlyDominates(guard2.nodeId, guard1.nodeId)} - | g2 dom m: ${domTree.strictlyDominates(guard2.nodeId, monitor.nodeId)} - | m dom g1: ${domTree.strictlyDominates(monitor.nodeId, guard1.nodeId)} - | - | m: $monitor - | g2: $guard2 - | m==g2: ${monitor == guard2} - | - |""".stripMargin - ) - - if ((guard1 == monitor || domTree.strictlyDominates(guard1.nodeId, monitor.nodeId)) && - (monitor == guard2 || domTree.strictlyDominates(monitor.nodeId, guard2.nodeId))) - println("dominates!!!") - else { - println("not dominates!!!!") - return false; - } - case _ ⇒ println("not dominates!!!"); return false; + result = checkWriteIsGuarded2(writeIndex, guardIndex, guardedIndex, method, code, cfg) + /* + println( + s""" method is synchronized: ${method.isSynchronized} + | (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) : ${(domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId))} + | (guardedBB == writeBB: ${guardedBB == writeBB} + | writeIndex: $writeIndex + |""".stripMargin + ) */ + + if ((monitorResult._1._1.isDefined && monitorResult._1._2.isDefined && monitorResult._2._1.isDefined) + && + ( + (domTree.strictlyDominates(monitorResult._2._1.get.nodeId, guardedBB.nodeId) || + (monitorResult._2._1.get == guardedBB && monitorResult._1._1.get < afterGuardRecognizedTheDefaultValueIndex)) && //writeIndex)) && //monitor enter dominates guard1 + ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) + || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex) //&& //true case dominates Write + )) { + + return LazyInitializedThreadSafeReference // result //DCL + } /*else if ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) + || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex) { + LazyInitializedNotThreadSafeOrNotDeterministicReference + }*/ else if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { + if (method.isSynchronized) { + return LazyInitializedThreadSafeReference + // result + } else { + LazyInitializedNotThreadSafeOrNotDeterministicReference + } + //TODO !! reasoning + /*if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { + return MutableReference + } else { + return LazyInitializedNotThreadSafeOrNotDeterministicReference + }*/ + } else if (((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) + || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { + return LazyInitializedNotThreadSafeOrNotDeterministicReference + } //only guard{ + else { + return MutableReference } - /** - * monitorResult._1 match { - * case (Some(enter), Some(exit)) ⇒ { - * - * //val outer = - * guardResults._1.exists(x ⇒ x._1 < enter && exit < x._2) && - * guardResults._1.exists(x ⇒ enter < x._1 && x._2 < exit) - * /*** val inner = - * return outer && inner;* - * */ - * } - * case _ ⇒ return false; - * }* - */ - true } @@ -205,42 +215,72 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]], tacCode: TACode[TACMethodParameter, V] - ): ((Option[Int], Option[Int]), Option[CFGNode]) = { //(implicit state: State) + )(implicit state: State): ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = { //(implicit state: State) var result: (Option[Int], Option[Int]) = (None, None) - var dclBBs: List[CFGNode] = List.empty + var dclEnterBBs: List[CFGNode] = List.empty + var dclExitBBs: List[CFGNode] = List.empty val startBB = cfg.bb(fieldWrite) var MonitorExitqueuedBBs: Set[CFGNode] = startBB.successors var worklistMonitorExit = getSuccessors(startBB, Set.empty) - /** - * def checkMonitor(pc: PC, v: UVar[ValueInformation], curBB: CFGNode): Boolean = { - * println("check monitor") - * v.defSites.filter(i ⇒ { - * if (i > 0) - * tacCode.stmts(i) match { - * case Assignment(pc1, DVar(value1, defSites1), GetField(pc2, t, name, classType, UVar(value2, defSites2))) ⇒ - * println("classType: "+classType) - * println("classFile.thisType: "+state.field.classFile.thisType) - * println( - * s""" - * |classFile comparison result: ${classType == state.field.classFile.thisType} - * - * |""". - * stripMargin - * ) - * classType == - * state.field. - * classFile.thisType - * //&& name == state.field.name - * case _ ⇒ false - * } - * else // (i == -1) - * true - * - * }).size == v.defSites.size - * } * - */ + def checkMonitor(pc: PC, v: UVar[ValueInformation], curBB: CFGNode)(implicit state: State): Boolean = { + v.defSites.filter(i ⇒ { + // println("current i: "+i) + if (i > 0) { + val stmt = tacCode.stmts(i) + // println("stmt: "+stmt) + stmt match { + case Assignment(pc1, DVar(useSites, value), cc @ ClassConst(_, constant)) ⇒ { + state.field.classFile.thisType == cc.value || state.field.fieldType == cc.value + } + case Assignment(pc1, DVar(value1, defSites1), GetField(pc2, t, name, classType, UVar(value2, defSites2))) ⇒ + classType == + state.field. + classFile.thisType + //&& name == state.field.name + case _ ⇒ false + } + } else // (i == -1) + true + + }).size == v.defSites.size + } + + var monitorEnterqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklistMonitorEnter = getPredecessors(startBB, Set.empty) + //find monitorenter + while (!worklistMonitorEnter.isEmpty) { + val curBB = worklistMonitorEnter.head + // println("curBB: "+curBB) + worklistMonitorEnter = worklistMonitorEnter.tail + //val startPC = curBB.startPC + //val endPC = curBB.endPC + val startPC = curBB.startPC + val endPC = curBB.endPC + //val cfStmt = code(startPC) //(endPC) + // println("cfg should be monitor enter: "+cfStmt) + + var flag = true + for (i ← startPC to endPC) { + code(i) match { + case me @ MonitorEnter(pc, v @ UVar(defSites, value)) //if(v.value.computationalType == state.field.fieldType) + ⇒ { + if (checkMonitor(pc, v, curBB)) { + result = (Some(tacCode.pcToIndex(pc)), (result._2)) + dclEnterBBs = curBB :: dclEnterBBs + flag = false + } + } + case _ ⇒ + } + } + if (flag) { + val predecessor = getPredecessors(curBB, monitorEnterqueuedBBs) + worklistMonitorEnter ++= predecessor + monitorEnterqueuedBBs ++= predecessor + } + } //find monitorexit while (!worklistMonitorExit.isEmpty) { @@ -252,67 +292,29 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization cfStmt match { case MonitorExit(pc, v @ UVar(defSites, value)) //if(v.value.computationalType == state.field.fieldType) ⇒ - //if(checkMonitor(pc, v, curBB)) { - - result = ((result._1), Some(tacCode.pcToIndex(pc))) - //} + if (checkMonitor(pc, v, curBB)) { + result = ((result._1), Some(tacCode.pcToIndex(pc))) + dclExitBBs = curBB :: dclExitBBs + } case _ ⇒ val successors = getSuccessors(curBB, MonitorExitqueuedBBs) - println("successors: "+successors.mkString(", ")) worklistMonitorExit ++= successors MonitorExitqueuedBBs ++= successors } } - var monitorEnterqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklistMonitorEnter = getPredecessors(startBB, Set.empty) - println("worklist monitorenter: "+worklistMonitorEnter.toList.mkString(", ")) - //find monitorenter - while (!worklistMonitorEnter.isEmpty) { - val curBB = worklistMonitorEnter.head - println("curBB: "+curBB) - worklistMonitorEnter = worklistMonitorEnter.tail - //val startPC = curBB.startPC - //val endPC = curBB.endPC - val startPC = curBB.startPC - val cfStmt = code(startPC) //(endPC) - println("cfg should be monitor enter: "+cfStmt) - cfStmt match { - case me @ MonitorEnter(pc, v @ UVar(defSites, value)) //if(v.value.computationalType == state.field.fieldType) - ⇒ - - /** - * if (v.defSites.filter(i ⇒ { - * tacCode.stmts(i) match { - * case Assignment(pc1, DVar(value1, defSites1), GetField(pc2, t, name, classType, UVar(value2, defSites2))) ⇒ - * classType == state.field.fieldType - * case _ ⇒ false - * } - * }).size == v.defSites.size) { - * result = (Some(tacCode.pcToIndex(pc)), result._2) - * } * - */ - - //if(checkMonitor(pc, v, curBB)) { - println("Monitor Enter:-----------------------------------------------------------------------------") - println("me") - result = (Some(tacCode.pcToIndex(pc)), (result._2)) - dclBBs = curBB :: dclBBs - //} - - case _ ⇒ - val predecessor = getPredecessors(curBB, monitorEnterqueuedBBs) - println("predecessor: "+predecessor.mkString(", ")) - worklistMonitorEnter ++= predecessor - monitorEnterqueuedBBs ++= predecessor - } + val bbsEnter = { + if (dclEnterBBs.size >= 1) + Some(dclEnterBBs.head) + else None } - val bbs = { - if (dclBBs.size == 1) - Some(dclBBs.head) + val bbsExit = { + if (dclExitBBs.size >= 1) + Some(dclExitBBs.head) else None } - (result, bbs) + (result, (bbsEnter, bbsExit)) + } /** @@ -324,131 +326,77 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization fieldWrite: Int, defaultValue: Any, code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - amount: Int - )(implicit state: State): (List[(Int, Int, Int)], (Option[(CFGNode, CFGNode)])) = { - println("start find guard") + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): (Option[(Int, Int, Int, CFGNode, Int)]) = { val startBB = cfg.bb(fieldWrite).asBasicBlock - var dclBBs: List[CFGNode] = List.empty - - println("start bb: "+startBB) var enqueuedBBs: Set[CFGNode] = startBB.predecessors var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - var result: List[(Int, Int)] = List.empty - var i: Int = 0 + var result: Option[(Int, Int, CFGNode, Int)] = None + while (worklist.nonEmpty) { val curBB = worklist.head worklist = worklist.tail - i = i + 1 - println(i.toString()+" curBB: "+curBB) - println("worklist: "+worklist.mkString(", ")) + val startPC = curBB.startPC val endPC = curBB.endPC - println( - s""" - |startPc: $startPC - |endPC: $endPC - |""".stripMargin - ) + val cfStmt = code(endPC) - println("cfStmt: "+cfStmt) (cfStmt.astID: @switch) match { - /** - * case MonitorEnter.ASTID ⇒ - * val monitorEnter = cfStmt.asMonitorEnter - * println("monitor-enter: "+monitorEnter) - */ case If.ASTID ⇒ val ifStmt = cfStmt.asIf - println("if stmt condition: "+ifStmt.condition) ifStmt.condition match { case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - println("EQ") - if (result.size >= amount) { - println("result: "+result.mkString(", ")) - if (result.head._1 != endPC || result.last._2 != endPC + 1) - return (List.empty, None); + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; } else { - dclBBs = curBB :: dclBBs - result = { (endPC, endPC + 1) } :: result + result = Some((endPC, endPC + 1, curBB, ifStmt.targetStmt)) } case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ - println("NE") - println(result.mkString(", ")) - if (result.size >= amount) { - println("result: "+result.mkString(", ")) - if (result.head._1 != endPC || result.last._2 != ifStmt.targetStmt) { - println( - s"""head 1 : ${result.head._1} - |endPc: $endPC - |last 2 : ${result.last._2} - |ifstmt target stmt : ${ifStmt.targetStmt} - |""".stripMargin - ) - return (List.empty, None); - } + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None } else { - println("else") - dclBBs = curBB :: dclBBs - result = (endPC, ifStmt.targetStmt) :: result + result = Some((endPC, ifStmt.targetStmt, curBB, endPC + 1)) } // Otherwise, we have to ensure that a guard is present for all predecessors case _ ⇒ - println("_") - if (startPC == 0) { - println("reached the end") - return (List.empty, None); - } + if (startPC == 0) return None; val predecessors = getPredecessors(curBB, enqueuedBBs) worklist ++= predecessors enqueuedBBs ++= predecessors - } // Otherwise, we have to ensure that a guard is present for all predecessors case _ ⇒ - if (startPC == 0) return (List.empty, None); + if (startPC == 0) return None; val predecessors = getPredecessors(curBB, enqueuedBBs) worklist ++= predecessors enqueuedBBs ++= predecessors } - if (result.size < amount) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - println("worklist end: "+worklist.mkString(", ")) - } - } - println("result: "+result.mkString(", ")) - if (result.size >= amount) { + + if (result.isDefined) { // 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.head._1).asIf + val ifStmt = code(result.get._1).asIf val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr val definitions = expr.asVar.definedBy val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ - use == result.head._1 || use == result.last._2 + use == result.get._1 || use == result.get._2 } - if (definitions.size == 1 && fieldReadUsedCorrectly) { - val BBs = - if (dclBBs.size == 2) - Some((dclBBs.head, dclBBs.last)) - else None - - return (result.map(x ⇒ (x._1, x._2, definitions.head)), BBs); - //return (result.head._1, result.last._2, definitions.head) :: Nil - }; // Found proper guard + if (definitions.size == 1 && fieldReadUsedCorrectly) + return Some((result.get._1, result.get._2, definitions.head, result.get._3, result.get._4)); // Found proper guard } - (List.empty, None) + None } /** @@ -464,76 +412,111 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]] )(implicit state: State): Boolean = { - println("check write is guarded") - println("-a") val startBB = cfg.bb(writeIndex).asBasicBlock - println("-b") var enqueuedBBs: Set[CFGNode] = Set(startBB) - println("-c") var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - println("-d") + val abnormalReturnNode = cfg.abnormalReturnNode - println("-e") + val caughtExceptions = code filter { stmt ⇒ - println("-f") stmt.astID == CaughtException.ASTID } flatMap { exception ⇒ exception.asCaughtException.origins.map { origin: Int ⇒ if (isImmediateVMException(origin)) { - println("-i") pcOfImmediateVMException(origin) } else { - println("-j") pcOfMethodExternalException(origin) } }.iterator } - println("-k") while (worklist.nonEmpty) { - println("-l") val curBB = worklist.head worklist = worklist.tail val startPC = curBB.startPC val endPC = curBB.endPC - println("-m") if (startPC == 0 || startPC == guardedIndex) return false; // Reached method start or wrong branch of guarding if-Statement - println("-n") // Exception thrown between guard and write, which is ok for deterministic methods, // but may be a problem otherwise as the initialization is not guaranteed to happen // (or never happen). if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) { - println("nn") if (!lazyInitializerIsDeterministic(method, code)) { - println("oo") - return false + return false; } }; - println("ooo") // Exception thrown between guard and write (caught somewhere, but we don't care) if ((curBB ne startBB) & caughtExceptions.contains(endPC)) { - println("ooo") if (!lazyInitializerIsDeterministic(method, code)) { - println("ooooo") - return false + return false; } }; - println("-p") // Check all predecessors except for the one that contains the guarding if-Statement - println("-q") val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - println("-r") worklist ++= predecessors - println("-s") enqueuedBBs ++= predecessors } - println("--true") true } + def checkWriteIsGuarded2( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): ReferenceImmutability = { + // println("check write is guarded 2") + val startBB = cfg.bb(writeIndex).asBasicBlock + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code filter { stmt ⇒ + stmt.astID == CaughtException.ASTID + + } flatMap { exception ⇒ + exception.asCaughtException.origins.map { origin: Int ⇒ + if (isImmediateVMException(origin)) { + pcOfImmediateVMException(origin) + } else { + pcOfMethodExternalException(origin) + } + }.iterator + } + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + if (startPC == 0 || startPC == guardedIndex) + return LazyInitializedNotThreadSafeOrNotDeterministicReference; // Reached method start or wrong branch of guarding if-Statement + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) { + if (!lazyInitializerIsDeterministic(method, code)) { + return LazyInitializedNotThreadSafeOrNotDeterministicReference + } + }; + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.contains(endPC)) { + if (!lazyInitializerIsDeterministic(method, code)) { + return LazyInitializedNotThreadSafeOrNotDeterministicReference + } + + }; + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + LazyInitializedThreadSafeReference + } + /** * Gets all predecessor BasicBlocks of a CFGNode. */ @@ -749,30 +732,20 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]] )(implicit state: State): Boolean = { - println("check Writes on field: "+state.field.name) val definitions = write.value.asVar.definedBy - println("a") val isDeterministic = if (definitions.size == 1) { - println("b") // The value written must be computed deterministically checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) } else { - println("c") // More than one definition site for the value might lead to differences between // invocations, but not if this method has no parameters and is deterministic // (in this case, the definition reaching the write will always be the same) - val result = propertyStore(declaredMethods(method), Purity.key) - println("purity result: "+result) method.descriptor.parametersCount == 0 && !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) } - println("d") val checkWriteIsGuardedResult = checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - println(s""" isDeterministic: $isDeterministic - | checkWriteIsGuarded: ${checkWriteIsGuardedResult} - |""".stripMargin) // The field write must be guarded correctly isDeterministic && checkWriteIsGuardedResult @@ -818,4 +791,43 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization false } + def checkThatTheValueOfTheFieldIsReturned( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + code: Array[Stmt[V]] + )(implicit state: State): Boolean = { + var index = writeIndex + 1 + + var load = -1 + + while (index < code.length) { + val stmt = code(index) + stmt.astID match { + case a @ Assignment.ASTID ⇒ + if (isReadOfCurrentField(stmt.asAssignment.expr)) { + load = index + } + // No field read or a field read of a different field + case ReturnValue.ASTID ⇒ + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) { + return true + }; // direct return of the written value + else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || + returnValueDefs == IntTrieSet(readIndex, load))) { + return true + }; // return of field value loaded by field read + else { + return false + }; // return of different value + case _ ⇒ ; + } + index += 1 + } + false + } + } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index 4bf4402be7..9df0c568df 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -1,6 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.immutability.reference +import org.opalj.br.BooleanType +import org.opalj.br.ByteType import org.opalj.br.ClassFile import org.opalj.br.DeclaredMethod import org.opalj.br.Field @@ -9,6 +11,7 @@ import org.opalj.br.IntegerType import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.PCs +import org.opalj.br.ShortType import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler @@ -23,7 +26,9 @@ import org.opalj.br.fpcf.properties.EscapeViaReturn import org.opalj.br.fpcf.properties.FieldImmutability import org.opalj.br.fpcf.properties.FieldPrematurelyRead import org.opalj.br.fpcf.properties.ImmutableReference -import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.NoEscape import org.opalj.br.fpcf.properties.Purity @@ -46,6 +51,9 @@ import org.opalj.fpcf.SomeEPS import org.opalj.tac.Assignment import org.opalj.tac.DVar import org.opalj.tac.GetField +import org.opalj.tac.If +import org.opalj.tac.MonitorEnter +import org.opalj.tac.MonitorExit import org.opalj.tac.NonVirtualMethodCall import org.opalj.tac.PutField import org.opalj.tac.PutStatic @@ -91,46 +99,49 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec private[analyses] def determineReferenceImmutability( field: Field ): ProperPropertyComputationResult = { - println( - "start determine ref imm analysis=====================================================================================================" - ) - println("field: "+field.name) + implicit val state: State = State(field) // Fields are not final if they are read prematurely! if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { - println("field is prematurely read") + //("field is prematurely read") return Result(field, MutableReference) }; //Result(field, NonFinalFieldByAnalysis); - println("01") + state.referenceImmutability = ImmutableReference(true) //EffectivelyFinalField val thisType = field.classFile.thisType if ((field.isPublic || field.isPackagePrivate || field.isProtected)) { - println("field: "+field+" is public, pack priv or protected") - if (!field.isFinal) + + if (!field.isFinal) { + return Result(field, MutableReference) - else + } else { + state.notEscapes = false + } }; //Result(field, NonFinalFieldByLackOfInformation) - println("02") + + if (field.isPublic) + return Result(field, MutableReference) + // 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) { if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + return Result(field, MutableReference); } project.classesPerPackage(thisType.packageName) } else { Set(field.classFile) } - println("03") + val classesHavingAccess: Iterator[ClassFile] = if (field.isProtected) { - if (typeExtensibility(thisType).isYesOrUnknown && !state.field.isFinal) { - return Result(field, MutableReference); //return Result(field, NonFinalFieldByLackOfInformation); + if (typeExtensibility(thisType).isYesOrUnknown) { + return Result(field, MutableReference); } val subclassesIterator: Iterator[ClassFile] = classHierarchy.allSubclassTypes(thisType, reflexive = false).flatMap { ot ⇒ @@ -140,45 +151,75 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } else { initialClasses.iterator } - println("04") + // If there are native methods, we give up - if (classesHavingAccess.exists(_.methods.exists(_.isNative)) && !state.field.isFinal) { - println("native") - return Result(field, MutableReference) - }; //return Result(field, NonFinalFieldByLackOfInformation); - println("05") + if (classesHavingAccess.exists(_.methods.exists(_.isNative))) + return Result(field, MutableReference); - println("1-----------------------------------------------------------------------") for { (method, pcs) ← fieldAccessInformation.readAccesses(field) taCode ← getTACAI(method, pcs) } { + //TODO !!!! + pcs.foreach(pc ⇒ { + val index = taCode.pcToIndex(pc) - if (index > 0) { + + val staticAddition = { + if (method.isStatic) { + + 1 + } else { + + 0 + } + } + + if (index > (-1 + staticAddition)) { taCode.stmts(index) match { - case Assignment(pc3, targetVar, GetField(pc4, cl, name, _, value)) ⇒ + case a @ Assignment(pc3, targetVar, GetField(pc4, cl, name, _, value)) ⇒ + if (name == field.name) { - state.notEscapes = false + targetVar.usedBy.foreach(i ⇒ + { + + taCode.stmts(i) match { + case MonitorEnter(_, _) ⇒ + case MonitorExit(_, _) ⇒ + case If(pc, left, condition, right, target) ⇒ + case u @ _ ⇒ state.notEscapes = false; + } + }) + } + case _ ⇒ } - } else state.notEscapes = false + } else { + + state.notEscapes = false + } }) } - println("0------------------") + for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method, pcs) } { - println("2-----------------------------------------------------------------------") - println("method: "+method) + for (pc ← pcs) { - println("pc: "+pc) + val index = taCode.pcToIndex(pc) - if (index > 0) { + val staticAddition = { + if (method.isStatic) + 1 + else + 0 + } + if (index > (-1 + staticAddition)) { val stmt = taCode.stmts(index) - println("stmt: "+stmt) + stmt match { case PutField(_, _, _, _, _, value) ⇒ value match { @@ -188,11 +229,12 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec { v.defSites.foreach( i ⇒ { - println("def site: "+i) + if (i > 0) { val stmt2 = taCode.stmts(i) - println("stmt2: "+stmt2) + stmt2 match { + //case Assignment(pcAssigment, v @ DVar(useSites, value), expr) ⇒ case Assignment(pcAssignment, dv @ DVar(useSites, value), expr) ⇒ //useSites dv.useSites.foreach( @@ -214,11 +256,13 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec //val isStaticIndex = if (method.isStatic) 0 else -1 vrs.defSites.foreach( dfste ⇒ { - if (dfste < 0) //(0 + isStaticIndex)) + if (dfste < 0) { + state.notEscapes = false + } //(0 + isStaticIndex)) else { val stmtDefSite = taCode.stmts(dfste) - println("stmt def site: "+stmtDefSite) + stmtDefSite match { case Assignment( pcA, @@ -261,14 +305,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } case _ ⇒ } - /** - * case NonVirtualMethodCall(pc,org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses.Generic_class1, - * isInterface=false, - * void (java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object), - * UVar(defSites={1},value=SObjectValue(type=org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses.Generic_class1,isNull=No,isPrecise=true)),(UVar(defSites={-5},value=SObjectValue(type=org.opalj.fpcf.fixt - * ures.immutability.classes.multinested_genericClasses.Generic_class1,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-3},value=SObjectValue(type=java.lang.Object,isNull=Unknown,isPrecise=false)),UVar(defSites={-4},value=SObjectValue(type=org.opalj.fpcf.fixtures.immutability.classes.mu - * ltinested_genericClasses.TrivialMutableClass,isNull=Unknown,isPrecise=false)))) => * - */ } ) @@ -279,7 +315,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * val NEW = expr.asNew * * val oType = NEW.tpe - * println("otype: "+oType) + * * * if (oType == ObjectType.Object) * state.notEscapes = false @@ -287,12 +323,12 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * val result = propertyStore(oType, TypeImmutability_new.key) * result match { * case FinalP(DependentImmutableType) ⇒ { - * println("depenent type") + * * state.notEscapes = false * } - * case fp @ FinalP(_) ⇒ println("etc. final: "+fp) + * case fp @ FinalP(_) ⇒ fp) * case ep @ _ ⇒ { - * println("type imm not yet computed") + * * state.typeDependees += ep * } * } @@ -314,36 +350,36 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * state.notEscapes = false* */ //TODO } - case _ ⇒ state.notEscapes = false + case _ ⇒ + state.notEscapes = false } } else { //constructor ?? + state.notEscapes = false } } ) } - case _ ⇒ state.notEscapes = false + case _ ⇒ + state.notEscapes = false } - case _ ⇒ state.notEscapes = false + case _ ⇒ + state.notEscapes = false } } else state.notEscapes = false } if (methodUpdatesField(method, taCode, pcs) && !state.field.isFinal) { - println("method does updates field") + return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnfalysis); - } else println("method does not updates fields") - println("st.ref imm 1: "+state.referenceImmutability+", reference: "+state.field) + } } - println("st.ref imm 2: "+state.referenceImmutability+", reference: "+state.field) if (state.lazyInitInvocation.isDefined) { - //val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - //TODO //handleCalls(calleesEOP) + //handleCalls(calleesEOP) } - println("st.ref imm 3: "+state.referenceImmutability+", reference: "+state.field) - println("finish; go to create results") + createResult() } @@ -353,7 +389,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec implicit state: State ): Boolean = { - println("cEOP: "+calleesEOP) calleesEOP match { case FinalP(callees) ⇒ state.calleesDependee = None @@ -361,9 +396,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec case InterimUBP(callees) ⇒ state.calleesDependee = Some(calleesEOP) handleCallees(callees) - case r @ _ ⇒ + case _ ⇒ state.calleesDependee = Some(calleesEOP) - println("false: ; + result "+r) false } } @@ -371,20 +405,11 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec def handleCallees(callees: Callees)(implicit state: State): Boolean = { val pc = state.lazyInitInvocation.get._2 if (callees.isIncompleteCallSite(pc)) { - println("is incomplete call site") state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis true } else { val targets = callees.callees(pc).toTraversable - targets.foreach(t ⇒ println("targets: "+propertyStore(t, Purity.key))) if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { - val nonDeterministicTargets = - targets.filter(target ⇒ isNonDeterministic(propertyStore(target, Purity.key))) - println( - "target non deterministic: "+nonDeterministicTargets - .mkString(", ") - ) - nonDeterministicTargets.foreach(t ⇒ println(propertyStore(t, Purity.key))) state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis true } else false @@ -401,72 +426,12 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec case FloatType ⇒ 0.0f case IntegerType ⇒ 0 case ObjectType(_) ⇒ null + case BooleanType ⇒ false + case ByteType ⇒ 0 + case ShortType ⇒ 0 + case _ ⇒ } ) - - //TODO ?? Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - - /* TODO Some lazy initialized fields use a different value to mark an uninitialized field - * The code below can be used to identify such value, but is not yet adapted to using the - * TACAI property */ - /* - var constantVal: Option[Any] = None - var allInitializeConstant = true - - val field = state.field - var constructors: Set[Method] = - if(field.isStatic) Set.empty else field.classFile.constructors.toSet - - val writesIterator = fieldAccessInformation.writeAccesses(field).iterator - while (writesIterator.hasNext && allInitializeConstant) { - val (method, pc) = writesIterator.next() - constructors -= method - val code = tacai(method).stmts - - val index = pcToIndex(pc) - val stmt = code(index) - if (stmt.astID == PutStatic.ASTID || - stmt.asPutField.objRef.asVar.definedBy == SelfReferenceParameter) { - val write = stmt.asFieldWriteAccessStmt - if (write.resolveField(p).contains(state.field)) { - val defs = write.value.asVar.definedBy - if (defs.size == 1 && defs.head >= 0) { - val defSite = code(defs.head).asAssignment.expr - val const = if (defSite.isIntConst) - Some(defSite.asIntConst.value) - else if (defSite.isFloatConst) - Some(defSite.asFloatConst.value) - else None - if (const.isDefined) { - if (constantVal.isDefined) { - if (constantVal != const) { - allInitializeConstant = false - constantVal = None - } - } else constantVal = const - } else { - allInitializeConstant = false - constantVal = None - } - } - } - } - } - - for (constructor ← constructors) { - // TODO iterate all statements - val NonVirtualMethodCall(_, declClass, _, name, _, rcvr, _) = stmt - // Consider calls to other constructors as initializations as either - // the called constructor will initialize the field or delegate to yet - // another constructor - if (declClass != state.field.classFile.thisType || name != "" || - rcvr.asVar.definedBy != SelfReferenceParameter) { - if (constantVal.isDefined) allInitializeConstant = false - else constantVal = Some(if (state.field.fieldType eq FloatType) 0.0f else 0) - } - } - - constantVal */ } /** @@ -474,11 +439,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * dependees or as Result otherwise. */ def createResult()(implicit state: State): ProperPropertyComputationResult = { - println("create results: ") - println("current ref imm: "+state.referenceImmutability) + if (state.hasDependees && (state.referenceImmutability ne MutableReference)) { //NonFinalFieldByAnalysis)) - println("has still dependendees") - println("create result; interim result; state dependees: "+state.dependees) InterimResult( state.field, MutableReference, //NonFinalFieldByAnalysis, @@ -487,9 +449,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec c ) } else { - println("end") - println("state.reference immutability: "+state.referenceImmutability) - println("state: "+state.notEscapes) if (state.field.isFinal) Result(state.field, ImmutableReference(state.notEscapes)) else @@ -505,7 +464,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * property of the method that initializes a (potentially) lazy initialized field. */ def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { - println("continuation") var isNotFinal = false eps.pk match { case EscapeProperty.key ⇒ @@ -529,13 +487,14 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec isNotFinal = isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) case Purity.key ⇒ val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] - println("purity continuation result: "+newEP) state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) - val r = isNonDeterministic(newEP) + val nonDeterministicResult = isNonDeterministic(newEP) //if (!r) state.referenceImmutability = LazyInitializedReference - println("continuation purity result: "+r) - isNotFinal = r - println("cont. is not final: "+isNotFinal) + if (state.referenceImmutability != LazyInitializedNotThreadSafeOrNotDeterministicReference && + state.referenceImmutability != LazyInitializedThreadSafeReference) { // both dont need determinism + isNotFinal = nonDeterministicResult + } + case ReferenceImmutability.key ⇒ val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] state.referenceImmutabilityDependees = @@ -566,12 +525,17 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec taCode: TACode[TACMethodParameter, V], pcs: PCs )(implicit state: State): Boolean = { - println("method updates field?") val field = state.field val stmts = taCode.stmts for (pc ← pcs) { val index = taCode.pcToIndex(pc) - if (index >= 0) { + val staticAddition = { + if (method.isStatic) + 1 + else + 0 + } + if (index > (-1 + staticAddition)) { val stmt = stmts(index) if (stmt.pc == pc) { @@ -591,7 +555,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec 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.referenceImmutability == LazyInitializedReference) //LazyInitializedField) + if (state.referenceImmutability == LazyInitializedThreadSafeReference || + state.referenceImmutability == LazyInitializedNotThreadSafeButDeterministicReference) //LazyInitializedField) return true; // A lazily initialized instance field must be initialized only // by its owning instance @@ -602,21 +567,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (defaultValue.isEmpty) return true; - //TODO Lazy Initialization here - /** - * val liResult = propertyStore(field, ReferenceImmutabilityLazyInitialization.key) - * - * liResult match { - * case FinalP(NoLazyInitialization) => return true; - * case FinalP(NotThreadSafeLazyInitialization) => return true; - * case FinalP(LazyInitialization) => - * state.referenceImmutability = LazyInitializedReference - * case _ => return true; - * } - */ // A field written outside an initializer must be lazily // initialized or it is non-final - val b = isDoubleCheckedLocking( + val dcl = isThreadSafeLazyInitialisation( index, defaultValue.get, method, @@ -625,21 +578,25 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec taCode.pcToIndex, taCode ) - if (!b && !isLazyInitialization( + val li = isLazyInitialization( index, defaultValue.get, method, taCode.stmts, taCode.cfg, taCode.pcToIndex - )) { - println("is not lazy initialized") - println("method: "+method) - return true - }; - println("is lazy initialized") - state.referenceImmutability = LazyInitializedReference - //LazyInitializedField + ) + dcl match { + case MutableReference if (!li) ⇒ return true; + case MutableReference if (li) ⇒ state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference + case LazyInitializedNotThreadSafeButDeterministicReference ⇒ + state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference + case LazyInitializedNotThreadSafeOrNotDeterministicReference ⇒ + if (li) state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference + else state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference + case lits @ LazyInitializedThreadSafeReference ⇒ state.referenceImmutability = lits + } + } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { // note that here we assume real three address code (flat hierarchy) @@ -666,24 +623,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec false } - /** - * 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 eps: InterimEP[Method, TACAI] ⇒ - * state.tacDependees += method → ((eps, pcs)) - * eps.ub.tac - * case epk ⇒ - * state.tacDependees += method → ((epk, pcs)) - * None - * } - * } - * Returns the TACode for a method if available, registering dependencies as necessary. - */ /** * Checks whether the object reference of a PutField does escape (except for being returned). */ @@ -692,16 +631,10 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec stmts: Array[Stmt[V]], method: Method )(implicit state: State): Boolean = { - println("ref: "+ref) - println("defined by: "+ref.definedBy) ref.definedBy.forall { defSite ⇒ - println("0") - println("field: "+state.field) - println("defsite: "+defSite) if (defSite < 0) true // Must be locally created else { - println("1") val definition = stmts(defSite).asAssignment // Must either be null or freshly allocated if (definition.expr.isNullExpr) false @@ -709,7 +642,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec else { val escape = propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - println("escape property: "+escape) handleEscapeProperty(escape) } } From 3f73b12536c49a25d458e5cadec4158fd3ab0401 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 9 Jul 2020 10:39:13 +0200 Subject: [PATCH 187/327] adapt tests and demos for new ref imm properties --- .../ReferenceImmutabilityAnalysisDemo.scala | 6 +- ...mutabilityAnalysisDemo_withNewPurity.scala | 58 ++++++++++++------- .../generic/ClassWithGenericField_deep.java | 4 +- .../ClassWithGenericField_shallow.java | 4 +- ...DependentClassWithGenericField_deep01.java | 4 +- .../DependentClassWithGenericField_deep1.java | 6 +- ...DependentClassWithGenericField_deep11.java | 6 +- .../DependentClassWithGenericField_deep2.java | 4 +- .../GenericAndDeepImmutableFields.java | 8 +-- .../generic/GenericAndMutableFields.java | 4 +- .../GenericClassWithExtFinalMutTypes.java | 8 +-- .../classes/generic/Generic_class1.java | 12 ++-- .../DeepGenericTest.java | 4 +- .../Generic_class2.java | 10 ++-- .../Generic_class3.java | 6 +- .../Generic_class4_deep.java | 4 +- .../Generic_class4_shallow.java | 4 +- .../multinested_genericClasses/One.java | 12 ++-- .../multinested_genericClasses/OneVirgin.java | 10 ++-- .../multinested_genericClasses/Two.java | 4 +- .../multinested_genericClasses/TwoVirgin.java | 6 +- .../trivials/DependentImmutableClass.java | 4 +- .../trivials/ShallowImmutableClass.java | 4 +- .../field/privateFieldNotBlank_deep.java | 4 +- .../field/privateFieldNotBlank_shallow.java | 4 +- ...FinalFieldBlank_costructorEscape_deep.java | 4 +- ...alFieldBlank_costructorEscape_shallow.java | 4 +- .../field/privateFinalFieldNotBlank_deep.java | 4 +- .../privateFinalFieldNotBlank_shallow.java | 4 +- .../field/private_getterEscape_deep.java | 4 +- .../field/private_getterEscape_shallow.java | 4 +- .../field/privatefinal_getterEscape_deep.java | 4 +- .../privatefinal_getterEscape_shallow.java | 4 +- .../reference/DeclaredFinalFields.java | 8 +-- .../reference/LazyInitialization.java | 34 +++++------ .../reference/PrivateFieldUpdater.java | 4 +- .../immutability/reference/Singleton.java | 7 ++- .../DoubleCheckedLockingClass1.java | 5 +- .../DoubleCheckedLockingClass2.java | 4 +- .../DoubleCheckedLockingClass3.java | 4 +- .../DoubleCheckedLockingClass4.java | 4 +- .../DoubleCheckedLockingClass5.java | 4 +- .../DoubleCheckedLockingClass6.java | 6 +- .../SimpleLazyInstantiation.java | 4 +- .../SimpleLazyIntInstantiation.java | 4 +- .../SimpleLazyObjectsInstantiation.java | 4 +- .../WithMutableAndImmutableFieldType.java | 6 +- ... ImmutableReferenceEscapesAnnotation.java} | 4 +- ...mutableReferenceNotEscapesAnnotation.java} | 10 ++-- ...ializedThreadSafeReferenceAnnotation.java} | 4 +- ...renceImmutabilityTests_withNewPurity.scala | 4 +- .../ReferenceImmutabilityMatcher.scala | 18 ++++-- 52 files changed, 194 insertions(+), 176 deletions(-) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/{ImmutableReferenceAnnotation.java => ImmutableReferenceEscapesAnnotation.java} (90%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java => reference_immutability/ImmutableReferenceNotEscapesAnnotation.java} (61%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/{LazyInitializedReferenceAnnotation.java => LazyInitializedThreadSafeReferenceAnnotation.java} (88%) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index 66898bf3e9..96d24f9195 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -8,7 +8,6 @@ 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.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey @@ -22,11 +21,12 @@ 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.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.fpcf.SomeEPS import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis - import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis /** @@ -91,7 +91,7 @@ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { sb = sb.append("\n Lazy Initialized Reference: \n") val lazyInitializedReferences = propertyStore - .finalEntities(LazyInitializedReference) + .finalEntities(LazyInitializedThreadSafeReference).toList ++ (propertyStore.finalEntities(LazyInitializedNotThreadSafeButDeterministicReference)) .toList sb = sb.append( lazyInitializedReferences.mkString(", \n") diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala index 23f13f9118..9ba29c8454 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala @@ -8,7 +8,6 @@ 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.br.fpcf.properties.LazyInitializedReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey @@ -21,6 +20,11 @@ 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.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference +import org.opalj.fpcf import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis @@ -86,37 +90,47 @@ object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisAp val mutableReferences = propertyStore .finalEntities(MutableReference) .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList + .toList.sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) sb = sb.append( mutableReferences.map(x ⇒ x.toString+"\n").toString() ) - sb = sb.append("\n Lazy Initialized Reference: \n") - val lazyInitializedReferences = propertyStore - .finalEntities(LazyInitializedReference) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - sb = sb.append( - lazyInitializedReferences - .map(x ⇒ x.toString+"\n") - .toString() + val lazyInitializedReferencesThreadSafe = propertyStore + .finalEntities(LazyInitializedThreadSafeReference).toList.sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) + + val lazyInitializedReferencesNotThreadSafeButDeterministic = propertyStore. + finalEntities(LazyInitializedNotThreadSafeButDeterministicReference).toList.sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) + + val notThreadSafeOrNotDeterministicLazyInitialization = propertyStore. + finalEntities(LazyInitializedNotThreadSafeOrNotDeterministicReference).toList.sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) + + sb.append( + s""" + | lazy initialized thread safe references: ${lazyInitializedReferencesThreadSafe.mkString(",\n")} + | + | lazy initialized not thread safe but deterministic references: ${lazyInitializedReferencesNotThreadSafeButDeterministic.mkString(", \n")} + | + | lazy initialized not thread safe or not deterministic references: ${notThreadSafeOrNotDeterministicLazyInitialization.mkString(", \n")} + | + |""".stripMargin ) - sb = sb.append("\nImmutable References: \n") + val immutableReferences = propertyStore.entities(eps ⇒ //allfieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field]) && + eps.isFinal && (eps.asFinal.p match { + case ImmutableReference(_) ⇒ true + case _ ⇒ false + })).toList.sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) + sb = sb.append( + immutableReferences.map(x ⇒ x.toString+"\n").mkString(", ") + ) - /** - * val immutableReferences = propertyStore - * .filter(x => allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - * .toList - * sb = sb.append( - * immutableReferences.map(x => x.toString + "\n").toString() - * )* - */ sb.append( s""" | mutable References: ${mutableReferences.size} - | lazy initialized References: ${lazyInitializedReferences.size} - | immutable References: ${} + | lazy initialized references not thread safe or deterministic: ${notThreadSafeOrNotDeterministicLazyInitialization.size} + | lazy initialized references not thread safe but deterministic: ${lazyInitializedReferencesNotThreadSafeButDeterministic.size} + | lazy initialized thread safe references: ${lazyInitializedReferencesThreadSafe.size} + | immutable References: ${immutableReferences.size} | | took : $analysisTime seconds | diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_deep.java index 994945fa64..f432311f21 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_deep.java @@ -3,7 +3,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; @@ -11,7 +11,7 @@ @DeepImmutableClassAnnotation("") public final class ClassWithGenericField_deep { @DeepImmutableFieldAnnotation("deep imm field") - @ImmutableReferenceAnnotation("eff imm ref") + @ImmutableReferenceNotEscapesAnnotation("eff imm ref") private Generic_class2 gc = new Generic_class2 (new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2()); diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java index 6029193461..f9369de75a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java @@ -2,14 +2,14 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; @DeepImmutableTypeAnnotation("") @DeepImmutableClassAnnotation("") public final class ClassWithGenericField_shallow { @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceNotEscapesAnnotation("") private Generic_class1 gc = new Generic_class1 (new TrivialMutableClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep01.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep01.java index 0dde411160..a456d03b43 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep01.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep01.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; @DeepImmutableTypeAnnotation("") @@ -13,7 +13,7 @@ public final class DependentClassWithGenericField_deep01 { private FinalEmptyClass fec; @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("eff imm ref") + @ImmutableReferenceEscapesAnnotation("eff imm ref") private Generic_class1 gc; public DependentClassWithGenericField_deep01(FinalEmptyClass fec) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java index d7b653cf05..5c25dc2dc0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; @@ -11,11 +11,11 @@ public final class DependentClassWithGenericField_deep1 { @DependentImmutableFieldAnnotation(value = "", genericString = "T1") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private T1 t1; @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") - @ImmutableReferenceAnnotation("eff imm ref") + @ImmutableReferenceEscapesAnnotation("eff imm ref") private Generic_class1 gc; public DependentClassWithGenericField_deep1(T1 t1) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep11.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep11.java index a779d2d363..e36f1343eb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep11.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep11.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; @@ -11,11 +11,11 @@ public final class DependentClassWithGenericField_deep11 { @DependentImmutableFieldAnnotation(value = "", genericString = "T1") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private T1 t1; @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") - @ImmutableReferenceAnnotation("eff imm ref") + @ImmutableReferenceEscapesAnnotation("eff imm ref") private DependentClassWithGenericField_deep1 gc; public DependentClassWithGenericField_deep11(T1 t1) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep2.java index bc7e773c93..b10fa5a068 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep2.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep2.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; @@ -11,7 +11,7 @@ public final class DependentClassWithGenericField_deep2 { @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") - @ImmutableReferenceAnnotation("eff imm ref") + @ImmutableReferenceEscapesAnnotation("eff imm ref") private Generic_class1 gc; public DependentClassWithGenericField_deep2(T1 t1) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndDeepImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndDeepImmutableFields.java index c36f2ca4b6..996bb38bdc 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndDeepImmutableFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndDeepImmutableFields.java @@ -3,7 +3,7 @@ import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; @DependentImmutableTypeAnnotation("") @@ -11,15 +11,15 @@ public final class GenericAndDeepImmutableFields { @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private T1 t1; @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private T2 t2; @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private FinalEmptyClass fec; GenericAndDeepImmutableFields(T1 t1, T2 t2, FinalEmptyClass fec){ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndMutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndMutableFields.java index cf7a2668bb..925f269911 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndMutableFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndMutableFields.java @@ -4,7 +4,7 @@ import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @@ -15,7 +15,7 @@ public class GenericAndMutableFields { @MutableReferenceAnnotation("Because of public field") public T1 t1; @DependentImmutableFieldAnnotation(value = "Because of generic type", genericString = "T2") - @ImmutableReferenceAnnotation("Because of effectively immutable final") + @ImmutableReferenceEscapesAnnotation("Because of effectively immutable final") private T2 t2; GenericAndMutableFields(T1 t1, T2 t2){ this.t1 = t1; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithExtFinalMutTypes.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithExtFinalMutTypes.java index 9805511d7b..4d0ef9850d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithExtFinalMutTypes.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithExtFinalMutTypes.java @@ -5,7 +5,7 @@ import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("") @@ -13,15 +13,15 @@ public class GenericClassWithExtFinalMutTypes { @ShallowImmutableFieldAnnotation("") -@ImmutableReferenceAnnotation("") +@ImmutableReferenceEscapesAnnotation("") private A a; @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private B b; @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private C c; GenericClassWithExtFinalMutTypes(A a, B b, C c){ this.a = a; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Generic_class1.java index af9776ab2e..b68cdc6c8f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Generic_class1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Generic_class1.java @@ -2,26 +2,26 @@ import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; @DependentImmutableTypeAnnotation("Dependent Immutability of T1,...,T5") @DependentImmutableClassAnnotation("Dependent Immutability of T1,...,T5") public final class Generic_class1 { @DependentImmutableFieldAnnotation(value = "T1",genericString = "T1") - @ImmutableReferenceAnnotation("effectively") + @ImmutableReferenceEscapesAnnotation("effectively") private T1 t1; @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") - @ImmutableReferenceAnnotation("effectively") + @ImmutableReferenceEscapesAnnotation("effectively") private T2 t2; @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") - @ImmutableReferenceAnnotation("effectively") + @ImmutableReferenceEscapesAnnotation("effectively") private T3 t3; @DependentImmutableFieldAnnotation(value = "T4", genericString = "T3") - @ImmutableReferenceAnnotation("effectively") + @ImmutableReferenceEscapesAnnotation("effectively") private T4 t4; @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") - @ImmutableReferenceAnnotation("effectively") + @ImmutableReferenceEscapesAnnotation("effectively") private T5 t5; public Generic_class1(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/DeepGenericTest.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/DeepGenericTest.java index 728f2735dd..e5c7ad8a2d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/DeepGenericTest.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/DeepGenericTest.java @@ -2,14 +2,14 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; @DeepImmutableTypeAnnotation("") @DeepImmutableClassAnnotation("") public final class DeepGenericTest { @DeepImmutableFieldAnnotation("") -@ImmutableReferenceAnnotation("") +@ImmutableReferenceEscapesAnnotation("") private Generic_class1 gc1; public DeepGenericTest(Generic_class1 gc1){ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class2.java index 47c9ef35cc..2ba61602d8 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class2.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class2.java @@ -2,26 +2,26 @@ import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; @DependentImmutableTypeAnnotation("") @DependentImmutableClassAnnotation("") public final class Generic_class2 { - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") @DependentImmutableFieldAnnotation(value="T1", genericString = "T1") private T1 t1; - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") @DependentImmutableFieldAnnotation(value="T2", genericString = "T2") private T2 t2; - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") @DependentImmutableFieldAnnotation(value="T3", genericString = "T3") private T3 t3; - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") @DependentImmutableFieldAnnotation(value = "", genericString = "") private Generic_class1 gc; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class3.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class3.java index 4bc19672e1..a9337d029d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class3.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class3.java @@ -2,18 +2,18 @@ import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; @DependentImmutableTypeAnnotation("") @DependentImmutableClassAnnotation("") public final class Generic_class3 { - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") private T1 t1; - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") @DependentImmutableFieldAnnotation(value = "", genericString = "") private Generic_class2 gc; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_deep.java index bfaee579b9..5a0ea1fb72 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_deep.java @@ -2,14 +2,14 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; @DeepImmutableTypeAnnotation("") @DeepImmutableClassAnnotation("") public final class Generic_class4_deep { - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") @DeepImmutableFieldAnnotation("") private Generic_class3 gc; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_shallow.java index 10a6dba431..61a3b720ad 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_shallow.java @@ -2,14 +2,14 @@ import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; @ShallowImmutableTypeAnnotation("") @ShallowImmutableClassAnnotation("") public final class Generic_class4_shallow { - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") @ShallowImmutableFieldAnnotation("") private Generic_class3 gc; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java index 1c38425c0c..02a2586a71 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java @@ -4,7 +4,7 @@ import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @@ -12,22 +12,22 @@ @MutableClassAnnotation("") public class One { @DependentImmutableFieldAnnotation(value="",genericString = "A") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private A a; @DependentImmutableFieldAnnotation(value="",genericString = "B") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private B b; @DependentImmutableFieldAnnotation(value="",genericString = "C") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private C c; @DependentImmutableFieldAnnotation(value="",genericString = "D") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private D d; @MutableFieldAnnotation("") @MutableReferenceAnnotation("") TrivialMutableClass tmc; @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private Generic_class1 gc1; public One(A a, B b, C c, D d, TrivialMutableClass tmc){ this.a = a; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java index 8e3b7f1d29..ce0d4905f9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java @@ -3,7 +3,7 @@ import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @@ -11,16 +11,16 @@ @MutableClassAnnotation("") public class OneVirgin { @DependentImmutableFieldAnnotation(value="",genericString = "A") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private A a; @DependentImmutableFieldAnnotation(value="",genericString = "B") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private B b; @DependentImmutableFieldAnnotation(value="",genericString = "C") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private C c; @DependentImmutableFieldAnnotation(value="",genericString = "D") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private D d; @MutableFieldAnnotation("") diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java index 8b0998cf0d..a77f9c5b7a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("") @@ -10,7 +10,7 @@ public class Two { @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private Generic_class1, B, B, B, TrivialMutableClass> gc1; public Two(A a, B b, TrivialMutableClass tmc, Generic_class1 gc1) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java index d7ba861186..ce57ade261 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java @@ -1,13 +1,11 @@ package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; public class TwoVirgin { @DependentImmutableFieldAnnotation(value="",genericString = "") - @ImmutableReferenceAnnotation("") + @ImmutableReferenceEscapesAnnotation("") private Generic_class1, B, C, C, C> gc1; public TwoVirgin(A a, B b, C c, Generic_class1 gc1) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java index a718eea2dc..ec12a20feb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java @@ -2,14 +2,14 @@ import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("Because of not final class") @DependentImmutableClassAnnotation("Das dependent immutable field") public class DependentImmutableClass { @DependentImmutableFieldAnnotation(value = "Because of type T",genericString = "T") - @ImmutableReferenceAnnotation("Private effectively final field") + @ImmutableReferenceEscapesAnnotation("Private effectively final field") private T t; public DependentImmutableClass(T t){ this.t = t; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java index 130dfce68b..59375156c8 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java @@ -2,13 +2,13 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("Because of not final class") @DeepImmutableClassAnnotation("has shallow immutable field") public class ShallowImmutableClass { @DeepImmutableFieldAnnotation("Because of mutable type") - @ImmutableReferenceAnnotation("Because it is private") + @ImmutableReferenceNotEscapesAnnotation("Because it is private") private MutableClass mutableClass = new MutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java index d78125203f..c3856793a1 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java @@ -2,13 +2,13 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("Because of not final class") @DeepImmutableClassAnnotation("Because it has only Deep Immutable Field Types") public class privateFieldNotBlank_deep { @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceAnnotation("Effectively Immutable Reference") + @ImmutableReferenceNotEscapesAnnotation("Effectively Immutable Reference") private FinalEmptyClass name = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java index a96a595112..17438376a7 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("Because of not final class") @@ -10,6 +10,6 @@ public class privateFieldNotBlank_shallow { @DeepImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") - @ImmutableReferenceAnnotation("Effectively Immutable Reference") + @ImmutableReferenceNotEscapesAnnotation("Effectively Immutable Reference") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java index ecc426d142..b72f81b2aa 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java @@ -2,13 +2,13 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; @DeepImmutableClassAnnotation("It has only Deep Immutable Fields") public class privateFinalFieldBlank_costructorEscape_deep { @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceAnnotation("Declared final Reference") + @ImmutableReferenceEscapesAnnotation("Declared final Reference") private final FinalEmptyClass fec; public privateFinalFieldBlank_costructorEscape_deep(FinalEmptyClass fec) { this.fec = fec; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java index 7d1c430b2b..304f3b1404 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java @@ -2,13 +2,13 @@ import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; @ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") public class privateFinalFieldBlank_costructorEscape_shallow { @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") - @ImmutableReferenceAnnotation("Declared final Field") + @ImmutableReferenceEscapesAnnotation("Declared final Field") private final TrivialMutableClass tmc; public privateFinalFieldBlank_costructorEscape_shallow(TrivialMutableClass tmc) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_deep.java index b000b5e549..8e86d0e607 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_deep.java @@ -3,11 +3,11 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; @DeepImmutableClassAnnotation("It has only Deep Immutable Fields") public class privateFinalFieldNotBlank_deep { @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceAnnotation("Declared final Field") + @ImmutableReferenceNotEscapesAnnotation("Declared final Field") private final FinalEmptyClass fec = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java index f010426abe..20303eca61 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java @@ -3,12 +3,12 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; @DeepImmutableClassAnnotation("It has only Shallow Immutable Fields") public class privateFinalFieldNotBlank_shallow { @DeepImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") - @ImmutableReferenceAnnotation("Declared final Field") + @ImmutableReferenceNotEscapesAnnotation("Declared final Field") private final TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java index c59b4e1c82..a08a4f6321 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("Because of not final class") @@ -12,6 +12,6 @@ public FinalEmptyClass getFec() { return fec; } @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceAnnotation("It is effectively immutable") + @ImmutableReferenceEscapesAnnotation("It is effectively immutable") private FinalEmptyClass fec = new FinalEmptyClass(); } \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java index 9ed4ec4eb1..01af50412b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("Because of not final class") @@ -13,7 +13,7 @@ public TrivialMutableClass getTmc() { } @ShallowImmutableFieldAnnotation("Because of Immutable Reference and Mutable Field Type") - @ImmutableReferenceAnnotation("effectively immutable") + @ImmutableReferenceEscapesAnnotation("effectively immutable") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java index 4963d71a63..dea464dccd 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; @DeepImmutableClassAnnotation("It has only Deep Immutable Fields") public class privatefinal_getterEscape_deep { @@ -11,7 +11,7 @@ public FinalEmptyClass getFec() { } @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceAnnotation("Declared final Field") + @ImmutableReferenceEscapesAnnotation("Declared final Field") private final FinalEmptyClass fec = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java index 02713bd81a..fc0bb8db10 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; @ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") public class privatefinal_getterEscape_shallow { @@ -11,6 +11,6 @@ public TrivialMutableClass getTmc() { } @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") - @ImmutableReferenceAnnotation("Declared final Reference") + @ImmutableReferenceEscapesAnnotation("Declared final Reference") private final TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java index cd16812cc3..0d352dcd67 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java @@ -28,7 +28,7 @@ */ package org.opalj.fpcf.fixtures.immutability.reference; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; /** @@ -54,13 +54,13 @@ public Super(){ */ public class DeclaredFinalFields extends Super { - @ImmutableReferenceAnnotation("Initialized directly") + @ImmutableReferenceNotEscapesAnnotation("Initialized directly") private final int a = 1; - @ImmutableReferenceAnnotation("Initialized through instance initializer") + @ImmutableReferenceNotEscapesAnnotation("Initialized through instance initializer") private final int b; - @ImmutableReferenceAnnotation("Initialized through constructor") + @ImmutableReferenceNotEscapesAnnotation("Initialized through constructor") private final int c; @MutableReferenceAnnotation(value = "Prematurely read through super constructor", prematurelyRead = true) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java index 9575272188..dd2d37ce1e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java @@ -29,9 +29,7 @@ package org.opalj.fpcf.fixtures.immutability.reference; import org.opalj.fpcf.properties.field_mutability.LazyInitialized; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.*; /** * Test classes for simple lazy initialization patterns and anti-patterns regarding reference immutability analysis. @@ -42,7 +40,7 @@ class Simple { - @LazyInitializedReferenceAnnotation("Simple lazy initialization") + @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Simple lazy initialization") private int x; public int init() { @@ -55,7 +53,7 @@ public int init() { class Local { - @LazyInitializedReferenceAnnotation("Lazy initialization with local") + @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Lazy initialization with local") private int x; public int init() { @@ -83,7 +81,7 @@ public int init() { class LocalReversed { - @LazyInitializedReferenceAnnotation("Lazy initialization with local (reversed)") + @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Lazy initialization with local (reversed)") private int x; public int init() { @@ -97,7 +95,7 @@ public int init() { class LocalReload { - @LazyInitializedReferenceAnnotation("Lazy initialization with local (reloading the field's value after the write)") + @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Lazy initialization with local (reloading the field's value after the write)") private int x; public int init() { @@ -112,7 +110,7 @@ public int init() { class SimpleReversed { - @LazyInitializedReferenceAnnotation("Simple lazy initialization (reversed)") + @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Simple lazy initialization (reversed)") private int x; public int init() { @@ -170,7 +168,7 @@ public int init() { class DeterministicCall { - @LazyInitializedReferenceAnnotation("Lazy initialization with call to deterministic method") + @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Lazy initialization with call to deterministic method") private int x; public int init() { @@ -187,7 +185,7 @@ private final int sum(int a, int b) { class DeterministicCallWithParam { - @MutableReferenceAnnotation("Lazy initialization is not the same for different invocations") + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("Lazy initialization is not the same for different invocations") private int x; public int init(int z) { @@ -204,10 +202,10 @@ private final int sum(int a, int b) { class DeterministicCallOnFinalField { - @LazyInitializedReferenceAnnotation("Lazy initialization with call to deterministic method on final field") + @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Lazy initialization with call to deterministic method on final field") private int x; - @ImmutableReferenceAnnotation("Declared final field") + @ImmutableReferenceEscapesAnnotation("Declared final field") private final Inner inner; public DeterministicCallOnFinalField(int v) { @@ -270,7 +268,7 @@ public int init() { class NondeterministicCall { - @MutableReferenceAnnotation("Wrong lazy initialization with call to non-deterministic method") + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("Wrong lazy initialization with call to non-deterministic method") private int x; private final Object object = new Object(); @@ -285,7 +283,7 @@ public int init() { class DoubleLocalAssignment { - @LazyInitializedReferenceAnnotation("Lazy initialization with a local that is updated twice") + @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Lazy initialization with a local that is updated twice") private int x; public int init() { @@ -337,7 +335,7 @@ 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. */ - @ImmutableReferenceAnnotation(value = "Field is never initialized, so it stays on its default value") + @ImmutableReferenceEscapesAnnotation(value = "Field is never initialized, so it stays on its default value") private int x; private int getZero() { @@ -356,7 +354,7 @@ public int init() { class PossibleExceptionInInitialization { - @MutableReferenceAnnotation("Incorrect because lazy initialization is may not happen due to exception") + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("Incorrect because lazy initialization is may not happen due to exception") private int x; public int init(int i) { @@ -370,8 +368,8 @@ public int init(int i) { } class CaughtExceptionInInitialization { - - @MutableReferenceAnnotation("Incorrect because lazy initialization is may not happen due to exception") + //TODO reasoning + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("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/immutability/reference/PrivateFieldUpdater.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFieldUpdater.java index 3513eb4956..cd63085956 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFieldUpdater.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFieldUpdater.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.reference; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; /** @@ -9,7 +9,7 @@ */ public class PrivateFieldUpdater { - @ImmutableReferenceAnnotation("only initialized by the constructor") + @ImmutableReferenceEscapesAnnotation("only initialized by the constructor") private String name; @MutableReferenceAnnotation("incremented whenever `this` object is passed to another `NonFinal` object") diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java index 105a9d77c5..afe0efe4b1 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java @@ -1,7 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.reference; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; public class Singleton { @@ -9,7 +10,7 @@ public class Singleton { @MutableReferenceAnnotation("written by static initializer after the field becomes (indirectly) readable") private String name; - @ImmutableReferenceAnnotation("only initialized once by the constructor") + @ImmutableReferenceNotEscapesAnnotation("only initialized once by the constructor") private Object mutex = new Object(); private Singleton() { @@ -24,7 +25,7 @@ public String getName() { // STATIC FUNCTIONALITY - @ImmutableReferenceAnnotation("only set in the static initializer") + @ImmutableReferenceEscapesAnnotation("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/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java index 85dc44dda5..412efe3af4 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java @@ -1,12 +1,11 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability_lazy_initialization.LazyInitializationAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; public class DoubleCheckedLockingClass1{ - @LazyInitializedReferenceAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") private DoubleCheckedLockingClass1 instance; public DoubleCheckedLockingClass1 getInstance() { if(instance==null){ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java index 79d94dbfdf..ddcc90a677 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java @@ -1,11 +1,11 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; public class DoubleCheckedLockingClass2 { - @LazyInitializedReferenceAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") private DoubleCheckedLockingClass2 instance; private DoubleCheckedLockingClass2 instance2 = new DoubleCheckedLockingClass2(); public DoubleCheckedLockingClass2 getInstance() { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass3.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass3.java index f1bcea58b8..09c1415cb5 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass3.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass3.java @@ -1,11 +1,11 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; public class DoubleCheckedLockingClass3 { - @MutableReferenceAnnotation("") + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") private DoubleCheckedLockingClass3 instance; public DoubleCheckedLockingClass3 getInstance() { if(instance==null){ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass4.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass4.java index 9f573435c9..a9a2365556 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass4.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass4.java @@ -1,10 +1,10 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; public class DoubleCheckedLockingClass4 { - @MutableReferenceAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") private DoubleCheckedLockingClass4 instance; public DoubleCheckedLockingClass4 getInstance() { synchronized(DoubleCheckedLockingClass4.class) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass5.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass5.java index 5fbe1a3bb1..86992fc679 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass5.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass5.java @@ -1,11 +1,11 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; public class DoubleCheckedLockingClass5 { - @MutableReferenceAnnotation("") + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") private DoubleCheckedLockingClass5 instance; public DoubleCheckedLockingClass5 getInstance() { if(instance==null){ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java index f2cbc3783f..5b2fd97902 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java @@ -1,17 +1,17 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; public class DoubleCheckedLockingClass6 { - @MutableReferenceAnnotation("") + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") private DoubleCheckedLockingClass6 instance; public DoubleCheckedLockingClass6 getInstance() { if(instance==null){ synchronized(this) { if(instance==null){ - instance = new DoubleCheckedLockingClass6(); + //instance = new DoubleCheckedLockingClass6(); } instance = new DoubleCheckedLockingClass6(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java index b1ee5cf034..a68146f07d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java @@ -1,9 +1,9 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; public class SimpleLazyInstantiation{ - @MutableReferenceAnnotation("") + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") private static SimpleLazyInstantiation instance; public static SimpleLazyInstantiation init() { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java index 0fe13875d7..d576eca7f8 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java @@ -1,9 +1,9 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; public class SimpleLazyIntInstantiation{ - @LazyInitializedReferenceAnnotation("") + @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("") private int i = 0; public int hashcode() { if(i==0) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java index 1bfe387607..7a88cc1316 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java @@ -1,9 +1,9 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; public class SimpleLazyObjectsInstantiation{ - @MutableReferenceAnnotation("") + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") private static SimpleLazyObjectsInstantiation instance; public static SimpleLazyObjectsInstantiation getInstance() { if(instance==null) 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 index 2b279385cf..b12a68c2fc 100644 --- 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 @@ -3,7 +3,7 @@ import org.opalj.fpcf.fixtures.immutability.field.TrivialMutableClass; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; @DeepImmutableTypeAnnotation("has shallow and mutable fields") @@ -11,11 +11,11 @@ public final class WithMutableAndImmutableFieldType { @DeepImmutableFieldAnnotation("immutable reference and deep immutable type") - @ImmutableReferenceAnnotation("private field") + @ImmutableReferenceNotEscapesAnnotation("private field") private FinalEmptyClass fec = new FinalEmptyClass(); @DeepImmutableFieldAnnotation("imm reference and mutable type") - @ImmutableReferenceAnnotation("private field") + @ImmutableReferenceNotEscapesAnnotation("private field") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceEscapesAnnotation.java similarity index 90% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceEscapesAnnotation.java index 15f389d409..bed16e38aa 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceEscapesAnnotation.java @@ -13,10 +13,10 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = ImmutableReferenceMatcher.class) +@PropertyValidator(key = "ReferenceImmutability",validator = ImmutableReferenceEscapesMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface ImmutableReferenceAnnotation { +public @interface ImmutableReferenceEscapesAnnotation { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceNotEscapesAnnotation.java similarity index 61% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceNotEscapesAnnotation.java index 74a85e9600..f9021299f4 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability_lazy_initialization/NoLazyInitializationAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceNotEscapesAnnotation.java @@ -1,9 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.reference_immutability_lazy_initialization; +package org.opalj.fpcf.properties.reference_immutability; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.L0ReferenceImmutabilityLazyInitializationAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.reference.L0ReferenceImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -14,16 +14,16 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutabilityLazyInitialization",validator = NoLazyInitializationMatcher.class) +@PropertyValidator(key = "ReferenceImmutability",validator = ImmutableReferenceNotEscapesMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface NoLazyInitializationAnnotation { +public @interface ImmutableReferenceNotEscapesAnnotation { /** * A short reasoning of this property. */ String value();// default = "N/A"; - Class[] analyses() default {L0ReferenceImmutabilityLazyInitializationAnalysis.class}; + Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedThreadSafeReferenceAnnotation.java similarity index 88% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedThreadSafeReferenceAnnotation.java index ab2bdefdf5..e29dcda138 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedThreadSafeReferenceAnnotation.java @@ -14,10 +14,10 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedReferenceMatcher.class) +@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedThreadSafeReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface LazyInitializedReferenceAnnotation { +public @interface LazyInitializedThreadSafeReferenceAnnotation { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala index 8c88a8d030..0513156e72 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala @@ -32,11 +32,11 @@ class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") ///reference_immutability_lazy_initialization/sandbox2") + List("org/opalj/fpcf/fixtures/immutability") ///reference_immutability_lazy_initialization/sandbox3") } override def init(p: Project[URL]): Unit = { - println("Java version: "+System.getProperty("java.version")) + //println("Java version: "+System.getProperty("java.version")) p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala index 725777e70a..24a000b914 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala @@ -5,7 +5,9 @@ import org.opalj.br.AnnotationLike import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.ImmutableReference -import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.br.fpcf.properties.ReferenceImmutability import org.opalj.fpcf.Entity import org.opalj.fpcf.Property @@ -44,8 +46,8 @@ class ReferenceImmutabilityMatcher(val property: ReferenceImmutability) if (!properties.exists(p ⇒ { val tmpP = { p match { - case ImmutableReference(_) ⇒ ImmutableReference(true) - case _ ⇒ p + //case ImmutableReference(_) ⇒ ImmutableReference(true) + case _ ⇒ p } } tmpP == property @@ -58,6 +60,12 @@ class ReferenceImmutabilityMatcher(val property: ReferenceImmutability) } } -class LazyInitializedReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedReference) +class LazyInitializedThreadSafeReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedThreadSafeReference) -class ImmutableReferenceMatcher extends ReferenceImmutabilityMatcher(ImmutableReference(true)) +class LazyInitializedNotThreadSafeButDeterministicReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedNotThreadSafeButDeterministicReference) + +class LazyInitializedNotThreadSafeOrNotDeterministicReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedNotThreadSafeOrNotDeterministicReference) + +class ImmutableReferenceNotEscapesMatcher extends ReferenceImmutabilityMatcher(ImmutableReference(true)) + +class ImmutableReferenceEscapesMatcher extends ReferenceImmutabilityMatcher(ImmutableReference(false)) From 8c9f22b44648704c7c7d55b426990c5d428340b7 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 9 Jul 2020 10:42:22 +0200 Subject: [PATCH 188/327] field imm analysis without prinltns, adapted to the new ref imm li properties and with recognition of generic fields of inner classes with the generic type parameter defined in signature of the outer class --- .../L0FieldImmutabilityAnalysis.scala | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index 0f7847d09c..9f0d71098c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -1,6 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.immutability +import org.opalj.br.Attribute import org.opalj.br.ClassSignature import org.opalj.br.ClassTypeSignature import org.opalj.br.Field @@ -25,7 +26,9 @@ 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.ImmutableReference -import org.opalj.br.fpcf.properties.LazyInitializedReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.br.fpcf.properties.MutableField import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.MutableType_new @@ -97,7 +100,8 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var classFormalTypeParameters: Option[Set[String]] = None def loadFormalTypeparameters() = { var result: Set[String] = Set.empty - field.classFile.attributes.foreach( + //TODO + def CheckAttributeWithRegardToFormalTypeParameter: Attribute ⇒ Unit = { x ⇒ x match { case SourceFile(_) ⇒ @@ -111,7 +115,20 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) case _ ⇒ } + } + if (field.classFile.outerType.isDefined) { + val cf = project.classFile(field.classFile.outerType.get._1) + if (cf.isDefined) { + cf.get.attributes.foreach( + CheckAttributeWithRegardToFormalTypeParameter + ) + } + } + + field.classFile.attributes.foreach( + CheckAttributeWithRegardToFormalTypeParameter ) + if (result.size > 0) { classFormalTypeParameters = Some(result) } @@ -121,7 +138,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (classFormalTypeParameters == None) false else - classFormalTypeParameters.head.contains(string) + classFormalTypeParameters.get.contains(string) } def handleTypeImmutability(state: State) = { @@ -195,6 +212,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) _ ) ) ⇒ { + val oPath = packageIdentifier1 match { case Some(pid1) ⇒ pid1 + packageIdentifier2 @@ -202,7 +220,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } genericFields = ObjectType(oPath) :: genericFields } - case _ ⇒ + case dc @ _ ⇒ flag_notShallow = false flag_onlyDeep = false state.typeImmutability = Some(false) @@ -236,6 +254,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) flag_notShallow = false flag_onlyDeep = false } + if (state.dependentImmutability != None) { if (flag_onlyDeep) state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) @@ -245,15 +264,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } def createResult(state: State): ProperPropertyComputationResult = { - println( - s"""reaching create result - |field: ${state.field} - | class: ${state.field.classFile.thisType} - | ref imm: ${state.referenceImmutability} - | type imm: ${state.typeImmutability} - | ref not esc: ${state.referenceNotEscapes} - | depend imm: ${state.dependentImmutability} - |""".stripMargin) state.referenceImmutability match { case Some(false) | None ⇒ Result(field, MutableField) @@ -271,11 +281,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(DependentImmutabilityKind.onlyDeepImmutable) ⇒ Result(field, DeepImmutableField) case _ ⇒ { - println( - s"""default case in field immutability - | state.dependentimmutability: ${state.dependentImmutability} - | - |""".stripMargin) Result(field, ShallowImmutableField) } } @@ -303,7 +308,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (state.dependentImmutability == None) state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } - case x @ FinalP(MutableReference) ⇒ { + case x @ FinalP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference) ⇒ { state.typeImmutability = Some(false) return Result(field, MutableField); } @@ -311,7 +316,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.referenceImmutability = Some(true) state.referenceNotEscapes = notEscapes } - case x @ FinalP(LazyInitializedReference) ⇒ { //TODO + case x @ FinalP(LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ { //TODO state.referenceImmutability = Some(true) } case x @ _ ⇒ @@ -335,9 +340,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.referenceImmutability = Some(true) state.referenceNotEscapes = notEscapes } - case FinalEP(_, LazyInitializedReference) ⇒ + case FinalEP(_, LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ state.referenceImmutability = Some(true) - case FinalP(MutableReference) ⇒ return Result(field, MutableField); + case FinalP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference) ⇒ return Result(field, MutableField); case x @ _ ⇒ { dependencies += x } From 553f6e86b70fbb8bc3b962adf9dfe39361a6b051 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Thu, 9 Jul 2020 10:55:50 +0200 Subject: [PATCH 189/327] annotations for new li ref imm properties --- ...feButDeterministicReferenceAnnotation.java | 29 +++++++++++++++++++ ...OrNotDeterministicReferenceAnnotation.java | 29 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation.java new file mode 100644 index 0000000000..6a7af07ba5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.reference.L0ReferenceImmutabilityAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is lazy initialized + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedNotThreadSafeButDeterministicReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation.java new file mode 100644 index 0000000000..687cfc13e0 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation.java @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.reference_immutability; + +import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.tac.fpcf.analyses.immutability.reference.L0ReferenceImmutabilityAnalysis; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to state that the annotated field is lazy initialized + * + * @author Tobias Peter Roth + */ +@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedNotThreadSafeOrNotDeterministicReferenceMatcher.class) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation { + + /** + * A short reasoning of this property. + */ + String value();// default = "N/A"; + + Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + +} From a92916db49cb583f8dfde8e25524e7058bfe3a69 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 14 Jul 2020 11:47:32 +0200 Subject: [PATCH 190/327] dcl combination and performance optimization of eff deep imm fields recognition --- ...mutabilityAnalysisLazyInitialization.scala | 115 +++-- .../L0ReferenceImmutabilityAnalysis.scala | 422 +++++++----------- 2 files changed, 239 insertions(+), 298 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index 4152b4b2e7..73b336125f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -15,6 +15,8 @@ import org.opalj.br.cfg.BasicBlock import org.opalj.br.cfg.CFG import org.opalj.br.cfg.CFGNode import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.fpcf.properties.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.br.fpcf.properties.MutableReference @@ -50,6 +52,51 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization extends AbstractReferenceImmutabilityAnalysis with FPCFAnalysis { + def handleLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int], + tacCai: TACode[TACMethodParameter, V] + )(implicit state: State): Option[Boolean] = { + var result: Option[Boolean] = None + + val dcl: ReferenceImmutability = isThreadSafeLazyInitialisation( + writeIndex, + defaultValue, + method, + code, + cfg, + pcToIndex, + tacCai + ) + dcl match { + case ir @ ImmutableReference(_) ⇒ state.referenceImmutability = ir + case lits @ LazyInitializedThreadSafeReference ⇒ state.referenceImmutability = lits + case lintbd @ LazyInitializedNotThreadSafeButDeterministicReference ⇒ + state.referenceImmutability = lintbd + case MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference ⇒ + val li = isLazyInitialization( + writeIndex, + defaultValue, + method, + code, + cfg, + pcToIndex + ) + if (dcl == MutableReference) { + if (!li) result = Some(true) + else state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference + } else if (dcl == LazyInitializedNotThreadSafeOrNotDeterministicReference) { + if (li) state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference + else state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference + } + } + result + } + /** * Checks whether a field write may be a lazy initialization. * @@ -147,9 +194,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization } val guardedBB = cfg.bb(afterGuardRecognizedTheDefaultValueIndex) - val monitorResult: ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = - findMonitor(writeIndex, defaultValue, code, cfg, tacCai) - val reads = fieldAccessInformation.readAccesses(state.field) if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { return MutableReference @@ -164,49 +208,30 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization } result = checkWriteIsGuarded2(writeIndex, guardIndex, guardedIndex, method, code, cfg) - /* - println( - s""" method is synchronized: ${method.isSynchronized} - | (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) : ${(domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId))} - | (guardedBB == writeBB: ${guardedBB == writeBB} - | writeIndex: $writeIndex - |""".stripMargin - ) */ - - if ((monitorResult._1._1.isDefined && monitorResult._1._2.isDefined && monitorResult._2._1.isDefined) - && - ( - (domTree.strictlyDominates(monitorResult._2._1.get.nodeId, guardedBB.nodeId) || - (monitorResult._2._1.get == guardedBB && monitorResult._1._1.get < afterGuardRecognizedTheDefaultValueIndex)) && //writeIndex)) && //monitor enter dominates guard1 - ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) - || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex) //&& //true case dominates Write - )) { - - return LazyInitializedThreadSafeReference // result //DCL - } /*else if ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) - || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex) { - LazyInitializedNotThreadSafeOrNotDeterministicReference - }*/ else if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { - if (method.isSynchronized) { - return LazyInitializedThreadSafeReference - // result - } else { - LazyInitializedNotThreadSafeOrNotDeterministicReference - } - //TODO !! reasoning - /*if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { - return MutableReference - } else { + + //when the method is synchronized the monitor has not to be searched + if (method.isSynchronized) { + if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { + return LazyInitializedThreadSafeReference; + } else return MutableReference; + } else { + val monitorResult: ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = + findMonitor(writeIndex, defaultValue, code, cfg, tacCai) + if ((monitorResult._1._1.isDefined && monitorResult._1._2.isDefined && monitorResult._2._1.isDefined) + && + ( + (domTree.strictlyDominates(monitorResult._2._1.get.nodeId, guardedBB.nodeId) || + (monitorResult._2._1.get == guardedBB && monitorResult._1._1.get < afterGuardRecognizedTheDefaultValueIndex)) && //writeIndex)) && //monitor enter dominates guard1 + ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) + || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex) //&& //true case dominates Write + )) + return LazyInitializedThreadSafeReference // result //DCL + else if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) return LazyInitializedNotThreadSafeOrNotDeterministicReference - }*/ - } else if (((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) - || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { - return LazyInitializedNotThreadSafeOrNotDeterministicReference - } //only guard{ - else { - return MutableReference + //only guard{ + else + return MutableReference } - } def findMonitor( @@ -669,7 +694,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization * For instance fields, the read must be on the `this` reference. */ def isReadOfCurrentField(expr: Expr[V])(implicit state: State): Boolean = { - val field = expr.astID match { + val field = (expr.astID: @switch) match { case GetField.ASTID ⇒ val objRefDefinition = expr.asGetField.objRef.asVar.definedBy if (objRefDefinition != SelfReferenceParameter) None diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index 9df0c568df..e59cdef964 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -48,9 +48,6 @@ import org.opalj.fpcf.PropertyComputationResult import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS -import org.opalj.tac.Assignment -import org.opalj.tac.DVar -import org.opalj.tac.GetField import org.opalj.tac.If import org.opalj.tac.MonitorEnter import org.opalj.tac.MonitorExit @@ -60,11 +57,12 @@ import org.opalj.tac.PutStatic import org.opalj.tac.Stmt import org.opalj.tac.TACMethodParameter import org.opalj.tac.TACode -import org.opalj.tac.UVar import org.opalj.tac.common.DefinitionSite import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.SelfReferenceParameter +import scala.annotation.switch + /** * * Implementation is used from the old L2FieldMutability implementation @@ -102,28 +100,22 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec implicit val state: State = State(field) // Fields are not final if they are read prematurely! - if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key)) & !field.isFinal) { //("field is prematurely read") return Result(field, MutableReference) }; //Result(field, NonFinalFieldByAnalysis); + //test + /*if (field.isFinal) + return Result(field, ImmutableReference(false)) */ + state.referenceImmutability = ImmutableReference(true) //EffectivelyFinalField val thisType = field.classFile.thisType - - if ((field.isPublic || field.isPackagePrivate || field.isProtected)) { - - if (!field.isFinal) { - - return Result(field, MutableReference) - } else { - - state.notEscapes = false - } - }; //Result(field, NonFinalFieldByLackOfInformation) - - if (field.isPublic) + if (field.isPublic && !field.isFinal) return Result(field, MutableReference) + //if ((field.isPublic || field.isPackagePrivate || field.isProtected)) { + state.notEscapes = !((field.isPublic || field.isPackagePrivate || field.isProtected)) //false // 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 @@ -141,6 +133,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val classesHavingAccess: Iterator[ClassFile] = if (field.isProtected) { if (typeExtensibility(thisType).isYesOrUnknown) { + state.notEscapes = false return Result(field, MutableReference); } val subclassesIterator: Iterator[ClassFile] = @@ -153,234 +146,155 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } // If there are native methods, we give up - if (classesHavingAccess.exists(_.methods.exists(_.isNative))) - return Result(field, MutableReference); + if (classesHavingAccess.exists(_.methods.exists(_.isNative))) { + state.notEscapes = false + if (!field.isFinal) + return Result(field, MutableReference) + } + //checkFieldReadsForEffImmutability(field) for { - (method, pcs) ← fieldAccessInformation.readAccesses(field) + (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method, pcs) + staticAddition = if (method.isStatic) 1 else 0 } { - //TODO !!!! - - pcs.foreach(pc ⇒ { - - val index = taCode.pcToIndex(pc) - - val staticAddition = { - if (method.isStatic) { - - 1 - } else { + //checkFieldWritesForEffImmutability(state, pcs, taCode, staticAddition) + if (methodUpdatesField(method, taCode, pcs)) { //&& !state.field.isFinal + return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnfalysis); + } + } + if (state.lazyInitInvocation.isDefined) { + val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) + handleCalls(calleesEOP) + } + createResult() + } - 0 + private def checkFieldReadsForEffImmutability(field: Field)(implicit state: State) = { + for { + (method, pcs) ← fieldAccessInformation.readAccesses(field) + taCode ← getTACAI(method, pcs) + pc ← pcs + } { + val staticAddition = if (method.isStatic) 1 else 0 + val index = taCode.pcToIndex(pc) + if (index > (-1 + staticAddition)) { + val stmt = taCode.stmts(index) + if (stmt.isAssignment) { + val assignment = taCode.stmts(index).asAssignment + for { + index ← assignment.targetVar.usedBy + } { + (taCode.stmts(index).astID: @switch) match { + case MonitorEnter.ASTID | MonitorExit.ASTID | If.ASTID ⇒ + case _ ⇒ state.notEscapes = false; //println("false0"); + } } } + } else { + state.notEscapes = false //println("false-1"); + } + } + } - if (index > (-1 + staticAddition)) { - taCode.stmts(index) match { - case a @ Assignment(pc3, targetVar, GetField(pc4, cl, name, _, value)) ⇒ - - if (name == field.name) { - targetVar.usedBy.foreach(i ⇒ - { - - taCode.stmts(i) match { - case MonitorEnter(_, _) ⇒ - case MonitorExit(_, _) ⇒ - case If(pc, left, condition, right, target) ⇒ - case u @ _ ⇒ state.notEscapes = false; + private def checkFieldWritesForEffImmutability(field: Field)(implicit state: State) = { + //println("field: "+field) + //println("write Acesses: "+fieldAccessInformation.writeAccesses(field)) + //var stillCheckedUseSites: Set[Int] = Set.empty + + def checkNonVirtualMethodCall(taCode: TACode[TACMethodParameter, V], nonVirtualMethodCall: NonVirtualMethodCall[V]) = { + for (param ← nonVirtualMethodCall.params) { + //println("param: "+param) + if (param.isVar) { + val v3 = param.asVar + //println("defSites2: "+v3.definedBy) + for (defs ← v3.definedBy) { + if (defs < 0) { + state.notEscapes = false + } else { + //println("Else") + val stmt4 = taCode.stmts(defs) + if (stmt4.isAssignment) { + val assignm = stmt4.asAssignment + if (assignm.expr.isGetField) { + val getField = assignm.expr.asGetField + val accessingField = state.field.classFile. + findField(getField.name, getField.declaredFieldType) + if (accessingField.isDefined) { + propertyStore(accessingField.get, FieldImmutability.key) match { + case FinalP(DeepImmutableField) ⇒ + case _ ⇒ state.notEscapes = false //println("false1"); } - }) - - } - - case _ ⇒ + } //else println("false2");state.notEscapes = false + } + } //else println("false3");state.notEscapes = false + } } - } else { - - state.notEscapes = false } - }) + } } for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method, pcs) + pc ← pcs } { - - for (pc ← pcs) { - - val index = taCode.pcToIndex(pc) - val staticAddition = { - if (method.isStatic) - 1 - else - 0 - } - if (index > (-1 + staticAddition)) { - val stmt = taCode.stmts(index) - - stmt match { - case PutField(_, _, _, _, _, value) ⇒ - value match { - case v @ UVar(defSites, value2) ⇒ //SObjectValue(t,_,_,_) => - //if (!v.defSites.filter(_ < 1).isEmpty) - //escape var - { - v.defSites.foreach( - i ⇒ { - - if (i > 0) { - val stmt2 = taCode.stmts(i) - - stmt2 match { - //case Assignment(pcAssigment, v @ DVar(useSites, value), expr) ⇒ - case Assignment(pcAssignment, dv @ DVar(useSites, value), expr) ⇒ - //useSites - dv.useSites.foreach( - x ⇒ { - val innerstmt = taCode.stmts(x) - innerstmt match { - case NonVirtualMethodCall( - pc, - bdeclaringClass, - isInterface, - name, - descriptor, - receiver, - params - ) ⇒ { - params.foreach(expr ⇒ { - expr match { - case vrs @ UVar(defSites, value) ⇒ { //: SObjectValue(tpe,isNull,isPrecise) - //val isStaticIndex = if (method.isStatic) 0 else -1 - vrs.defSites.foreach( - dfste ⇒ { - if (dfste < 0) { - - state.notEscapes = false - } //(0 + isStaticIndex)) - else { - val stmtDefSite = taCode.stmts(dfste) - - stmtDefSite match { - case Assignment( - pcA, - targetVar, - GetField( - pc, - declaringClass, - name, - declaredFieldType, - objRef - ) - ) ⇒ { - val fs = project.allFields.filter( - { f ⇒ - f.name.equals(name) && f.fieldType.equals( - declaredFieldType - ) && f.classFile.equals( - state.field.classFile - ) - } - ) - fs.foreach(f ⇒ { - val result = - propertyStore(f, FieldImmutability.key) - result match { - case FinalP(DeepImmutableField) ⇒ - case _ ⇒ state.notEscapes = false - } - }) - } - case _ ⇒ - } - } - - } - ) - } - } - }) - } - case _ ⇒ - } - - } - ) - if (expr.isNew || expr.isConst) { - if (expr.isNew) { - - /** - * val NEW = expr.asNew - * - * val oType = NEW.tpe - * - * - * if (oType == ObjectType.Object) - * state.notEscapes = false - * else { - * val result = propertyStore(oType, TypeImmutability_new.key) - * result match { - * case FinalP(DependentImmutableType) ⇒ { - * - * state.notEscapes = false - * } - * case fp @ FinalP(_) ⇒ fp) - * case ep @ _ ⇒ { - * - * state.typeDependees += ep - * } - * } - * }* - */ - - } - /** - * propertyStore( - * definitionSites(method, pc), - * EscapeProperty.key - * ) match { - * case FinalP(NoEscape) ⇒ // nothing to do - * case FinalP(AtMost(EscapeViaParameter)) ⇒ // introduced via this - * case _ ⇒ - * state.notEscapes = false - * } - * } else { - * state.notEscapes = false* - */ //TODO - } - case _ ⇒ - state.notEscapes = false - } - } else { - //constructor ?? - - state.notEscapes = false - } + //println("pcs: "+pcs) + //println("pc: "+pc) + val index = taCode.pcToIndex(pc) + val staticAddition = if (method.isStatic) 1 else 0 + if (index > (-1 + staticAddition)) { + val stmt = taCode.stmts(index) + if (stmt.isPutField) { + val putField = stmt.asPutField + if (putField.value.isVar) { + //println("putfield: "+putField) + //println("putfield value: "+putField.value) + //println("putfield value defined by: "+putField.value.asVar.definedBy) + val defSites = putField.value.asVar.definedBy + //println("defsites: "+defSites) + for { + i ← defSites + } { + if (i > 0) { + val stmt2 = taCode.stmts(i) + //println("stmt2: "+stmt2) + if (stmt2.isAssignment) { + val assignment = stmt2.asAssignment + if (assignment.expr.isVar) { + val v2 = assignment.expr.asVar + for (x ← v2.usedBy) { + val stmt3 = taCode.stmts(x) + //println("stmt3: "+stmt3) + if (stmt3.isNonVirtualMethodCall) { + val nonVirtualMethodCall = stmt3.asNonVirtualMethodCall + //println("nonvirtualmethodcall: "+nonVirtualMethodCall) + checkNonVirtualMethodCall(taCode, nonVirtualMethodCall) + } //else println("false4");state.notEscapes = false + } + } else if (assignment.expr.isNew) { + val constrUseSites = assignment.targetVar.usedBy + //constrUseSites.foreach(x ⇒ println("uusites: "+taCode.stmts(x))) + for (us ← constrUseSites) { + val stmt = taCode.stmts(us) + if (stmt.isNonVirtualMethodCall) { + checkNonVirtualMethodCall(taCode, stmt.asNonVirtualMethodCall) } - ) - } - case _ ⇒ - state.notEscapes = false - } - case _ ⇒ - state.notEscapes = false - } - } else state.notEscapes = false - } - - if (methodUpdatesField(method, taCode, pcs) && !state.field.isFinal) { + } - return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnfalysis); - } - } + } else if (!assignment.expr.isConst) { state.notEscapes = false } //println("assignment expr: "+assignment.expr); println("false5"); - if (state.lazyInitInvocation.isDefined) { - //handleCalls(calleesEOP) + } //else { + //constructor ?? + //println("false6");state.notEscapes = false + //} + } else { state.notEscapes = false } //println("false7"); + } + } + //else println("false8");state.notEscapes = false + } + } else { state.notEscapes = false } //println("false9"); } - - createResult() } def handleCalls( @@ -405,12 +319,13 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec def handleCallees(callees: Callees)(implicit state: State): Boolean = { val pc = state.lazyInitInvocation.get._2 if (callees.isIncompleteCallSite(pc)) { - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + + state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference //TODO //MutableReference //NonFinalFieldByAnalysis true } else { val targets = callees.callees(pc).toTraversable if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis + state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference //MutableReference //NonFinalFieldByAnalysis true } else false } @@ -439,7 +354,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * dependees or as Result otherwise. */ def createResult()(implicit state: State): ProperPropertyComputationResult = { - if (state.hasDependees && (state.referenceImmutability ne MutableReference)) { //NonFinalFieldByAnalysis)) InterimResult( state.field, @@ -450,12 +364,23 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec ) } else { if (state.field.isFinal) - Result(state.field, ImmutableReference(state.notEscapes)) - else - state.referenceImmutability match { - case ImmutableReference(_) ⇒ Result(state.field, ImmutableReference(state.notEscapes)) - case _ ⇒ Result(state.field, state.referenceImmutability) + state.referenceImmutability = ImmutableReference(state.notEscapes) + state.referenceImmutability match { + case ImmutableReference(doesNotEscape) ⇒ { + if (!doesNotEscape) { + Result(state.field, ImmutableReference(state.notEscapes)) + } else { + checkFieldWritesForEffImmutability(state.field) + if (!state.notEscapes) // || "45" == 45.toString + Result(state.field, ImmutableReference(state.notEscapes)) + else { + checkFieldReadsForEffImmutability(state.field) + Result(state.field, ImmutableReference(state.notEscapes)) + } + } } + case _ ⇒ Result(state.field, state.referenceImmutability) + } } } @@ -510,7 +435,12 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } } - if (isNotFinal && !state.field.isFinal) { + if (isNotFinal && !state.field.isFinal && { + state.referenceImmutability match { + case LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeOrNotDeterministicReference ⇒ false + case _ ⇒ true + } + }) { Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) } else createResult() @@ -569,7 +499,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec // A field written outside an initializer must be lazily // initialized or it is non-final - val dcl = isThreadSafeLazyInitialisation( + + val result = handleLazyInitialization( index, defaultValue.get, method, @@ -578,24 +509,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec taCode.pcToIndex, taCode ) - val li = isLazyInitialization( - index, - defaultValue.get, - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex - ) - dcl match { - case MutableReference if (!li) ⇒ return true; - case MutableReference if (li) ⇒ state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference - case LazyInitializedNotThreadSafeButDeterministicReference ⇒ - state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference - case LazyInitializedNotThreadSafeOrNotDeterministicReference ⇒ - if (li) state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference - else state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference - case lits @ LazyInitializedThreadSafeReference ⇒ state.referenceImmutability = lits - } + if (result.isDefined) + return result.get; } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { @@ -624,6 +539,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } /** + * f * Checks whether the object reference of a PutField does escape (except for being returned). */ def referenceHasEscaped( From 037eaa55eacabcf2507ba0939f558aae7b207ab3 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 14 Jul 2020 11:48:09 +0200 Subject: [PATCH 191/327] tests revise --- .../immutability/classes/multinested_genericClasses/One.java | 2 +- .../classes/multinested_genericClasses/OneVirgin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java index 02a2586a71..a9282ca633 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java @@ -25,7 +25,7 @@ public class One { private D d; @MutableFieldAnnotation("") @MutableReferenceAnnotation("") - TrivialMutableClass tmc; + public TrivialMutableClass tmc; @ShallowImmutableFieldAnnotation("") @ImmutableReferenceEscapesAnnotation("") private Generic_class1 gc1; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java index ce0d4905f9..a1be293c97 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java @@ -25,7 +25,7 @@ public class OneVirgin { @MutableFieldAnnotation("") @MutableReferenceAnnotation("") - TrivialMutableClass tmc; + public TrivialMutableClass tmc; Generic_class1 gc1; public OneVirgin(A a, B b, C c, D d, TrivialMutableClass e){ From ddecfecda6e28d4f4e4925c2a1e6e538cb3157f1 Mon Sep 17 00:00:00 2001 From: Tobias Roth Date: Tue, 14 Jul 2020 11:55:39 +0200 Subject: [PATCH 192/327] new dcl tests --- .../immutability/classes/generic/Nested.java | 33 +++++ .../immutability/reference/Escapers.java | 59 +++++++++ ...rivateFinalArrayEscapesViaConstructor.java | 30 +++++ .../immutability/reference/Template.java | 30 +++++ .../DCL.java | 125 ++++++++++++++++++ .../DoubleCheckedLockingClass10.java | 20 +++ .../DoubleCheckedLockingClass11.java | 26 ++++ .../DoubleCheckedLockingClass12.java | 22 +++ .../DoubleCheckedLockingClass13.java | 22 +++ .../DoubleCheckedLockingClass14.java | 23 ++++ .../DoubleCheckedLockingClass15.java | 24 ++++ .../DoubleCheckedLockingClass16.java | 24 ++++ .../DoubleCheckedLockingClass17.java | 28 ++++ .../DoubleCheckedLockingClass18.java | 23 ++++ .../DoubleCheckedLockingClass19.java | 20 +++ .../DoubleCheckedLockingClass9.java | 20 +++ ...leCheckedLockingClassWithStaticFields.java | 22 +++ .../NotDeterministicLazyIntInstantiation.java | 17 +++ .../S.java | 32 +++++ 19 files changed, 600 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Nested.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Escapers.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFinalArrayEscapesViaConstructor.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass10.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass11.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass12.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass13.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass14.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass15.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass16.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass17.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass18.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass19.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass9.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/NotDeterministicLazyIntInstantiation.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/S.java 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..11f36816e1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Nested.java @@ -0,0 +1,33 @@ +package org.opalj.fpcf.fixtures.immutability.classes.generic; + +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; + +public class Nested{ +} + +class Simple{ + class Inner{ + @DependentImmutableFieldAnnotation(value= "", genericString = "T") + private T t; + public Inner(T t){ + this.t = t; + } + } +} + +class Complex{ + class Inner { + @DependentImmutableFieldAnnotation(value="", genericString = "T") + private Generic_class1 gc1; + public Inner(Generic_class1 gc1){ + this.gc1 = gc1; + } + + + } +} + + + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Escapers.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Escapers.java new file mode 100644 index 0000000000..1c962f117f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Escapers.java @@ -0,0 +1,59 @@ +package org.opalj.fpcf.fixtures.immutability.reference; + +import org.opalj.fpcf.fixtures.immutability.classes.generic.TrivialMutableClass; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; + +public class Escapers{ + +} + +class TransitiveEscape1 { + @ImmutableReferenceEscapesAnnotation("") + private TrivialMutableClass tmc = new TrivialMutableClass(); + + public void printTMC(){ + System.out.println(tmc.name); + } + + public TrivialMutableClass get(){ + TrivialMutableClass tmc1 = this.tmc; + return tmc1; + } +} + +class TransitiveEscape2 { + @ImmutableReferenceEscapesAnnotation("") + private TrivialMutableClass tmc = new TrivialMutableClass(); + + public void printTMC(){ + System.out.println(tmc.name); + } + + public TrivialMutableClass get(){ + TrivialMutableClass tmc1 = this.tmc; + TrivialMutableClass tmc2 = tmc1; + return tmc2; + } +} +class NotEscape { + @ImmutableReferenceNotEscapesAnnotation("") + private TrivialMutableClass tmc1 = new TrivialMutableClass(); + + @LazyInitializedThreadSafeReferenceAnnotation("") + private TrivialMutableClass tmc2; + + public TrivialMutableClass set() { + TrivialMutableClass tmc11 = tmc1; + TrivialMutableClass tmc22 = tmc2; + if (tmc11 != null) { + synchronized (this) { + if (tmc22 == null) { + tmc2 = new TrivialMutableClass(); + } + } + } + return tmc2; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFinalArrayEscapesViaConstructor.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFinalArrayEscapesViaConstructor.java new file mode 100644 index 0000000000..8a4a199e0b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFinalArrayEscapesViaConstructor.java @@ -0,0 +1,30 @@ +package org.opalj.fpcf.fixtures.immutability.reference; + +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; + +public class PrivateFinalArrayEscapesViaConstructor { + + @ImmutableReferenceEscapesAnnotation("") + private final char[] charArray; + + @ImmutableReferenceEscapesAnnotation("") + private final byte[] byteArray; + + @ImmutableReferenceEscapesAnnotation("") + private final int[] intArray; + + @ImmutableReferenceEscapesAnnotation("") + private final long[] longArray; + + @ImmutableReferenceEscapesAnnotation("") + 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/reference/Template.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java new file mode 100644 index 0000000000..006390b129 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java @@ -0,0 +1,30 @@ +package org.opalj.fpcf.fixtures.immutability.reference; + +import com.sun.org.apache.xalan.internal.xsltc.compiler.SyntaxTreeNode; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; + +public class Template { + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + private Template _template; + 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/reference_immutability_lazy_initialization/DCL.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java new file mode 100644 index 0000000000..5f0a1a0d78 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java @@ -0,0 +1,125 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; + +import java.util.Random; + +public class DCL { + +} + +class DCLint { + @LazyInitializedThreadSafeReferenceAnnotation("") + private int n; + public int getN() { + if(n==0){ + synchronized(this) { + if(n==0){ + n = 42; + } + } + } + return n; + } +} + +class DCLIntRandom { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private int r; + + public int getR(){ + if(r==0){ + synchronized(this){ + if(r==0){ + r = new Random().nextInt(); + } + } + } + return r; + } +} + +class DCLwithEarlyReturns { + @LazyInitializedThreadSafeReferenceAnnotation("") + private DCLwithEarlyReturns instance; + public DCLwithEarlyReturns getInstance(){ + if(instance!=null) + return instance; + synchronized(this) { + if(instance==null) + instance = new DCLwithEarlyReturns(); + } + return instance; + } + } + + + class DCLChallenge { + @LazyInitializedThreadSafeReferenceAnnotation("") + private static org.omg.CORBA.TypeCode __typeCode = null; + private static boolean __active = false; + private static String _id = "IDL:omg.org/CORBA/ValueMember:1.0"; + synchronized public static org.omg.CORBA.TypeCode type () + { + if (__typeCode == null) + { + synchronized (org.omg.CORBA.TypeCode.class) + { + if (__typeCode == null) + { + if (__active) + { + return org.omg.CORBA.ORB.init().create_recursive_tc ( _id ); + } + __active = true; + org.omg.CORBA.StructMember[] _members0 = new org.omg.CORBA.StructMember [7]; + org.omg.CORBA.TypeCode _tcOf_members0 = null; + _tcOf_members0 = org.omg.CORBA.ORB.init ().create_string_tc (0); + _tcOf_members0 = org.omg.CORBA.ORB.init ().create_alias_tc (org.omg.CORBA.IdentifierHelper.id (), "Identifier", _tcOf_members0); + _members0[0] = new org.omg.CORBA.StructMember ( + "name", + _tcOf_members0, + null); + _tcOf_members0 = org.omg.CORBA.ORB.init ().create_string_tc (0); + _tcOf_members0 = org.omg.CORBA.ORB.init ().create_alias_tc (org.omg.CORBA.RepositoryIdHelper.id (), "RepositoryId", _tcOf_members0); + _members0[1] = new org.omg.CORBA.StructMember ( + "id", + _tcOf_members0, + null); + _tcOf_members0 = org.omg.CORBA.ORB.init ().create_string_tc (0); + _tcOf_members0 = org.omg.CORBA.ORB.init ().create_alias_tc (org.omg.CORBA.RepositoryIdHelper.id (), "RepositoryId", _tcOf_members0); + _members0[2] = new org.omg.CORBA.StructMember ( + "defined_in", + _tcOf_members0, + null); + _tcOf_members0 = org.omg.CORBA.ORB.init ().create_string_tc (0); + _tcOf_members0 = org.omg.CORBA.ORB.init ().create_alias_tc (org.omg.CORBA.VersionSpecHelper.id (), "VersionSpec", _tcOf_members0); + _members0[3] = new org.omg.CORBA.StructMember ( + "version", + _tcOf_members0, + null); + _tcOf_members0 = org.omg.CORBA.ORB.init ().get_primitive_tc (org.omg.CORBA.TCKind.tk_TypeCode); + _members0[4] = new org.omg.CORBA.StructMember ( + "type", + _tcOf_members0, + null); + _tcOf_members0 = org.omg.CORBA.IDLTypeHelper.type (); + _members0[5] = new org.omg.CORBA.StructMember ( + "type_def", + _tcOf_members0, + null); + _tcOf_members0 = org.omg.CORBA.ORB.init ().get_primitive_tc (org.omg.CORBA.TCKind.tk_short); + _tcOf_members0 = org.omg.CORBA.ORB.init ().create_alias_tc (org.omg.CORBA.VisibilityHelper.id (), "Visibility", _tcOf_members0); + _members0[6] = new org.omg.CORBA.StructMember ( + "access", + _tcOf_members0, + null); + __typeCode = org.omg.CORBA.ORB.init ().create_struct_tc (org.omg.CORBA.ValueMemberHelper.id (), "ValueMember", _members0); + __active = false; + } + } + } + return __typeCode; + } + } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass10.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass10.java new file mode 100644 index 0000000000..bd286e37dc --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass10.java @@ -0,0 +1,20 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class DoubleCheckedLockingClass10 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass10 instance; + + public DoubleCheckedLockingClass10 getInstance() { + if (instance == null) { + synchronized (this) { + if (instance == null) { + } + } + } + instance = new DoubleCheckedLockingClass10(); + return instance; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass11.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass11.java new file mode 100644 index 0000000000..34f2c5286c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass11.java @@ -0,0 +1,26 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; + +public class DoubleCheckedLockingClass11 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass11 instance; + public DoubleCheckedLockingClass11 getInstance() { + for(int i=0; i<1000; i++){} + if(instance==null){ + for(int i=0; i<1000; i++){} + synchronized(this) { + for(int i=0; i<1000; i++){} + if(instance==null){ + for(int i=0; i<1000; i++){} + instance = new DoubleCheckedLockingClass11(); + } + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass12.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass12.java new file mode 100644 index 0000000000..06cc162bc6 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass12.java @@ -0,0 +1,22 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; + +public class DoubleCheckedLockingClass12 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass12 instance; + public DoubleCheckedLockingClass12 getInstance() { + if(instance==null){ + } + synchronized(this) { + if(instance==null){ + instance = new DoubleCheckedLockingClass12(); + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass13.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass13.java new file mode 100644 index 0000000000..33e0757970 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass13.java @@ -0,0 +1,22 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; + +public class DoubleCheckedLockingClass13 { + + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + private DoubleCheckedLockingClass13 instance; + public DoubleCheckedLockingClass13 getInstance() { + if(instance==null){ + } + synchronized(this) { + } + if(instance==null){ + instance = new DoubleCheckedLockingClass13(); + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass14.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass14.java new file mode 100644 index 0000000000..b25d50eca7 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass14.java @@ -0,0 +1,23 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class DoubleCheckedLockingClass14 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass14 instance; + public DoubleCheckedLockingClass14 getInstance() { + if(instance==null){ + } + synchronized(this) { + } + if(instance==null){ + + } + instance = new DoubleCheckedLockingClass14(); + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass15.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass15.java new file mode 100644 index 0000000000..ce917e888d --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass15.java @@ -0,0 +1,24 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; + +public class DoubleCheckedLockingClass15 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass15 instance; + public DoubleCheckedLockingClass15 getInstance() { + if(instance==null){ + synchronized(this) { + if(instance==null){ + if(instance==null) { + instance = new DoubleCheckedLockingClass15(); + } + } + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass16.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass16.java new file mode 100644 index 0000000000..28151704f0 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass16.java @@ -0,0 +1,24 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; + +public class DoubleCheckedLockingClass16 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass16 instance; + public DoubleCheckedLockingClass16 getInstance() { + if(instance==null){ + if(instance==null){ + synchronized(this) { + if(instance==null) { + instance = new DoubleCheckedLockingClass16(); + } + } + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass17.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass17.java new file mode 100644 index 0000000000..adf3b8c58a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass17.java @@ -0,0 +1,28 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; + +public class DoubleCheckedLockingClass17 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass17 instance; + public DoubleCheckedLockingClass17 getInstance() { + if(instance==null){ + if(instance==null) { + if (instance == null) { + synchronized (this) { + if (instance == null) { + if (instance == null) { + instance = new DoubleCheckedLockingClass17(); + } + } + } + } + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass18.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass18.java new file mode 100644 index 0000000000..da919e1e39 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass18.java @@ -0,0 +1,23 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; + +public class DoubleCheckedLockingClass18 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass18 instance; + private boolean lock = true; + public DoubleCheckedLockingClass18 getInstance() { + if(instance==null && lock){ + synchronized(this) { + if(instance==null && lock){ + instance = new DoubleCheckedLockingClass18(); + } + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass19.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass19.java new file mode 100644 index 0000000000..fb4c2db095 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass19.java @@ -0,0 +1,20 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; + +public class DoubleCheckedLockingClass19 { + + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + private DoubleCheckedLockingClass19 instance; + + public DoubleCheckedLockingClass19 getInstance() { + if (instance == null) { + synchronized (this) { + if (instance == null) { + } + } + instance = new DoubleCheckedLockingClass19(); + } + return instance; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass9.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass9.java new file mode 100644 index 0000000000..7c3bebf174 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass9.java @@ -0,0 +1,20 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; + +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class DoubleCheckedLockingClass9 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass9 instance; + + public DoubleCheckedLockingClass9 getInstance() { + if (instance != null) { + synchronized (this) { + if (instance != null) { + instance = new DoubleCheckedLockingClass9(); + } + } + } + return instance; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java new file mode 100644 index 0000000000..76a31ff643 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java @@ -0,0 +1,22 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html + + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; + +public class DoubleCheckedLockingClassWithStaticFields { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private static DoubleCheckedLockingClassWithStaticFields instance; + public static DoubleCheckedLockingClassWithStaticFields getInstance() { + if(instance==null){ + synchronized(DoubleCheckedLockingClassWithStaticFields.class) { + if(instance==null){ + instance = new DoubleCheckedLockingClassWithStaticFields(); + } + } + } + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/NotDeterministicLazyIntInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/NotDeterministicLazyIntInstantiation.java new file mode 100644 index 0000000000..ad79d6b4cf --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/NotDeterministicLazyIntInstantiation.java @@ -0,0 +1,17 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; + +import java.util.Random; + +public class NotDeterministicLazyIntInstantiation { + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + private int r; + + public int getR(){ + if(r==0){ + r = new Random().nextInt(); + } + return r; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/S.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/S.java new file mode 100644 index 0000000000..4d17efa185 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/S.java @@ -0,0 +1,32 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; + +//import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; + +public class S { + private final char value[]; + + /** Cache the hash code for the string */ + + //@LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("") + private int hash; // Default to 0 + + + public S(S original) { + this.value = original.value; + this.hash = original.hash; + } + + public int hashCode() { + int h = hash; + + if (h == 0 && value.length > 0) { + char val[] = value; + + for (int i = 0; i < value.length; i++) { + h = 31 * h + val[i]; + } + hash = h; + } + return h; + } +} From 95491e9e5b5f26849de27abeb61c01fd8d6f7b9d Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 14 Jul 2020 15:30:43 +0200 Subject: [PATCH 193/327] updating running analyses list --- .../opalj/fpcf/ReferenceImmutabilityTests.scala | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index d65ff7522c..0bcc57eeb3 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -16,11 +16,11 @@ import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis -import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater +import org.opalj.tac.fpcf.analyses.purity.{L2PurityAnalysis_new, LazyL2PurityAnalysis_new, SystemOutLoggingAllExceptionRater} /** * @author Tobias Peter Roth @@ -46,24 +46,25 @@ class ReferenceImmutabilityTests extends PropertiesTest { validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) } - L2PurityAnalysis.setRater(Some(SystemOutLoggingAllExceptionRater)) + L2PurityAnalysis_new.setRater(Some(SystemOutLoggingAllExceptionRater)) describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { val as = executeAnalyses( Set( EagerL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, + LazyL0FieldImmutabilityAnalysis, LazyInterProceduralEscapeAnalysis, LazyLxTypeImmutabilityAnalysis_new, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis, LazyL2FieldMutabilityAnalysis, LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis + LazyTypeImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, ) ) as.propertyStore.shutdown() From fb668c8ad95e01c81dbe7415c54a1d40c631f3c9 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 14 Jul 2020 16:01:16 +0200 Subject: [PATCH 194/327] revise imports --- .../scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index 0bcc57eeb3..de845800ae 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -20,13 +20,15 @@ import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.{L2PurityAnalysis_new, LazyL2PurityAnalysis_new, SystemOutLoggingAllExceptionRater} +import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis_new /** * @author Tobias Peter Roth */ class ReferenceImmutabilityTests extends PropertiesTest { + import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater + override def withRT = true override def fixtureProjectPackage: List[String] = { @@ -49,6 +51,7 @@ class ReferenceImmutabilityTests extends PropertiesTest { L2PurityAnalysis_new.setRater(Some(SystemOutLoggingAllExceptionRater)) describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { + import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new val as = executeAnalyses( Set( EagerL0ReferenceImmutabilityAnalysis, @@ -64,7 +67,7 @@ class ReferenceImmutabilityTests extends PropertiesTest { LazyTypeImmutabilityAnalysis, LazyLxClassImmutabilityAnalysis_new, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, + LazyL2PurityAnalysis_new ) ) as.propertyStore.shutdown() From b89d41c338c0217a7c680d5575340afc1842382c Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 17 Jul 2020 18:34:29 +0200 Subject: [PATCH 195/327] performance optimizations --- .../properties/ReferenceImmutability.scala | 4 + ...bstractReferenceImmutabilityAnalysis.scala | 30 +- ...mutabilityAnalysisLazyInitialization.scala | 383 +++++++++++------- .../L0ReferenceImmutabilityAnalysis.scala | 271 +++++++------ 4 files changed, 420 insertions(+), 268 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala index b68913cdf4..b5d73c0033 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -31,6 +31,8 @@ sealed trait ReferenceImmutability extends OrderedProperty with ReferenceImmutabilityPropertyMetaInformation { final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key + def isImmutableReference = false + def doesNotEscapes = false } object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { @@ -48,6 +50,8 @@ object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformatio } case class ImmutableReference(notEscapes: Boolean) extends ReferenceImmutability { + override def isImmutableReference = true + override def doesNotEscapes: Boolean = notEscapes override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} def meet(that: ReferenceImmutability): ReferenceImmutability = if (this == that) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala index 545780357d..bdb8a3d7e5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala @@ -42,6 +42,8 @@ import org.opalj.value.ValueInformation trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { + import scala.collection.mutable + type V = DUVar[ValueInformation] final val typeExtensibility = project.get(TypeExtensibilityKey) @@ -76,6 +78,10 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { } } + var stillCalculatedMethods: mutable.HashMap[Method, TACode[TACMethodParameter, V]] = + new mutable.HashMap[Method, TACode[TACMethodParameter, V]]() + //var stillCalculatedMethods: mutable.HashSet[Method] = new mutable.HashSet[Method] + /** * Returns the TACode for a method if available, registering dependencies as necessary. */ @@ -83,16 +89,20 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { 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 eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) - None - } + if (stillCalculatedMethods.contains(method)) + stillCalculatedMethods.get(method) + else + propertyStore(method, TACAI.key) match { + case finalEP: FinalEP[Method, TACAI] ⇒ + stillCalculatedMethods.put(method, finalEP.ub.tac.get) + finalEP.ub.tac + case eps: InterimEP[Method, TACAI] ⇒ + state.tacDependees += method -> ((eps, pcs)) + eps.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) + None + } } /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index 73b336125f..b31d9c1868 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -44,7 +44,8 @@ import org.opalj.tac.TACStmts import org.opalj.tac.TACode import org.opalj.tac.UVar import org.opalj.tac.VirtualFunctionCall -import org.opalj.value.ValueInformation +import org.opalj.tac.Throw +import org.opalj.br.ObjectType import scala.annotation.switch @@ -52,6 +53,8 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization extends AbstractReferenceImmutabilityAnalysis with FPCFAnalysis { + import scala.collection.mutable + def handleLazyInitialization( writeIndex: Int, defaultValue: Any, @@ -62,7 +65,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization tacCai: TACode[TACMethodParameter, V] )(implicit state: State): Option[Boolean] = { var result: Option[Boolean] = None - + //println("III") val dcl: ReferenceImmutability = isThreadSafeLazyInitialisation( writeIndex, defaultValue, @@ -72,6 +75,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization pcToIndex, tacCai ) + //println("dcl: "+dcl) dcl match { case ir @ ImmutableReference(_) ⇒ state.referenceImmutability = ir case lits @ LazyInitializedThreadSafeReference ⇒ state.referenceImmutability = lits @@ -90,9 +94,11 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization if (!li) result = Some(true) else state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference } else if (dcl == LazyInitializedNotThreadSafeOrNotDeterministicReference) { - if (li) state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference + if (li) + state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference else state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference - } + } //state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference + case _ ⇒ //TODO } result } @@ -117,58 +123,56 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization pcToIndex: Array[Int] )(implicit state: State): Boolean = { val write = code(writeIndex).asFieldWriteAccessStmt - //println("0001") + if (state.field.fieldType.computationalType != ComputationalTypeInt && state.field.fieldType.computationalType != ComputationalTypeFloat) { // Only handle lazy initialization of ints and floats as they are guaranteed to be // written atomically return false; } - //println("0002") + //-------------------------------------------------------- //TODO reasoning if there is another way to do this val writes = fieldAccessInformation.writeAccesses(state.field) - //println("writes: "+writes) - if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) + + if (writes.iterator.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) return false; // more than one write in the method //---------------------------------------------------------------- val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + if (reads.iterator.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { return false; // Reads outside the (single) lazy initialization method } - //println("0003") + // There must be a guarding if-Statement // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the // first statement that is executed after the if-Statement if the field's value was not the // default value val (guardIndex, guardedIndex, readIndex) = { - val findGuardResult = findGuard(writeIndex, defaultValue, code, cfg) - //println("find guard result: "+findGuardResult) + val findGuardResult = findGuard(method, writeIndex, defaultValue, code, cfg) if (findGuardResult.isDefined) (findGuardResult.get._1, findGuardResult.get._2, findGuardResult.get._3) else return false; /** * findGuardResult match { - * case Some((guard, guarded, read)) ⇒ (guard, guarded, read) + * case Some((guard, guarded, read))f ⇒ (guard, guarded, read) * case None ⇒ return false; * } * */ } - //println("0004") // Detect only simple patterns where the lazily initialized value is returned immediately if (!checkImmediateReturn(write, writeIndex, readIndex, code)) return false; - //println("0005") + // The value written must be computed deterministically and the writes guarded correctly if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) return false; - //println("0006") + // Field reads (except for the guard) may only be executed if the field's value is not the // default value if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) return false; - //println("0007") + true } @@ -181,42 +185,62 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization pcToIndex: Array[Int], tacCai: TACode[TACMethodParameter, V] )(implicit state: State): ReferenceImmutability = { - var result: ReferenceImmutability = LazyInitializedThreadSafeReference + //var result: ReferenceImmutability = LazyInitializedThreadSafeReference val write = code(writeIndex).asFieldWriteAccessStmt val writeBB = cfg.bb(writeIndex).asBasicBlock val domTree = cfg.dominatorTree val (guardIndex, guardedIndex, readIndex, afterGuardRecognizedTheDefaultValueIndex) = { - val findGuardResult: (Option[(Int, Int, Int, CFGNode, Int)]) = findGuard(writeIndex, defaultValue, code, cfg) + val findGuardResult: (Option[(Int, Int, Int, CFGNode, Int)]) = + findGuard(method, writeIndex, defaultValue, code, cfg) if (findGuardResult.isDefined) - (findGuardResult.get._1, findGuardResult.get._2, findGuardResult.get._3, findGuardResult.get._5) + ( + findGuardResult.get._1, + findGuardResult.get._2, + findGuardResult.get._3, + findGuardResult.get._5 + ) else return MutableReference; } val guardedBB = cfg.bb(afterGuardRecognizedTheDefaultValueIndex) val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + if (reads.iterator.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { return MutableReference } val writes = fieldAccessInformation.writeAccesses(state.field) - if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { + if (writes.iterator.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { return MutableReference }; if (method.returnType == state.field.fieldType && !checkThatTheValueOfTheFieldIsReturned(write, writeIndex, readIndex, code)) { return MutableReference; } - - result = checkWriteIsGuarded2(writeIndex, guardIndex, guardedIndex, method, code, cfg) - + //result = checkWriteIsGuarded2(writeIndex, guardIndex, guardedIndex, method, code, cfg) //when the method is synchronized the monitor has not to be searched if (method.isSynchronized) { if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { - return LazyInitializedThreadSafeReference; - } else return MutableReference; + val resultCaughtsAndThrows = findCaughtsThrowsAndResults(tacCai, cfg) + if (resultCaughtsAndThrows._1.size == resultCaughtsAndThrows._2.size && + resultCaughtsAndThrows._1.iterator.forall( + bbCatch ⇒ + resultCaughtsAndThrows._2.iterator.exists( + bbThrow ⇒ + ( + (domTree + .strictlyDominates(bbCatch._4.nodeId, bbThrow._3.nodeId) || //domination + (bbCatch._4 == bbThrow._3 && bbCatch._1 < bbThrow._1)) && // or equal and successor + bbThrow._2 == bbCatch._3 + ) + ) + )) { + LazyInitializedThreadSafeReference // result //DCL + } else LazyInitializedNotThreadSafeOrNotDeterministicReference + } else MutableReference; } else { val monitorResult: ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = - findMonitor(writeIndex, defaultValue, code, cfg, tacCai) + findMonitorCaughtsAndThrowsAndResult(writeIndex, defaultValue, code, cfg, tacCai) + if ((monitorResult._1._1.isDefined && monitorResult._1._2.isDefined && monitorResult._2._1.isDefined) && ( @@ -224,80 +248,135 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization (monitorResult._2._1.get == guardedBB && monitorResult._1._1.get < afterGuardRecognizedTheDefaultValueIndex)) && //writeIndex)) && //monitor enter dominates guard1 ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex) //&& //true case dominates Write - )) - return LazyInitializedThreadSafeReference // result //DCL - else if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) - return LazyInitializedNotThreadSafeOrNotDeterministicReference - //only guard{ + )) { + val resultCaughtsAndThrows = findCaughtsThrowsAndResults(tacCai, cfg) + if (resultCaughtsAndThrows._1.size == resultCaughtsAndThrows._2.size && + resultCaughtsAndThrows._1.iterator.forall( + bbCatch ⇒ + resultCaughtsAndThrows._2.iterator.exists( + bbThrow ⇒ + ( + (domTree + .strictlyDominates(bbCatch._4.nodeId, bbThrow._3.nodeId) || //domination + (bbCatch._4 == bbThrow._3 && bbCatch._1 < bbThrow._1)) && // or equal and successor + bbThrow._2 == bbCatch._3 + ) + ) + )) { + LazyInitializedThreadSafeReference // result //DCL + } else LazyInitializedNotThreadSafeOrNotDeterministicReference + } else if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) + LazyInitializedNotThreadSafeOrNotDeterministicReference else - return MutableReference + MutableReference } } - def findMonitor( + def findCaughtsThrowsAndResults( + tacCode: TACode[TACMethodParameter, V], + cfg: CFG[Stmt[V], TACStmts[V]] + ): (List[(Int, ObjectType, IntTrieSet, CFGNode)], List[(Int, IntTrieSet, CFGNode)], Option[CFGNode]) = { + var exceptions: List[(Int, ObjectType, IntTrieSet, CFGNode)] = List.empty + var throwers: List[(Int, IntTrieSet, CFGNode)] = List.empty + var resultNode: Option[CFGNode] = None + for (stmt ← tacCode.stmts) { + val curBB = cfg.bb(tacCode.pcToIndex(stmt.pc)) + (stmt.astID: @switch) match { + case CaughtException.ASTID ⇒ + val ce = stmt.asCaughtException + val et = + if (ce.exceptionType.isDefined) { + val intermdT = ce.exceptionType.get + if (intermdT.isObjectType) + intermdT.asObjectType + else ObjectType.Exception + } else ObjectType.Exception + exceptions = (ce.pc, et, ce.origins, curBB) :: exceptions + + case Throw.ASTID ⇒ + val t = stmt.asThrow + val ds = if (t.exception.isVar) { + val v = t.exception.asVar + v.definedBy + } else IntTrieSet.empty + throwers = (t.pc.toInt, ds, curBB) :: throwers + case ReturnValue.ASTID ⇒ + //case ReturnValue(pc, expr) ⇒ + resultNode = Some(curBB) + case _ ⇒ + } + } + (exceptions, throwers, resultNode) + } + + def findMonitorCaughtsAndThrowsAndResult( fieldWrite: Int, defaultValue: Any, code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]], tacCode: TACode[TACMethodParameter, V] - )(implicit state: State): ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = { //(implicit state: State) + )(implicit state: State): ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = { var result: (Option[Int], Option[Int]) = (None, None) var dclEnterBBs: List[CFGNode] = List.empty var dclExitBBs: List[CFGNode] = List.empty val startBB = cfg.bb(fieldWrite) var MonitorExitqueuedBBs: Set[CFGNode] = startBB.successors - var worklistMonitorExit = getSuccessors(startBB, Set.empty) - - def checkMonitor(pc: PC, v: UVar[ValueInformation], curBB: CFGNode)(implicit state: State): Boolean = { - v.defSites.filter(i ⇒ { - // println("current i: "+i) - if (i > 0) { - val stmt = tacCode.stmts(i) - // println("stmt: "+stmt) - stmt match { - case Assignment(pc1, DVar(useSites, value), cc @ ClassConst(_, constant)) ⇒ { - state.field.classFile.thisType == cc.value || state.field.fieldType == cc.value + var worklistMonitorExit = getSuccessors(startBB, Set.empty).toList + + def checkMonitor(pc: PC, v: V, curBB: CFGNode)( + implicit + state: State + ): Boolean = { + v.definedBy.iterator + //v.defSites + .filter(i ⇒ { + if (i > 0) { + val stmt = tacCode.stmts(i) + stmt match { + case Assignment(pc1, DVar(useSites, value), cc @ ClassConst(_, constant)) ⇒ { + state.field.classFile.thisType == cc.value || state.field.fieldType == cc.value + } + case Assignment( + pc1, + DVar(value1, defSites1), + GetField(pc2, t, name, classType, UVar(value2, defSites2)) + ) ⇒ + classType == + state.field.classFile.thisType + //&& name == state.field.name + case _ ⇒ false } - case Assignment(pc1, DVar(value1, defSites1), GetField(pc2, t, name, classType, UVar(value2, defSites2))) ⇒ - classType == - state.field. - classFile.thisType - //&& name == state.field.name - case _ ⇒ false - } - } else // (i == -1) - true + } else // (i == -1) + true - }).size == v.defSites.size + }) + .size == v.definedBy.size //v.defSites.size } var monitorEnterqueuedBBs: Set[CFGNode] = startBB.predecessors var worklistMonitorEnter = getPredecessors(startBB, Set.empty) + //find monitorenter while (!worklistMonitorEnter.isEmpty) { val curBB = worklistMonitorEnter.head - // println("curBB: "+curBB) + worklistMonitorEnter = worklistMonitorEnter.tail - //val startPC = curBB.startPC - //val endPC = curBB.endPC val startPC = curBB.startPC val endPC = curBB.endPC //val cfStmt = code(startPC) //(endPC) - // println("cfg should be monitor enter: "+cfStmt) var flag = true for (i ← startPC to endPC) { - code(i) match { - case me @ MonitorEnter(pc, v @ UVar(defSites, value)) //if(v.value.computationalType == state.field.fieldType) - ⇒ { - if (checkMonitor(pc, v, curBB)) { - result = (Some(tacCode.pcToIndex(pc)), (result._2)) + (code(i).astID: @switch) match { + case MonitorEnter.ASTID ⇒ + val me = code(i).asMonitorEnter + if (checkMonitor(me.pc, me.objRef.asVar, curBB)) { + result = (Some(tacCode.pcToIndex(me.pc)), (result._2)) dclEnterBBs = curBB :: dclEnterBBs flag = false } - } - case _ ⇒ + case x @ _ ⇒ } } if (flag) { @@ -311,14 +390,16 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization while (!worklistMonitorExit.isEmpty) { val curBB = worklistMonitorExit.head worklistMonitorExit = worklistMonitorExit.tail + //val startPC = curBB.startPC val endPC = curBB.endPC + val cfStmt = code(endPC) - cfStmt match { - case MonitorExit(pc, v @ UVar(defSites, value)) //if(v.value.computationalType == state.field.fieldType) - ⇒ - if (checkMonitor(pc, v, curBB)) { - result = ((result._1), Some(tacCode.pcToIndex(pc))) + (cfStmt.astID: @switch) match { + case MonitorExit.ASTID ⇒ + val mex = cfStmt.asMonitorExit + if (checkMonitor(mex.pc, mex.objRef.asVar, curBB)) { + result = ((result._1), Some(tacCode.pcToIndex(mex.pc))) dclExitBBs = curBB :: dclExitBBs } case _ ⇒ @@ -347,81 +428,102 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization * first statement executed if the field does not have its default value and the index of the * field read used for the guard. */ + var savedGuards: mutable.HashMap[(Int, Method), ((Option[(Int, Int, Int, CFGNode, Int)]))] = + new mutable.HashMap[(Int, Method), ((Option[(Int, Int, Int, CFGNode, Int)]))]() + def findGuard( + method: Method, fieldWrite: Int, defaultValue: Any, code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]] )(implicit state: State): (Option[(Int, Int, Int, CFGNode, Int)]) = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int, CFGNode, Int)] = None - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID ⇒ - val ifStmt = cfStmt.asIf - ifStmt.condition match { - case EQ if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + if (1 == 2 + 3) //savedGuards.contains(fieldWrite)) + savedGuards.get((fieldWrite, method)).get + else { + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int, CFGNode, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID ⇒ + val ifStmt = cfStmt.asIf + //ifStmt.condition match { + if (ifStmt.condition.equals(EQ) && curBB != startBB && isGuard( + ifStmt, + defaultValue, + code + )) { + //case EQ + //if => if (result.isDefined) { if (result.get._1 != endPC || result.get._2 != endPC + 1) return None; } else { result = Some((endPC, endPC + 1, curBB, ifStmt.targetStmt)) } - - case NE if curBB != startBB && isGuard(ifStmt, defaultValue, code) ⇒ + } else if (ifStmt.condition.equals(NE) && curBB != startBB && isGuard( + ifStmt, + defaultValue, + code + )) { + //case NE + //if => if (result.isDefined) { if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) return None } else { result = Some((endPC, ifStmt.targetStmt, curBB, endPC + 1)) } - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ + } else { + // Otherwise, we have to ensure that a guard is present for all predecessors + //case _ => if (startPC == 0) return None; - val predecessors = getPredecessors(curBB, enqueuedBBs) worklist ++= predecessors enqueuedBBs ++= predecessors - } + } + //} - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } } - } - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && fieldReadUsedCorrectly) - return Some((result.get._1, result.get._2, definitions.head, result.get._3, result.get._4)); // Found proper guard + val finalResult: Option[(Int, Int, Int, CFGNode, Int)] = + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + Some((result.get._1, result.get._2, definitions.head, result.get._3, result.get._4)); // Found proper guard + else None + } else None + savedGuards.put((fieldWrite, method), finalResult) + finalResult } - - None } /** @@ -443,7 +545,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt ⇒ + val caughtExceptions = code.iterator.filter { stmt ⇒ stmt.astID == CaughtException.ASTID } flatMap { exception ⇒ @@ -479,7 +581,8 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization }; // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + val predecessors = + getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) worklist ++= predecessors enqueuedBBs ++= predecessors } @@ -494,12 +597,12 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]] )(implicit state: State): ReferenceImmutability = { - // println("check write is guarded 2") + val startBB = cfg.bb(writeIndex).asBasicBlock var enqueuedBBs: Set[CFGNode] = Set(startBB) var worklist: List[BasicBlock] = List(startBB.asBasicBlock) val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt ⇒ + val caughtExceptions = code.iterator.filter { stmt ⇒ stmt.astID == CaughtException.ASTID } flatMap { exception ⇒ @@ -535,7 +638,8 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization }; // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + val predecessors = + getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) worklist ++= predecessors enqueuedBBs ++= predecessors } @@ -546,24 +650,27 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization * Gets all predecessor BasicBlocks of a CFGNode. */ def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode ⇒ - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) + def getPredecessors_(node: CFGNode, visited: Set[CFGNode]): Iterator[BasicBlock] = { + node.predecessors.iterator flatMap { curNode ⇒ + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors_(curNode, visited) + } } - result.toList + getPredecessors_(node, visited).toList } def getSuccessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.successors.iterator flatMap ({ - currentNode ⇒ + def getSuccessors_(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) - }) - result.toList + }) + } + getSuccessors_(node, visited).toList } /** @@ -792,7 +899,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization while (index < code.length) { val stmt = code(index) - stmt.astID match { + (stmt.astID: @switch) match { case Assignment.ASTID ⇒ if (isReadOfCurrentField(stmt.asAssignment.expr)) load = index @@ -828,7 +935,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization while (index < code.length) { val stmt = code(index) - stmt.astID match { + (stmt.astID: @switch) match { case a @ Assignment.ASTID ⇒ if (isReadOfCurrentField(stmt.asAssignment.expr)) { load = index diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index e59cdef964..eaeab012e6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -48,9 +48,6 @@ import org.opalj.fpcf.PropertyComputationResult import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS -import org.opalj.tac.If -import org.opalj.tac.MonitorEnter -import org.opalj.tac.MonitorExit import org.opalj.tac.NonVirtualMethodCall import org.opalj.tac.PutField import org.opalj.tac.PutStatic @@ -60,7 +57,6 @@ import org.opalj.tac.TACode import org.opalj.tac.common.DefinitionSite import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.SelfReferenceParameter - import scala.annotation.switch /** @@ -70,22 +66,24 @@ import scala.annotation.switch * * @note Requires that the 3-address code's expressions are not deeply nested. * + * @author Tobias Peter Roth * @author Dominik Helm * @author Florian Kübler * @author Michael Eichberg - * @author Tobias Peter Roth + * */ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProject) extends AbstractReferenceImmutabilityAnalysisLazyInitialization with AbstractReferenceImmutabilityAnalysis with FPCFAnalysis { - def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field ⇒ determineReferenceImmutability(field) - case _ ⇒ - val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = + entity match { + case field: Field ⇒ determineReferenceImmutability(field) + case _ ⇒ + val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" + throw new IllegalArgumentException(m) + } /** * Analyzes the mutability of private non-final fields. @@ -98,6 +96,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec field: Field ): ProperPropertyComputationResult = { + //println("--------------------------------------------------- reference: " + field) + implicit val state: State = State(field) // Fields are not final if they are read prematurely! if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key)) & !field.isFinal) { @@ -105,12 +105,13 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec return Result(field, MutableReference) }; //Result(field, NonFinalFieldByAnalysis); - //test - /*if (field.isFinal) - return Result(field, ImmutableReference(false)) */ - state.referenceImmutability = ImmutableReference(true) //EffectivelyFinalField + //test + //if (field.isFinal) + //{return createResult(); + //return Result(field, ImmutableReference(false))} + val thisType = field.classFile.thisType if (field.isPublic && !field.isFinal) return Result(field, MutableReference) @@ -171,6 +172,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } private def checkFieldReadsForEffImmutability(field: Field)(implicit state: State) = { + for { (method, pcs) ← fieldAccessInformation.readAccesses(field) taCode ← getTACAI(method, pcs) @@ -182,52 +184,62 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val stmt = taCode.stmts(index) if (stmt.isAssignment) { val assignment = taCode.stmts(index).asAssignment + val useSites = assignment.targetVar.usedBy for { - index ← assignment.targetVar.usedBy + index ← useSites.iterator } { - (taCode.stmts(index).astID: @switch) match { - case MonitorEnter.ASTID | MonitorExit.ASTID | If.ASTID ⇒ - case _ ⇒ state.notEscapes = false; //println("false0"); + val stmt2 = taCode.stmts(index) + if (!(stmt2.isMonitorEnter || stmt2.isMonitorExit || stmt2.isIfStmt)) { + state.notEscapes = false } } } } else { - state.notEscapes = false //println("false-1"); + state.notEscapes = false } } } private def checkFieldWritesForEffImmutability(field: Field)(implicit state: State) = { - //println("field: "+field) - //println("write Acesses: "+fieldAccessInformation.writeAccesses(field)) + import scala.collection.mutable //var stillCheckedUseSites: Set[Int] = Set.empty - - def checkNonVirtualMethodCall(taCode: TACode[TACMethodParameter, V], nonVirtualMethodCall: NonVirtualMethodCall[V]) = { - for (param ← nonVirtualMethodCall.params) { - //println("param: "+param) + val workedlist: mutable.HashSet[Int] = new mutable.HashSet[Int]() + + def checkNonVirtualMethodCall( + taCode: TACode[TACMethodParameter, V], + nonVirtualMethodCall: NonVirtualMethodCall[V] + ) = { + //println("params: " + nonVirtualMethodCall.params.count(_ => true)) + for (param ← nonVirtualMethodCall.params.iterator) { if (param.isVar) { val v3 = param.asVar - //println("defSites2: "+v3.definedBy) - for (defs ← v3.definedBy) { - if (defs < 0) { - state.notEscapes = false - } else { - //println("Else") - val stmt4 = taCode.stmts(defs) - if (stmt4.isAssignment) { - val assignm = stmt4.asAssignment - if (assignm.expr.isGetField) { - val getField = assignm.expr.asGetField - val accessingField = state.field.classFile. - findField(getField.name, getField.declaredFieldType) - if (accessingField.isDefined) { - propertyStore(accessingField.get, FieldImmutability.key) match { - case FinalP(DeepImmutableField) ⇒ - case _ ⇒ state.notEscapes = false //println("false1"); + + //println("v3: " + v3) + //println("defsites: " + v3.definedBy) + for (defs ← v3.definedBy.iterator) { + if (!workedlist.contains(defs)) { + if (defs < 0) { + state.notEscapes = false + } else { + val stmt4 = taCode.stmts(defs) + if (stmt4.isAssignment) { + val assignm = stmt4.asAssignment + //println("assignment: " + assignm) + if (assignm.expr.isGetField) { + val getField = assignm.expr.asGetField + val accessingField = + state.field.classFile.findField(getField.name, getField.declaredFieldType) + //println("accessing field:" + accessingField) + if (accessingField.isDefined) { + val resultField = propertyStore(accessingField.get, FieldImmutability.key) + resultField match { + case FinalP(DeepImmutableField) ⇒ + case _ ⇒ state.notEscapes = false; //println("false6") + } } - } //else println("false2");state.notEscapes = false + } } - } //else println("false3");state.notEscapes = false + } } } } @@ -235,65 +247,79 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode ← getTACAI(method, pcs) - pc ← pcs + (method, pcs) ← fieldAccessInformation.writeAccesses(field).iterator + taCode ← getTACAI(method, pcs).iterator + pc ← pcs.iterator } { - //println("pcs: "+pcs) - //println("pc: "+pc) val index = taCode.pcToIndex(pc) - val staticAddition = if (method.isStatic) 1 else 0 - if (index > (-1 + staticAddition)) { - val stmt = taCode.stmts(index) - if (stmt.isPutField) { - val putField = stmt.asPutField - if (putField.value.isVar) { - //println("putfield: "+putField) - //println("putfield value: "+putField.value) - //println("putfield value defined by: "+putField.value.asVar.definedBy) - val defSites = putField.value.asVar.definedBy - //println("defsites: "+defSites) - for { - i ← defSites - } { - if (i > 0) { - val stmt2 = taCode.stmts(i) - //println("stmt2: "+stmt2) - if (stmt2.isAssignment) { - val assignment = stmt2.asAssignment - if (assignment.expr.isVar) { - val v2 = assignment.expr.asVar - for (x ← v2.usedBy) { - val stmt3 = taCode.stmts(x) - //println("stmt3: "+stmt3) - if (stmt3.isNonVirtualMethodCall) { - val nonVirtualMethodCall = stmt3.asNonVirtualMethodCall - //println("nonvirtualmethodcall: "+nonVirtualMethodCall) - checkNonVirtualMethodCall(taCode, nonVirtualMethodCall) - } //else println("false4");state.notEscapes = false - } - } else if (assignment.expr.isNew) { - val constrUseSites = assignment.targetVar.usedBy - //constrUseSites.foreach(x ⇒ println("uusites: "+taCode.stmts(x))) - for (us ← constrUseSites) { - val stmt = taCode.stmts(us) - if (stmt.isNonVirtualMethodCall) { - checkNonVirtualMethodCall(taCode, stmt.asNonVirtualMethodCall) + if (!workedlist.contains(pc)) { + workedlist.add(pc) + val staticAddition = if (method.isStatic) 1 else 0 + if (index > (-1 + staticAddition)) { + val stmt = taCode.stmts(index) + //println("stmt: " + stmt) + if (stmt.isPutField) { + val putField = stmt.asPutField + if (putField.value.isVar) { + val defSites = putField.value.asVar.definedBy + //println("defSites: " + defSites) + for { + i ← defSites.iterator + } { + if (!workedlist.contains(i)) { + workedlist.add(i) + if (i > 0) { + val stmt2 = taCode.stmts(i) + //println("stmt2: " + stmt2) + if (stmt2.isAssignment) { + val assignment = stmt2.asAssignment + if (assignment.expr.isVar) { + val v2 = assignment.expr.asVar + //println("v2: " + v2) + for (x ← v2.usedBy.iterator) { + val stmt3 = taCode.stmts(x) + //println("stmt3: " + stmt3) + if (stmt3.isNonVirtualMethodCall) { + val nonVirtualMethodCall = stmt3.asNonVirtualMethodCall + checkNonVirtualMethodCall(taCode, nonVirtualMethodCall) + } + } + } else if (assignment.expr.isNew) { + //println("New: " + assignment.expr.asNew) + val constrUseSites = assignment.targetVar.usedBy + //println("constrUseSite: " + constrUseSites) + for (us ← constrUseSites.iterator) { + val stmt = taCode.stmts(us) + //println("stmt::: " + stmt) + if (stmt.isNonVirtualMethodCall) { + //println("nvmc: " + stmt.asNonVirtualMethodCall) + checkNonVirtualMethodCall(taCode, stmt.asNonVirtualMethodCall) + } + } + + } else if (!assignment.expr.isConst) { + //println("ass no const: " + assignment.expr) + //println("false2") + state.notEscapes = false } - } - - } else if (!assignment.expr.isConst) { state.notEscapes = false } //println("assignment expr: "+assignment.expr); println("false5"); - } //else { - //constructor ?? - //println("false6");state.notEscapes = false - //} - } else { state.notEscapes = false } //println("false7"); + } //else { + //constructor ?? + //} + } else { + //println("meta else") + //println("false1") + state.notEscapes = false + } + } + } } } - //else println("false8");state.notEscapes = false } - } else { state.notEscapes = false } //println("false9"); + } else { + //println("false0") + state.notEscapes = false + } } } @@ -365,22 +391,24 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } else { if (state.field.isFinal) state.referenceImmutability = ImmutableReference(state.notEscapes) - state.referenceImmutability match { - case ImmutableReference(doesNotEscape) ⇒ { - if (!doesNotEscape) { + //state.referenceImmutability match { + //case ImmutableReference(doesNotEscape) => + if (state.referenceImmutability.isImmutableReference) { + if (!state.referenceImmutability.doesNotEscapes) { + Result(state.field, ImmutableReference(state.notEscapes)) + } else { + checkFieldWritesForEffImmutability(state.field) + if (!state.notEscapes) // || "45" == 45.toString + Result(state.field, ImmutableReference(state.notEscapes)) + else { + checkFieldReadsForEffImmutability(state.field) Result(state.field, ImmutableReference(state.notEscapes)) - } else { - checkFieldWritesForEffImmutability(state.field) - if (!state.notEscapes) // || "45" == 45.toString - Result(state.field, ImmutableReference(state.notEscapes)) - else { - checkFieldReadsForEffImmutability(state.field) - Result(state.field, ImmutableReference(state.notEscapes)) - } } } - case _ ⇒ Result(state.field, state.referenceImmutability) - } + } else + //case _ => + Result(state.field, state.referenceImmutability) + //} } } @@ -390,6 +418,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec */ def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { var isNotFinal = false + eps.pk match { case EscapeProperty.key ⇒ val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] @@ -437,7 +466,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (isNotFinal && !state.field.isFinal && { state.referenceImmutability match { - case LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeOrNotDeterministicReference ⇒ false + case LazyInitializedThreadSafeReference | + LazyInitializedNotThreadSafeOrNotDeterministicReference ⇒ + false case _ ⇒ true } }) { @@ -457,19 +488,19 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec )(implicit state: State): Boolean = { val field = state.field val stmts = taCode.stmts - for (pc ← pcs) { + val staticAddition = { + if (method.isStatic) + 1 + else + 0 + } + //println("stmts: "+stmts.mkString(", \n")) + for (pc ← pcs.iterator) { val index = taCode.pcToIndex(pc) - val staticAddition = { - if (method.isStatic) - 1 - else - 0 - } if (index > (-1 + staticAddition)) { val stmt = stmts(index) - if (stmt.pc == pc) { - stmt.astID match { + (stmt.astID: @switch) match { case PutStatic.ASTID | PutField.ASTID ⇒ if (method.isInitializer) { if (field.isStatic) { From 6fc7a41a2512f887ab6082739d9e11eafc255de2 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 20 Jul 2020 13:13:37 +0200 Subject: [PATCH 196/327] revise iterator handling => failed test correction --- ...mutabilityAnalysisLazyInitialization.scala | 151 +++++++++--------- 1 file changed, 74 insertions(+), 77 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index b31d9c1868..fa9e992035 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -65,7 +65,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization tacCai: TACode[TACMethodParameter, V] )(implicit state: State): Option[Boolean] = { var result: Option[Boolean] = None - //println("III") val dcl: ReferenceImmutability = isThreadSafeLazyInitialisation( writeIndex, defaultValue, @@ -75,7 +74,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization pcToIndex, tacCai ) - //println("dcl: "+dcl) dcl match { case ir @ ImmutableReference(_) ⇒ state.referenceImmutability = ir case lits @ LazyInitializedThreadSafeReference ⇒ state.referenceImmutability = lits @@ -135,11 +133,11 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization //TODO reasoning if there is another way to do this val writes = fieldAccessInformation.writeAccesses(state.field) - if (writes.iterator.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) + if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) return false; // more than one write in the method //---------------------------------------------------------------- val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.iterator.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { return false; // Reads outside the (single) lazy initialization method } @@ -158,6 +156,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization * case None ⇒ return false; * } * */ + } // Detect only simple patterns where the lazily initialized value is returned immediately @@ -545,7 +544,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code.iterator.filter { stmt ⇒ + val caughtExceptions = code filter { stmt ⇒ stmt.astID == CaughtException.ASTID } flatMap { exception ⇒ @@ -588,89 +587,87 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization } true } - - def checkWriteIsGuarded2( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): ReferenceImmutability = { - - val startBB = cfg.bb(writeIndex).asBasicBlock - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code.iterator.filter { stmt ⇒ - stmt.astID == CaughtException.ASTID - - } flatMap { exception ⇒ - exception.asCaughtException.origins.map { origin: Int ⇒ - if (isImmediateVMException(origin)) { - pcOfImmediateVMException(origin) - } else { - pcOfMethodExternalException(origin) - } - }.iterator - } - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - if (startPC == 0 || startPC == guardedIndex) - return LazyInitializedNotThreadSafeOrNotDeterministicReference; // Reached method start or wrong branch of guarding if-Statement - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) { - if (!lazyInitializerIsDeterministic(method, code)) { - return LazyInitializedNotThreadSafeOrNotDeterministicReference - } - }; - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) { - if (!lazyInitializerIsDeterministic(method, code)) { - return LazyInitializedNotThreadSafeOrNotDeterministicReference - } - - }; - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = - getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors + /* + def checkWriteIsGuarded2( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): ReferenceImmutability = { + + val startBB = cfg.bb(writeIndex).asBasicBlock + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + val abnormalReturnNode = cfg.abnormalReturnNode + val caughtExceptions = code.filter { stmt => + stmt.astID == CaughtException.ASTID + + }.toList flatMap { + import org.opalj.tac.fpcf.analyses.Statement + exception=> + exception.asCaughtException.origins.map { origin: Int => + if (isImmediateVMException(origin)) { + pcOfImmediateVMException(origin) + } else { + pcOfMethodExternalException(origin) } - LazyInitializedThreadSafeReference + } } + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + if (startPC == 0 || startPC == guardedIndex) + return LazyInitializedNotThreadSafeOrNotDeterministicReference; // Reached method start or wrong branch of guarding if-Statement + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) { + if (!lazyInitializerIsDeterministic(method, code)) { + return LazyInitializedNotThreadSafeOrNotDeterministicReference + } + }; + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.toSet.contains(endPC)) { + if (!lazyInitializerIsDeterministic(method, code)) { + return LazyInitializedNotThreadSafeOrNotDeterministicReference + } + }; + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = + getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + LazyInitializedThreadSafeReference + } + */ /** * Gets all predecessor BasicBlocks of a CFGNode. */ def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - def getPredecessors_(node: CFGNode, visited: Set[CFGNode]): Iterator[BasicBlock] = { - node.predecessors.iterator flatMap { curNode ⇒ - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors_(curNode, visited) - } + val result = node.predecessors.iterator flatMap { curNode ⇒ + if (curNode.isBasicBlock) + if (visited.contains(curNode)) None + else Some(curNode.asBasicBlock) + else getPredecessors(curNode, visited) } - getPredecessors_(node, visited).toList + result.toList } def getSuccessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - def getSuccessors_(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) - }) - } - getSuccessors_(node, visited).toList + val result = node.successors.iterator flatMap ({ currentNode ⇒ + if (currentNode.isBasicBlock) + if (visited.contains(currentNode)) None + else Some(currentNode.asBasicBlock) + else getSuccessors(currentNode, visited) + }) + result.toList } /** From 209a3e4a1e63103c5fc0d2a7ba5420a59b0be864 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 20 Jul 2020 14:05:19 +0200 Subject: [PATCH 197/327] iterator handling revise 2 --- ...mutabilityAnalysisLazyInitialization.scala | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index fa9e992035..b50add0e26 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -133,11 +133,11 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization //TODO reasoning if there is another way to do this val writes = fieldAccessInformation.writeAccesses(state.field) - if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) + if (writes.iterator.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) return false; // more than one write in the method //---------------------------------------------------------------- val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + if (reads.iterator.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { return false; // Reads outside the (single) lazy initialization method } @@ -544,18 +544,21 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code filter { stmt ⇒ - stmt.astID == CaughtException.ASTID + val caughtExceptions = code.iterator + .filter { stmt ⇒ + stmt.astID == CaughtException.ASTID - } flatMap { exception ⇒ - exception.asCaughtException.origins.map { origin: Int ⇒ - if (isImmediateVMException(origin)) { - pcOfImmediateVMException(origin) - } else { - pcOfMethodExternalException(origin) + } + .flatMap { exception ⇒ + exception.asCaughtException.origins.iterator.map { origin: Int ⇒ + if (isImmediateVMException(origin)) { + pcOfImmediateVMException(origin) + } else { + pcOfMethodExternalException(origin) + } } - }.iterator - } + } + .toSet while (worklist.nonEmpty) { val curBB = worklist.head worklist = worklist.tail @@ -581,7 +584,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization }; // Check all predecessors except for the one that contains the guarding if-Statement val predecessors = - getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + getPredecessors(curBB, enqueuedBBs).iterator.filterNot(_.endPC == guardIndex).toList worklist ++= predecessors enqueuedBBs ++= predecessors } @@ -748,7 +751,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization ): Boolean = { // There is only a single method with reads aside from initializers (checked by // isLazilyInitialized), so we have to check only reads from that one method. - reads.filter(!_._1.isInitializer).head._2 forall { readPC ⇒ + reads.iterator.filter(!_._1.isInitializer).toList.head._2.iterator forall { readPC: Int ⇒ val index = pcToIndex(readPC) index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) } From aef317115daf96b361fee02624dae12666b936f6 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 21 Jul 2020 11:19:33 +0200 Subject: [PATCH 198/327] using iterators --- .../L0FieldImmutabilityAnalysis.scala | 60 +++++++++++-------- .../LxClassImmutabilityAnalysis_new.scala | 6 +- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index 9f0d71098c..3cd7bdc5eb 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -101,31 +101,30 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def loadFormalTypeparameters() = { var result: Set[String] = Set.empty //TODO - def CheckAttributeWithRegardToFormalTypeParameter: Attribute ⇒ Unit = { - x ⇒ - x match { - case SourceFile(_) ⇒ - case ClassSignature(typeParameters, _, _) ⇒ - typeParameters.foreach( - y ⇒ - y match { - case FormalTypeParameter(identifier, _, _) ⇒ result += identifier - case _ ⇒ - } - ) - case _ ⇒ - } + def CheckAttributeWithRegardToFormalTypeParameter: Attribute ⇒ Unit = { x ⇒ + x match { + case SourceFile(_) ⇒ + case ClassSignature(typeParameters, _, _) ⇒ + typeParameters.iterator.foreach( + y ⇒ + y match { + case FormalTypeParameter(identifier, _, _) ⇒ result += identifier + case _ ⇒ + } + ) + case _ ⇒ + } } if (field.classFile.outerType.isDefined) { val cf = project.classFile(field.classFile.outerType.get._1) if (cf.isDefined) { - cf.get.attributes.foreach( + cf.get.attributes.iterator.foreach( CheckAttributeWithRegardToFormalTypeParameter ) } } - field.classFile.attributes.foreach( + field.classFile.attributes.iterator.foreach( CheckAttributeWithRegardToFormalTypeParameter ) @@ -151,7 +150,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.typeImmutability = Some(false) } else { val result = propertyStore(objectType, TypeImmutability_new.key) - dependencies = dependencies.filter(_.e ne result.e) + dependencies = dependencies.iterator.filter(_.e ne result.e).toSet result match { case FinalEP(e, DeepImmutableType) ⇒ case FinalEP(f, DependentImmutableType) ⇒ { @@ -175,7 +174,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var flag_notShallow = true var flag_onlyDeep = true var genericFields: List[ObjectType] = List() - state.field.asField.attributes.foreach( + state.field.asField.attributes.iterator.foreach( _ match { case RuntimeInvisibleAnnotationTable(_) ⇒ case SourceFile(_) ⇒ @@ -190,7 +189,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) SimpleClassTypeSignature(simpleName, typeArguments), _ ) ⇒ - typeArguments + typeArguments.iterator .foreach({ _ match { case ProperTypeArgument( @@ -233,9 +232,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) flag_onlyDeep = false } ) - genericFields.foreach(objectType ⇒ { + genericFields.iterator.foreach(objectType ⇒ { val result = propertyStore(objectType, TypeImmutability_new.key) - dependencies = dependencies.filter(_.e ne result.e) + dependencies = dependencies.iterator.filter(_.e ne result.e).toSet result match { case FinalP(DeepImmutableType) ⇒ //nothing to to here: default value is deep imm case FinalP(ShallowImmutableType | DependentImmutableType | MutableType_new) ⇒ { @@ -291,7 +290,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - dependencies = dependencies.filter(_.e ne eps.e) + dependencies = dependencies.iterator.filter(_.e ne eps.e).toSet (eps: @unchecked) match { case x: InterimEP[_, _] ⇒ { dependencies += eps @@ -308,7 +307,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (state.dependentImmutability == None) state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } - case x @ FinalP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference) ⇒ { + case x @ FinalP( + MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference + ) ⇒ { state.typeImmutability = Some(false) return Result(field, MutableField); } @@ -316,7 +317,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.referenceImmutability = Some(true) state.referenceNotEscapes = notEscapes } - case x @ FinalP(LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ { //TODO + case x @ FinalP( + LazyInitializedThreadSafeReference | + LazyInitializedNotThreadSafeButDeterministicReference + ) ⇒ { //TODO state.referenceImmutability = Some(true) } case x @ _ ⇒ @@ -340,9 +344,13 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.referenceImmutability = Some(true) state.referenceNotEscapes = notEscapes } - case FinalEP(_, LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ + case FinalEP( + _, + LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference + ) ⇒ state.referenceImmutability = Some(true) - case FinalP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference) ⇒ return Result(field, MutableField); + case FinalP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference) ⇒ + return Result(field, MutableField); case x @ _ ⇒ { dependencies += x } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala index ce4e6ffbcc..9ec7c740f4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala @@ -222,9 +222,9 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal // Collect all fields for which we need to determine the effective mutability! var hasFieldsWithUnknownMutability = false - val instanceFields = cf.fields.filter { f ⇒ + val instanceFields = cf.fields.iterator.filter { f ⇒ !f.isStatic - } + }.toList var hasShallowImmutableFields = false var hasDependentImmutableFields = false @@ -316,7 +316,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal def c(someEPS: SomeEPS): ProperPropertyComputationResult = { //[DEBUG] //val oldDependees = dependees - dependees = dependees.filter(_._1 ne someEPS.e) + dependees = dependees.iterator.filter(_._1 ne someEPS.e).toMap someEPS match { // Superclass related dependencies: // From 0c142942f38df22cac97d174a6b9aee0dec8a231 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 21 Jul 2020 19:21:22 +0200 Subject: [PATCH 199/327] imm evaluation tool --- .../org/opalj/support/info/Immutability.scala | 599 ++++++++++++++++++ 1 file changed, 599 insertions(+) create mode 100644 DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Immutability.scala 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..2223b3769f --- /dev/null +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Immutability.scala @@ -0,0 +1,599 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.support.info + +import org.opalj.br.analyses.BasicReport +import org.opalj.br.analyses.Project.JavaClassFileReader +import org.opalj.br.fpcf.PropertyStoreKey +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.reference.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.br.ObjectType +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +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.ImmutableReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference +import org.opalj.br.fpcf.properties.MutableField +import org.opalj.br.fpcf.properties.MutableReference +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_new +import org.opalj.br.fpcf.properties.ShallowImmutableClass +import org.opalj.br.fpcf.properties.ShallowImmutableType +import org.opalj.fpcf.PropertyStoreContext +import org.opalj.log.LogContext +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.bytecode.JRELibraryFolder + +import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis +import java.nio.file.Path +import org.opalj.br.analyses.Project +import org.opalj.log.DevNullLogger +import org.opalj.log.GlobalLogContext +import org.opalj.log.OPALLogger + +/** + * Determines the immutability of ref, fields, classes and types of a project + * + * @author Tobias Peter Roth + */ +object Immutability { + + import java.io.File + + import org.opalj.support.info.Immutability.RunningAnalysis.RunningAnalysis + + //override def title: String = "Immutability Analysis" + + //override def description: String = "determines the immutability of references, fields, classes and types" + + object RunningAnalysis extends Enumeration { + type RunningAnalysis = Value + val References, Fields, Classes, Types, All = Value + } + import java.io.BufferedWriter + import java.io.FileWriter + import java.util.Calendar + + import RunningAnalysis._ + def evaluate( + analysis: RunningAnalysis, + numThreads: Int, + cp: File, + resultsFolder: Path, + timeEvaluation: Boolean + + ): BasicReport = { + import scala.collection.mutable + + OPALLogger.updateLogger(GlobalLogContext, DevNullLogger) + val classFiles = JavaClassFileReader().ClassFiles(cp) + /* + 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) + + //println("JDKFiles: "+JDKFiles.size) + + val project = //time { + Project( + classFiles, // JDKFiles, + JDKFiles, //libFiles ++ + libraryClassFilesAreInterfacesOnly = false, + Traversable.empty + ) + //} { t ⇒ projectTime = t.toSeconds } + + // The following measurements (t) are done such that the results are comparable with the + // reactive async approach developed by P. Haller and Simon Gries. + //var t = Seconds.None + //val ps = time { + + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + + val ra: RunningAnalysis = All + + val referenceDependencies: List[FPCFAnalysisScheduler] = List( + EagerL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis, + LazyLxTypeImmutabilityAnalysis_new + ) + val fieldDependencies: List[FPCFAnalysisScheduler] = List( + LazyL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + EagerL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + val classDepencencies: List[FPCFAnalysisScheduler] = List( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + val typeDependencies: List[FPCFAnalysisScheduler] = List( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + val allImmAnalysisDepencies: List[FPCFAnalysisScheduler] = + List( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + EagerL0ReferenceImmutabilityAnalysis, + EagerL0FieldImmutabilityAnalysis, + EagerLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + var dependencies: List[FPCFAnalysisScheduler] = List.empty + if (ra == References) + dependencies = referenceDependencies + if (ra == Fields) + dependencies = fieldDependencies + if (ra == Classes) + dependencies = classDepencencies + if (ra == Types) + dependencies = typeDependencies + if (ra == All) + dependencies = allImmAnalysisDepencies + + //var timeResults: List[Seconds] = List.empty + val threadTimeResults: mutable.HashMap[Int, List[Seconds]] = mutable.HashMap.empty + var threads: List[Int] = List.empty + if (timeEvaluation) + threads = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24) + else + threads = List(0) + + for (thread ← threads) { + var timeResults: List[Seconds] = List.empty + for (i ← 1 to (if (timeEvaluation) 10 else 1)) { + import java.net.URL + val newProject: Project[URL] = project.recreate() + val analysesManager = newProject.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + + analysesManager.project.recreate() + time { + propertyStore = analysesManager + .runAll( + dependencies + ) + ._1 + //val numThreads = 18 + newProject.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.ParTasksManagerConfig.MaxThreads = thread + // FIXME: this property store is broken + org.opalj.fpcf.par.PKECPropertyStore(context: _*) + } + } + ) + //new PKECPropertyStore(context, project.logContext) + //org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = 8 + //var context: Map[Class[_], AnyRef] = Map.empty + + propertyStore.waitOnPhaseCompletion() + + } { t ⇒ + analysisTime = t.toSeconds + } + timeResults = analysisTime :: timeResults + } + threadTimeResults.put(thread, timeResults) + } + + val stringBuilderResults: StringBuilder = new StringBuilder() + + val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet + val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet + + println("all fields in project class files: "+allfieldsInProjectClassFiles.size) + //References + val mutableReferences = propertyStore + .finalEntities(MutableReference) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) + .toList + .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) + + val lazyInitializedReferencesThreadSafe = propertyStore + .finalEntities(LazyInitializedThreadSafeReference) + .toList + .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) + + val lazyInitializedReferencesNotThreadSafeButDeterministic = propertyStore + .finalEntities(LazyInitializedNotThreadSafeButDeterministicReference) + .toList + .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) + + val notThreadSafeOrNotDeterministicLazyInitialization = propertyStore + .finalEntities(LazyInitializedNotThreadSafeOrNotDeterministicReference) + .toList + .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) + val immutableReferences = propertyStore + .entities( + eps ⇒ //allfieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field]) && + eps.isFinal && (eps.asFinal.p match { + case ImmutableReference(_) ⇒ true + case _ ⇒ false + }) + ) + .toList + .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) + + stringBuilderResults.append( + s""" + | mutable References: + | ${mutableReferences.mkString("|| mutable Reference \n")} + | + | lazy initalized not thread safe and not deterministic references: + | ${notThreadSafeOrNotDeterministicLazyInitialization.mkString("|| no ts & n dt ref\n")} + | + | lazy initialized not thread safe but deterministic references: + | ${lazyInitializedReferencesNotThreadSafeButDeterministic.mkString("|| li no ts b dt\n")} + | + | lazy initialized thread safe reference: + | ${lazyInitializedReferencesThreadSafe.mkString("|| li thread safe\n")} + | + | immutable Reference: + | ${immutableReferences.mkString("|| immutable References \n")} + | + |""".stripMargin + ) + + //Fields + val mutableFields = propertyStore + .finalEntities(MutableField) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) + .toList + val shallowImmutableFields = propertyStore + .finalEntities(ShallowImmutableField) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) + .toList + + val dependentImmutableFields = propertyStore + .finalEntities(DependentImmutableField) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) + .toList + + val deepImmutableFields = propertyStore + .finalEntities(DeepImmutableField) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) + .toList + stringBuilderResults.append( + s""" + | mutable fields: + | + | + | shallow immutable fields: + | + | + | dependent immutable fields: + | + | + | deep immutable fields: + | + |""".stripMargin + ) + + //Classes + + val mutableClasses = propertyStore + .finalEntities(MutableClass) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) + .toList + val shallowImmutableClasses = propertyStore + .finalEntities(ShallowImmutableClass) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) + .toList + + val dependentImmutableClasses = propertyStore + .finalEntities(DependentImmutableClass) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) + .toList + val allInterfaces = + project.allProjectClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet + + val deepImmutables = propertyStore + .finalEntities(DeepImmutableClass) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) + .toList + val deepImmutableClassesInterfaces = deepImmutables + .filter(x ⇒ x.isInstanceOf[ObjectType] && allInterfaces.contains(x.asInstanceOf[ObjectType])) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) + val deepImmutableClasses = + deepImmutables + .filter(!deepImmutableClassesInterfaces.toSet.contains(_)) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) + + //Types + + val allProjectClassFilesIterator = project.allProjectClassFiles + val types = + allProjectClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType).toSet + val mutableTypes = propertyStore + .finalEntities(MutableType_new) + .filter({ x ⇒ + types.contains(x.asInstanceOf[ObjectType]) + }) + .toList + + val shallowImmutableTypes = propertyStore + .finalEntities(ShallowImmutableType) + .filter({ x ⇒ + types.contains(x.asInstanceOf[ObjectType]) + }) + .toList + val dependentImmutableTypes = propertyStore + .finalEntities(DependentImmutableType) + .filter({ x ⇒ + types.contains(x.asInstanceOf[ObjectType]) + }) + .toList + val deepImmutableTypes = propertyStore + .finalEntities(DeepImmutableType) + .filter({ x ⇒ + types.contains(x.asInstanceOf[ObjectType]) + }) + .toList + val stringBuilderAmounts: StringBuilder = new StringBuilder + + if (ra == References || ra == All) { + stringBuilderAmounts.append( + s""" + | mutable References: ${mutableReferences.size} + | lazy initialized not thread safe or not deterministic ref.: ${notThreadSafeOrNotDeterministicLazyInitialization.size} + | lazy initialization not thread safe but deterministic: ${lazyInitializedReferencesNotThreadSafeButDeterministic.size} + | lazy initialization thread safe: ${lazyInitializedReferencesThreadSafe.size} + | immutable references: ${immutableReferences.size} + |""".stripMargin + ) + } + if (ra == Fields || ra == All) { + stringBuilderAmounts.append( + s""" + | mutable fields: ${mutableFields.size} + | shallow immutable fields: ${shallowImmutableFields.size} + | depenent immutable fields: ${dependentImmutableFields.size} + | deep immutable fields: ${deepImmutableFields.size} + |""".stripMargin + ) + } + if (ra == Classes || ra == All) { + stringBuilderAmounts.append( + s""" + | mutable classes: ${mutableClasses.size} + | shallow immutable classes: ${shallowImmutableClasses.size} + | depenent immutable classes: ${dependentImmutableClasses.size} + | deep immutable classes: ${deepImmutableClasses.size} + |""".stripMargin + ) + } + if (ra == Types || ra == All) + stringBuilderAmounts.append( + s""" + | mutable types: ${mutableTypes.size} + | shallow immutable types: ${shallowImmutableTypes.size} + | dependent immutable types: ${dependentImmutableTypes.size} + | deep immutable types: ${deepImmutableTypes.size} + | + |""".stripMargin + ) + + stringBuilderAmounts.append( + s""" + |took $analysisTime seconds + |""".stripMargin + ) + + /*(o._1, o._2.iterator.fold(0.0, + {(x:Double,y:Seconds)⇒x+y.timeSpan})))*/ + + threadTimeResults.map(x ⇒ (x._1, x._2.fold(Seconds.None)((x, y) ⇒ x + y).timeSpan / threadTimeResults.size)) + + println( + s""" ${stringBuilderResults.toString()} + | + | ${stringBuilderAmounts.toString()} + | + | Time Results: + | ${threadTimeResults.map(x ⇒ s"""${x._1} Thread :: as average ${x._2} seconds""")} + | + |"""".stripMargin + ) + + val calendar = Calendar.getInstance() + val file = new File( + s"${resultsFolder.toAbsolutePath.toString}/wholeImmResult_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" + ) + file.createNewFile() + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(s""" ${stringBuilderResults.toString()} + | + | ${stringBuilderAmounts.toString()} + |"""".stripMargin) + bw.close() + BasicReport( + stringBuilderAmounts.toString() + ) + } + + def main(args: Array[String]): Unit = { + def usage: String = { + "Usage: java …ImmutabilityAnalysisEvaluation \n"+ + "-cp OR -JDK\n"+ + "[-analysis ]\n"+ + "[-threads ]\n"+ + "[-resultFolder ]\n" /*+ + "[-projectDir ]\n"+ + "[-libDir ]\n"+ + "[-analysis (Default: L2, the most precise analysis configuration)]\n"+ + "[-fieldMutability (Default: Depends on analysis level)]\n"+ + "[-escape (Default: L1, the most precise configuration)]\n"+ + "[-domain ]\n"+ + "[-rater ]\n"+ + "[-callGraph (Default: RTA)]\n"+ + "[-eager] (supporting analyses are executed eagerly)\n"+ + "[-noJDK] (do not analyze any JDK methods)\n"+ + "[-individual] (reports the purity result for each method)\n"+ + "[-closedWorld] (uses closed world assumption, i.e. no class can be extended)\n"+ + "[-library] (assumes that the target is a library)\n"+ + "[-debug] (enable debug output from PropertyStore)\n"+ + "[-multi] (analyzes multiple projects in the subdirectories of -cp)\n"+ + "[-eval ]\n"+ + "[-j (0 for the sequential implementation)]\n"+ + "[-analysisName ]\n"+ + "[-schedulingStrategy ]\n"+ + "Example:\n\tjava …PurityAnalysisEvaluation -JDK -individual -closedWorld"*/ + } + var i = 0 + var cp: File = null + var resultFolder: Path = null + var numThreads = 0 + var timeEvaluation: Boolean = false + + def readNextArg(): String = { + i += 1 + if (i < args.length) { + args(i) + } else { + println(usage) + throw new IllegalArgumentException(s"missing argument: ${args(i - 1)}") + } + } + + var analysis: RunningAnalysis = RunningAnalysis.All + //val numberOfThreads:Option[String] = None + + while (i < args.length) { + args(i) match { + case "-analysis" ⇒ { + val result = readNextArg() + if (result == "All") + analysis = RunningAnalysis.All + else if (result == "References") + analysis = RunningAnalysis.References + else if (result == "Fields") + analysis = RunningAnalysis.Fields + else if (result == "Classes") + analysis = RunningAnalysis.Classes + else if (result == "Types") + analysis = RunningAnalysis.Fields + } + case "-threads" ⇒ numThreads = readNextArg().toInt + case "-cp" ⇒ cp = new File(readNextArg()) + case "-resultFolder" ⇒ + import java.nio.file.FileSystems + + resultFolder = FileSystems.getDefault().getPath(readNextArg()) + case "-timeEvaluation" ⇒ timeEvaluation = true + /* + case "-projectDir" ⇒ projectDir = Some(readNextArg()) + case "-libDir" ⇒ libDir = Some(readNextArg()) + case "-analysis" ⇒ analysisName = Some(readNextArg()) + case "-fieldMutability" ⇒ fieldMutabilityAnalysisName = Some(readNextArg()) + case "-escape" ⇒ escapeAnalysisName = Some(readNextArg()) + case "-domain" ⇒ domainName = Some(readNextArg()) + case "-rater" ⇒ raterName = Some(readNextArg()) + case "-callGraph" ⇒ callGraphName = Some(readNextArg()) + case "-analysisName" ⇒ configurationName = Some(readNextArg()) + case "-schedulingStrategy" ⇒ schedulingStrategy = Some(readNextArg()) + case "-eager" ⇒ eager = true + case "-individual" ⇒ individual = true + case "-closedWorld" ⇒ cwa = true + case "-library" ⇒ isLibrary = true + case "-debug" ⇒ debug = true + case "-multi" ⇒ multiProjects = true + case "-eval" ⇒ evaluationDir = Some(new File(readNextArg())) + case "-j" ⇒ numThreads = readNextArg().toInt + case "-noJDK" ⇒ withoutJDK = true */ + case "-JDK" ⇒ + cp = JRELibraryFolder; //withoutJDK = true + + case unknown ⇒ + Console.println(usage) + throw new IllegalArgumentException(s"unknown parameter: $unknown") + } + i += 1 + } + + evaluate(analysis, numThreads, cp, resultFolder, timeEvaluation) + } +} + From f6a5553baeebd8b89cae2c864ae787ec66b3a8e7 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 27 Jul 2020 16:15:40 +0200 Subject: [PATCH 200/327] wip: reference imm without eff immutability checking --- ...bstractReferenceImmutabilityAnalysis.scala | 17 +- ...mutabilityAnalysisLazyInitialization.scala | 2 +- .../L0ReferenceImmutabilityAnalysis.scala | 280 ++++-------------- 3 files changed, 63 insertions(+), 236 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala index bdb8a3d7e5..98cb410eea 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala @@ -27,7 +27,6 @@ import org.opalj.fpcf.EOptionP import org.opalj.fpcf.EUBP import org.opalj.fpcf.Entity import org.opalj.fpcf.FinalEP -import org.opalj.fpcf.InterimEP import org.opalj.fpcf.LBP import org.opalj.fpcf.Property import org.opalj.fpcf.UBP @@ -54,7 +53,7 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { case class State( field: Field, - var referenceImmutability: ReferenceImmutability = ImmutableReference(true), + var referenceImmutability: ReferenceImmutability = ImmutableReference, var prematurelyReadDependee: Option[EOptionP[Field, FieldPrematurelyRead]] = None, var purityDependees: Set[EOptionP[DeclaredMethod, Purity]] = Set.empty, var lazyInitInvocation: Option[(DeclaredMethod, PC)] = None, @@ -62,7 +61,6 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { var referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = Set.empty, var escapeDependees: Set[EOptionP[DefinitionSite, EscapeProperty]] = Set.empty, var tacDependees: Map[Method, (EOptionP[Method, TACAI], PCs)] = Map.empty, - var notEscapes: Boolean = true, var typeDependees: Set[EOptionP[ObjectType, TypeImmutability_new]] = Set.empty, var lazyInitializerIsDeterministicFlag: Boolean = false ) { @@ -96,11 +94,8 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { case finalEP: FinalEP[Method, TACAI] ⇒ stillCalculatedMethods.put(method, finalEP.ub.tac.get) finalEP.ub.tac - case eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) + state.tacDependees += method -> ((epk, pcs)) // + alle pcs die schon existieren None } } @@ -141,10 +136,10 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { def isImmutableReference( eop: EOptionP[Field, ReferenceImmutability] )(implicit state: State): Boolean = eop match { - case FinalEP(e, ImmutableReference(_)) ⇒ true - case FinalEP(e, MutableReference) ⇒ false - case LBP(ImmutableReference(_)) ⇒ true - case UBP(MutableReference) ⇒ false + case FinalEP(e, ImmutableReference) ⇒ true + case FinalEP(e, MutableReference) ⇒ false + case LBP(ImmutableReference) ⇒ true + case UBP(MutableReference) ⇒ false case _ ⇒ state.referenceImmutabilityDependees += eop true diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index b50add0e26..67898dfc57 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -75,7 +75,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization tacCai ) dcl match { - case ir @ ImmutableReference(_) ⇒ state.referenceImmutability = ir + case ir @ ImmutableReference ⇒ state.referenceImmutability = ir case lits @ LazyInitializedThreadSafeReference ⇒ state.referenceImmutability = lits case lintbd @ LazyInitializedNotThreadSafeButDeterministicReference ⇒ state.referenceImmutability = lintbd diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index eaeab012e6..c295433c3e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -18,12 +18,9 @@ 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.DeepImmutableField -import org.opalj.br.fpcf.properties.DependentImmutableType 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.FieldImmutability import org.opalj.br.fpcf.properties.FieldPrematurelyRead import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference @@ -33,7 +30,6 @@ import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.NoEscape import org.opalj.br.fpcf.properties.Purity import org.opalj.br.fpcf.properties.ReferenceImmutability -import org.opalj.br.fpcf.properties.TypeImmutability_new import org.opalj.br.fpcf.properties.cg.Callees import org.opalj.fpcf.EOptionP import org.opalj.fpcf.Entity @@ -48,7 +44,6 @@ import org.opalj.fpcf.PropertyComputationResult import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS -import org.opalj.tac.NonVirtualMethodCall import org.opalj.tac.PutField import org.opalj.tac.PutStatic import org.opalj.tac.Stmt @@ -61,8 +56,21 @@ import scala.annotation.switch /** * - * Implementation is used from the old L2FieldMutability implementation - * but the lattice is mapped to the new reference immutability lattice. + * Determines the immutability of the reference of a classes field. + * In cases of fields with primitive types, we consider them also as a combination of fields and types + * The field immutability will be determined in the FieldImmutabilityAnalysis. + * + * Examples: + * class ... { + * ... + * final Object o; + * int n; + * ... + * } + * + * In both cases we consider o and an as references with their respective types. + * The combination of both is the field immutability. + * * * @note Requires that the 3-address code's expressions are not deeply nested. * @@ -86,7 +94,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } /** - * Analyzes the mutability of private non-final fields. + * 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 @@ -96,27 +104,23 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec field: Field ): ProperPropertyComputationResult = { - //println("--------------------------------------------------- reference: " + field) - implicit val state: State = State(field) // Fields are not final if they are read prematurely! - if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key)) & !field.isFinal) { - //("field is prematurely read") + if (!field.isFinal && isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { return Result(field, MutableReference) - }; //Result(field, NonFinalFieldByAnalysis); - - state.referenceImmutability = ImmutableReference(true) //EffectivelyFinalField + }; + state.referenceImmutability = ImmutableReference - //test - //if (field.isFinal) - //{return createResult(); - //return Result(field, ImmutableReference(false))} + // It still has to be determined of the referred object could escape + if (field.isFinal) + return Result(field, ImmutableReference) + //return createResult(); val thisType = field.classFile.thisType + if (field.isPublic && !field.isFinal) return Result(field, MutableReference) - //if ((field.isPublic || field.isPackagePrivate || field.isProtected)) { - state.notEscapes = !((field.isPublic || field.isPackagePrivate || field.isProtected)) //false + //in cases of a public, package private or protected reference, the referenced object could escape // 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 @@ -134,7 +138,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val classesHavingAccess: Iterator[ClassFile] = if (field.isProtected) { if (typeExtensibility(thisType).isYesOrUnknown) { - state.notEscapes = false + //state.notEscapes = false return Result(field, MutableReference); } val subclassesIterator: Iterator[ClassFile] = @@ -148,20 +152,17 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec // If there are native methods, we give up if (classesHavingAccess.exists(_.methods.exists(_.isNative))) { - state.notEscapes = false + //state.notEscapes = false if (!field.isFinal) return Result(field, MutableReference) } - //checkFieldReadsForEffImmutability(field) for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method, pcs) - staticAddition = if (method.isStatic) 1 else 0 } { - //checkFieldWritesForEffImmutability(state, pcs, taCode, staticAddition) - if (methodUpdatesField(method, taCode, pcs)) { //&& !state.field.isFinal - return Result(field, MutableReference); //return Result(field, NonFinalFieldByAnfalysis); + if (methodUpdatesField(method, taCode, pcs)) { + return Result(field, MutableReference); } } if (state.lazyInitInvocation.isDefined) { @@ -171,158 +172,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec createResult() } - private def checkFieldReadsForEffImmutability(field: Field)(implicit state: State) = { - - for { - (method, pcs) ← fieldAccessInformation.readAccesses(field) - taCode ← getTACAI(method, pcs) - pc ← pcs - } { - val staticAddition = if (method.isStatic) 1 else 0 - val index = taCode.pcToIndex(pc) - if (index > (-1 + staticAddition)) { - val stmt = taCode.stmts(index) - if (stmt.isAssignment) { - val assignment = taCode.stmts(index).asAssignment - val useSites = assignment.targetVar.usedBy - for { - index ← useSites.iterator - } { - val stmt2 = taCode.stmts(index) - if (!(stmt2.isMonitorEnter || stmt2.isMonitorExit || stmt2.isIfStmt)) { - state.notEscapes = false - } - } - } - } else { - state.notEscapes = false - } - } - } - - private def checkFieldWritesForEffImmutability(field: Field)(implicit state: State) = { - import scala.collection.mutable - //var stillCheckedUseSites: Set[Int] = Set.empty - val workedlist: mutable.HashSet[Int] = new mutable.HashSet[Int]() - - def checkNonVirtualMethodCall( - taCode: TACode[TACMethodParameter, V], - nonVirtualMethodCall: NonVirtualMethodCall[V] - ) = { - //println("params: " + nonVirtualMethodCall.params.count(_ => true)) - for (param ← nonVirtualMethodCall.params.iterator) { - if (param.isVar) { - val v3 = param.asVar - - //println("v3: " + v3) - //println("defsites: " + v3.definedBy) - for (defs ← v3.definedBy.iterator) { - if (!workedlist.contains(defs)) { - if (defs < 0) { - state.notEscapes = false - } else { - val stmt4 = taCode.stmts(defs) - if (stmt4.isAssignment) { - val assignm = stmt4.asAssignment - //println("assignment: " + assignm) - if (assignm.expr.isGetField) { - val getField = assignm.expr.asGetField - val accessingField = - state.field.classFile.findField(getField.name, getField.declaredFieldType) - //println("accessing field:" + accessingField) - if (accessingField.isDefined) { - val resultField = propertyStore(accessingField.get, FieldImmutability.key) - resultField match { - case FinalP(DeepImmutableField) ⇒ - case _ ⇒ state.notEscapes = false; //println("false6") - } - } - } - } - } - } - } - } - } - } - - for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field).iterator - taCode ← getTACAI(method, pcs).iterator - pc ← pcs.iterator - } { - val index = taCode.pcToIndex(pc) - if (!workedlist.contains(pc)) { - workedlist.add(pc) - val staticAddition = if (method.isStatic) 1 else 0 - if (index > (-1 + staticAddition)) { - val stmt = taCode.stmts(index) - //println("stmt: " + stmt) - if (stmt.isPutField) { - val putField = stmt.asPutField - if (putField.value.isVar) { - val defSites = putField.value.asVar.definedBy - //println("defSites: " + defSites) - for { - i ← defSites.iterator - } { - if (!workedlist.contains(i)) { - workedlist.add(i) - if (i > 0) { - val stmt2 = taCode.stmts(i) - //println("stmt2: " + stmt2) - if (stmt2.isAssignment) { - val assignment = stmt2.asAssignment - if (assignment.expr.isVar) { - val v2 = assignment.expr.asVar - //println("v2: " + v2) - for (x ← v2.usedBy.iterator) { - val stmt3 = taCode.stmts(x) - //println("stmt3: " + stmt3) - if (stmt3.isNonVirtualMethodCall) { - val nonVirtualMethodCall = stmt3.asNonVirtualMethodCall - checkNonVirtualMethodCall(taCode, nonVirtualMethodCall) - } - } - } else if (assignment.expr.isNew) { - //println("New: " + assignment.expr.asNew) - val constrUseSites = assignment.targetVar.usedBy - //println("constrUseSite: " + constrUseSites) - for (us ← constrUseSites.iterator) { - val stmt = taCode.stmts(us) - //println("stmt::: " + stmt) - if (stmt.isNonVirtualMethodCall) { - //println("nvmc: " + stmt.asNonVirtualMethodCall) - checkNonVirtualMethodCall(taCode, stmt.asNonVirtualMethodCall) - } - } - - } else if (!assignment.expr.isConst) { - //println("ass no const: " + assignment.expr) - //println("false2") - state.notEscapes = false - } - - } //else { - //constructor ?? - //} - } else { - //println("meta else") - //println("false1") - state.notEscapes = false - } - } - } - } - } - } - } else { - //println("false0") - state.notEscapes = false - } - } - } - def handleCalls( calleesEOP: EOptionP[DeclaredMethod, Callees] )( @@ -349,7 +198,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference //TODO //MutableReference //NonFinalFieldByAnalysis true } else { - val targets = callees.callees(pc).toTraversable + val targets = callees.callees(pc) if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference //MutableReference //NonFinalFieldByAnalysis true @@ -362,17 +211,20 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * values. */ def getDefaultValue()(implicit state: State): Option[Any] = { - Some( - state.field.fieldType match { - case FloatType ⇒ 0.0f - case IntegerType ⇒ 0 - case ObjectType(_) ⇒ null - case BooleanType ⇒ false - case ByteType ⇒ 0 - case ShortType ⇒ 0 - case _ ⇒ - } - ) + import org.opalj.br.ArrayType + + state.field.fieldType.isReferenceType + state.field.fieldType match { + case FloatType ⇒ Some(0.0f) + case IntegerType ⇒ Some(0) + case ObjectType(_) ⇒ Some(null) //TODO ReferenceType + //case ReferenceType(_) ⇒ Some(null) //TODO + case BooleanType ⇒ Some(false) + case ByteType ⇒ Some(0) + case ShortType ⇒ Some(0) + case ArrayType(_) ⇒ Some(null) + case _ ⇒ None //TODO test mit Array + } } /** @@ -389,26 +241,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec c ) } else { - if (state.field.isFinal) - state.referenceImmutability = ImmutableReference(state.notEscapes) - //state.referenceImmutability match { - //case ImmutableReference(doesNotEscape) => - if (state.referenceImmutability.isImmutableReference) { - if (!state.referenceImmutability.doesNotEscapes) { - Result(state.field, ImmutableReference(state.notEscapes)) - } else { - checkFieldWritesForEffImmutability(state.field) - if (!state.notEscapes) // || "45" == 45.toString - Result(state.field, ImmutableReference(state.notEscapes)) - else { - checkFieldReadsForEffImmutability(state.field) - Result(state.field, ImmutableReference(state.notEscapes)) - } - } - } else - //case _ => - Result(state.field, state.referenceImmutability) - //} + Result(state.field, state.referenceImmutability) } } @@ -423,10 +256,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec case EscapeProperty.key ⇒ val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) - val escapes = handleEscapeProperty(newEP) - if (escapes) - state.notEscapes = false - isNotFinal = escapes + isNotFinal = handleEscapeProperty(newEP) case TACAI.key ⇒ val newEP = eps.asInstanceOf[EOptionP[Method, TACAI]] val method = newEP.e @@ -434,6 +264,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec state.tacDependees -= method if (eps.isRefinable) state.tacDependees += method -> ((newEP, pcs)) + //TODO tacai funktionen alle ausführen isNotFinal = methodUpdatesField(method, newEP.ub.tac.get, pcs) case Callees.key ⇒ isNotFinal = handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) @@ -454,17 +285,17 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec state.referenceImmutabilityDependees = state.referenceImmutabilityDependees.filter(_.e ne newEP.e) isNotFinal = !isImmutableReference(newEP) - case TypeImmutability_new.key ⇒ + /*case TypeImmutability_new.key ⇒ val newEP = eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] state.typeDependees = state.typeDependees.filter(_.e ne newEP.e) newEP match { - case FinalP(DependentImmutableType) ⇒ state.notEscapes = false + case FinalP(DependentImmutableType) ⇒ case FinalP(_) ⇒ - case ep @ _ ⇒ state.typeDependees += ep - } + case ep ⇒ state.typeDependees += ep + } */ } - if (isNotFinal && !state.field.isFinal && { + if (!state.field.isFinal && { state.referenceImmutability match { case LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeOrNotDeterministicReference ⇒ @@ -497,7 +328,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec //println("stmts: "+stmts.mkString(", \n")) for (pc ← pcs.iterator) { val index = taCode.pcToIndex(pc) - if (index > (-1 + staticAddition)) { + if (index > (-1 + staticAddition)) { //TODO unnötig val stmt = stmts(index) if (stmt.pc == pc) { (stmt.astID: @switch) match { @@ -559,6 +390,8 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } } + //TODO assignment statement ... + //case Assignment. case _ ⇒ throw new RuntimeException("unexpected field access"); } } else { @@ -636,10 +469,9 @@ trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { final override def uses: Set[PropertyBounds] = Set( PropertyBounds.lub(Purity), PropertyBounds.lub(FieldPrematurelyRead), - PropertyBounds.ub(TACAI), + PropertyBounds.finalP(TACAI), PropertyBounds.lub(ReferenceImmutability), PropertyBounds.ub(EscapeProperty), - PropertyBounds.lub(TypeImmutability_new) ) final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) From beaa78e53449977938ec5c903c5a8c6499480562 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 27 Jul 2020 16:16:48 +0200 Subject: [PATCH 201/327] wip field imm analysis with eff imm checking. 12 failing tests left. --- .../L0FieldImmutabilityAnalysis.scala | 442 +++++++++++++++--- 1 file changed, 377 insertions(+), 65 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index 3cd7bdc5eb..b2c9c48d9b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -50,8 +50,24 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS import org.opalj.tac.fpcf.analyses.immutability.DependentImmutabilityKind.DependentImmutabilityKind +import org.opalj.br.Method +import org.opalj.br.fpcf.properties.EscapeProperty +import org.opalj.tac.NonVirtualMethodCall +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACode +import org.opalj.tac.fpcf.analyses.AbstractIFDSAnalysis.V +import org.opalj.tac.common.DefinitionSitesKey +import org.opalj.collection.immutable.IntTrieSet +import org.opalj.br.fpcf.properties.AtMost +import org.opalj.br.fpcf.properties.EscapeInCallee +import org.opalj.br.fpcf.properties.EscapeViaReturn +import org.opalj.br.fpcf.properties.NoEscape +import org.opalj.fpcf.InterimUBP +import org.opalj.tac.common.DefinitionSite +import org.opalj.tac.fpcf.properties.TACAI case class State(f: Field) { + var field: Field = f var typeImmutability: Option[Boolean] = Some(true) var referenceImmutability: Option[Boolean] = None @@ -60,6 +76,15 @@ case class State(f: Field) { DependentImmutabilityKind.dependent ) var genericTypeSetNotDeepImmutable = false + var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + + def hasDependees: Boolean = { + dependencies.nonEmpty + } + + def dependees: Set[EOptionP[Entity, Property]] = { + dependencies + } } object DependentImmutabilityKind extends Enumeration { @@ -68,11 +93,10 @@ object DependentImmutabilityKind extends Enumeration { } /** - * Analyses that determines the mutability of org.opalj.br.Field + * Analyses that determines the immutability of org.opalj.br.Field * Because it depends on the Field Immutability Lattice it combines the immutability of the fields reference and * it's type. Thus needs the information of the reference of the field from the [[L0ReferenceImmutabilityAnalysis]] * and the information of the type immutability determined by the type immutability analysis. - * Till now it uses the old type immutability analysis. * * @author Tobias Peter Roth */ @@ -82,13 +106,14 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) final val typeExtensibility = project.get(TypeExtensibilityKey) final val closedPackages = project.get(ClosedPackagesKey) final val fieldAccessInformation = project.get(FieldAccessInformationKey) + final val definitionSites = project.get(DefinitionSitesKey) def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { entity match { case field: Field ⇒ determineFieldImmutability(field) case _ ⇒ - val m = entity.getClass.getName+"is not an org.opalj.br.Field" + val m = s"""${entity.getClass.getName} is not an org.opalj.br.Field""" throw new IllegalArgumentException(m) } } @@ -96,29 +121,35 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) private[analyses] def determineFieldImmutability( field: Field ): ProperPropertyComputationResult = { - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + + if (field.name.contains("hash") && + field.classFile.thisType.simpleName.contains("AclEntry")) + println(s"field: ${field}") + var classFormalTypeParameters: Option[Set[String]] = None def loadFormalTypeparameters() = { var result: Set[String] = Set.empty //TODO - def CheckAttributeWithRegardToFormalTypeParameter: Attribute ⇒ Unit = { x ⇒ - x match { - case SourceFile(_) ⇒ - case ClassSignature(typeParameters, _, _) ⇒ - typeParameters.iterator.foreach( - y ⇒ - y match { + def CheckAttributeWithRegardToFormalTypeParameter: Attribute ⇒ Unit = { + attribute ⇒ + attribute match { + case SourceFile(_) ⇒ + case ClassSignature(typeParameters, _, _) ⇒ + for (parameter ← typeParameters.iterator) { + parameter match { case FormalTypeParameter(identifier, _, _) ⇒ result += identifier case _ ⇒ } - ) - case _ ⇒ - } + } + case _ ⇒ + } } + + //if the genericity is nested in an inner class if (field.classFile.outerType.isDefined) { - val cf = project.classFile(field.classFile.outerType.get._1) - if (cf.isDefined) { - cf.get.attributes.iterator.foreach( + val outerClassFile = project.classFile(field.classFile.outerType.get._1) + if (outerClassFile.isDefined) { + outerClassFile.get.attributes.iterator.foreach( CheckAttributeWithRegardToFormalTypeParameter ) } @@ -129,34 +160,31 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) if (result.size > 0) { + println("result: "+result) classFormalTypeParameters = Some(result) } } - def isInClassesGenericTypeParameters(string: String): Boolean = { - if (classFormalTypeParameters == None) - false - else - classFormalTypeParameters.get.contains(string) - } + def isInClassesGenericTypeParameters(string: String): Boolean = + classFormalTypeParameters.isDefined && classFormalTypeParameters.get.contains(string) def handleTypeImmutability(state: State) = { val objectType = field.fieldType.asFieldType if (objectType == ObjectType.Object) { state.typeImmutability = Some(false) //handling generic fields - } else if (objectType.isBaseType || objectType == ObjectType("java/lang/String")) { + } else if (objectType.isBaseType || objectType == ObjectType.String) { //state.typeImmutability = Some(true) // true is default } else if (objectType.isArrayType) { state.typeImmutability = Some(false) } else { val result = propertyStore(objectType, TypeImmutability_new.key) - dependencies = dependencies.iterator.filter(_.e ne result.e).toSet + state.dependencies = state.dependencies.iterator.filter(_.e ne result.e).toSet result match { - case FinalEP(e, DeepImmutableType) ⇒ - case FinalEP(f, DependentImmutableType) ⇒ { + case FinalP(DeepImmutableType) ⇒ + case FinalP(DependentImmutableType) ⇒ { state.typeImmutability = Some(false) } - case FinalEP(e, ShallowImmutableType | MutableType_new) ⇒ { + case FinalP(ShallowImmutableType | MutableType_new) ⇒ { state.typeImmutability = Some(false) state.dependentImmutability = None if (state.field.fieldType.isObjectType && @@ -164,8 +192,8 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.dependentImmutability = None //when the generic type is still final } } - case ep: InterimEP[e, p] ⇒ dependencies += ep - case epk @ _ ⇒ dependencies += epk + case ep: InterimEP[e, p] ⇒ state.dependencies += ep + case epk @ _ ⇒ state.dependencies += epk } } } @@ -174,16 +202,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var flag_notShallow = true var flag_onlyDeep = true var genericFields: List[ObjectType] = List() + println("Attributes: "+state.field.asField.attributes) state.field.asField.attributes.iterator.foreach( _ match { case RuntimeInvisibleAnnotationTable(_) ⇒ case SourceFile(_) ⇒ case TypeVariableSignature(t) ⇒ + println("T: "+t) flag_onlyDeep = false - if (!isInClassesGenericTypeParameters(t)) { + if (!isInClassesGenericTypeParameters(t)) flag_notShallow = false - } - case ClassTypeSignature( packageIdentifier, SimpleClassTypeSignature(simpleName, typeArguments), @@ -196,6 +224,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) variance, TypeVariableSignature(identifier: String) ) ⇒ + println("identifier: "+identifier) flag_onlyDeep = false if (!isInClassesGenericTypeParameters(identifier)) { flag_notShallow = false @@ -214,12 +243,12 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val oPath = packageIdentifier1 match { - case Some(pid1) ⇒ pid1 + packageIdentifier2 - case _ ⇒ packageIdentifier2 + case Some(packageIdentifier1) ⇒ packageIdentifier1 + packageIdentifier2 + case _ ⇒ packageIdentifier2 } genericFields = ObjectType(oPath) :: genericFields } - case dc @ _ ⇒ + case _ ⇒ flag_notShallow = false flag_onlyDeep = false state.typeImmutability = Some(false) @@ -232,9 +261,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) flag_onlyDeep = false } ) - genericFields.iterator.foreach(objectType ⇒ { + genericFields.foreach(objectType ⇒ { val result = propertyStore(objectType, TypeImmutability_new.key) - dependencies = dependencies.iterator.filter(_.e ne result.e).toSet + state.dependencies = state.dependencies.iterator.filter(_.e ne result.e).toSet + println("result: "+result) result match { case FinalP(DeepImmutableType) ⇒ //nothing to to here: default value is deep imm case FinalP(ShallowImmutableType | DependentImmutableType | MutableType_new) ⇒ { @@ -243,7 +273,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.typeImmutability = Some(false) } case ep @ _ ⇒ - dependencies += ep + state.dependencies += ep } }) @@ -262,7 +292,259 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } + def getTACAI( + method: Method + )(implicit state: State): Option[TACode[TACMethodParameter, V]] = { + propertyStore(method, TACAI.key) match { + case finalEP: FinalEP[Method, TACAI] ⇒ + finalEP.ub.tac + case epk ⇒ + state.dependencies += epk + 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 handleEscapeProperty( + ep: EOptionP[DefinitionSite, EscapeProperty] + )(implicit state: State): Boolean = { + println("ep: "+ep) + ep match { + case FinalP(NoEscape) ⇒ // | EscapeInCallee | EscapeViaReturn + false + + case FinalP(EscapeInCallee | EscapeViaReturn) ⇒ true + case FinalP(AtMost(_)) ⇒ + true + + case _: FinalEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return + + case InterimUBP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ + state.dependencies += ep + false + + case InterimUBP(AtMost(_)) ⇒ + true + + case _: InterimEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return + + case _ ⇒ + state.dependencies += ep + false + } + } + + def checkIfReferencedObjectCanEscape(implicit state: State): Unit = { + + def checkFieldReadsForEffImmutability(field: Field)(implicit state: State) = { + + for { + (method, pcs) ← fieldAccessInformation.readAccesses(field) + taCode ← getTACAI(method) + pc ← pcs + } { + + val index = taCode.pcToIndex(pc) + println("stmt: "+taCode.stmts(index)) + val stmt = taCode.stmts(index) + // if (stmt.isAssignment) { + val assignment = stmt.asAssignment + val useSites = assignment.targetVar.usedBy + for { + index ← useSites + } { + val fieldsUseSiteStmt = taCode.stmts(index) + if (!(fieldsUseSiteStmt.isMonitorEnter || + fieldsUseSiteStmt.isMonitorExit || + fieldsUseSiteStmt.isIfStmt)) { + state.referenceNotEscapes = false + } + } + // } else if (stmt.isExprStmt) { + // //is ignored + //} else state.referenceNotEscapes = false + + } + } + + def checkFieldWritesForEffImmutability(field: Field)(implicit state: State) = { + import org.opalj.tac.Stmt + def checkNonVirtualMethodCall( //TODO loswerden über escape analyse + method: Method, + nonVirtualMethodCall: NonVirtualMethodCall[V], + tacCode: TACode[TACMethodParameter, V] + ): Unit = { + println("check nvmc parameters") + println(state.field.classFile) + println("nvmc: "+nonVirtualMethodCall) + println("params: "+nonVirtualMethodCall.params) + nonVirtualMethodCall.params.foreach( + param ⇒ { + println("param :"+param) + + param.asVar.definedBy.foreach( + index ⇒ + if (index < 0) + state.referenceNotEscapes = false + else { + println("A") + val definitionSides = definitionSites(method, index) + println("B") + println("C") + var flag = true + val stmt = tacCode.stmts(tacCode.pcToIndex(definitionSides.pc)) + if (stmt.isNonVirtualMethodCall) { + checkNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) + flag = false + } else if (stmt.isPutField || stmt.isPutStatic) { + flag = false + //referenceNotEscapes = false + checkPuts(stmt, method, tacCode) + } + if (flag) { + val propertyStoreResult = + propertyStore(definitionSides, EscapeProperty.key) + if (handleEscapeProperty(propertyStoreResult)) { + println("nvmc") + state.referenceNotEscapes = false + } + } + } + ) + } + ) + } + def checkPuts(stmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { + var defSites: IntTrieSet = IntTrieSet.empty + if (stmt.isPutField) { + val putField = stmt.asPutField + defSites = putField.value.asVar.definedBy + } else if (stmt.isPutStatic) { + val putStatic = stmt.asPutStatic + defSites = putStatic.value.asVar.definedBy + } else { + println("false1") + state.referenceNotEscapes = false + } + println("defsites: "+defSites) + for { + i ← defSites + } { + if (i > 0) { + val stmt2 = tacCode.stmts(i) + val assignment = stmt2.asAssignment + println( + s"""i: $i + | assignment: $assignment + |""".stripMargin + ) + if (assignment.expr.isVar) { + val v2 = assignment.expr.asVar + for (x ← v2.usedBy.iterator) { + val stmt3 = tacCode.stmts(x) + if (stmt3.isNonVirtualMethodCall) { + println("is nvmc") + val nonVirtualMethodCall = stmt3.asNonVirtualMethodCall + checkNonVirtualMethodCall(method, nonVirtualMethodCall, tacCode) + } else { state.referenceNotEscapes = false } + //TODO andere F#lle bedenken + } + } else if (assignment.expr.isNew) { + if (!method.isConstructor) { + val propertyStoreResult = + propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) + if (handleEscapeProperty(propertyStoreResult)) { + println("false3") + state.referenceNotEscapes = false + } + } else { + val useSites = + assignment.targetVar.usedBy + + for (i ← useSites) { + val stmt = tacCode.stmts(i) + if (stmt.isNonVirtualMethodCall) { + checkNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) + } + if (stmt.isPutStatic || stmt.isPutField) { + //checkPuts(stmt, method, tacCode) + } //else state.referenceNotEscapes = false + } + } + + /*taCode.stmts(i) match { + case nvmc: NonVirtualMethodCall ⇒ checkNonVirtualMethodCall(method, nvmc) + } */ + //else state.referenceNotEscapes = false + //) + //} + + /*val constrUseSites = assignment.targetVar.usedBy + for (us ← constrUseSites.iterator) { + val stmt = taCode.stmts(us) + if (stmt.isNonVirtualMethodCall) { + checkNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall) + } + //TODO alle Fälle abdecken + //TODO escape analyse für Object + // else ; aktuelles putfield bedenken + }*/ + } else if (!assignment.expr.isConst) { + println("false5") + state.referenceNotEscapes = false + } + } else { + println("false6") + state.referenceNotEscapes = false + } + } + } + + for { + (method, pcs) ← fieldAccessInformation.writeAccesses(field) + taCode ← getTACAI(method) + pc ← pcs + } { + val index = taCode.pcToIndex(pc) + val staticAddition = if (method.isStatic) 1 else 0 + if (index > (-1 + staticAddition)) { + val stmt = taCode.stmts(index) + checkPuts(stmt, method, taCode) + + } + } + } + println(s"""ne: ${state.referenceNotEscapes}""") + state.referenceNotEscapes = field.isPrivate + println(s"""ne: ${state.referenceNotEscapes}""") + if (state.referenceNotEscapes) + checkFieldWritesForEffImmutability(state.field) + println(s"""ne: ${state.referenceNotEscapes}""") + if (state.referenceNotEscapes) + checkFieldReadsForEffImmutability(state.field) + println(s"""ne: ${state.referenceNotEscapes}""") + + } + def createResult(state: State): ProperPropertyComputationResult = { + /*if (field.name.contains("hash") && + state.field.classFile.thisType.simpleName.contains("AclEntry")) */ + //". == ObjectType("java/nio/file/attribute/AclEntry")) + println( + s""" + | field: $field + | ref imm: ${state.referenceImmutability} + | type imm: ${state.typeImmutability} + | not escapes: ${state.referenceNotEscapes} + | dep imm: ${state.dependentImmutability} + |""".stripMargin + ) state.referenceImmutability match { case Some(false) | None ⇒ Result(field, MutableField) @@ -290,11 +572,22 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - dependencies = dependencies.iterator.filter(_.e ne eps.e).toSet - (eps: @unchecked) match { + state.dependencies = state.dependencies.iterator.filter(_.e ne eps.e).toSet + + if (state.field.name.contains("hash") && state.field.classFile.thisType.simpleName.contains("AclEntry")) + println("continuation; eps: "+eps) + + eps match { //(: @unchecked) + + /*case InterimLUBP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference, _) ⇒ + state.typeImmutability = Some(false) + if (field.name.contains("hash") && + field.classFile.thisType.simpleName.contains("AclEntry")) + println("here") + return Result(field, MutableField); */ case x: InterimEP[_, _] ⇒ { - dependencies += eps - InterimResult(field, MutableField, DeepImmutableField, dependencies, c(state)) + state.dependencies += eps + InterimResult(field, MutableField, DeepImmutableField, state.dependencies, c(state)) } case FinalP(DeepImmutableType) ⇒ //state.typeImmutability = Some(true) case FinalEP(t, MutableType_new | ShallowImmutableType) ⇒ @@ -307,74 +600,93 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (state.dependentImmutability == None) state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } - case x @ FinalP( + case FinalP( MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference ) ⇒ { state.typeImmutability = Some(false) return Result(field, MutableField); } - case x @ FinalP(ImmutableReference(notEscapes)) ⇒ { + case FinalP(ImmutableReference) ⇒ { state.referenceImmutability = Some(true) - state.referenceNotEscapes = notEscapes + //state.referenceNotEscapes = notEscapes } - case x @ FinalP( + case FinalP( LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference ) ⇒ { //TODO state.referenceImmutability = Some(true) } - case x @ _ ⇒ - dependencies = dependencies + x + + case _ if eps.isFinal && eps.asEPK.pk == TACAI.key ⇒ + checkIfReferencedObjectCanEscape(state) + + //case _: FinalEP[_, TACAI] ⇒ + // checkIfReferencedObjectCanEscape(state) + //TODO hier weiter machen + + //case EPS(,Te)⇒ + + case ep ⇒ + state.dependencies = state.dependencies + ep } - if (dependencies.isEmpty) createResult(state) + if (state.dependencies.isEmpty) createResult(state) else InterimResult( field, MutableField, DeepImmutableField, - dependencies, + state.dependencies, c(state) ) } - val state: State = new State(field) - val result = propertyStore(state.field, ReferenceImmutability.key) - result match { - case FinalP(ImmutableReference(notEscapes)) ⇒ { + implicit val state: State = State(field) + propertyStore(state.field, ReferenceImmutability.key) match { + case FinalP(ImmutableReference) ⇒ { state.referenceImmutability = Some(true) - state.referenceNotEscapes = notEscapes + //state.referenceNotEscapes = notEscapes } - case FinalEP( - _, - LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference - ) ⇒ + case FinalP(LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ state.referenceImmutability = Some(true) case FinalP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference) ⇒ return Result(field, MutableField); - case x @ _ ⇒ { - dependencies += x + case ep @ _ ⇒ { + state.dependencies += ep } } loadFormalTypeparameters() + handleTypeImmutability(state) + hasGenericType(state) - if (dependencies.isEmpty) + + if (state.referenceImmutability == Some(true) && state.typeImmutability != Some(true)) { + checkIfReferencedObjectCanEscape + } + + if (state.dependencies.isEmpty) { createResult(state) - else + } else { InterimResult( field, MutableField, DeepImmutableField, - dependencies, + state.dependencies, c(state) ) + } + } } trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { + import org.opalj.tac.fpcf.properties.TACAI + final override def uses: Set[PropertyBounds] = Set( + PropertyBounds.finalP(TACAI), + PropertyBounds.ub(EscapeProperty), PropertyBounds.lub(ReferenceImmutability), PropertyBounds.lub(TypeImmutability_new), PropertyBounds.lub(FieldImmutability) From dfa31192ecb68190110ae3b1ba2e5d4591fa19ca Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 28 Jul 2020 16:28:50 +0200 Subject: [PATCH 202/327] Revert "compiling tests on github before run" This reverts commit 60b3f52a --- .github/workflows/scala.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/scala.yml b/.github/workflows/scala.yml index 8fd4069e8f..19d693f6ca 100644 --- a/.github/workflows/scala.yml +++ b/.github/workflows/scala.yml @@ -32,7 +32,5 @@ jobs: needs: build steps: - - name: compile Tests - run: sbt -J-Xmx20G test:compile - name: Run Tests run: sbt -J-Xmx20G test From 26eb303b089ae32d460edeeb59ef65e482566226 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 28 Jul 2020 16:35:32 +0200 Subject: [PATCH 203/327] revert changes --- .../analyses/ClassImmutabilityAnalysis.scala | 57 +++++++------------ 1 file changed, 22 insertions(+), 35 deletions(-) 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/ClassImmutabilityAnalysis.scala index 1bab0f0a80..c116c173ac 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/ClassImmutabilityAnalysis.scala @@ -44,7 +44,6 @@ import org.opalj.br.fpcf.properties.NonFinalField import org.opalj.br.fpcf.properties.TypeImmutability /** - * * 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 @@ -68,13 +67,13 @@ import org.opalj.br.fpcf.properties.TypeImmutability */ class ClassImmutabilityAnalysis(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. - */ + * 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 @@ -85,9 +84,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { immutability: MutableObject ): MultiResult = { val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) - val r = allSubtypes.map { st ⇒ - new FinalEP(st, immutability) - }.toSeq + val r = allSubtypes.map { st ⇒ new FinalEP(st, immutability) }.toSeq MultiResult(r) } @@ -104,10 +101,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { project.classFile(t) match { case Some(scf) ⇒ nextComputations ::= ( - ( - determineClassImmutability(t, cfMutability, cfMutabilityIsFinal, false) _, - scf - ) + (determineClassImmutability(t, cfMutability, cfMutabilityIsFinal, false) _, scf) ) case None ⇒ OPALLogger.warn( @@ -183,15 +177,13 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { var dependees = Map.empty[Entity, EOptionP[Entity, Property]] if (!superClassMutabilityIsFinal) { - dependees += (SuperClassKey -> superClassInformation) + dependees += (SuperClassKey → superClassInformation) } // Collect all fields for which we need to determine the effective mutability! var hasFieldsWithUnknownMutability = false - val instanceFields = cf.fields.filter { f ⇒ - !f.isStatic - } + val instanceFields = cf.fields.filter { f ⇒ !f.isStatic } dependees ++= (propertyStore(instanceFields, FieldMutability) collect { case FinalP(_: NonFinalField) ⇒ // <=> The class is definitively mutable and therefore also all subclasses. @@ -231,7 +223,8 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { var fieldTypes: Set[ObjectType] = Set.empty if (maxLocalImmutability == ImmutableObject) { - fieldTypes = // IMPROVE Use the precise type of the field (if available)! + fieldTypes = + // IMPROVE Use the precise type of the field (if available)! cf.fields.collect { case f if !f.isStatic && f.fieldType.isObjectType ⇒ f.fieldType.asObjectType }.toSet @@ -281,7 +274,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { eOptP.hasUBP && eOptP.ub == ImmutableType && eOptP.isFinal } fieldTypesWithUndecidedMutability.foreach { eOptP ⇒ - dependees += (eOptP.e -> eOptP) + dependees += (eOptP.e → eOptP) } } @@ -351,7 +344,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { if (someEPS.isRefinable) { val entity = if (someEPS.pk == ClassImmutability.key) SuperClassKey else someEPS.e - dependees += (entity -> someEPS) + dependees += (entity → someEPS) } /*[DEBUG] @@ -359,7 +352,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { oldDependees != dependees, s"dependees are not correctly updated $e($p)\n:old=$oldDependees\nnew=$dependees" ) - */ + */ // Lift lower bound once no dependencies other than field type mutabilities are left if (minLocalImmutability != ImmutableContainer && @@ -384,7 +377,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { s"maxLocalImmutability=$maxLocalImmutability - "+ s"(old dependees: ${oldDependees.mkString(",")}" ) - */ + */ Result(t, maxLocalImmutability) @@ -401,10 +394,7 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { else { val isFinal = dependees.isEmpty createIncrementalResult( - t, - EPS(t, minLocalImmutability, maxLocalImmutability), - isFinal, - result + t, EPS(t, minLocalImmutability, maxLocalImmutability), isFinal, result ) } } @@ -457,11 +447,9 @@ trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { // 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 { + 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 @@ -540,8 +528,7 @@ object LazyClassImmutabilityAnalysis ): FPCFAnalysis = { val analysis = new ClassImmutabilityAnalysis(p) ps.registerLazyPropertyComputation( - ClassImmutability.key, - analysis.doDetermineClassImmutability + ClassImmutability.key, analysis.doDetermineClassImmutability ) analysis } From 743272fa4d16542189b322d72e209a867fa29e53 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 28 Jul 2020 16:38:06 +0200 Subject: [PATCH 204/327] revert changes --- .../analyses/TypeImmutabilityAnalysis.scala | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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/TypeImmutabilityAnalysis.scala index c8330220d8..499e0586aa 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/TypeImmutabilityAnalysis.scala @@ -101,6 +101,7 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal ps(t, ClassImmutability.key) match { case FinalP(ImmutableObject) ⇒ + case FinalP(_: MutableObject) ⇒ return Result(t, MutableType); @@ -111,16 +112,17 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal case eps @ InterimLUBP(lb, ub) ⇒ joinedImmutability = lb.correspondingTypeImmutability maxImmutability = ub.correspondingTypeImmutability - dependencies += (t -> eps) + dependencies += (t → eps) case eOptP ⇒ joinedImmutability = MutableType - dependencies += (t -> eOptP) + dependencies += (t → eOptP) } directSubtypes foreach { subtype ⇒ ps(subtype, TypeImmutability.key) match { case FinalP(ImmutableType) ⇒ + case UBP(MutableType) ⇒ return Result(t, MutableType); @@ -169,7 +171,8 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal case lb: TypeImmutability ⇒ joinedImmutability = joinedImmutability.meet(lb) case lb: ClassImmutability ⇒ - joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) + joinedImmutability = + joinedImmutability.meet(lb.correspondingTypeImmutability) } else { joinedImmutability = MutableType @@ -181,11 +184,8 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal Result(t, maxImmutability) } else { InterimResult( - t, - joinedImmutability, - maxImmutability, - dependencies.values, - c + t, joinedImmutability, maxImmutability, + dependencies.values, c ) } } @@ -210,7 +210,8 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal case subtypeP: TypeImmutability ⇒ maxImmutability = maxImmutability.meet(subtypeP) case subtypeP: ClassImmutability ⇒ - maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) + maxImmutability = + maxImmutability.meet(subtypeP.correspondingTypeImmutability) } nextResult() } @@ -264,7 +265,6 @@ object LazyTypeImmutabilityAnalysis with BasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - /** * Registers the analysis as a lazy computation, that is, the method * will call `ProperytStore.scheduleLazyComputation`. From 83d6f7410a41e0390f14c6a2050429dc9e1b2ee6 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 28 Jul 2020 16:40:50 +0200 Subject: [PATCH 205/327] revert changes --- .../analyses/L2FieldMutabilityAnalysis.scala | 55 ++++++++----------- 1 file changed, 23 insertions(+), 32 deletions(-) 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/L2FieldMutabilityAnalysis.scala index d6e9a6ba59..5970c63b5b 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/L2FieldMutabilityAnalysis.scala @@ -14,13 +14,20 @@ 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 @@ -28,6 +35,8 @@ 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 @@ -170,9 +179,10 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext return Result(field, NonFinalFieldByLackOfInformation); } val subclassesIterator: Iterator[ClassFile] = - classHierarchy.allSubclassTypes(thisType, reflexive = false).flatMap { ot ⇒ - project.classFile(ot).filter(cf ⇒ !initialClasses.contains(cf)) - } + classHierarchy.allSubclassTypes(thisType, reflexive = false). + flatMap { ot ⇒ + project.classFile(ot).filter(cf ⇒ !initialClasses.contains(cf)) + } initialClasses.iterator ++ subclassesIterator } else { initialClasses.iterator @@ -202,25 +212,6 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext // } // } - /** - * Returns the TACode for a method if available, registering dependencies as 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 eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method -> ((eps, pcs)) - eps.ub.tac - case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) - None - } - } - for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method, pcs) @@ -278,8 +269,8 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext Some(if (state.field.fieldType eq FloatType) 0.0f else 0) /* TODO Some lazy initialized fields use a different value to mark an uninitialized field - * The code below can be used to identify such value, but is not yet adapted to using the - * TACAI property */ + * The code below can be used to identify such value, but is not yet adapted to using the + * TACAI property */ /* var constantVal: Option[Any] = None var allInitializeConstant = true @@ -324,7 +315,6 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } } - for (constructor ← constructors) { // TODO iterate all statements val NonVirtualMethodCall(_, declClass, _, name, _, rcvr, _) = stmt @@ -374,7 +364,7 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext val pcs = state.tacDependees(method)._2 state.tacDependees -= method if (eps.isRefinable) - state.tacDependees += method -> ((newEP, pcs)) + state.tacDependees += method → ((newEP, pcs)) methodUpdatesField(method, newEP.ub.tac.get, pcs) case Callees.key ⇒ handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) @@ -386,7 +376,8 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext isNonDeterministic(newEP) case FieldMutability.key ⇒ val newEP = eps.asInstanceOf[EOptionP[Field, FieldMutability]] - state.fieldMutabilityDependees = state.fieldMutabilityDependees.filter(_.e ne newEP.e) + state.fieldMutabilityDependees = + state.fieldMutabilityDependees.filter(_.e ne newEP.e) !isFinalField(newEP) } @@ -624,10 +615,10 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext case finalEP: FinalEP[Method, TACAI] ⇒ finalEP.ub.tac case eps: InterimEP[Method, TACAI] ⇒ - state.tacDependees += method -> ((eps, pcs)) + state.tacDependees += method → ((eps, pcs)) eps.ub.tac case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) + state.tacDependees += method → ((epk, pcs)) None } } @@ -791,7 +782,8 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext case _ ⇒ // Unknown field false } - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + 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 @@ -1129,8 +1121,7 @@ object LazyL2FieldMutabilityAnalysis ): FPCFAnalysis = { val analysis = new L2FieldMutabilityAnalysis(p) ps.registerLazyPropertyComputation( - FieldMutability.key, - analysis.determineFieldMutability + FieldMutability.key, analysis.determineFieldMutability ) analysis } From 5fdd75894ffea0a85fbd1b76d61f995b66bfa583 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 28 Jul 2020 16:46:10 +0200 Subject: [PATCH 206/327] revert changes --- .../analyses/ImmutabilityAnalysisDemo.scala | 66 ++++++++----------- 1 file changed, 27 insertions(+), 39 deletions(-) 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 067d5b6ed5..ec0f165a99 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 @@ -61,12 +61,10 @@ object ImmutabilityAnalysisDemo extends ProjectAnalysisApplication { r = time[() ⇒ String](10, 50, 15, analyze(project, parallelismLevel))(handleResults) println( s"Results with $parallelismLevel threads:\n"+ - performanceData.values - .map(v ⇒ v.map(_.toSeconds.toString(false))) - .map( - v ⇒ List("setup\t", "analysis\t").zip(v).map(e ⇒ e._1 + e._2).mkString("", "\n", "\n") - ) - .mkString("\n") + performanceData.values. + map(v ⇒ v.map(_.toSeconds.toString(false))). + map(v ⇒ List("setup\t", "analysis\t").zip(v).map(e ⇒ e._1 + e._2).mkString("", "\n", "\n")). + mkString("\n") ) gc() @@ -84,32 +82,24 @@ object ImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val propertyStore = project.get(PropertyStoreKey) time { - propertyStore.setupPhase( - Set[PropertyKind]( - FieldMutability.key, - ClassImmutability.key, - TypeImmutability.key - ) - ) + propertyStore.setupPhase(Set[PropertyKind]( + FieldMutability.key, ClassImmutability.key, TypeImmutability.key + )) LazyL0FieldMutabilityAnalysis.register(project, propertyStore, null) EagerClassImmutabilityAnalysis.start(project, propertyStore, null) EagerTypeImmutabilityAnalysis.start(project, propertyStore, null) propertyStore.waitOnPhaseCompletion() - } { r ⇒ - analysisTime = r - } + } { r ⇒ analysisTime = r } result += s"\t- analysis time: ${analysisTime.toSeconds}\n" () ⇒ { val immutableClasses = - propertyStore - .entities(ClassImmutability.key) - .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) - .toBuffer - .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) - .map { kv ⇒ + propertyStore.entities(ClassImmutability.key). + filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration).toBuffer. + groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub). + map { kv ⇒ ( kv._1, kv._2.toList.sortWith { (a, b) ⇒ @@ -121,29 +111,27 @@ object ImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } val immutableClassesPerCategory = - immutableClasses.map(kv ⇒ "\t\t"+kv._1+": "+kv._2.size).toBuffer.sorted.mkString("\n") + immutableClasses. + map(kv ⇒ "\t\t"+kv._1+": "+kv._2.size). + toBuffer.sorted. + mkString("\n") val immutableTypes = - propertyStore - .entities(TypeImmutability.key) - .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) - .toBuffer - .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) - .map(kv ⇒ (kv._1, kv._2.size)) + propertyStore.entities(TypeImmutability.key). + filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration).toBuffer. + groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub). + map(kv ⇒ (kv._1, kv._2.size)) val immutableTypesPerCategory = immutableTypes.map(kv ⇒ "\t\t"+kv._1+": "+kv._2).toBuffer.sorted.mkString("\n") val immutableClassesInfo = - immutableClasses.values.flatten - .filter { ep ⇒ - !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration - } - .map { eps ⇒ - eps.e.asInstanceOf[ClassFile].thisType.toJava+ - " => "+eps.ub+ - " => "+propertyStore(eps.e, TypeImmutability.key).ub - } - .mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") + immutableClasses.values.flatten.filter { ep ⇒ + !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration + }.map { eps ⇒ + eps.e.asInstanceOf[ClassFile].thisType.toJava+ + " => "+eps.ub+ + " => "+propertyStore(eps.e, TypeImmutability.key).ub + }.mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") "\t- details:\n"+ immutableClassesInfo+ From 78c462d13309d0d1bb4b02bb95a095dc5de51857 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 28 Jul 2020 16:50:42 +0200 Subject: [PATCH 207/327] unnecessary --- .../ImmutabilityAnalysisDemo_new.scala | 195 ------------------ 1 file changed, 195 deletions(-) delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala deleted file mode 100644 index 2141fd5af5..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo_new.scala +++ /dev/null @@ -1,195 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.analyses - -import java.net.URL - -import org.opalj.br.ClassFile -import org.opalj.br.analyses.BasicReport -import org.opalj.br.analyses.Project -import org.opalj.br.analyses.ProjectAnalysisApplication -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.LazyUnsoundPrematurelyReadFieldsAnalysis -import org.opalj.br.fpcf.properties.ClassImmutability -import org.opalj.br.fpcf.properties.ClassImmutability_new -import org.opalj.br.fpcf.properties.FieldImmutability -import org.opalj.br.fpcf.properties.FieldMutability -import org.opalj.br.fpcf.properties.FieldPrematurelyRead -import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.properties.ReferenceImmutability -import org.opalj.br.fpcf.properties.TypeImmutability -import org.opalj.br.fpcf.properties.TypeImmutability_new -import org.opalj.fpcf.EPS -import org.opalj.fpcf.Entity -import org.opalj.fpcf.Property -import org.opalj.fpcf.PropertyKind -import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis -import org.opalj.util.Nanoseconds -import org.opalj.util.PerformanceEvaluation.time -import org.opalj.util.gc - -/** - * Determines the immutability of the classes of a project. - * - * @author Michael Eichberg - * @author Tobias Peter Roth - * - */ -object ImmutabilityAnalysisDemo_new extends ProjectAnalysisApplication { - - override def title: String = "determines the immutability of objects and types" - - override def description: String = "determines the immutability of objects and types" - - private[this] var setupTime = Nanoseconds.None - private[this] var analysisTime = Nanoseconds.None - private[this] var performanceData: Map[Nanoseconds, List[Nanoseconds]] = Map.empty - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - var r: () ⇒ String = null - - def handleResults(t: Nanoseconds, ts: Seq[Nanoseconds]) = { - performanceData += ((t, List(setupTime, analysisTime))) - performanceData = performanceData.filter((t_ts) ⇒ ts.contains(t_ts._1)) - } - - List(1).foreach { parallelismLevel ⇒ - performanceData = Map.empty - gc() - - println(s"\nRunning analysis with $parallelismLevel thread(s):") - r = time[() ⇒ String](10, 50, 15, analyze(project, parallelismLevel))(handleResults) - println( - s"Results with $parallelismLevel threads:\n"+ - performanceData.values - .map(v ⇒ v.map(_.toSeconds.toString(false))) - .map( - v ⇒ List("setup\t", "analysis\t").zip(v).map(e ⇒ e._1 + e._2).mkString("", "\n", "\n") - ) - .mkString("\n") - ) - - gc() - } - BasicReport(r()) - } - - def analyze(theProject: Project[URL], parallelismLevel: Int): () ⇒ String = { - var result = "Results:\n" - val project = Project.recreate(theProject) // We need an empty project(!) - project.get(RTACallGraphKey) - - // The following measurements (t) are done such that the results are comparable with the - // reactive async approach developed by P. Haller and Simon Gries. - PropertyStoreKey.parallelismLevel = parallelismLevel - //PropertyStoreKey - val propertyStore = project.get(PropertyStoreKey) - - time { - propertyStore.setupPhase( - Set[PropertyKind]( - FieldMutability.key, - ClassImmutability.key, - TypeImmutability.key, - FieldPrematurelyRead.key, - Purity.key, - FieldImmutability.key, - ReferenceImmutability.key, - ClassImmutability_new.key, - TypeImmutability_new.key - ) - ) - //LazyL0FieldMutabilityAnalysis.register(project, propertyStore, null) // (project, propertyStore) - //EagerClassImmutabilityAnalysis.start(project, propertyStore, project.allClassFiles) - //EagerTypeImmutabilityAnalysis.start(project, propertyStore, null) //project.allClassFiles) - - LazyClassImmutabilityAnalysis.register(project, propertyStore, project.allClassFiles) - LazyL2FieldMutabilityAnalysis.register(project, propertyStore, null) - LazyTypeImmutabilityAnalysis.register(project, propertyStore, null) - LazyUnsoundPrematurelyReadFieldsAnalysis.register(project, propertyStore, null) - LazyL2PurityAnalysis.register(project, propertyStore, null) - - EagerL0ReferenceImmutabilityAnalysis.start(project, propertyStore, null) - EagerL0FieldImmutabilityAnalysis.start(project, propertyStore, null) - EagerLxClassImmutabilityAnalysis_new.start(project, propertyStore, project.allClassFiles) - EagerLxTypeImmutabilityAnalysis_new.start(project, propertyStore, null) - //propertyStore.suppressError = true - //propertyStore.waitOnPhaseCompletion() - } { r ⇒ - analysisTime = r - } - - result += s"\t- analysis time: ${analysisTime.toSeconds}\n" - - () ⇒ { - val immutableReferences = - propertyStore.entities(ReferenceImmutability.key) - val immutableClasses = - propertyStore - .entities(ClassImmutability_new.key) - .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) - .toBuffer - .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) - .map { kv ⇒ - ( - kv._1, - kv._2.toList.sortWith { (a, b) ⇒ - val cfA = a.e.asInstanceOf[ClassFile] - val cfB = b.e.asInstanceOf[ClassFile] - cfA.thisType.toJava < cfB.thisType.toJava - } - ) - } - - val immutableClassesPerCategory = - immutableClasses - .map(kv ⇒ "\t\t"+kv._1+": "+kv._2.size) - .toBuffer - .sorted - .mkString("\n") - - val immutableTypes = - propertyStore - .entities(TypeImmutability_new.key) - .filter(eps ⇒ !eps.e.asInstanceOf[ClassFile].isInterfaceDeclaration) - .toBuffer - .groupBy((eps: EPS[_ <: Entity, _ <: Property]) ⇒ eps.ub) - .map(kv ⇒ (kv._1, kv._2.size)) - val immutableTypesPerCategory = - immutableTypes.map(kv ⇒ "\t\t"+kv._1+": "+kv._2).toBuffer.sorted.mkString("\n") - - val immutableClassesInfo = - immutableClasses.values.flatten - .filter { ep ⇒ - !ep.e.asInstanceOf[ClassFile].isInterfaceDeclaration - } - .map { eps ⇒ - eps.e.asInstanceOf[ClassFile].thisType.toJava+ - " => "+eps.ub+ - " => "+propertyStore(eps.e, TypeImmutability_new.key).ub - } - .mkString("\t\timmutability:\n\t\t", "\n\t\t", "\n") - - "immutable References: "+immutableReferences.size+"\n" - "\t- details:\n"+ - immutableClassesInfo+ - "\nSummary (w.r.t classes):\n"+ - "\tObject Immutability:\n"+ - immutableClassesPerCategory+"\n"+ - "\tType Immutability:\n"+ - immutableTypesPerCategory+"\n"+ - "\n"+propertyStore.toString(false) - } - } -} From d6aadaefe9b3841a5bf1254d87aa0601a9c6a220 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 28 Jul 2020 16:59:12 +0200 Subject: [PATCH 208/327] removing the unnecessary demos. The ones using the purity analysis with the new immutability analysis are now the standard ones we use here. --- .../ClassImmutabilityAnalysisDemo.scala | 121 ++++++----- ...AnalysisDemo_performanceMeasurements.scala | 110 ---------- ...mutabilityAnalysisDemo_withNewPurity.scala | 164 --------------- .../FieldImmutabilityAnalysisDemo.scala | 120 +++++------ ...AnalysisDemo_performanceMeasurements.scala | 114 ---------- ...mutabilityAnalysisDemo_withNewPurity.scala | 154 -------------- .../ReferenceImmutabilityAnalysisDemo.scala | 194 ++++++++++-------- ...AnalysisDemo_performanceMeasurements.scala | 110 ---------- ...mutabilityAnalysisDemo_withNewPurity.scala | 155 -------------- .../TypeImmutabilityAnalysisDemo.scala | 99 +++++---- ...AnalysisDemo_performanceMeasurements.scala | 111 ---------- ...mutabilityAnalysisDemo_withNewPurity.scala | 153 -------------- 12 files changed, 274 insertions(+), 1331 deletions(-) delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala 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 index 59682429b8..33aba994bb 100644 --- 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 @@ -8,22 +8,13 @@ import java.net.URL import java.util.Calendar import org.opalj.br.ObjectType -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.util.PerformanceEvaluation.memory - -//import org.opalj.br.ObjectType +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new 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.LazyClassImmutabilityAnalysis 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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.DeepImmutableClass import org.opalj.br.fpcf.properties.DependentImmutableClass @@ -34,7 +25,10 @@ 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.purity.LazyL2PurityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -59,52 +53,49 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { - var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - memory { - val analysesManager = project.get(FPCFAnalysesManagerKey) + val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + analysesManager.project.get(RTACallGraphKey) - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - } { mu ⇒ - memoryConsumption = mu + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxTypeImmutabilityAnalysis_new, + EagerLxClassImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds } + val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet val sb = new StringBuilder sb.append("Mutable Class: \n") val mutableClasses = propertyStore .finalEntities(MutableClass) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) .toList sb.append( mutableClasses .map(x ⇒ x.toString+" |Mutable Class\n") ) sb.append("\nShallow Immutable Class:\n") - val shallowImmutableClasses = propertyStore.finalEntities(ShallowImmutableClass).toList + val shallowImmutableClasses = propertyStore + .finalEntities(ShallowImmutableClass) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) + .toList sb.append( shallowImmutableClasses .map(x ⇒ x.toString+" |Shallow Immutable Class\n") @@ -112,6 +103,7 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { sb.append("\nDependent Immutable Class: \n") val dependentImmutableClasses = propertyStore .finalEntities(DependentImmutableClass) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) .toList sb.append( dependentImmutableClasses @@ -119,40 +111,44 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { ) sb.append("\nDeep Immutable Class Classes:\n") - val allInterfaces = project.allClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet - val deepImmutableClasses = propertyStore + val allInterfaces = + project.allProjectClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet + + val deepImmutables = propertyStore .finalEntities(DeepImmutableClass) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) .toList - .filter( - x ⇒ !x.isInstanceOf[ObjectType] || !allInterfaces.contains(x.asInstanceOf[ObjectType]) - ) + val deepImmutableClassesInterfaces = deepImmutables + .filter(x ⇒ x.isInstanceOf[ObjectType] && allInterfaces.contains(x.asInstanceOf[ObjectType])) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) + val deepImmutableClasses = + deepImmutables + .filter(!deepImmutableClassesInterfaces.toSet.contains(_)) + .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) sb.append( deepImmutableClasses.toList .map(x ⇒ x.toString+" |Deep Immutable Class\n") ) sb.append("\nDeep Immutable Class Classes: Interface\n") - val deepImmutableClassesInterfaces = propertyStore - .finalEntities(DeepImmutableClass) - .toList - .filter(x ⇒ x.isInstanceOf[ObjectType] && allInterfaces.contains(x.asInstanceOf[ObjectType])) + sb.append( deepImmutableClassesInterfaces .map(x ⇒ x.toString+" |Deep Immutable Class Interface\n") ) sb.append(s""" - | 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} - | - | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes - |"""".stripMargin) + | 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} + | + | took : $analysisTime seconds + |"""".stripMargin) val calendar = Calendar.getInstance() val file = new File( - s"C:/MA/results/classImm_${calendar.get(Calendar.YEAR)}_"+ + s"C:/MA/results/classImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ s"${calendar.get(Calendar.MILLISECOND)}.txt" @@ -162,8 +158,7 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { bw.close() s""" - | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes - |""".stripMargin + | took : $analysisTime seconds + |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala deleted file mode 100644 index d81c37d01d..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_performanceMeasurements.scala +++ /dev/null @@ -1,110 +0,0 @@ -/* 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.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.fpcf.PropertyStore -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.EagerLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new -import org.opalj.util.PerformanceEvaluation.time -import org.opalj.util.Seconds; - -/** - * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result - * - * @author Tobias Peter Roth - */ -object ClassImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { - - override def title: String = "run EagerLxClassImmutabilityAnalysis_new" - - override def description: String = "run EagerLxClassImmutabilityAnalysis_new" - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(theProject: Project[URL]): String = { - var times: List[Seconds] = Nil: List[Seconds] - for (i ← 0 to 10) { - val project = theProject.recreate() - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - times = analysisTime :: times - } - - val sortedList = times.sortWith(_.timeSpan < _.timeSpan) - val median = sortedList(5) - - val output = - s""" - | - |${sortedList.mkString("\n")} - | - |Median: $median - |lowest: ${sortedList(0)} - |highest: ${sortedList(sortedList.size - 1)} - |""".stripMargin - - val calendar = Calendar.getInstance() - val file = new File( - s"C:/MA/results_time/classImm_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(output) - bw.close() - - s""" - | $output - | took: $median seconds as median - |""".stripMargin - } -} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala deleted file mode 100644 index 551680d778..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ClassImmutabilityAnalysisDemo_withNewPurity.scala +++ /dev/null @@ -1,164 +0,0 @@ -/* 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.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new -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.fpcf.PropertyStore -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.EagerLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.util.PerformanceEvaluation.time -import org.opalj.util.Seconds - -/** - * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result - * - * @author Tobias Peter Roth - */ -object ClassImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplication { - - override def title: String = "run EagerLxClassImmutabilityAnalysis_new" - - override def description: String = "run EagerLxClassImmutabilityAnalysis_new" - - 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) - - analysesManager.project.get(RTACallGraphKey) - - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet - - val sb = new StringBuilder - sb.append("Mutable Class: \n") - val mutableClasses = propertyStore - .finalEntities(MutableClass) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - .toList - sb.append( - mutableClasses - .map(x ⇒ x.toString+" |Mutable Class\n") - ) - sb.append("\nShallow Immutable Class:\n") - val shallowImmutableClasses = propertyStore - .finalEntities(ShallowImmutableClass) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - .toList - sb.append( - shallowImmutableClasses - .map(x ⇒ x.toString+" |Shallow Immutable Class\n") - ) - sb.append("\nDependent Immutable Class: \n") - val dependentImmutableClasses = propertyStore - .finalEntities(DependentImmutableClass) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - .toList - sb.append( - dependentImmutableClasses - .map(x ⇒ x.toString+" |Dependent Immutable Class\n") - ) - - sb.append("\nDeep Immutable Class Classes:\n") - val allInterfaces = - project.allProjectClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet - - val deepImmutables = propertyStore - .finalEntities(DeepImmutableClass) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - .toList - val deepImmutableClassesInterfaces = deepImmutables - .filter(x ⇒ x.isInstanceOf[ObjectType] && allInterfaces.contains(x.asInstanceOf[ObjectType])) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - val deepImmutableClasses = - deepImmutables - .filter(!deepImmutableClassesInterfaces.toSet.contains(_)) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - sb.append( - deepImmutableClasses.toList - .map(x ⇒ x.toString+" |Deep Immutable Class\n") - ) - sb.append("\nDeep Immutable Class Classes: Interface\n") - - sb.append( - deepImmutableClassesInterfaces - .map(x ⇒ x.toString+" |Deep Immutable Class Interface\n") - ) - sb.append(s""" - | 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} - | - | took : $analysisTime seconds - |"""".stripMargin) - - val calendar = Calendar.getInstance() - val file = new File( - s"C:/MA/results/classImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() - - s""" - | took : $analysisTime seconds - |""".stripMargin - } -} 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 index 3c988d7107..ab432232f9 100644 --- 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 @@ -1,16 +1,19 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.analyses +import java.io.BufferedWriter +import java.io.FileWriter +import java.io.File import java.net.URL +import java.util.Calendar +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.LazyClassImmutabilityAnalysis 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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.DeepImmutableField import org.opalj.br.fpcf.properties.DependentImmutableField @@ -19,15 +22,13 @@ import org.opalj.br.fpcf.properties.ShallowImmutableField import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis -import org.opalj.util.PerformanceEvaluation.memory +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds @@ -53,47 +54,40 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { - var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - memory { - val analysesManager = project.get(FPCFAnalysesManagerKey) - - analysesManager.project.get(RTACallGraphKey) - - time { - - propertyStore = analysesManager - .runAll( - LazyLxClassImmutabilityAnalysis_new, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - } { mu ⇒ - memoryConsumption = mu + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + time { + propertyStore = analysesManager + .runAll( + LazyL0ReferenceImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + EagerL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds } - val sb: StringBuilder = new StringBuilder sb.append("Mutable Fields: \n") + val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields } + //.filter(f ⇒ (!f.isTransient && !f.isSyn)) // for ReImComparison + .toSet val mutableFields = propertyStore .finalEntities(MutableField) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) .toList + sb.append( mutableFields .map(x ⇒ x.toString+" |Mutable Field\n") @@ -102,6 +96,7 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { sb.append("\nShallow Immutable Fields: \n") val shallowImmutableFields = propertyStore .finalEntities(ShallowImmutableField) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) .toList sb.append( shallowImmutableFields @@ -111,6 +106,7 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { sb.append("\nDependet Immutable Fields: \n") val dependentImmutableFields = propertyStore .finalEntities(DependentImmutableField) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) .toList sb.append( dependentImmutableFields @@ -120,6 +116,7 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { sb.append("Deep Immutable Fields: ") val deepImmutableFields = propertyStore .finalEntities(DeepImmutableField) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) .toList sb.append( deepImmutableFields @@ -129,34 +126,29 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { sb.append("\n\n") sb.append( s""" mutable fields: ${mutableFields.size} - | shallow immutable fields: ${shallowImmutableFields.size} - | dependent immutable fields: ${dependentImmutableFields.size} - | deep immutable fields: ${deepImmutableFields.size} - | - | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes - |""".stripMargin + | shallow immutable fields: ${shallowImmutableFields.size} + | dependent immutable fields: ${dependentImmutableFields.size} + | deep immutable fields: ${deepImmutableFields.size} + | + | count: ${mutableFields.size + shallowImmutableFields.size + dependentImmutableFields.size + deepImmutableFields.size} + | + | took : $analysisTime seconds + |""".stripMargin + ) + val calendar = Calendar.getInstance() + val file = new File( + s"C:/MA/results/fieldImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" ) - // val calendar = Calendar.getInstance() - /** - * val file = new File( - * s"C:/MA/results/fieldImm_${calendar.get(Calendar.YEAR)}_" + - * s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_" + - * s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_" + - * s"${calendar.get(Calendar.MILLISECOND)}.txt" - * ) * - */ - // val bw = new BufferedWriter(new FileWriter(file)) - //bw.write(sb.toString()) - //bw.close() + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() - /** - * s""" - * | took : $analysisTime seconds - * | needs : ${memoryConsumption / 1024 / 1024} MBytes - * |""".stripMargin * - */ - sb.toString() + s""" + | took : $analysisTime seconds + |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala deleted file mode 100644 index 0d32cb996d..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_performanceMeasurements.scala +++ /dev/null @@ -1,114 +0,0 @@ -/* 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.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.fpcf.PropertyStore -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.EagerL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new -import org.opalj.util.PerformanceEvaluation.time -import org.opalj.util.Seconds - -/** - * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. - * - * @author Tobias Peter Roth - */ -object FieldImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { - - override def title: String = "runs the EagerL0FieldImmutabilityAnalysis" - - override def description: String = - "runs the EagerL0FieldImmutabilityAnalysis" - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(theProject: Project[URL]): String = { - var tmpProject = theProject - var times: List[Seconds] = Nil: List[Seconds] - println("before") - for (i ← 0 to 10) { - val project = tmpProject.recreate() - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyLxClassImmutabilityAnalysis_new, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - times = analysisTime :: times - tmpProject = project - } - - val sortedList = times.sortWith(_.timeSpan < _.timeSpan) - val median = sortedList(5) - - val output = - s""" - | - |${sortedList.mkString("\n")} - | - |Median: $median - |lowest: ${sortedList(0)} - |highest: ${sortedList(sortedList.size - 1)} - |""".stripMargin - - val calendar = Calendar.getInstance() - val file = new File( - s"C:/MA/results_time/fieldImm_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(output) - bw.close() - - s""" - | $output - | took: $median seconds as median - |""".stripMargin - } -} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala deleted file mode 100644 index 751a28b7af..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldImmutabilityAnalysisDemo_withNewPurity.scala +++ /dev/null @@ -1,154 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.analyses - -import java.io.BufferedWriter -import java.io.FileWriter -import java.io.File -import java.net.URL -import java.util.Calendar - -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.fpcf.PropertyStore -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.EagerL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new -import org.opalj.util.PerformanceEvaluation.time -import org.opalj.util.Seconds - -/** - * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. - * - * @author Tobias Peter Roth - */ -object FieldImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplication { - - override def title: String = "runs the EagerL0FieldImmutabilityAnalysis" - - override def description: String = - "runs the EagerL0FieldImmutabilityAnalysis" - - 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) - analysesManager.project.get(RTACallGraphKey) - time { - propertyStore = analysesManager - .runAll( - LazyL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - EagerL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - val sb: StringBuilder = new StringBuilder - sb.append("Mutable Fields: \n") - val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields } - //.filter(f ⇒ (!f.isTransient && !f.isSyn)) // for ReImComparison - .toSet - val mutableFields = propertyStore - .finalEntities(MutableField) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - - sb.append( - mutableFields - .map(x ⇒ x.toString+" |Mutable Field\n") - .toString() - ) - sb.append("\nShallow Immutable Fields: \n") - val shallowImmutableFields = propertyStore - .finalEntities(ShallowImmutableField) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - sb.append( - shallowImmutableFields - .map(x ⇒ x.toString+" |Shallow Immutable Field\n") - .toString() - ) - sb.append("\nDependet Immutable Fields: \n") - val dependentImmutableFields = propertyStore - .finalEntities(DependentImmutableField) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - sb.append( - dependentImmutableFields - .map(x ⇒ x.toString+" |Dependent Immutable Field\n") - .toString() - ) - sb.append("Deep Immutable Fields: ") - val deepImmutableFields = propertyStore - .finalEntities(DeepImmutableField) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - sb.append( - deepImmutableFields - .map(x ⇒ x.toString+" |Deep Immutable Field\n") - .toString - ) - sb.append("\n\n") - sb.append( - s""" mutable fields: ${mutableFields.size} - | shallow immutable fields: ${shallowImmutableFields.size} - | dependent immutable fields: ${dependentImmutableFields.size} - | deep immutable fields: ${deepImmutableFields.size} - | - | count: ${mutableFields.size + shallowImmutableFields.size + dependentImmutableFields.size + deepImmutableFields.size} - | - | took : $analysisTime seconds - |""".stripMargin - ) - val calendar = Calendar.getInstance() - val file = new File( - s"C:/MA/results/fieldImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) - - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() - - s""" - | took : $analysisTime seconds - |""".stripMargin - } -} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index 96d24f9195..bda16324d2 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -12,22 +12,26 @@ import org.opalj.br.fpcf.properties.MutableReference import org.opalj.fpcf.PropertyStore 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.util.PerformanceEvaluation.time import org.opalj.util.Seconds -import org.opalj.util.PerformanceEvaluation.memory -import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis +import java.io._ +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.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference -import org.opalj.fpcf.SomeEPS +import org.opalj.fpcf import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. @@ -51,102 +55,126 @@ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { - var memoryConsumption: Long = 0 + var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - memory { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyFieldLocalityAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion() - - } { t ⇒ - analysisTime = t.toSeconds - } - } { mu ⇒ - memoryConsumption = mu + + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + time { + propertyStore = analysesManager + .runAll( + EagerL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyInterProceduralEscapeAnalysis, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + + propertyStore.waitOnPhaseCompletion() + + } { t ⇒ + analysisTime = t.toSeconds } var sb: StringBuilder = new StringBuilder() + val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet + //.filter(f ⇒ (!f.isTransient && !f.isSyn)) // for ReImComparison + .toSet sb = sb.append("Mutable References: \n") - val mutableReferences = propertyStore.finalEntities(MutableReference).toList + val mutableReferences = propertyStore + .finalEntities(MutableReference) + .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) + .toList + .sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) sb = sb.append( - mutableReferences.mkString(", \n") + mutableReferences.map(x ⇒ x.toString+"\n").toString() ) - sb = sb.append("\n Lazy Initialized Reference: \n") - val lazyInitializedReferences = propertyStore - .finalEntities(LazyInitializedThreadSafeReference).toList ++ (propertyStore.finalEntities(LazyInitializedNotThreadSafeButDeterministicReference)) + val lazyInitializedReferencesThreadSafe = propertyStore + .finalEntities(LazyInitializedThreadSafeReference) .toList - sb = sb.append( - lazyInitializedReferences.mkString(", \n") - ) + .sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) + + val lazyInitializedReferencesNotThreadSafeButDeterministic = propertyStore + .finalEntities(LazyInitializedNotThreadSafeButDeterministicReference) + .toList + .sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) - val immutableReferencesTrue = propertyStore.entities({ eps: SomeEPS ⇒ - eps.ub match { - case ImmutableReference(true) ⇒ true - case _ ⇒ false + val notThreadSafeOrNotDeterministicLazyInitialization = propertyStore + .finalEntities(LazyInitializedNotThreadSafeOrNotDeterministicReference) + .toList + .sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) + + sb.append( + s""" + | lazy initialized thread safe references: ${ + lazyInitializedReferencesThreadSafe + .mkString(",\n") + } + | + | lazy initialized not thread safe but deterministic references: ${ + lazyInitializedReferencesNotThreadSafeButDeterministic + .mkString(", \n") } - }) - val immutableReferencesFalse = propertyStore.entities({ eps: SomeEPS ⇒ - eps.ub match { - case ImmutableReference(false) ⇒ true - case _ ⇒ false + | + | lazy initialized not thread safe or not deterministic references: ${ + notThreadSafeOrNotDeterministicLazyInitialization + .mkString(", \n") } - }) + | + |""".stripMargin + ) + val immutableReferences = propertyStore + .entities( + eps ⇒ //allfieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field]) && + eps.isFinal && (eps.asFinal.p match { + case ImmutableReference(_) ⇒ true + case _ ⇒ false + }) + ) + .toList + .sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) sb = sb.append( - s""" - | imm ref true: - |${immutableReferencesTrue.mkString(", \n")} - | - | - | imm ref false: - | ${immutableReferencesFalse.mkString(", \n")} - |""".stripMargin + immutableReferences.map(x ⇒ x.toString+"\n").mkString(", ") ) sb.append( s""" - | mutable References: ${mutableReferences.size} - | lazy initialized References: ${lazyInitializedReferences.size} - | immutable References: ${} - | - | took : $analysisTime seconds - | needed: ${memoryConsumption / 1024 / 1024} MBytes - | - |""".stripMargin + | mutable References: ${mutableReferences.size} + | lazy initialized references not thread safe or deterministic: ${notThreadSafeOrNotDeterministicLazyInitialization.size} + | lazy initialized references not thread safe but deterministic: ${lazyInitializedReferencesNotThreadSafeButDeterministic.size} + | lazy initialized thread safe references: ${lazyInitializedReferencesThreadSafe.size} + | immutable References: ${immutableReferences.size} + | + | took : $analysisTime seconds + | + |""".stripMargin + ) + + val calendar = Calendar.getInstance() + val file = new File( + s"/home/tobias/results/immutability/reference/refImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}.txt" ) + if (!file.exists()) + file.createNewFile() + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(sb.toString()) + bw.close() + s""" + | ps: ${propertyStore.getClass} + | took : $analysisTime seconds + |""".stripMargin - /* val calendar = Calendar.getInstance() - val file = new File( - s"C:/MA/results/refImm_${calendar.get(Calendar.YEAR)}_" + - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_" + - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_" + - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() **/ - //s""" - // | took : $analysisTime seconds - // | needs : ${memoryConsumption / 1024 / 1024} MBytes - // |""".stripMargin - sb.toString() } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala deleted file mode 100644 index b92a36f5bb..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_performanceMeasurements.scala +++ /dev/null @@ -1,110 +0,0 @@ -/* 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.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.fpcf.PropertyStore -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.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new -import org.opalj.util.PerformanceEvaluation.time -import org.opalj.util.Seconds - -/** - * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. - * - * @author Tobias Peter Roth - */ -object ReferenceImmutabilityAnalysisDemo_performanceMeasurements - extends ProjectAnalysisApplication { - - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(theProject: Project[URL]): String = { - var times: List[Seconds] = Nil: List[Seconds] - for (i ← 0 to 10) { - val project = Project.recreate(theProject) - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyFieldLocalityAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - LazyLxClassImmutabilityAnalysis_new - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - times = analysisTime :: times - } - val sortedList = times.sortWith(_.timeSpan < _.timeSpan) - val median = sortedList(5) - - val output = - s""" - | - |${sortedList.mkString("\n")} - | - |Median: $median - |lowest: ${sortedList(0)} - |highest: ${sortedList(sortedList.size - 1)} - |""".stripMargin - - val calendar = Calendar.getInstance() - val file = new File( - s"C:/MA/results_time/refImm_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(output) - bw.close() - - s""" - |$output - |took: $median seconds as median - |""".stripMargin - } - -} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala deleted file mode 100644 index 9ba29c8454..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo_withNewPurity.scala +++ /dev/null @@ -1,155 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.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.br.fpcf.properties.MutableReference -import org.opalj.fpcf.PropertyStore -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.io._ -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.br.fpcf.properties.ImmutableReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference -import org.opalj.fpcf -import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new - -/** - * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. - * - * @author Tobias Peter Roth - */ -object ReferenceImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplication { - - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" - - 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) - analysesManager.project.get(RTACallGraphKey) - - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyInterProceduralEscapeAnalysis, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyLxTypeImmutabilityAnalysis_new - ) - ._1 - propertyStore.waitOnPhaseCompletion() - - } { t ⇒ - analysisTime = t.toSeconds - } - var sb: StringBuilder = new StringBuilder() - val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields } - //.filter(f ⇒ (!f.isTransient && !f.isSyn)) // for ReImComparison - .toSet - sb = sb.append("Mutable References: \n") - val mutableReferences = propertyStore - .finalEntities(MutableReference) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList.sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) - sb = sb.append( - mutableReferences.map(x ⇒ x.toString+"\n").toString() - ) - - val lazyInitializedReferencesThreadSafe = propertyStore - .finalEntities(LazyInitializedThreadSafeReference).toList.sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) - - val lazyInitializedReferencesNotThreadSafeButDeterministic = propertyStore. - finalEntities(LazyInitializedNotThreadSafeButDeterministicReference).toList.sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) - - val notThreadSafeOrNotDeterministicLazyInitialization = propertyStore. - finalEntities(LazyInitializedNotThreadSafeOrNotDeterministicReference).toList.sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) - - sb.append( - s""" - | lazy initialized thread safe references: ${lazyInitializedReferencesThreadSafe.mkString(",\n")} - | - | lazy initialized not thread safe but deterministic references: ${lazyInitializedReferencesNotThreadSafeButDeterministic.mkString(", \n")} - | - | lazy initialized not thread safe or not deterministic references: ${notThreadSafeOrNotDeterministicLazyInitialization.mkString(", \n")} - | - |""".stripMargin - ) - - val immutableReferences = propertyStore.entities(eps ⇒ //allfieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field]) && - eps.isFinal && (eps.asFinal.p match { - case ImmutableReference(_) ⇒ true - case _ ⇒ false - })).toList.sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) - sb = sb.append( - immutableReferences.map(x ⇒ x.toString+"\n").mkString(", ") - ) - - sb.append( - s""" - | mutable References: ${mutableReferences.size} - | lazy initialized references not thread safe or deterministic: ${notThreadSafeOrNotDeterministicLazyInitialization.size} - | lazy initialized references not thread safe but deterministic: ${lazyInitializedReferencesNotThreadSafeButDeterministic.size} - | lazy initialized thread safe references: ${lazyInitializedReferencesThreadSafe.size} - | immutable References: ${immutableReferences.size} - | - | took : $analysisTime seconds - | - |""".stripMargin - ) - - val calendar = Calendar.getInstance() - val file = new File( - s"C:/MA/results/refImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() - s""" - | took : $analysisTime seconds - |""".stripMargin - - } -} 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 index 1d08313e77..7e9cb1d5d5 100644 --- 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 @@ -12,10 +12,8 @@ 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.LazyClassImmutabilityAnalysis 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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.DeepImmutableType import org.opalj.br.fpcf.properties.DependentImmutableType @@ -24,17 +22,15 @@ 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.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.util.PerformanceEvaluation.memory +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -57,46 +53,42 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { - var memoryConsumption: Long = 0 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - memory { - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - EagerLxTypeImmutabilityAnalysis_new, - LazyLxClassImmutabilityAnalysis_new, - LazyFieldLocalityAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyL2FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - } { mu ⇒ - memoryConsumption = mu + time { + propertyStore = analysesManager + .runAll( + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new, + LazyStaticDataUsageAnalysis, + LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis + ) + ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ + analysisTime = t.toSeconds } val sb: StringBuilder = new StringBuilder + val allProjectClassFilesIterator = project.allProjectClassFiles + val types = + allProjectClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType).toSet sb.append("\nMutableTypes: \n") - val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet val mutableTypes = propertyStore .finalEntities(MutableType_new) + .filter({ x ⇒ + types.contains(x.asInstanceOf[ObjectType]) + }) .toList - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) sb.append( mutableTypes .map(x ⇒ x.toString+" |Mutable Type\n") @@ -105,39 +97,47 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { sb.append("\nShallow Immutable Types:\n") val shallowImmutableTypes = propertyStore .finalEntities(ShallowImmutableType) + .filter({ x ⇒ + types.contains(x.asInstanceOf[ObjectType]) + }) .toList - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) sb.append(shallowImmutableTypes.map(x ⇒ x.toString+" |Shallow Immutable Type\n")) sb.append("\nDependent Immutable Types: \n") val dependentImmutableTypes = propertyStore .finalEntities(DependentImmutableType) + .filter({ x ⇒ + types.contains(x.asInstanceOf[ObjectType]) + }) .toList - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) sb.append( dependentImmutableTypes.map(x ⇒ x.toString+" |Dependent Immutable Type\n") ) sb.append("\nDeep Immutable Types:\n") - val deepImmutableTypes = propertyStore.finalEntities(DeepImmutableType).toList + val deepImmutableTypes = propertyStore + .finalEntities(DeepImmutableType) + .filter({ x ⇒ + types.contains(x.asInstanceOf[ObjectType]) + }) + .toList sb.append(deepImmutableTypes.map(x ⇒ x.toString+" |Deep Immutable Type\n")) - sb.append(s"\nType immutability analysis took: $analysisTime on average") sb.append("\n\n") sb.append( s""" - | mutable types: ${mutableTypes.size} - | shallow immutable types: ${shallowImmutableTypes.size} - | dependent immutable types: ${dependentImmutableTypes.size} - | deep immutable types: ${deepImmutableTypes.size} - | - | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes - |""".stripMargin + | 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 calendar = Calendar.getInstance() val file = new File( - s"C:/MA/results/typeImm_${calendar.get(Calendar.YEAR)}_"+ + s"C:/MA/results/typeImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ s"${calendar.get(Calendar.MILLISECOND)}.txt" @@ -148,7 +148,6 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { s""" | took : $analysisTime seconds - | needs : ${memoryConsumption / 1024 / 1024} MBytes |""".stripMargin } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala deleted file mode 100644 index e2810a06f7..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_performanceMeasurements.scala +++ /dev/null @@ -1,111 +0,0 @@ -/* 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.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.fpcf.PropertyStore -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.EagerLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new -import org.opalj.util.PerformanceEvaluation.time -import org.opalj.util.Seconds - -/** - * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result - * - * @author Tobias Peter Roth - */ -object TypeImmutabilityAnalysisDemo_performanceMeasurements extends ProjectAnalysisApplication { - - override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" - - override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" - - override def doAnalyze( - project: Project[URL], - parameters: Seq[String], - isInterrupted: () ⇒ Boolean - ): BasicReport = { - val result = analyze(project) - BasicReport(result) - } - - def analyze(theProject: Project[URL]): String = { - - var times: List[Seconds] = Nil: List[Seconds] - for (i ← 0 until 10) { - val project = theProject.recreate() - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - EagerLxTypeImmutabilityAnalysis_new, - LazyLxClassImmutabilityAnalysis_new, - LazyFieldLocalityAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - times = analysisTime :: times - } - - val sortedList = times.sortWith(_.timeSpan < _.timeSpan) - val median = sortedList(5) - - val output = - s""" - | - |${sortedList.mkString("\n")} - | - |Median: $median - |lowest: ${sortedList(0)} - |highest: ${sortedList(sortedList.size - 1)} - |""".stripMargin - - val calendar = Calendar.getInstance() - val file = new File( - s"C:/MA/results_time/typeImm_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(output) - bw.close() - - s""" - | $output - | took: $median seconds as median - |""".stripMargin - } -} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala deleted file mode 100644 index 52446c864d..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TypeImmutabilityAnalysisDemo_withNewPurity.scala +++ /dev/null @@ -1,153 +0,0 @@ -/* 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_new -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.EagerLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new - -/** - * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result - * - * @author Tobias Peter Roth - */ -object TypeImmutabilityAnalysisDemo_withNewPurity extends ProjectAnalysisApplication { - - override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" - - override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" - - 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) - analysesManager.project.get(RTACallGraphKey) - - time { - propertyStore = analysesManager - .runAll( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ._1 - propertyStore.waitOnPhaseCompletion(); - } { t ⇒ - analysisTime = t.toSeconds - } - val sb: StringBuilder = new StringBuilder - val allProjectClassFilesIterator = project.allProjectClassFiles - val types = - allProjectClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType).toSet - sb.append("\nMutableTypes: \n") - val mutableTypes = propertyStore - .finalEntities(MutableType_new) - .filter({ x ⇒ - types.contains(x.asInstanceOf[ObjectType]) - }) - .toList - sb.append( - mutableTypes - .map(x ⇒ x.toString+" |Mutable Type\n") - .toString - ) - sb.append("\nShallow Immutable Types:\n") - val shallowImmutableTypes = propertyStore - .finalEntities(ShallowImmutableType) - .filter({ x ⇒ - types.contains(x.asInstanceOf[ObjectType]) - }) - .toList - sb.append(shallowImmutableTypes.map(x ⇒ x.toString+" |Shallow Immutable Type\n")) - sb.append("\nDependent Immutable Types: \n") - val dependentImmutableTypes = propertyStore - .finalEntities(DependentImmutableType) - .filter({ x ⇒ - types.contains(x.asInstanceOf[ObjectType]) - }) - .toList - sb.append( - dependentImmutableTypes.map(x ⇒ x.toString+" |Dependent Immutable Type\n") - ) - sb.append("\nDeep Immutable Types:\n") - val deepImmutableTypes = propertyStore - .finalEntities(DeepImmutableType) - .filter({ x ⇒ - types.contains(x.asInstanceOf[ObjectType]) - }) - .toList - sb.append(deepImmutableTypes.map(x ⇒ x.toString+" |Deep Immutable Type\n")) - sb.append(s"\nType immutability analysis took: $analysisTime on average") - - sb.append("\n\n") - sb.append( - s""" - | 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 calendar = Calendar.getInstance() - val file = new File( - s"C:/MA/results/typeImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() - - s""" - | took : $analysisTime seconds - |""".stripMargin - } -} From 5d0741af7fd44c826706a4aa1b9fd492c827b4b9 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:07:27 +0200 Subject: [PATCH 209/327] delete the old tests. The ones using the new purity analysis (that uses the new immutability analysis) are the new standard ones. --- .../opalj/fpcf/ClassImmutabilityTests.scala | 53 ++++++---- ...ClassImmutabilityTests_withNewPurity.scala | 96 ------------------- .../opalj/fpcf/FieldImmutabilityTests.scala | 16 ++-- ...FieldImmutabilityTests_withNewPurity.scala | 91 ------------------ .../fpcf/ReferenceImmutabilityTests.scala | 22 ++--- ...renceImmutabilityTests_withNewPurity.scala | 76 --------------- .../opalj/fpcf/TypeImmutabilityTests.scala | 27 +----- .../TypeImmutabilityTests_withNewPurity.scala | 72 -------------- 8 files changed, 59 insertions(+), 394 deletions(-) delete mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala delete mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala delete mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala delete mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala index 371a11b6d8..e4246af2b3 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -6,21 +6,18 @@ 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.LazyClassImmutabilityAnalysis 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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** * @author Tobias Peter Roth @@ -38,18 +35,19 @@ class ClassImmutabilityTests extends PropertiesTest { p.get(RTACallGraphKey) } - describe("no analysis is scheduled") { - val as = executeAnalyses(Set.empty) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) - } - + /** + * describe("no analysis is scheduled") { + * val as = executeAnalyses(Set.empty) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) + * }* + */ describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { println(1) val as = executeAnalyses( Set( LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, + LazyL2PurityAnalysis_new, LazyL0ReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, LazyLxTypeImmutabilityAnalysis_new, @@ -58,18 +56,41 @@ class ClassImmutabilityTests extends PropertiesTest { LazyL0CompileTimeConstancyAnalysis, LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis + LazyFieldLocalityAnalysis ) ) as.propertyStore.shutdown() validateProperties( as, - //classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1, tp._2, tp._3)), classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), Set("ClassImmutability_new") ) } + /** + * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL1FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * }* + */ + /** + * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { + * val as = executeAnalyses( + * Set( + * EagerL2FieldMutabilityAnalysis, + * LazyUnsoundPrematurelyReadFieldsAnalysis, + * LazyL2PurityAnalysis, + * LazyInterProceduralEscapeAnalysis + * ) + * ) + * as.propertyStore.shutdown() + * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) + * } * + */ } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala deleted file mode 100644 index 064907abef..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests_withNewPurity.scala +++ /dev/null @@ -1,96 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf - -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.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.EagerLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new - -/** - * @author Tobias Peter Roth - */ -class ClassImmutabilityTests_withNewPurity extends PropertiesTest { - - override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") - } - - 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("ClassImmutability_new")) - * }* - */ - describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { - println(1) - val as = executeAnalyses( - Set( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ) - as.propertyStore.shutdown() - validateProperties( - as, - classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), - Set("ClassImmutability_new") - ) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ -} 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 index 2303f029ad..90125eb46e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -6,21 +6,18 @@ 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.LazyClassImmutabilityAnalysis 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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** * @author Tobias Peter Roth @@ -28,7 +25,7 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis class FieldImmutabilityTests extends PropertiesTest { override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") + List("org/opalj/fpcf/fixtures/immutability") ///sandbox6") } override def init(p: Project[URL]): Unit = { @@ -45,22 +42,21 @@ class FieldImmutabilityTests extends PropertiesTest { } describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { + import org.opalj.br.fpcf.analyses.LazyVirtualCallAggregatingEscapeAnalysis val as = executeAnalyses( Set( - LazyTypeImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis, LazyL0ReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, + LazyL2PurityAnalysis_new, EagerL0FieldImmutabilityAnalysis, - LazyClassImmutabilityAnalysis, LazyLxClassImmutabilityAnalysis_new, LazyLxTypeImmutabilityAnalysis_new, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis + LazyFieldLocalityAnalysis, + LazyVirtualCallAggregatingEscapeAnalysis ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala deleted file mode 100644 index 02588c7884..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests_withNewPurity.scala +++ /dev/null @@ -1,91 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf - -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.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.EagerL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new - -/** - * @author Tobias Peter Roth - */ -class FieldImmutabilityTests_withNewPurity extends PropertiesTest { - - override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") - } - - 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")) - } - - describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { - val as = executeAnalyses( - Set( - LazyL0ReferenceImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - EagerL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) - } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ - -} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index de845800ae..0ec7f5d7e5 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -13,7 +13,7 @@ import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis 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.LazyL2FieldMutabilityAnalysis +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.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis @@ -21,21 +21,22 @@ import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_ import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater /** * @author Tobias Peter Roth */ class ReferenceImmutabilityTests extends PropertiesTest { - import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater - override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") + List("org/opalj/fpcf/fixtures/immutability/sandbox_dcoff") ///sandbox/reference_immutability_lazy_initialization/sandbox") } override def init(p: Project[URL]): Unit = { + //println("Java version: "+System.getProperty("java.version")) p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) } @@ -51,23 +52,22 @@ class ReferenceImmutabilityTests extends PropertiesTest { L2PurityAnalysis_new.setRater(Some(SystemOutLoggingAllExceptionRater)) describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { - import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new val as = executeAnalyses( Set( EagerL0ReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, - LazyInterProceduralEscapeAnalysis, + LazyLxClassImmutabilityAnalysis_new, LazyLxTypeImmutabilityAnalysis_new, + LazyUnsoundPrematurelyReadFieldsAnalysis, LazyStaticDataUsageAnalysis, + LazyL2PurityAnalysis_new, LazyL0CompileTimeConstancyAnalysis, + LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis, - LazyL2FieldMutabilityAnalysis, + LazyL1FieldMutabilityAnalysis, LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new + LazyTypeImmutabilityAnalysis ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala deleted file mode 100644 index 0513156e72..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests_withNewPurity.scala +++ /dev/null @@ -1,76 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf - -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.LazyClassImmutabilityAnalysis -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.LazyUnsoundPrematurelyReadFieldsAnalysis -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.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis_new -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new -import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater - -/** - * @author Tobias Peter Roth - */ -class ReferenceImmutabilityTests_withNewPurity extends PropertiesTest { - - override def withRT = true - - override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") ///reference_immutability_lazy_initialization/sandbox3") - } - - override def init(p: Project[URL]): Unit = { - //println("Java version: "+System.getProperty("java.version")) - 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("ReferenceImmutability")) - } - - L2PurityAnalysis_new.setRater(Some(SystemOutLoggingAllExceptionRater)) - - describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { - val as = executeAnalyses( - Set( - EagerL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyStaticDataUsageAnalysis, - LazyL2PurityAnalysis_new, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis - ) - ) - as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala index b2f9d2ceb9..1beef6ed8c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala @@ -6,21 +6,18 @@ 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.LazyClassImmutabilityAnalysis 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.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.LazyL2FieldMutabilityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new /** * Tests the Type Immutability Analysis with the new lattice @@ -30,7 +27,7 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis class TypeImmutabilityTests extends PropertiesTest { override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") + List("org/opalj/fpcf/fixtures/immutability/sandbox") } override def init(p: Project[URL]): Unit = { @@ -51,31 +48,17 @@ class TypeImmutabilityTests extends PropertiesTest { println(1) val as = executeAnalyses( Set( - /** - * LazyTypeImmutabilityAnalysis, - * LazyClassImmutabilityAnalysis, - * LazyL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyL0ReferenceImmutabilityAnalysis, - * LazyL0FieldImmutabilityAnalysis, - * EagerLxClassImmutabilityAnalysis_new, - * EagerLxTypeImmutabilityAnalysis_new* - */ LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, + LazyL2PurityAnalysis_new, LazyL0ReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, - EagerLxTypeImmutabilityAnalysis_new, LazyLxClassImmutabilityAnalysis_new, + EagerLxTypeImmutabilityAnalysis_new, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis, - LazyL2FieldMutabilityAnalysis + LazyFieldLocalityAnalysis ) ) as.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala deleted file mode 100644 index cbd9142a01..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests_withNewPurity.scala +++ /dev/null @@ -1,72 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf - -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.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.EagerLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new - -/** - * Tests the Type Immutability Analysis with the new lattice - * - * @author Tobias Peter Roth - */ -class TypeImmutabilityTests_withNewPurity extends PropertiesTest { - - override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") - } - - 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("TypeImmutability_new")) - * } - */ - describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { - println(1) - val as = executeAnalyses( - Set( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ) - as.propertyStore.shutdown() - validateProperties( - as, - // fieldsWithAnnotations(as.project), - classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), - Set("TypeImmutability_new") - ) //TODO class files ... with annotation - } -} From 642dd224ea273253a59322a53c733972952fffe1 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:10:00 +0200 Subject: [PATCH 210/327] removed the escapes property from the immutable reference property --- .../ReferenceImmutabilityMatcher.scala | 4 +--- .../br/fpcf/properties/ReferenceImmutability.scala | 10 ++++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala index 24a000b914..a5633a0af1 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala @@ -66,6 +66,4 @@ class LazyInitializedNotThreadSafeButDeterministicReferenceMatcher extends Refer class LazyInitializedNotThreadSafeOrNotDeterministicReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedNotThreadSafeOrNotDeterministicReference) -class ImmutableReferenceNotEscapesMatcher extends ReferenceImmutabilityMatcher(ImmutableReference(true)) - -class ImmutableReferenceEscapesMatcher extends ReferenceImmutabilityMatcher(ImmutableReference(false)) +class ImmutableReferenceMatcher extends ReferenceImmutabilityMatcher(ImmutableReference) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala index b5d73c0033..2c2c2c1b55 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -32,7 +32,6 @@ sealed trait ReferenceImmutability with ReferenceImmutabilityPropertyMetaInformation { final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key def isImmutableReference = false - def doesNotEscapes = false } object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { @@ -49,9 +48,8 @@ object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformatio } } -case class ImmutableReference(notEscapes: Boolean) extends ReferenceImmutability { +case object ImmutableReference extends ReferenceImmutability { override def isImmutableReference = true - override def doesNotEscapes: Boolean = notEscapes override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} def meet(that: ReferenceImmutability): ReferenceImmutability = if (this == that) @@ -62,7 +60,7 @@ case class ImmutableReference(notEscapes: Boolean) extends ReferenceImmutability case object LazyInitializedThreadSafeReference extends ReferenceImmutability { override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = other match { - case ImmutableReference(_) ⇒ + case ImmutableReference ⇒ throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); case _ ⇒ } @@ -77,7 +75,7 @@ case object LazyInitializedThreadSafeReference extends ReferenceImmutability { case object LazyInitializedNotThreadSafeButDeterministicReference extends ReferenceImmutability { override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = other match { - case ImmutableReference(_) | LazyInitializedThreadSafeReference ⇒ + case ImmutableReference | LazyInitializedThreadSafeReference ⇒ throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); case _ ⇒ } @@ -100,7 +98,7 @@ case object LazyInitializedNotThreadSafeOrNotDeterministicReference extends Refe } override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { other match { - case ImmutableReference(_) | LazyInitializedNotThreadSafeButDeterministicReference | + case ImmutableReference | LazyInitializedNotThreadSafeButDeterministicReference | LazyInitializedThreadSafeReference ⇒ throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); case _ ⇒ From 7e2941b5d99b6226bde6cf171df6a441c6f5a6d8 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:13:26 +0200 Subject: [PATCH 211/327] revert changes --- .../validate/src/test/scala/org/opalj/fpcf/PurityTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a31688f92e..8f58c68769 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 @@ -44,7 +44,7 @@ class PurityTests extends PropertiesTest { } override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability/reference/sandbox") + List("org/opalj/fpcf/fixtures/purity") } describe("the org.opalj.fpcf.analyses.L0PurityAnalysis is executed") { From ac769a93fc7a7354dec1b9a740523fb36a76352c Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:32:35 +0200 Subject: [PATCH 212/327] revert changes --- .../analyses/ReferenceImmutabilityAnalysisDemo.scala | 4 ++-- .../tac/fpcf/analyses/purity/L2PurityAnalysis.scala | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index bda16324d2..f1682a0abb 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -136,8 +136,8 @@ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .entities( eps ⇒ //allfieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field]) && eps.isFinal && (eps.asFinal.p match { - case ImmutableReference(_) ⇒ true - case _ ⇒ false + case ImmutableReference ⇒ true + case _ ⇒ false }) ) .toList 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 162f66709c..84678b3dba 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 @@ -791,9 +791,8 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst return determineMethodPurity(eps.ub.asInstanceOf[TACAI].tac.get.cfg); } - if (state.ubPurity eq ImpureByAnalysis) { - return Result(state.definedMethod, ImpureByAnalysis) - }; + if (state.ubPurity eq ImpureByAnalysis) + return Result(state.definedMethod, ImpureByAnalysis); if (state.ubPurity ne oldPurity) cleanupDependees() // Remove dependees that we don't need anymore. @@ -909,15 +908,14 @@ class L2PurityAnalysis private[analyses] (val project: SomeProject) extends Abst val tacaiO = getTACAI(method) - if (tacaiO.isEmpty) { + if (tacaiO.isEmpty) return InterimResult( definedMethod, ImpureByAnalysis, CompileTimePure, state.dependees, c - ) - }; + ); determineMethodPurity(tacaiO.get.cfg) } From f04c7f120783f5d00cc5808bff06dcad8cc7fa63 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:33:43 +0200 Subject: [PATCH 213/327] revert changes --- .../purity/AbstractPurityAnalysis.scala | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) 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 8e464d51d2..3831cb7691 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 @@ -389,19 +389,17 @@ trait AbstractPurityAnalysis extends FPCFAnalysis { def checkFieldMutability( ep: EOptionP[Field, FieldMutability], 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 - if (objRef.isDefined) { - if (state.ubPurity.isDeterministic) - isLocal(objRef.get, SideEffectFree) - } else atMost(SideEffectFree) - case _ ⇒ - reducePurityLB(SideEffectFree) + )(implicit state: StateType): Unit = ep match { + case LBP(_: FinalField) ⇒ // Final fields don't impede purity + case _: FinalEP[Field, FieldMutability] ⇒ // Mutable field + if (objRef.isDefined) { if (state.ubPurity.isDeterministic) - handleUnknownFieldMutability(ep, objRef) - } + isLocal(objRef.get, SideEffectFree) + } else atMost(SideEffectFree) + case _ ⇒ + reducePurityLB(SideEffectFree) + if (state.ubPurity.isDeterministic) + handleUnknownFieldMutability(ep, objRef) } /** From ca0fd75eb12e8d64007eb47cb3a8e661970af366 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:37:00 +0200 Subject: [PATCH 214/327] revert changes --- .../opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala index ca5e16ee6a..fdada7307e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/RTACallGraphAnalysis.scala @@ -89,6 +89,7 @@ class RTACallGraphAnalysis private[analyses] ( call.name, call.descriptor ) + handleCall( caller, call.name, From a314b03c0e4ee61eec7029730f50086fedab92bf Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:44:31 +0200 Subject: [PATCH 215/327] revert changes --- .../org/opalj/fpcf/ComputationSpecification.scala | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala index 68e34762c6..4f4ecc259c 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ComputationSpecification.scala @@ -94,16 +94,21 @@ trait ComputationSpecification[A] { val uses = this.uses(ps).iterator.map(_.toSpecification).mkString("uses={", ", ", "}") val derivesLazily = - this.derivesLazily.iterator.map(_.toSpecification).mkString("derivesLazily={", ", ", "}") + this.derivesLazily.iterator. + map(_.toSpecification). + mkString("derivesLazily={", ", ", "}") val derivesEagerly = - this.derivesEagerly.iterator.map(_.toSpecification).mkString("derivesEagerly={", ", ", "}") + this.derivesEagerly.iterator. + map(_.toSpecification). + mkString("derivesEagerly={", ", ", "}") val derivesCollaboratively = - this.derivesCollaboratively.iterator - .map(_.toSpecification) - .mkString("derivesCollaboratively={", ", ", "}") + this.derivesCollaboratively.iterator. + map(_.toSpecification). + mkString("derivesCollaboratively={", ", ", "}") s"ComputationSpecification(name=$name,type=$computationType,"+ s"$uses,$derivesLazily,$derivesEagerly,$derivesCollaboratively)" + } override def toString: String = { From 6e72a27cb404807de82fc49fb7a95fc4e398c9dd Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:46:33 +0200 Subject: [PATCH 216/327] revert changes --- .../scala/org/opalj/br/fpcf/properties/ClassImmutability.scala | 1 - 1 file changed, 1 deletion(-) 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 b3f5c2ab8e..2c9af888d4 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 @@ -93,7 +93,6 @@ sealed trait ClassImmutability /** `true` if instances of the class are mutable. */ def isMutable: Boolean } - /** * Common constants use by all [[ClassImmutability]] properties associated with methods. */ From d41ce8af86e5990009642f04a17b4f3d5e5593fd Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:51:55 +0200 Subject: [PATCH 217/327] revert changes --- OPAL/br/src/main/scala/org/opalj/br/Field.scala | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/Field.scala b/OPAL/br/src/main/scala/org/opalj/br/Field.scala index 1b63086f4b..18fe1cfa55 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/Field.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/Field.scala @@ -61,10 +61,7 @@ sealed abstract class JVMField extends ClassMember with Ordered[JVMField] { private[br] def prepareClassFileAttachement(): Field = { new Field( null /*will be set by class file*/ , - accessFlags, - name, - fieldType, - attributes + accessFlags, name, fieldType, attributes ) } @@ -100,8 +97,6 @@ sealed abstract class JVMField extends ClassMember with Ordered[JVMField] { def isTransient: Boolean = (ACC_TRANSIENT.mask & accessFlags) != 0 - def isSyn: Boolean = super.isSynthetic - def isVolatile: Boolean = (ACC_VOLATILE.mask & accessFlags) != 0 /** @@ -224,9 +219,7 @@ object Field { fieldAttributeBuilder: FieldAttributeBuilder ): FieldTemplate = { this( - accessFlags, - name, - fieldType, + accessFlags, name, fieldType, RefArray(fieldAttributeBuilder(accessFlags, name, fieldType)) ) } From 738344f25156a52d08f52377d87196d66151e712 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:53:17 +0200 Subject: [PATCH 218/327] revert changes --- .../fpcf/properties/field_mutability/NonFinalMatcher.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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/field_mutability/NonFinalMatcher.scala index a53af488ff..08526a42b0 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/field_mutability/NonFinalMatcher.scala @@ -39,9 +39,7 @@ class NonFinalMatcher extends AbstractPropertyMatcher { if (!analyses.exists(as.contains)) return false; - val prematurelyRead = getValue(p, annotationType, a.elementValuePairs, "prematurelyRead") - .asInstanceOf[BooleanValue] - .value + val prematurelyRead = getValue(p, annotationType, a.elementValuePairs, "prematurelyRead").asInstanceOf[BooleanValue].value if (prematurelyRead) { val propertyStore = p.get(PropertyStoreKey) @@ -67,4 +65,5 @@ class NonFinalMatcher extends AbstractPropertyMatcher { Some(a.elementValuePairs.head.value.toString) } } + } From 8725f5a12af38175c5058df109f8d749a31db7ff Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:54:20 +0200 Subject: [PATCH 219/327] revert changes --- .../properties/field_mutability/FieldMutabilityMatcher.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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/field_mutability/FieldMutabilityMatcher.scala index d29dbffb92..2f04704357 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/field_mutability/FieldMutabilityMatcher.scala @@ -21,7 +21,7 @@ import org.opalj.br.fpcf.properties.LazyInitializedField */ class FieldMutabilityMatcher(val property: FieldMutability) extends AbstractPropertyMatcher { - final private val PropertyReasonID = 0 + private final val PropertyReasonID = 0 override def isRelevant( p: SomeProject, From f698a4e20e2e98287968c48f111a14347941a929 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:57:16 +0200 Subject: [PATCH 220/327] revert changes --- .../scala/org/opalj/fpcf/PropertiesTest.scala | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) 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 7919cbb919..67158a575c 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 @@ -116,22 +116,18 @@ 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) @@ -307,9 +303,8 @@ 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 ) } @@ -334,9 +329,8 @@ 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 ) } @@ -366,11 +360,11 @@ abstract class PropertiesTest extends FunSpec with Matchers { PropertyStoreKey, (context: List[PropertyStoreContext[AnyRef]]) ⇒ { /* - val ps = PKEParallelTasksPropertyStore.create( - new RecordAllPropertyStoreTracer, - context.iterator.map(_.asTuple).toMap - ) - */ + val ps = PKEParallelTasksPropertyStore.create( + new RecordAllPropertyStoreTracer, + context.iterator.map(_.asTuple).toMap + ) + */ val ps = PKESequentialPropertyStore(context: _*) ps } From 2e0afde6b145877bd3539c861422d481df614b96 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 09:59:48 +0200 Subject: [PATCH 221/327] revert changes --- .../org/opalj/fpcf/FieldMutabilityTests.scala | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala index f7047444ca..b92877b5b9 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldMutabilityTests.scala @@ -14,10 +14,6 @@ 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.EagerL2FieldMutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis /** * Tests if the properties specified in the test project (the classes in the (sub-)package of @@ -29,8 +25,8 @@ import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutab class FieldMutabilityTests extends PropertiesTest { override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { + _ ⇒ Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) } p.get(RTACallGraphKey) } @@ -49,11 +45,7 @@ class FieldMutabilityTests extends PropertiesTest { val as = executeAnalyses( Set( EagerL0FieldMutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new + LazyUnsoundPrematurelyReadFieldsAnalysis ) ) as.propertyStore.shutdown() @@ -65,11 +57,7 @@ class FieldMutabilityTests extends PropertiesTest { Set( EagerL1FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new + LazyInterProceduralEscapeAnalysis ) ) as.propertyStore.shutdown() @@ -82,11 +70,7 @@ class FieldMutabilityTests extends PropertiesTest { EagerL2FieldMutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new + LazyInterProceduralEscapeAnalysis ) ) as.propertyStore.shutdown() From d332038c38f131db403164c29318539a90c6d720 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 10:39:35 +0200 Subject: [PATCH 222/327] corrected the naming of a property --- OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 f59b319e0b..f446e5f8d3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala @@ -84,7 +84,7 @@ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { def isNonVirtualMethodCall: Boolean = false def isVirtualMethodCall: Boolean = false def isStaticMethodCall: Boolean = false - def isIfStmt: Boolean = false + def isIf: Boolean = false def isMonitorEnter: Boolean = false def isMonitorExit: Boolean = false def isPutStatic: Boolean = false @@ -115,7 +115,7 @@ case class If[+V <: Var[V]]( final override def asIf: this.type = this - final override def isIfStmt: Boolean = true + 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 From b903a0ed1d7b282723392d7ebb9c9ddb09eec195 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 10:44:15 +0200 Subject: [PATCH 223/327] current status, still WIP --- .../L0ReferenceImmutabilityAnalysis.scala | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index c295433c3e..916e5dd7ca 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -9,7 +9,6 @@ import org.opalj.br.Field import org.opalj.br.FloatType import org.opalj.br.IntegerType import org.opalj.br.Method -import org.opalj.br.ObjectType import org.opalj.br.PCs import org.opalj.br.ShortType import org.opalj.br.analyses.SomeProject @@ -53,6 +52,7 @@ import org.opalj.tac.common.DefinitionSite import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.SelfReferenceParameter import scala.annotation.switch +import org.opalj.br.ReferenceType /** * @@ -211,19 +211,15 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * values. */ def getDefaultValue()(implicit state: State): Option[Any] = { - import org.opalj.br.ArrayType - state.field.fieldType.isReferenceType state.field.fieldType match { - case FloatType ⇒ Some(0.0f) - case IntegerType ⇒ Some(0) - case ObjectType(_) ⇒ Some(null) //TODO ReferenceType - //case ReferenceType(_) ⇒ Some(null) //TODO - case BooleanType ⇒ Some(false) - case ByteType ⇒ Some(0) - case ShortType ⇒ Some(0) - case ArrayType(_) ⇒ Some(null) - case _ ⇒ None //TODO test mit Array + case FloatType ⇒ Some(0.0f) + case IntegerType ⇒ Some(0) + case _: ReferenceType ⇒ Some(null) + case BooleanType ⇒ Some(false) + case ByteType ⇒ Some(0) + case ShortType ⇒ Some(0) + case _ ⇒ None } } @@ -294,18 +290,22 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec case ep ⇒ state.typeDependees += ep } */ } + if (isNotFinal) + state.referenceImmutability = MutableReference + /*println("is not final: " + isNotFinal) if (!state.field.isFinal && { state.referenceImmutability match { - case LazyInitializedThreadSafeReference | - LazyInitializedNotThreadSafeOrNotDeterministicReference ⇒ - false + case ImmutableReference | + LazyInitializedThreadSafeReference | + LazyInitializedNotThreadSafeButDeterministicReference => //OrNotDeterministicReference ⇒ + false case _ ⇒ true } }) { Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) - } else - createResult() + } else */ + createResult() } /** @@ -470,8 +470,8 @@ trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { PropertyBounds.lub(Purity), PropertyBounds.lub(FieldPrematurelyRead), PropertyBounds.finalP(TACAI), - PropertyBounds.lub(ReferenceImmutability), PropertyBounds.ub(EscapeProperty), + PropertyBounds.ub(ReferenceImmutability) ) final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) From 1f587de16a0be1146ec72d17996429e55bfd9bec Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 10:55:10 +0200 Subject: [PATCH 224/327] adapt the purity analysis to the immutable reference property change --- .../tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala index 409f3bd12a..bc3814af36 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala @@ -450,7 +450,7 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { //case LBP(ImmutableReference(_)) ⇒ //case LBP(LazyInitializedReference) ⇒ - case LBP(ImmutableReference(_) | LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ + case LBP(ImmutableReference | LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ //_: FinalField) ⇒ // Final fields don't impede purity case FinalP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference) ⇒ //_: FinalEP[Field, ReferenceImmutability] ⇒ //FieldMutability] ⇒ // Mutable field From 17b0b786c99610a38ec5e2838eff8b0307aa3439 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 11:42:11 +0200 Subject: [PATCH 225/327] current status, still WIP --- .../L0FieldImmutabilityAnalysis.scala | 240 +++++++++--------- 1 file changed, 121 insertions(+), 119 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index b2c9c48d9b..b26f77eedf 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -65,6 +65,7 @@ import org.opalj.br.fpcf.properties.NoEscape import org.opalj.fpcf.InterimUBP import org.opalj.tac.common.DefinitionSite import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.tac.Stmt case class State(f: Field) { @@ -324,7 +325,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case _: FinalEP[DefinitionSite, EscapeProperty] ⇒ true // Escape state is worse than via return - case InterimUBP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ + case InterimUBP(NoEscape) ⇒ // | EscapeInCallee | EscapeViaReturn) ⇒ state.dependencies += ep false @@ -349,69 +350,63 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) taCode ← getTACAI(method) pc ← pcs } { - val index = taCode.pcToIndex(pc) - println("stmt: "+taCode.stmts(index)) val stmt = taCode.stmts(index) - // if (stmt.isAssignment) { - val assignment = stmt.asAssignment - val useSites = assignment.targetVar.usedBy - for { - index ← useSites - } { - val fieldsUseSiteStmt = taCode.stmts(index) - if (!(fieldsUseSiteStmt.isMonitorEnter || - fieldsUseSiteStmt.isMonitorExit || - fieldsUseSiteStmt.isIfStmt)) { - state.referenceNotEscapes = false + if (stmt.isAssignment) { + val assignment = stmt.asAssignment + val useSites = assignment.targetVar.usedBy + for { + index ← useSites + } { + val fieldsUseSiteStmt = taCode.stmts(index) + if (!fieldsUseSiteStmt.isMonitorEnter && + !fieldsUseSiteStmt.isMonitorExit && + !fieldsUseSiteStmt.isIf) { + println("x13") + println("fieldUseSiteStmt: "+fieldsUseSiteStmt) + state.referenceNotEscapes = false + } } + } else if (stmt.isExprStmt) { + //is ignored + } else { + println("x14") + state.referenceNotEscapes = false } - // } else if (stmt.isExprStmt) { - // //is ignored - //} else state.referenceNotEscapes = false - } } def checkFieldWritesForEffImmutability(field: Field)(implicit state: State) = { - import org.opalj.tac.Stmt - def checkNonVirtualMethodCall( //TODO loswerden über escape analyse + + def checkNonVirtualMethodCall( method: Method, nonVirtualMethodCall: NonVirtualMethodCall[V], tacCode: TACode[TACMethodParameter, V] ): Unit = { - println("check nvmc parameters") - println(state.field.classFile) - println("nvmc: "+nonVirtualMethodCall) - println("params: "+nonVirtualMethodCall.params) + println("check nonvirtualmethod call of method: "+method) + println(nonVirtualMethodCall) nonVirtualMethodCall.params.foreach( param ⇒ { - println("param :"+param) - + println("param: "+param) + //println("used by: " + para) param.asVar.definedBy.foreach( index ⇒ - if (index < 0) + if (index < 0) { + println("x1") state.referenceNotEscapes = false - else { - println("A") + } else { val definitionSides = definitionSites(method, index) - println("B") - println("C") - var flag = true val stmt = tacCode.stmts(tacCode.pcToIndex(definitionSides.pc)) if (stmt.isNonVirtualMethodCall) { checkNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) - flag = false } else if (stmt.isPutField || stmt.isPutStatic) { - flag = false - //referenceNotEscapes = false checkPuts(stmt, method, tacCode) - } - if (flag) { + } else { val propertyStoreResult = propertyStore(definitionSides, EscapeProperty.key) + println("propertyStoreResult: "+propertyStoreResult) if (handleEscapeProperty(propertyStoreResult)) { - println("nvmc") + println("x2") state.referenceNotEscapes = false } } @@ -421,91 +416,87 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) } def checkPuts(stmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { - var defSites: IntTrieSet = IntTrieSet.empty + var putDefinitionSites: IntTrieSet = IntTrieSet.empty + println("check put: "+stmt) if (stmt.isPutField) { val putField = stmt.asPutField - defSites = putField.value.asVar.definedBy + putDefinitionSites = putField.value.asVar.definedBy } else if (stmt.isPutStatic) { val putStatic = stmt.asPutStatic - defSites = putStatic.value.asVar.definedBy + putDefinitionSites = putStatic.value.asVar.definedBy } else { - println("false1") + println("x3") state.referenceNotEscapes = false } - println("defsites: "+defSites) for { - i ← defSites + i ← putDefinitionSites } { if (i > 0) { - val stmt2 = tacCode.stmts(i) - val assignment = stmt2.asAssignment - println( - s"""i: $i - | assignment: $assignment - |""".stripMargin - ) - if (assignment.expr.isVar) { - val v2 = assignment.expr.asVar - for (x ← v2.usedBy.iterator) { - val stmt3 = tacCode.stmts(x) - if (stmt3.isNonVirtualMethodCall) { - println("is nvmc") - val nonVirtualMethodCall = stmt3.asNonVirtualMethodCall + val definitionSiteStatement = tacCode.stmts(i) + val definitionSiteAssignment = definitionSiteStatement.asAssignment + if (definitionSiteAssignment.expr.isVar) { + val definitionSiteVar = definitionSiteAssignment.expr.asVar + for (definitionSiteVarUseSite ← definitionSiteVar.usedBy.iterator) { + val definitionSiteVarUseSiteStmt = tacCode.stmts(definitionSiteVarUseSite) + if (definitionSiteVarUseSiteStmt.isNonVirtualMethodCall) { + val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall checkNonVirtualMethodCall(method, nonVirtualMethodCall, tacCode) - } else { state.referenceNotEscapes = false } - //TODO andere F#lle bedenken + } else { + println("x4") + state.referenceNotEscapes = false + } + //TODO andere Fälle bedenken } - } else if (assignment.expr.isNew) { + } else if (definitionSiteAssignment.expr.isNew) { if (!method.isConstructor) { val propertyStoreResult = - propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) + propertyStore( + definitionSites(method, definitionSiteAssignment.pc), + EscapeProperty.key + ) if (handleEscapeProperty(propertyStoreResult)) { - println("false3") + println("x5") state.referenceNotEscapes = false } } else { val useSites = - assignment.targetVar.usedBy - + definitionSiteAssignment.targetVar.usedBy for (i ← useSites) { - val stmt = tacCode.stmts(i) - if (stmt.isNonVirtualMethodCall) { - checkNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) - } - if (stmt.isPutStatic || stmt.isPutField) { + val useSiteStmt = tacCode.stmts(i) + if (useSiteStmt.isNonVirtualMethodCall) { + checkNonVirtualMethodCall(method, useSiteStmt.asNonVirtualMethodCall, tacCode) + } else if (useSiteStmt.isPutStatic || useSiteStmt.isPutField) { //checkPuts(stmt, method, tacCode) - } //else state.referenceNotEscapes = false - } - } - - /*taCode.stmts(i) match { - case nvmc: NonVirtualMethodCall ⇒ checkNonVirtualMethodCall(method, nvmc) - } */ - //else state.referenceNotEscapes = false - //) - //} + if (useSiteStmt != stmt) { + println("x6") + state.referenceNotEscapes = false //TODO + } - /*val constrUseSites = assignment.targetVar.usedBy - for (us ← constrUseSites.iterator) { - val stmt = taCode.stmts(us) - if (stmt.isNonVirtualMethodCall) { - checkNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall) + } else if (useSiteStmt.isAssignment) { + println("x7") + state.referenceNotEscapes = false //TODO + //val assignment = stmt.asAssignment + } else { + println("x8") + state.referenceNotEscapes = false } - //TODO alle Fälle abdecken - //TODO escape analyse für Object - // else ; aktuelles putfield bedenken - }*/ - } else if (!assignment.expr.isConst) { - println("false5") + } + } + //TODO alle Fälle abdecken + //TODO escape analyse für Object + // else ; aktuelles putfield bedenken + } else if (!definitionSiteAssignment.expr.isConst) { + println("x9") state.referenceNotEscapes = false } } else { - println("false6") + println("x10") state.referenceNotEscapes = false } } } - + // start of method + state.referenceNotEscapes = field.isPrivate for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method) @@ -516,20 +507,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (index > (-1 + staticAddition)) { val stmt = taCode.stmts(index) checkPuts(stmt, method, taCode) - + } else { + println("x11") + state.referenceNotEscapes = false } } } - println(s"""ne: ${state.referenceNotEscapes}""") - state.referenceNotEscapes = field.isPrivate - println(s"""ne: ${state.referenceNotEscapes}""") if (state.referenceNotEscapes) checkFieldWritesForEffImmutability(state.field) - println(s"""ne: ${state.referenceNotEscapes}""") if (state.referenceNotEscapes) checkFieldReadsForEffImmutability(state.field) - println(s"""ne: ${state.referenceNotEscapes}""") - } def createResult(state: State): ProperPropertyComputationResult = { @@ -538,6 +525,8 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) //". == ObjectType("java/nio/file/attribute/AclEntry")) println( s""" + | ========================================================= + | create Result | field: $field | ref imm: ${state.referenceImmutability} | type imm: ${state.typeImmutability} @@ -579,26 +568,25 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) eps match { //(: @unchecked) - /*case InterimLUBP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference, _) ⇒ - state.typeImmutability = Some(false) - if (field.name.contains("hash") && - field.classFile.thisType.simpleName.contains("AclEntry")) - println("here") - return Result(field, MutableField); */ case x: InterimEP[_, _] ⇒ { state.dependencies += eps InterimResult(field, MutableField, DeepImmutableField, state.dependencies, c(state)) } - case FinalP(DeepImmutableType) ⇒ //state.typeImmutability = Some(true) + case FinalP(DeepImmutableType) ⇒ { //state.typeImmutability = Some(true) + if (!state.dependentImmutability.isDefined) + state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) + + } case FinalEP(t, MutableType_new | ShallowImmutableType) ⇒ state.typeImmutability = Some(false) - if (t != ObjectType.Object) { + if (t != ObjectType.Object) { // in case of generic fields state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } case FinalEP(f, DependentImmutableType) ⇒ { + //println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") state.typeImmutability = Some(false) - if (state.dependentImmutability == None) - state.dependentImmutability = Some(DependentImmutabilityKind.dependent) + if (!state.dependentImmutability.isDefined) + state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) } case FinalP( MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference @@ -606,19 +594,23 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.typeImmutability = Some(false) return Result(field, MutableField); } - case FinalP(ImmutableReference) ⇒ { - state.referenceImmutability = Some(true) - //state.referenceNotEscapes = notEscapes - } case FinalP( + ImmutableReference | LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference ) ⇒ { //TODO state.referenceImmutability = Some(true) } - case _ if eps.isFinal && eps.asEPK.pk == TACAI.key ⇒ + case eps if eps.isFinal && eps.asEPK.pk == TACAI.key ⇒ + checkIfReferencedObjectCanEscape(state) + + case eps if eps.isFinal && eps.asEPK.pk == EscapeProperty.key ⇒ checkIfReferencedObjectCanEscape(state) + if (handleEscapeProperty(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) { + println("x12") + state.referenceNotEscapes = false + } //case _: FinalEP[_, TACAI] ⇒ // checkIfReferencedObjectCanEscape(state) @@ -644,7 +636,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) propertyStore(state.field, ReferenceImmutability.key) match { case FinalP(ImmutableReference) ⇒ { state.referenceImmutability = Some(true) - //state.referenceNotEscapes = notEscapes } case FinalP(LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ state.referenceImmutability = Some(true) @@ -656,12 +647,23 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } loadFormalTypeparameters() - handleTypeImmutability(state) - hasGenericType(state) - if (state.referenceImmutability == Some(true) && state.typeImmutability != Some(true)) { + if (field.classFile.thisType.simpleName.contains("Generic_class2") || + field.classFile.thisType.simpleName.contains("Generic_class3")) + println( + s""" + | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + | ${field.classFile.thisType.simpleName} + | ${field.name} + | ref imm: ${state.referenceImmutability} + | typ imm: ${state.typeImmutability} + |""".stripMargin + ) + //it is possible that the type immutability not already determined at this point + if (!state.referenceImmutability.isDefined || state.referenceImmutability.get) { + checkIfReferencedObjectCanEscape } From 63720a498ae45a47903a09fff67e1d311389b430 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 12:16:09 +0200 Subject: [PATCH 226/327] rearrange tests --- .../generic/ClassWithGenericField_deep.java | 50 ---- .../ClassWithGenericField_mutable.java | 19 -- .../ClassWithGenericField_shallow.java | 17 -- .../DependentClassInheritingMutableOne.java | 13 - ...DependentClassWithGenericField_deep01.java | 27 -- .../DependentClassWithGenericField_deep1.java | 236 +++++++++++++++++- ...DependentClassWithGenericField_deep11.java | 30 --- .../DependentClassWithGenericField_deep2.java | 25 -- ...DependentClassWithGenericTypeArgument.java | 14 -- .../classes/generic/FinalEmptyClass.java | 10 - .../GenericAndDeepImmutableFields.java | 30 --- .../generic/GenericAndMutableFields.java | 24 -- .../GenericAndShallowImmutableFields.java | 24 -- .../GenericClassWithDeepImmParams.java | 21 -- .../GenericClassWithExtFinalMutTypes.java | 42 ---- .../classes/generic/Generic_class1.java | 35 --- .../immutability/classes/generic/Nested.java | 15 +- .../classes/generic/TrivialMutableClass.java | 18 -- 18 files changed, 241 insertions(+), 409 deletions(-) delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_deep.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_mutable.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassInheritingMutableOne.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep01.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep11.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep2.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericTypeArgument.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/FinalEmptyClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndDeepImmutableFields.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndMutableFields.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndShallowImmutableFields.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithDeepImmParams.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithExtFinalMutTypes.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Generic_class1.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/TrivialMutableClass.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_deep.java deleted file mode 100644 index f432311f21..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_deep.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; -import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -public final class ClassWithGenericField_deep { - @DeepImmutableFieldAnnotation("deep imm field") - @ImmutableReferenceNotEscapesAnnotation("eff imm ref") - private Generic_class2 gc = - new Generic_class2 - (new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2(), new FinalEmptyClass2()); -} - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -final class Generic_class2 { - - private T1 t1; - - private T2 t2; - - private T3 t3; - - private T4 t4; - - private T5 t5; - - public Generic_class2(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ - this.t1 = t1; - this.t2 = t2; - this.t3 = t3; - this.t4 = t4; - this.t5 = t5; - } - -} -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -final class FinalEmptyClass2 { - -} - - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_mutable.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_mutable.java deleted file mode 100644 index 77153e5a8c..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_mutable.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -public final class ClassWithGenericField_mutable { - @MutableFieldAnnotation("deep imm field") - @MutableReferenceAnnotation("eff imm ref") - public Generic_class1 gc = - new Generic_class1 - (new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); -} - - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java deleted file mode 100644 index f9369de75a..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/ClassWithGenericField_shallow.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -public final class ClassWithGenericField_shallow { - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceNotEscapesAnnotation("") - private Generic_class1 gc = - new Generic_class1 - (new TrivialMutableClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); -} - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassInheritingMutableOne.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassInheritingMutableOne.java deleted file mode 100644 index 32c2df516f..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassInheritingMutableOne.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -public class DependentClassInheritingMutableOne extends TrivialMutableClass { - private final T field; - public DependentClassInheritingMutableOne(T field) { - this.field = field; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep01.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep01.java deleted file mode 100644 index a456d03b43..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep01.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -public final class DependentClassWithGenericField_deep01 { - - @DeepImmutableFieldAnnotation(value = "") - private FinalEmptyClass fec; - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceEscapesAnnotation("eff imm ref") - private Generic_class1 gc; - - public DependentClassWithGenericField_deep01(FinalEmptyClass fec) { - this.fec = fec; - gc = new Generic_class1 - (fec, new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); - } -} - - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java index 5c25dc2dc0..f84590bdef 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java @@ -1,9 +1,18 @@ package org.opalj.fpcf.fixtures.immutability.classes.generic; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @DependentImmutableTypeAnnotation("") @@ -11,21 +20,236 @@ public final class DependentClassWithGenericField_deep1 { @DependentImmutableFieldAnnotation(value = "", genericString = "T1") - @ImmutableReferenceEscapesAnnotation("") + @ImmutableReferenceAnnotation("") private T1 t1; @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") - @ImmutableReferenceEscapesAnnotation("eff imm ref") - private Generic_class1 gc; + @ImmutableReferenceAnnotation("eff imm ref") + private SimpleGenericClass gc; public DependentClassWithGenericField_deep1(T1 t1) { this.t1 = t1; - gc = new Generic_class1 - (t1, new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); + gc = new SimpleGenericClass + (t1, new FinalEmptyClass(), new FinalEmptyClass()); } +} + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +final class DependentClassWithGenericField_deep01 { + + @DeepImmutableFieldAnnotation(value = "") + private FinalEmptyClass fec; + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("eff imm ref") + private SimpleGenericClass gc; + + public DependentClassWithGenericField_deep01(FinalEmptyClass fec) { + this.fec = fec; + gc = new SimpleGenericClass + (fec, new FinalEmptyClass(), new FinalEmptyClass()); + } +} + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +final class DependentClassWithGenericField_deep2 { + + @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") + @ImmutableReferenceAnnotation("eff imm ref") + private SimpleGenericClass sgc; + + public DependentClassWithGenericField_deep2(T1 t1) { + sgc = new SimpleGenericClass + (t1, new FinalEmptyClass(), new FinalEmptyClass()); + } + +} + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +final class DependentClassWithGenericField_deep11 { + + @DependentImmutableFieldAnnotation(value = "", genericString = "T1") + @ImmutableReferenceAnnotation("") + private T1 t1; + + @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") + @ImmutableReferenceAnnotation("eff imm ref") + private DependentClassWithGenericField_deep1 gc; + + public DependentClassWithGenericField_deep11(T1 t1) { + this.t1 = t1; + gc = new DependentClassWithGenericField_deep1(t1); + } +} + +@MutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +class DependentClassWithGenericTypeArgument { + private final SimpleGenericClass sgc; + DependentClassWithGenericTypeArgument(SimpleGenericClass sgc) { + this.sgc = sgc; + } +} + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("It has no fields and is final") +final class FinalEmptyClass { + +} + +@DependentImmutableTypeAnnotation("Dependent Immutability of T1,...,T5") +@DependentImmutableClassAnnotation("Dependent Immutability of T1,...,T5") +final class SimpleGenericClass { + @DependentImmutableFieldAnnotation(value = "T1",genericString = "T1") + @ImmutableReferenceAnnotation("effectively") + private T1 t1; + @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") + @ImmutableReferenceAnnotation("effectively") + private T2 t2; + @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") + @ImmutableReferenceAnnotation("effectively") + private T3 t3; + + public SimpleGenericClass(T1 t1, T2 t2, T3 t3){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + } +} + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +final class GenericAndDeepImmutableFields { + + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") + @ImmutableReferenceAnnotation("") + private T1 t1; + + @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") + @ImmutableReferenceAnnotation("") + private T2 t2; + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private FinalEmptyClass fec; + GenericAndDeepImmutableFields(T1 t1, T2 t2, FinalEmptyClass fec){ + this.t1 = t1; + this.t2 = t2; + this.fec = fec; + } +} + +@MutableTypeAnnotation("") +@MutableClassAnnotation("Because of mutable field") +class GenericAndMutableFields { + @MutableFieldAnnotation("Because of mutable reference") + @MutableReferenceAnnotation("Because of public field") + public T1 t1; + @DependentImmutableFieldAnnotation(value = "Because of generic type", genericString = "T2") + @ImmutableReferenceAnnotation("Because of effectively immutable final") + private T2 t2; + GenericAndMutableFields(T1 t1, T2 t2){ + this.t1 = t1; + this.t2 = t2; + } +} + +@MutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +class GenericAndShallowImmutableFields { + + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") + private T1 t1; + @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") + private T2 t2; + @ShallowImmutableFieldAnnotation("") + private SimpleMutableClass smc; + GenericAndShallowImmutableFields(T1 t1, T2 t2, SimpleMutableClass smc){ + this.t1 = t1; + this.t2 = t2; + this.smc = smc; + } + +} + +@MutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +class GenericClassWithDeepImmParam { + @DeepImmutableFieldAnnotation("") + private A a; + GenericClassWithDeepImmParam(A a) { + this.a = a; + } } +@MutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +class GenericClassWithExtendingFinalMutableType { + + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private A a; + + GenericClassWithExtendingFinalMutableType(A a){ + this.a = a; + } +} + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +final class FinalMutableClass{ + public int n = 0; +} + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +final class ClassWithGenericField_deep { + @DeepImmutableFieldAnnotation("deep imm field") + @ImmutableReferenceAnnotation("eff imm ref") + private SimpleGenericClass gc = + new SimpleGenericClass + (new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); +} + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +final class ClassWithGenericField_mutable { + @MutableFieldAnnotation("deep imm field") + @MutableReferenceAnnotation("eff imm ref") + public SimpleGenericClass gc = + new SimpleGenericClass + (new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); +} + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +final class ClassWithGenericField_shallow { + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private SimpleGenericClass gc = + new SimpleGenericClass + (new SimpleMutableClass(), new FinalEmptyClass(), new FinalEmptyClass()); +} + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +class DependentClassInheritingMutableOne extends SimpleMutableClass { + private final T field; + public DependentClassInheritingMutableOne(T field) { + this.field = field; + } +} + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +class SimpleMutableClass{ public int n = 0;} + + + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep11.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep11.java deleted file mode 100644 index e36f1343eb..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep11.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; - - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -public final class DependentClassWithGenericField_deep11 { - - @DependentImmutableFieldAnnotation(value = "", genericString = "T1") - @ImmutableReferenceEscapesAnnotation("") - private T1 t1; - - @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") - @ImmutableReferenceEscapesAnnotation("eff imm ref") - private DependentClassWithGenericField_deep1 gc; - - public DependentClassWithGenericField_deep11(T1 t1) { - this.t1 = t1; - gc = new DependentClassWithGenericField_deep1(t1); - } - - -} - - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep2.java deleted file mode 100644 index b10fa5a068..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep2.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; - - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -public final class DependentClassWithGenericField_deep2 { - - @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") - @ImmutableReferenceEscapesAnnotation("eff imm ref") - private Generic_class1 gc; - - public DependentClassWithGenericField_deep2(T1 t1) { - gc = new Generic_class1 - (t1, new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); - } - -} - - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericTypeArgument.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericTypeArgument.java deleted file mode 100644 index 76c4726cd4..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericTypeArgument.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - - -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") -public class DependentClassWithGenericTypeArgument { - private final Generic_class1 gc1; - DependentClassWithGenericTypeArgument(Generic_class1 gc1) { - this.gc1 = gc1; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/FinalEmptyClass.java deleted file mode 100644 index eca41e36e6..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/FinalEmptyClass.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("It has no fields and is final") -public final class FinalEmptyClass { - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndDeepImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndDeepImmutableFields.java deleted file mode 100644 index 996bb38bdc..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndDeepImmutableFields.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -public final class GenericAndDeepImmutableFields { - - @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") - @ImmutableReferenceEscapesAnnotation("") - private T1 t1; - - @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") - @ImmutableReferenceEscapesAnnotation("") - private T2 t2; - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceEscapesAnnotation("") - private FinalEmptyClass fec; - - GenericAndDeepImmutableFields(T1 t1, T2 t2, FinalEmptyClass fec){ - this.t1 = t1; - this.t2 = t2; - this.fec = fec; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndMutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndMutableFields.java deleted file mode 100644 index 925f269911..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndMutableFields.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("") -@MutableClassAnnotation("Because of mutable field") -public class GenericAndMutableFields { - @MutableFieldAnnotation("Because of mutable reference") - @MutableReferenceAnnotation("Because of public field") - public T1 t1; - @DependentImmutableFieldAnnotation(value = "Because of generic type", genericString = "T2") - @ImmutableReferenceEscapesAnnotation("Because of effectively immutable final") - private T2 t2; - GenericAndMutableFields(T1 t1, T2 t2){ - this.t1 = t1; - this.t2 = t2; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndShallowImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndShallowImmutableFields.java deleted file mode 100644 index 6db7823ee6..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericAndShallowImmutableFields.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") -public class GenericAndShallowImmutableFields { - - @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") - private T1 t1; - @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") - private T2 t2; - @ShallowImmutableFieldAnnotation("") - private TrivialMutableClass tmc; - GenericAndShallowImmutableFields(T1 t1, T2 t2, TrivialMutableClass tmc){ - this.t1 = t1; - this.t2 = t2; - this.tmc = tmc; - } - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithDeepImmParams.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithDeepImmParams.java deleted file mode 100644 index cb585c1454..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithDeepImmParams.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -public class GenericClassWithDeepImmParams { - @DeepImmutableFieldAnnotation("") - private A a; - @DeepImmutableFieldAnnotation("") - private B b; - @DeepImmutableFieldAnnotation("") - private C c; - GenericClassWithDeepImmParams(A a, B b, C c) { - this.a = a; - this.b = b; - this.c = c; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithExtFinalMutTypes.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithExtFinalMutTypes.java deleted file mode 100644 index 4d0ef9850d..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/GenericClassWithExtFinalMutTypes.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -//TODO - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") -public class GenericClassWithExtFinalMutTypes { - - @ShallowImmutableFieldAnnotation("") -@ImmutableReferenceEscapesAnnotation("") - private A a; - - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceEscapesAnnotation("") - private B b; - - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceEscapesAnnotation("") - private C c; - GenericClassWithExtFinalMutTypes(A a, B b, C c){ - this.a = a; - this.b = b; - this.c = c; - } - -} - -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -final class FinalMutableClass{ - public int n = 0; -} - - - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Generic_class1.java deleted file mode 100644 index b68cdc6c8f..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/Generic_class1.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; - -@DependentImmutableTypeAnnotation("Dependent Immutability of T1,...,T5") -@DependentImmutableClassAnnotation("Dependent Immutability of T1,...,T5") -public final class Generic_class1 { - @DependentImmutableFieldAnnotation(value = "T1",genericString = "T1") - @ImmutableReferenceEscapesAnnotation("effectively") - private T1 t1; - @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") - @ImmutableReferenceEscapesAnnotation("effectively") - private T2 t2; - @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") - @ImmutableReferenceEscapesAnnotation("effectively") - private T3 t3; - @DependentImmutableFieldAnnotation(value = "T4", genericString = "T3") - @ImmutableReferenceEscapesAnnotation("effectively") - private T4 t4; - @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") - @ImmutableReferenceEscapesAnnotation("effectively") - private T5 t5; - - public Generic_class1(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ - this.t1 = t1; - this.t2 = t2; - this.t3 = t3; - this.t4 = t4; - this.t5 = t5; - } - -} \ 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 index 11f36816e1..a929b8dbd8 100644 --- 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 @@ -18,12 +18,18 @@ public Inner(T t){ class Complex{ class Inner { @DependentImmutableFieldAnnotation(value="", genericString = "T") - private Generic_class1 gc1; - public Inner(Generic_class1 gc1){ - this.gc1 = gc1; + private GenericClass gc; + public Inner(GenericClass gc){ + this.gc = gc; } + } +} - +class GenericClass { + @DependentImmutableFieldAnnotation(value = "", genericString = "T") + private T t; + public GenericClass(T t){ + this.t = t; } } @@ -31,3 +37,4 @@ public Inner(Generic_class1 gc1){ + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/TrivialMutableClass.java deleted file mode 100644 index 4e73c769b2..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/TrivialMutableClass.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("") -@MutableClassAnnotation("It has Mutable Fields") -public class TrivialMutableClass { - @MutableReferenceAnnotation("public") - @MutableFieldAnnotation("mutable reference") - public int n = 0; - - @MutableReferenceAnnotation("public") - @MutableFieldAnnotation("mutable reference") - public String name = "name"; -} From d0070e9375fdb2ef5a26a440177980eb7c0d4953 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 12:23:26 +0200 Subject: [PATCH 227/327] rearranging tests --- .../classes/inheriting/EmptyClass.java | 23 +++++++++++++++++++ .../EmptyClassInheritingEmptyClass.java | 9 -------- .../EmptyClassInheritingMutableClass.java | 10 -------- .../classes/inheriting/MutableClass.java | 14 ----------- .../classes/interfaces/EmptyInterface.java | 11 +++++++++ .../TrivialInterfaceWithMethods.java | 15 ------------ 6 files changed, 34 insertions(+), 48 deletions(-) delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingEmptyClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingMutableClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/MutableClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/TrivialInterfaceWithMethods.java 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 index 7905b333a1..34c33d88ef 100644 --- 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 @@ -1,10 +1,33 @@ package org.opalj.fpcf.fixtures.immutability.classes.inheriting; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +import org.opalj.fpcf.properties.type_mutability.MutableType; @MutableTypeAnnotation("Not final class") @DeepImmutableClassAnnotation("Class has no fields but is not final") public class EmptyClass { } + +@MutableTypeAnnotation("Not final class") +@DeepImmutableClassAnnotation("empty class inheriting empty class") +class EmptyClassInheritingEmptyClass extends EmptyClass{ +} + +@MutableType("Because of mutable class") +@MutableClassAnnotation("Because of extending mutable class") +final class EmptyClassInheritingMutableClass extends MutableClass { + +} + +@MutableTypeAnnotation("Because of mutable class") +@MutableClassAnnotation("Because of mutable field") +class MutableClass { + @MutableFieldAnnotation("Mutable reference") + @MutableReferenceAnnotation("public field") + 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/inheriting/EmptyClassInheritingEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingEmptyClass.java deleted file mode 100644 index 819ce5354b..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingEmptyClass.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.inheriting; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Not final class") -@DeepImmutableClassAnnotation("empty class inheriting empty class") -public class EmptyClassInheritingEmptyClass extends EmptyClass{ -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingMutableClass.java deleted file mode 100644 index c1e01d5301..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/EmptyClassInheritingMutableClass.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.inheriting; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.type_mutability.MutableType; - -@MutableType("Because of mutable class") -@MutableClassAnnotation("Because of extending mutable class") -public final class EmptyClassInheritingMutableClass extends MutableClass { - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/MutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/MutableClass.java deleted file mode 100644 index 15fb955390..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/inheriting/MutableClass.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.inheriting; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Because of mutable class") -@MutableClassAnnotation("Because of mutable field") -public class MutableClass { - @MutableFieldAnnotation("Mutable reference") - @MutableReferenceAnnotation("public field") - public int n= 0; -} 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 index 20f13f83c3..9bc600028b 100644 --- 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 @@ -7,3 +7,14 @@ @DeepImmutableClassAnnotation("Empty Interface") public interface EmptyInterface { } + +@MutableTypeAnnotation("Not final Interface") +@DeepImmutableClassAnnotation("Interface") +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/interfaces/TrivialInterfaceWithMethods.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/TrivialInterfaceWithMethods.java deleted file mode 100644 index 7b16c9767e..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/interfaces/TrivialInterfaceWithMethods.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.interfaces; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Not final Interface") -@DeepImmutableClassAnnotation("Interface") -public interface TrivialInterfaceWithMethods { - -public String getName(); -public String getAge(); -public void setName(); -public void setAge(); - -} From 196d00fc59e33cd7f0537cd5238962bd81045a93 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 12:51:43 +0200 Subject: [PATCH 228/327] rearranging tests --- .../DeepGenericTest.java | 19 -- .../FinalEmptyClass.java | 10 - .../Generic_class1.java | 249 +++++++++++++++++- .../Generic_class1_extString.java | 38 --- .../Generic_class2.java | 35 --- .../Generic_class3.java | 24 -- .../Generic_class4_deep.java | 19 -- .../Generic_class4_shallow.java | 19 -- .../multinested_genericClasses/One.java | 42 --- .../multinested_genericClasses/OneVirgin.java | 41 --- .../multinested_genericClasses/TestTest.java | 35 --- .../TrivialMutableClass.java | 19 -- .../multinested_genericClasses/Two.java | 22 -- .../multinested_genericClasses/TwoVirgin.java | 16 -- .../trivials/DependentImmutableClass.java | 17 -- .../classes/trivials/EmptyClass.java | 9 - .../classes/trivials/FinalEmptyClass.java | 9 - .../GenericTypeIsNotUsedAsFieldType.java | 13 - .../classes/trivials/MutableClass.java | 14 - ...entImmutableClassBecauseOfPublicField.java | 17 -- .../trivials/ShallowImmutableClass.java | 61 ++++- 21 files changed, 306 insertions(+), 422 deletions(-) delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/DeepGenericTest.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/FinalEmptyClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1_extString.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class2.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class3.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_deep.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_shallow.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TestTest.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TrivialMutableClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/EmptyClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/FinalEmptyClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/GenericTypeIsNotUsedAsFieldType.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/MutableClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/NoDependentImmutableClassBecauseOfPublicField.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/DeepGenericTest.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/DeepGenericTest.java deleted file mode 100644 index e5c7ad8a2d..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/DeepGenericTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -public final class DeepGenericTest { - @DeepImmutableFieldAnnotation("") -@ImmutableReferenceEscapesAnnotation("") -private Generic_class1 gc1; - - public DeepGenericTest(Generic_class1 gc1){ - this.gc1 = gc1; - } - -} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/FinalEmptyClass.java deleted file mode 100644 index 420f8b2310..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/FinalEmptyClass.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -public final class FinalEmptyClass { - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java index 74a22b4cd0..95724c65a9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java @@ -1,8 +1,19 @@ package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; @DependentImmutableTypeAnnotation("") @DependentImmutableClassAnnotation("") @@ -31,4 +42,240 @@ public Generic_class1(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ this.t5 = t5; } -} \ No newline at end of file +} + +@ShallowImmutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +final class Generic_class1_extString { + + @ShallowImmutableFieldAnnotation("") + private T1 t1; + + @DeepImmutableFieldAnnotation(value = "T2") + private T2 t2; + + @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") + private T3 t3; + + @DependentImmutableFieldAnnotation(value = "T4", genericString = "T4") + private T4 t4; + + @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") + private T5 t5; + + public Generic_class1_extString(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + } + +} + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +final class Generic_class2 { + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value="T1", genericString = "T1") + private T1 t1; + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value="T2", genericString = "T2") + private T2 t2; + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value="T3", genericString = "T3") + private T3 t3; + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value = "", genericString = "") + private Generic_class1 gc; + + + public Generic_class2(T1 t1, T2 t2, T3 t3, FinalEmptyClass fec1, FinalEmptyClass fec2){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + gc = new Generic_class1(fec1, fec2, t1,t2,t3); + } + +} + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +final class Generic_class3 { + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") + private T1 t1; + + @ImmutableReferenceAnnotation("") + @DependentImmutableFieldAnnotation(value = "", genericString = "") + private Generic_class2 gc; + + public Generic_class3(T1 t1, FinalEmptyClass fec1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4){ + this.t1 = t1; + gc = new Generic_class2(t1, fec1, fec2, fec3, fec4); + } +} + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +final class Generic_class4_deep { + + @ImmutableReferenceAnnotation("") + @DeepImmutableFieldAnnotation("") + private Generic_class3 gc; + + public Generic_class4_deep(FinalEmptyClass fec1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4, FinalEmptyClass fec5){ + gc = new Generic_class3(fec1, fec2, fec3, fec4, fec5); + } +} + +@ShallowImmutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +final class Generic_class4_shallow { + + @ImmutableReferenceAnnotation("") + @ShallowImmutableFieldAnnotation("") + private Generic_class3 gc; + + public Generic_class4_shallow(SimpleMutableClass tmc1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4, FinalEmptyClass fec5){ + gc = new Generic_class3(tmc1, fec2, fec3, fec4, fec5); + } +} + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +final class DeepGeneric { + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Generic_class1 gc1; + + public DeepGeneric(Generic_class1 gc1){ + this.gc1 = gc1; + } + +} + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +class One { + @DependentImmutableFieldAnnotation(value="",genericString = "A") + @ImmutableReferenceAnnotation("") + private A a; + @DependentImmutableFieldAnnotation(value="",genericString = "B") + @ImmutableReferenceAnnotation("") + private B b; + @DependentImmutableFieldAnnotation(value="",genericString = "C") + @ImmutableReferenceAnnotation("") + private C c; + @DependentImmutableFieldAnnotation(value="",genericString = "D") + @ImmutableReferenceAnnotation("") + private D d; + @MutableFieldAnnotation("") + @MutableReferenceAnnotation("") + public SimpleMutableClass tmc; + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Generic_class1 gc1; + public One(A a, B b, C c, D d, SimpleMutableClass tmc){ + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.tmc = tmc; + this.gc1 = new Generic_class1(this.a,this.b,this.c,this.d, this.tmc); + } +} + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +class OneVirgin { + @DependentImmutableFieldAnnotation(value="",genericString = "A") + @ImmutableReferenceAnnotation("") + private A a; + @DependentImmutableFieldAnnotation(value="",genericString = "B") + @ImmutableReferenceAnnotation("") + private B b; + @DependentImmutableFieldAnnotation(value="",genericString = "C") + @ImmutableReferenceAnnotation("") + private C c; + @DependentImmutableFieldAnnotation(value="",genericString = "D") + @ImmutableReferenceAnnotation("") + private D d; + + @MutableFieldAnnotation("") + @MutableReferenceAnnotation("") + public SimpleMutableClass tmc; + + Generic_class1 gc1; + public OneVirgin(A a, B b, C c, D d, SimpleMutableClass e){ + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.tmc = tmc; + this.gc1 = new Generic_class1(this.a, this.b, this.c, this.d, this.tmc); + } + + +} + +@MutableTypeAnnotation("") +@ShallowImmutableClassAnnotation("") +class Two { + + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Generic_class1, B, B, B, SimpleMutableClass> gc1; + + public Two(A a, B b, SimpleMutableClass tmc, Generic_class1 gc1) { + this.gc1 = new Generic_class1, B, B, B, SimpleMutableClass>(gc1,b,b,b,tmc); + } +} + +class TwoVirgin { + @DependentImmutableFieldAnnotation(value="",genericString = "") + @ImmutableReferenceAnnotation("") + private Generic_class1, B, C, C, C> gc1; + + public TwoVirgin(A a, B b, C c, Generic_class1 gc1) { + this.gc1 = new Generic_class1, B, C, C, C>(gc1,b,c,c,c); + } +} + +@DependentImmutableTypeAnnotation("") +@DependentImmutableClassAnnotation("") +final class TestTest { + + @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") + private Generic_class1 t1; + + @DeepImmutableFieldAnnotation(value = "T2") + private T2 t2; + + @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") + private T3 t3; + + @DependentImmutableFieldAnnotation(value = "T4", genericString = "T4") + private T4 t4; + + @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") + private T5 t5; + + public TestTest(Generic_class1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + } + +} + +class SimpleMutableClass { public int n = 0;} +final class FinalEmptyClass {} + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1_extString.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1_extString.java deleted file mode 100644 index 3e8efad03c..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1_extString.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; -import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; - -@ShallowImmutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") -public final class Generic_class1_extString { - - @ShallowImmutableFieldAnnotation("") - private T1 t1; - - @DeepImmutableFieldAnnotation(value = "T2") - private T2 t2; - - @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") - private T3 t3; - - @DependentImmutableFieldAnnotation(value = "T4", genericString = "T4") - private T4 t4; - - @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") - private T5 t5; - - public Generic_class1_extString(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ - this.t1 = t1; - this.t2 = t2; - this.t3 = t3; - this.t4 = t4; - this.t5 = t5; - } - -} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class2.java deleted file mode 100644 index 2ba61602d8..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class2.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -public final class Generic_class2 { - - @ImmutableReferenceEscapesAnnotation("") - @DependentImmutableFieldAnnotation(value="T1", genericString = "T1") - private T1 t1; - - @ImmutableReferenceEscapesAnnotation("") - @DependentImmutableFieldAnnotation(value="T2", genericString = "T2") - private T2 t2; - - @ImmutableReferenceEscapesAnnotation("") - @DependentImmutableFieldAnnotation(value="T3", genericString = "T3") - private T3 t3; - - @ImmutableReferenceEscapesAnnotation("") - @DependentImmutableFieldAnnotation(value = "", genericString = "") - private Generic_class1 gc; - - public Generic_class2(T1 t1, T2 t2, T3 t3, FinalEmptyClass fec1, FinalEmptyClass fec2){ - this.t1 = t1; - this.t2 = t2; - this.t3 = t3; - gc = new Generic_class1(fec1, fec2, t1,t2,t3); - } - -} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class3.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class3.java deleted file mode 100644 index a9337d029d..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class3.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -public final class Generic_class3 { - - @ImmutableReferenceEscapesAnnotation("") - @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") - private T1 t1; - - @ImmutableReferenceEscapesAnnotation("") - @DependentImmutableFieldAnnotation(value = "", genericString = "") - private Generic_class2 gc; - - public Generic_class3(T1 t1, FinalEmptyClass fec1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4){ - this.t1 = t1; - gc = new Generic_class2(t1, fec1, fec2, fec3, fec4); - } -} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_deep.java deleted file mode 100644 index 5a0ea1fb72..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_deep.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -public final class Generic_class4_deep { - - @ImmutableReferenceEscapesAnnotation("") - @DeepImmutableFieldAnnotation("") - private Generic_class3 gc; - - public Generic_class4_deep(FinalEmptyClass fec1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4, FinalEmptyClass fec5){ - gc = new Generic_class3(fec1, fec2, fec3, fec4, fec5); - } -} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_shallow.java deleted file mode 100644 index 61a3b720ad..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class4_shallow.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; - -@ShallowImmutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") -public final class Generic_class4_shallow { - - @ImmutableReferenceEscapesAnnotation("") - @ShallowImmutableFieldAnnotation("") - private Generic_class3 gc; - - public Generic_class4_shallow(TrivialMutableClass tmc1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4, FinalEmptyClass fec5){ - gc = new Generic_class3(tmc1, fec2, fec3, fec4, fec5); - } -} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java deleted file mode 100644 index a9282ca633..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/One.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -public class One { - @DependentImmutableFieldAnnotation(value="",genericString = "A") - @ImmutableReferenceEscapesAnnotation("") - private A a; - @DependentImmutableFieldAnnotation(value="",genericString = "B") - @ImmutableReferenceEscapesAnnotation("") - private B b; - @DependentImmutableFieldAnnotation(value="",genericString = "C") - @ImmutableReferenceEscapesAnnotation("") - private C c; - @DependentImmutableFieldAnnotation(value="",genericString = "D") - @ImmutableReferenceEscapesAnnotation("") - private D d; - @MutableFieldAnnotation("") - @MutableReferenceAnnotation("") - public TrivialMutableClass tmc; - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceEscapesAnnotation("") - private Generic_class1 gc1; - public One(A a, B b, C c, D d, TrivialMutableClass tmc){ - this.a = a; - this.b = b; - this.c = c; - this.d = d; - this.tmc = tmc; - this.gc1 = new Generic_class1(this.a,this.b,this.c,this.d, this.tmc); - } - - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java deleted file mode 100644 index a1be293c97..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/OneVirgin.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -public class OneVirgin { - @DependentImmutableFieldAnnotation(value="",genericString = "A") - @ImmutableReferenceEscapesAnnotation("") - private A a; - @DependentImmutableFieldAnnotation(value="",genericString = "B") - @ImmutableReferenceEscapesAnnotation("") - private B b; - @DependentImmutableFieldAnnotation(value="",genericString = "C") - @ImmutableReferenceEscapesAnnotation("") - private C c; - @DependentImmutableFieldAnnotation(value="",genericString = "D") - @ImmutableReferenceEscapesAnnotation("") - private D d; - - @MutableFieldAnnotation("") - @MutableReferenceAnnotation("") - public TrivialMutableClass tmc; - - Generic_class1 gc1; - public OneVirgin(A a, B b, C c, D d, TrivialMutableClass e){ - this.a = a; - this.b = b; - this.c = c; - this.d = d; - this.tmc = tmc; - this.gc1 = new Generic_class1(this.a, this.b, this.c, this.d, this.tmc); - } - - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TestTest.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TestTest.java deleted file mode 100644 index c0a1ef3898..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TestTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -public final class TestTest { - - @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") - private Generic_class1 t1; - - @DeepImmutableFieldAnnotation(value = "T2") - private T2 t2; - - @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") - private T3 t3; - - @DependentImmutableFieldAnnotation(value = "T4", genericString = "T4") - private T4 t4; - - @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") - private T5 t5; - - public TestTest(Generic_class1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ - this.t1 = t1; - this.t2 = t2; - this.t3 = t3; - this.t4 = t4; - this.t5 = t5; - } - -} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TrivialMutableClass.java deleted file mode 100644 index 9caf81eb11..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TrivialMutableClass.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -public class TrivialMutableClass { - - @MutableFieldAnnotation("") - @MutableReferenceAnnotation("") - public int n = 0; - - @MutableFieldAnnotation("") - @MutableReferenceAnnotation("") - public String name = "name"; -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java deleted file mode 100644 index a77f9c5b7a..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Two.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") -public class Two { - - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceEscapesAnnotation("") - private Generic_class1, B, B, B, TrivialMutableClass> gc1; - - public Two(A a, B b, TrivialMutableClass tmc, Generic_class1 gc1) { - this.gc1 = new Generic_class1, B, B, B, TrivialMutableClass>(gc1,b,b,b,tmc); - - } - - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java deleted file mode 100644 index ce57ade261..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/TwoVirgin.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; - -public class TwoVirgin { - @DependentImmutableFieldAnnotation(value="",genericString = "") - @ImmutableReferenceEscapesAnnotation("") - private Generic_class1, B, C, C, C> gc1; - - public TwoVirgin(A a, B b, C c, Generic_class1 gc1) { - this.gc1 = new Generic_class1, B, C, C, C>(gc1,b,c,c,c); - } - - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java deleted file mode 100644 index ec12a20feb..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/DependentImmutableClass.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.trivials; - -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Because of not final class") -@DependentImmutableClassAnnotation("Das dependent immutable field") -public class DependentImmutableClass { - @DependentImmutableFieldAnnotation(value = "Because of type T",genericString = "T") - @ImmutableReferenceEscapesAnnotation("Private effectively final field") - private T t; - public DependentImmutableClass(T t){ - this.t = t; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/EmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/EmptyClass.java deleted file mode 100644 index ef7402982a..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/EmptyClass.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.trivials; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Because not final class") -@DeepImmutableClassAnnotation("Class has no fields but is not final") -public class EmptyClass { -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/FinalEmptyClass.java deleted file mode 100644 index 42c55c83f5..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/FinalEmptyClass.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.trivials; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; - -@DeepImmutableTypeAnnotation("Because of final deep immutable class") -@DeepImmutableClassAnnotation("Class has no fields and is final") -public final class FinalEmptyClass { -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/GenericTypeIsNotUsedAsFieldType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/GenericTypeIsNotUsedAsFieldType.java deleted file mode 100644 index a24f943256..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/GenericTypeIsNotUsedAsFieldType.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.trivials; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Because of not final class") -@DeepImmutableClassAnnotation("Generic Type is not used as field Type") -public class GenericTypeIsNotUsedAsFieldType { - private int n = 0; - GenericTypeIsNotUsedAsFieldType(T t){ - String s = t.toString(); - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/MutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/MutableClass.java deleted file mode 100644 index 853040c499..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/MutableClass.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.trivials; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_mutability.MutableType; - -@MutableType("Because of not final mutable class") -@MutableClassAnnotation("Because of mutable field") -public class MutableClass { - @MutableFieldAnnotation("Because of mutable reference") - @MutableReferenceAnnotation("Because of public field") - public int n = 0; -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/NoDependentImmutableClassBecauseOfPublicField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/NoDependentImmutableClassBecauseOfPublicField.java deleted file mode 100644 index fa4d5cc953..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/NoDependentImmutableClassBecauseOfPublicField.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.trivials; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Because of mutable not final class") -@MutableClassAnnotation("Generic class but public field") -public class NoDependentImmutableClassBecauseOfPublicField { - @MutableFieldAnnotation("Because of mutable reference") - @MutableReferenceAnnotation("Field is public") - public T t; - NoDependentImmutableClassBecauseOfPublicField(T t){ - this.t = t; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java index 59375156c8..250c92f4a3 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java @@ -1,14 +1,69 @@ package org.opalj.fpcf.fixtures.immutability.classes.trivials; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +import org.opalj.fpcf.properties.type_mutability.MutableType; @MutableTypeAnnotation("Because of not final class") @DeepImmutableClassAnnotation("has shallow immutable field") public class ShallowImmutableClass { - @DeepImmutableFieldAnnotation("Because of mutable type") - @ImmutableReferenceNotEscapesAnnotation("Because it is private") + @DeepImmutableFieldAnnotation("Because object can not escape") + @ImmutableReferenceAnnotation("Because it is private") private MutableClass mutableClass = new MutableClass(); } + +@MutableType("Because of not final mutable class") +@MutableClassAnnotation("Because of mutable field") +class MutableClass { + @MutableFieldAnnotation("Because of mutable reference") + @MutableReferenceAnnotation("Because of public field") + public int n = 0; +} + +@MutableTypeAnnotation("Because not final class") +@DeepImmutableClassAnnotation("Class has no fields but is not final") +class EmptyClass { +} + +@DeepImmutableTypeAnnotation("Because of final deep immutable class") +@DeepImmutableClassAnnotation("Class has no fields and is final") +final class FinalEmptyClass { +} + +@MutableTypeAnnotation("Because of not final class") +@DependentImmutableClassAnnotation("Das dependent immutable field") +class DependentImmutableClass { + @DependentImmutableFieldAnnotation(value = "Because of type T",genericString = "T") + @ImmutableReferenceAnnotation("Private effectively final field") + private T t; + public DependentImmutableClass(T t){ + this.t = t; + } +} +@MutableTypeAnnotation("Because of not final class") +@DeepImmutableClassAnnotation("Generic Type is not used as field Type") +class GenericTypeIsNotUsedAsFieldType { + private int n = 0; + GenericTypeIsNotUsedAsFieldType(T t){ + String s = t.toString(); + } +} + +@MutableTypeAnnotation("Because of mutable not final class") +@MutableClassAnnotation("Generic class but public field") +class ClassWithGenericPublicField { + @MutableFieldAnnotation("Because of mutable reference") + @MutableReferenceAnnotation("Field is public") + public T t; + ClassWithGenericPublicField(T t){ + this.t = t; + } +} From 37d668a6703428cc957592c94aec23ca613f397e Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 13:39:00 +0200 Subject: [PATCH 229/327] rearranging tests --- .../immutability/type/EmptyClass.java | 9 ------- .../immutability/type/FinalEmptyClass.java | 9 ------- .../WithMutableAndImmutableFieldType.java | 25 +++++++++++++++---- 3 files changed, 20 insertions(+), 23 deletions(-) delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/EmptyClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/FinalEmptyClass.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/EmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/EmptyClass.java deleted file mode 100644 index 0f4e690167..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/EmptyClass.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.type; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_mutability.MutableType; - -@MutableType("Class has no fields but is not final") -@DeepImmutableClassAnnotation("Class has no fields") -public class EmptyClass { -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/FinalEmptyClass.java deleted file mode 100644 index 02fd29241b..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/type/FinalEmptyClass.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.type; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; - -@DeepImmutableClassAnnotation("Class has no fields and is final") -@DeepImmutableTypeAnnotation("Class has no fields") -public 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 index b12a68c2fc..fb4e2166c5 100644 --- 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 @@ -1,21 +1,36 @@ package org.opalj.fpcf.fixtures.immutability.type; -import org.opalj.fpcf.fixtures.immutability.field.TrivialMutableClass; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; +import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +import org.opalj.fpcf.properties.type_mutability.MutableType; @DeepImmutableTypeAnnotation("has shallow and mutable fields") @DeepImmutableClassAnnotation("has shallow and immutable fields") public final class WithMutableAndImmutableFieldType { @DeepImmutableFieldAnnotation("immutable reference and deep immutable type") - @ImmutableReferenceNotEscapesAnnotation("private field") + @ImmutableReferenceAnnotation("private field") private FinalEmptyClass fec = new FinalEmptyClass(); @DeepImmutableFieldAnnotation("imm reference and mutable type") - @ImmutableReferenceNotEscapesAnnotation("private field") - private TrivialMutableClass tmc = new TrivialMutableClass(); + @ImmutableReferenceAnnotation("private field") + private SimpleMutableClass tmc = new SimpleMutableClass(); +} + +@MutableType("Class has no fields but is not final") +@DeepImmutableClassAnnotation("Class has no fields") +class EmptyClass { +} +@DeepImmutableClassAnnotation("Class has no fields and is final") +@DeepImmutableTypeAnnotation("Class has no fields") +final class FinalEmptyClass { } + +@MutableTypeAnnotation("") +@MutableClassAnnotation("") +class SimpleMutableClass{ public int n=0;} From 8581e32231f27452bdbca6d20430f5e4fef4b27e Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 13:40:15 +0200 Subject: [PATCH 230/327] rearranging tests --- .../DCL.java | 264 ++++++++++++++++++ .../DoubleCheckedLockingClass1.java | 22 -- .../DoubleCheckedLockingClass10.java | 20 -- .../DoubleCheckedLockingClass11.java | 26 -- .../DoubleCheckedLockingClass12.java | 22 -- .../DoubleCheckedLockingClass13.java | 22 -- .../DoubleCheckedLockingClass14.java | 23 -- .../DoubleCheckedLockingClass15.java | 24 -- .../DoubleCheckedLockingClass16.java | 24 -- .../DoubleCheckedLockingClass17.java | 28 -- .../DoubleCheckedLockingClass18.java | 23 -- .../DoubleCheckedLockingClass19.java | 20 -- .../DoubleCheckedLockingClass2.java | 23 -- .../DoubleCheckedLockingClass3.java | 20 -- .../DoubleCheckedLockingClass4.java | 19 -- .../DoubleCheckedLockingClass5.java | 20 -- .../DoubleCheckedLockingClass6.java | 24 -- .../DoubleCheckedLockingClass7.java | 24 -- .../DoubleCheckedLockingClass8.java | 23 -- .../DoubleCheckedLockingClass9.java | 20 -- ...leCheckedLockingClassWithStaticFields.java | 22 -- .../NotDeterministicLazyIntInstantiation.java | 17 -- .../SimpleLazyInstantiation.java | 23 ++ .../SimpleLazyIntInstantiation.java | 13 - .../SimpleLazyObjectsInstantiation.java | 14 - 25 files changed, 287 insertions(+), 493 deletions(-) delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass10.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass11.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass12.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass13.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass14.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass15.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass16.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass17.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass18.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass19.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass3.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass4.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass5.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass7.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass8.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass9.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/NotDeterministicLazyIntInstantiation.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java index 5f0a1a0d78..86fb72c1a0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java @@ -1,5 +1,7 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; import java.util.Random; @@ -123,3 +125,265 @@ synchronized public static org.omg.CORBA.TypeCode type () return __typeCode; } } + +class DCL5 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + DCL5 instance; + + public DCL5 DCL5(){ + if(instance==null){ + synchronized(this){ + if(instance==null){ + try{ + instance = new DCL5(); + } + catch (Exception e) + { + throw e; + } + } + } + } + return instance; + } +} + +class DCL6 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DCL6 instance; + + public DCL6 getInstance() throws Exception{ + if(instance==null){ + synchronized(this){ + if(instance==null){ + instance = new DCL6(); + } + } + } + return instance; + } +} + +class SimpleLockingSynchronizedBlock { + @LazyInitializedThreadSafeReferenceAnnotation("") + private SimpleLockingSynchronizedBlock instance; + private SimpleLockingSynchronizedBlock() {} + public SimpleLockingSynchronizedBlock getInstance(){ + synchronized(this){ + if(instance==null) + instance = new SimpleLockingSynchronizedBlock(); + } + return instance; + } +} + +class SimpleLockingSynchronizedFunction1 { + @LazyInitializedThreadSafeReferenceAnnotation("") + private SimpleLockingSynchronizedFunction1 instance; + private SimpleLockingSynchronizedFunction1() {} + public synchronized SimpleLockingSynchronizedFunction1 getInstance() { + if(instance ==null) + instance = new SimpleLockingSynchronizedFunction1(); + return instance; + } +} + + + + +class NoDCL1 { + + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + NoDCL1 instance; + + public NoDCL1 NoDCL1(){ + if(instance==null){ + synchronized(this){ + if(instance==null){ + try{ + instance = new NoDCL1(); + } + catch(Exception e){} + }}} + return instance; + } + +} + +class NoDCL2 { + + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + NoDCL2 instance; + + public NoDCL2 NoDCL1(){ + try{ + if(instance==null){ + synchronized(this){ + if(instance==null){ + + instance = new NoDCL2(); + } + + }}}catch(Exception e){} + return instance; + } + +} + +class NoDCL3 { + + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + NoDCL3 instance; + + public NoDCL3 NoDCL1(){ + + if(instance==null){ + try{ + synchronized(this){ + if(instance==null){ + + instance = new NoDCL3(); + } + }}catch(Exception e){}} + return instance; + } + +} + +class NoDCL4 { + + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + NoDCL4 instance; + + public NoDCL4 NoDCL1(){ + + if(instance==null){ + try{ + synchronized(this){ + if(instance==null){ + try{ + instance = new NoDCL4(); + } + catch (Exception e) + { + throw e; + } + } + }}catch(Exception e){}} + return instance; + } + +} + + +class NoDCL6 { + + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + NoDCL6 instance; + + public NoDCL6 NoDCL6() throws IndexOutOfBoundsException{ + if(instance==null){ + synchronized(this){ + if(instance==null){ + try{ + instance = new NoDCL6(); + } + catch (Exception e) + { + throw new IndexOutOfBoundsException(); + } + } + } + } + return instance; + } +} + +class NoDCLIntRandom { + + @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Random function is not deterministic") + private int r; + + public int getR(){ + if(r==0){ + r = new Random().nextInt(); + } + + return r; + } +} + +class DoubleCheckedLockingClassWithStaticFields { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private static DoubleCheckedLockingClassWithStaticFields instance; + public static DoubleCheckedLockingClassWithStaticFields getInstance() { + if(instance==null){ + synchronized(DoubleCheckedLockingClassWithStaticFields.class) { + if(instance==null){ + instance = new DoubleCheckedLockingClassWithStaticFields(); + } + } + } + return instance; + } +} + +class DoubleCheckedLockingClassArray1 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private Object[] instance; + public Object[] getInstance() { + if(instance==null){ + synchronized(this) { + if(instance==null){ + instance = new Object[10]; + } + } + } + return instance; + } +} + +class DoubleCheckedLockingClass19 { + + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + private DoubleCheckedLockingClass19 instance; + + public DoubleCheckedLockingClass19 getInstance() { + if (instance == null) { + synchronized (this) { + if (instance == null) { + } + } + instance = new DoubleCheckedLockingClass19(); + } + return instance; + } +} + +class ArrayLazyInitializationNotThreadSafe { + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("While the initialization phase there is no lock") + int[] values; + + public int[] getValues(){ + if(values==null){ + values = new int[] {1,2,3,4,5,6,7,8,9,10}; + } + return values; + } +} + +class ArrayLazyInitializationThreadSafe { + @LazyInitializedThreadSafeReferenceAnnotation("it is a lock via the synchronized method") + int[] values; + + public synchronized int[] getValues(){ + if(values==null){ + values = new int[] {1,2,3,4,5,6,7,8,9,10}; + } + return values; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java deleted file mode 100644 index 412efe3af4..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass1.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; - -public class DoubleCheckedLockingClass1{ - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass1 instance; - public DoubleCheckedLockingClass1 getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - instance = new DoubleCheckedLockingClass1(); - } - } - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass10.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass10.java deleted file mode 100644 index bd286e37dc..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass10.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; - -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; - -public class DoubleCheckedLockingClass10 { - - @MutableReferenceAnnotation("") - private DoubleCheckedLockingClass10 instance; - - public DoubleCheckedLockingClass10 getInstance() { - if (instance == null) { - synchronized (this) { - if (instance == null) { - } - } - } - instance = new DoubleCheckedLockingClass10(); - return instance; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass11.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass11.java deleted file mode 100644 index 34f2c5286c..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass11.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; - -public class DoubleCheckedLockingClass11 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass11 instance; - public DoubleCheckedLockingClass11 getInstance() { - for(int i=0; i<1000; i++){} - if(instance==null){ - for(int i=0; i<1000; i++){} - synchronized(this) { - for(int i=0; i<1000; i++){} - if(instance==null){ - for(int i=0; i<1000; i++){} - instance = new DoubleCheckedLockingClass11(); - } - } - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass12.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass12.java deleted file mode 100644 index 06cc162bc6..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass12.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; - -public class DoubleCheckedLockingClass12 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass12 instance; - public DoubleCheckedLockingClass12 getInstance() { - if(instance==null){ - } - synchronized(this) { - if(instance==null){ - instance = new DoubleCheckedLockingClass12(); - } - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass13.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass13.java deleted file mode 100644 index 33e0757970..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass13.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; - -public class DoubleCheckedLockingClass13 { - - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") - private DoubleCheckedLockingClass13 instance; - public DoubleCheckedLockingClass13 getInstance() { - if(instance==null){ - } - synchronized(this) { - } - if(instance==null){ - instance = new DoubleCheckedLockingClass13(); - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass14.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass14.java deleted file mode 100644 index b25d50eca7..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass14.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; - -public class DoubleCheckedLockingClass14 { - - @MutableReferenceAnnotation("") - private DoubleCheckedLockingClass14 instance; - public DoubleCheckedLockingClass14 getInstance() { - if(instance==null){ - } - synchronized(this) { - } - if(instance==null){ - - } - instance = new DoubleCheckedLockingClass14(); - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass15.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass15.java deleted file mode 100644 index ce917e888d..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass15.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; - -public class DoubleCheckedLockingClass15 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass15 instance; - public DoubleCheckedLockingClass15 getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - if(instance==null) { - instance = new DoubleCheckedLockingClass15(); - } - } - } - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass16.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass16.java deleted file mode 100644 index 28151704f0..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass16.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; - -public class DoubleCheckedLockingClass16 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass16 instance; - public DoubleCheckedLockingClass16 getInstance() { - if(instance==null){ - if(instance==null){ - synchronized(this) { - if(instance==null) { - instance = new DoubleCheckedLockingClass16(); - } - } - } - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass17.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass17.java deleted file mode 100644 index adf3b8c58a..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass17.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; - -public class DoubleCheckedLockingClass17 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass17 instance; - public DoubleCheckedLockingClass17 getInstance() { - if(instance==null){ - if(instance==null) { - if (instance == null) { - synchronized (this) { - if (instance == null) { - if (instance == null) { - instance = new DoubleCheckedLockingClass17(); - } - } - } - } - } - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass18.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass18.java deleted file mode 100644 index da919e1e39..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass18.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; - -public class DoubleCheckedLockingClass18 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass18 instance; - private boolean lock = true; - public DoubleCheckedLockingClass18 getInstance() { - if(instance==null && lock){ - synchronized(this) { - if(instance==null && lock){ - instance = new DoubleCheckedLockingClass18(); - } - } - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass19.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass19.java deleted file mode 100644 index fb4c2db095..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass19.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; - -public class DoubleCheckedLockingClass19 { - - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") - private DoubleCheckedLockingClass19 instance; - - public DoubleCheckedLockingClass19 getInstance() { - if (instance == null) { - synchronized (this) { - if (instance == null) { - } - } - instance = new DoubleCheckedLockingClass19(); - } - return instance; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java deleted file mode 100644 index ddcc90a677..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass2.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; - -public class DoubleCheckedLockingClass2 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass2 instance; - private DoubleCheckedLockingClass2 instance2 = new DoubleCheckedLockingClass2(); - public DoubleCheckedLockingClass2 getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - instance = new DoubleCheckedLockingClass2(); - } - } - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass3.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass3.java deleted file mode 100644 index 09c1415cb5..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass3.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; - -public class DoubleCheckedLockingClass3 { - - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") - private DoubleCheckedLockingClass3 instance; - public DoubleCheckedLockingClass3 getInstance() { - if(instance==null){ - synchronized(DoubleCheckedLockingClass3.class) { - instance = new DoubleCheckedLockingClass3(); - } - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass4.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass4.java deleted file mode 100644 index a9a2365556..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass4.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; - -public class DoubleCheckedLockingClass4 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass4 instance; - public DoubleCheckedLockingClass4 getInstance() { - synchronized(DoubleCheckedLockingClass4.class) { - if(instance==null){ - instance = new DoubleCheckedLockingClass4(); - } - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass5.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass5.java deleted file mode 100644 index 86992fc679..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass5.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; - -public class DoubleCheckedLockingClass5 { - - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") - private DoubleCheckedLockingClass5 instance; - public DoubleCheckedLockingClass5 getInstance() { - if(instance==null){ - if(instance==null){ - instance = new DoubleCheckedLockingClass5(); - } - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java deleted file mode 100644 index 5b2fd97902..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass6.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; - -public class DoubleCheckedLockingClass6 { - - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") - private DoubleCheckedLockingClass6 instance; - public DoubleCheckedLockingClass6 getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - //instance = new DoubleCheckedLockingClass6(); - } - instance = new DoubleCheckedLockingClass6(); - } - - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass7.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass7.java deleted file mode 100644 index 2bd6da16cd..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass7.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; - -public class DoubleCheckedLockingClass7 { - - @MutableReferenceAnnotation("") - private DoubleCheckedLockingClass7 instance; - public DoubleCheckedLockingClass7 getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - instance = new DoubleCheckedLockingClass7(); - } - - } - instance = new DoubleCheckedLockingClass7(); - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass8.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass8.java deleted file mode 100644 index 7747635e2d..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass8.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; - -public class DoubleCheckedLockingClass8 { - - @MutableReferenceAnnotation("") - private DoubleCheckedLockingClass8 instance; - public DoubleCheckedLockingClass8 getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - instance = new DoubleCheckedLockingClass8(); - } - } - } - instance = new DoubleCheckedLockingClass8(); - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass9.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass9.java deleted file mode 100644 index 7c3bebf174..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClass9.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; - -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; - -public class DoubleCheckedLockingClass9 { - - @MutableReferenceAnnotation("") - private DoubleCheckedLockingClass9 instance; - - public DoubleCheckedLockingClass9 getInstance() { - if (instance != null) { - synchronized (this) { - if (instance != null) { - instance = new DoubleCheckedLockingClass9(); - } - } - } - return instance; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java deleted file mode 100644 index 76a31ff643..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization;//source: https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html - - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; - -public class DoubleCheckedLockingClassWithStaticFields { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private static DoubleCheckedLockingClassWithStaticFields instance; - public static DoubleCheckedLockingClassWithStaticFields getInstance() { - if(instance==null){ - synchronized(DoubleCheckedLockingClassWithStaticFields.class) { - if(instance==null){ - instance = new DoubleCheckedLockingClassWithStaticFields(); - } - } - } - return instance; - } -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/NotDeterministicLazyIntInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/NotDeterministicLazyIntInstantiation.java deleted file mode 100644 index ad79d6b4cf..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/NotDeterministicLazyIntInstantiation.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; - -import java.util.Random; - -public class NotDeterministicLazyIntInstantiation { - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") - private int r; - - public int getR(){ - if(r==0){ - r = new Random().nextInt(); - } - return r; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java index a68146f07d..4c12581277 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java @@ -1,5 +1,6 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; public class SimpleLazyInstantiation{ @@ -12,3 +13,25 @@ public static SimpleLazyInstantiation init() { return instance; } } + +class SimpleLazyIntInstantiation{ + @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("") + private int i = 0; + public int hashcode() { + if(i==0) + i = 5; + return i; + } +} + +class SimpleLazyObjectsInstantiation{ + @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + private static SimpleLazyObjectsInstantiation instance; + public static SimpleLazyObjectsInstantiation getInstance() { + if(instance==null) + instance = new SimpleLazyObjectsInstantiation(); + return instance; + } +} + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java deleted file mode 100644 index d576eca7f8..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyIntInstantiation.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; - -public class SimpleLazyIntInstantiation{ - @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("") - 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/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java deleted file mode 100644 index 7a88cc1316..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyObjectsInstantiation.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; - -public class SimpleLazyObjectsInstantiation{ - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") - private static SimpleLazyObjectsInstantiation instance; - public static SimpleLazyObjectsInstantiation getInstance() { - if(instance==null) - instance = new SimpleLazyObjectsInstantiation(); - return instance; - } -} - From b7fe248cd7c2eabace1e0db20f4b2ffddc7fd48b Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 13:42:02 +0200 Subject: [PATCH 231/327] revert immutable reference property changes --- ...java => ImmutableReferenceAnnotation.java} | 4 +-- ...mmutableReferenceNotEscapesAnnotation.java | 29 ------------------- 2 files changed, 2 insertions(+), 31 deletions(-) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/{ImmutableReferenceEscapesAnnotation.java => ImmutableReferenceAnnotation.java} (90%) delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceNotEscapesAnnotation.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceEscapesAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java similarity index 90% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceEscapesAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java index bed16e38aa..15f389d409 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceEscapesAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java @@ -13,10 +13,10 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = ImmutableReferenceEscapesMatcher.class) +@PropertyValidator(key = "ReferenceImmutability",validator = ImmutableReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface ImmutableReferenceEscapesAnnotation { +public @interface ImmutableReferenceAnnotation { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceNotEscapesAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceNotEscapesAnnotation.java deleted file mode 100644 index f9021299f4..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceNotEscapesAnnotation.java +++ /dev/null @@ -1,29 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.reference_immutability; - -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.reference.L0ReferenceImmutabilityAnalysis; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Annotation to state that the annotated reference is immutable - * - * @author Tobias Peter Roth - */ -@PropertyValidator(key = "ReferenceImmutability",validator = ImmutableReferenceNotEscapesMatcher.class) -@Documented -@Retention(RetentionPolicy.CLASS) -public @interface ImmutableReferenceNotEscapesAnnotation { - - /** - * A short reasoning of this property. - */ - String value();// default = "N/A"; - - Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; - -} From d77434932f2d0343a5d1512a1195cfbade24c778 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 29 Jul 2020 14:04:35 +0200 Subject: [PATCH 232/327] current status, still WIP --- .../org/opalj/support/info/Immutability.scala | 549 +++++++++--------- 1 file changed, 284 insertions(+), 265 deletions(-) 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 index 2223b3769f..d962444271 100644 --- 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 @@ -3,7 +3,6 @@ package org.opalj.support.info import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.Project.JavaClassFileReader -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis @@ -37,13 +36,10 @@ import org.opalj.br.fpcf.properties.MutableClass import org.opalj.br.fpcf.properties.MutableType_new import org.opalj.br.fpcf.properties.ShallowImmutableClass import org.opalj.br.fpcf.properties.ShallowImmutableType -import org.opalj.fpcf.PropertyStoreContext -import org.opalj.log.LogContext import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new import org.opalj.bytecode.JRELibraryFolder - import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new @@ -54,91 +50,90 @@ import org.opalj.br.analyses.Project import org.opalj.log.DevNullLogger import org.opalj.log.GlobalLogContext import org.opalj.log.OPALLogger - +import java.io.File +import java.io.BufferedWriter +import java.io.FileWriter +import java.util.Calendar +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.PropertyStoreContext +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory +import org.opalj.support.info.RunningAnalyses.RunningAnalysis +import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater +import java.nio.file.FileSystems /** - * Determines the immutability of ref, fields, classes and types of a project + * Determines the immutability of references, fields, classes and types of a project * * @author Tobias Peter Roth */ -object Immutability { - - import java.io.File - - import org.opalj.support.info.Immutability.RunningAnalysis.RunningAnalysis - - //override def title: String = "Immutability Analysis" - - //override def description: String = "determines the immutability of references, fields, classes and types" - - object RunningAnalysis extends Enumeration { - type RunningAnalysis = Value - val References, Fields, Classes, Types, All = Value - } - import java.io.BufferedWriter - import java.io.FileWriter - import java.util.Calendar +object RunningAnalyses extends Enumeration { + type RunningAnalysis = Value + val References, Fields, Classes, Types, All = Value +} - import RunningAnalysis._ +object Immutability { def evaluate( - analysis: RunningAnalysis, - numThreads: Int, - cp: File, - resultsFolder: Path, - timeEvaluation: Boolean - + cp: File, + analysis: RunningAnalysis, + numThreads: Int, + projectDir: Option[String], + libDir: Option[String], + resultsFolder: Path, + timeEvaluation: Boolean, + threadEvaluation: Boolean, + withoutJDK: Boolean, + isLibrary: Boolean, + closedWorldAssumption: Boolean ): BasicReport = { - import scala.collection.mutable OPALLogger.updateLogger(GlobalLogContext, DevNullLogger) - val classFiles = JavaClassFileReader().ClassFiles(cp) - /* - 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 classFiles = projectDir match { + case Some(dir) ⇒ JavaClassFileReader().ClassFiles(cp.toPath.resolve(dir).toFile) + case None ⇒ JavaClassFileReader().ClassFiles(cp) + } - val JDKFiles = /*if (withoutJDK) Traversable.empty - else */ JavaClassFileReader().ClassFiles(JRELibraryFolder) + val libFiles = libDir match { + case Some(dir) ⇒ JavaClassFileReader().ClassFiles(cp.toPath.resolve(dir).toFile) + case None ⇒ Traversable.empty + } - //println("JDKFiles: "+JDKFiles.size) + val JDKFiles = if (withoutJDK) Traversable.empty + else JavaClassFileReader().ClassFiles(JRELibraryFolder) - val project = //time { + // TODO: use variables for the constants + implicit var config: Config = if (isLibrary) + ConfigFactory.load("LibraryProject.conf") + else + ConfigFactory.load("ApplicationProject.conf") + + // TODO: in case of application this value is already set + if (closedWorldAssumption) { + import com.typesafe.config.ConfigValueFactory + config = config.withValue( + "org.opalj.br.analyses.cg.ClassExtensibilityKey.analysis", + ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.ClassHierarchyIsNotExtensible") + ) + } + + val project = Project( - classFiles, // JDKFiles, - JDKFiles, //libFiles ++ + classFiles, + libFiles ++ JDKFiles, libraryClassFilesAreInterfacesOnly = false, Traversable.empty ) - //} { t ⇒ projectTime = t.toSeconds } - - // The following measurements (t) are done such that the results are comparable with the - // reactive async approach developed by P. Haller and Simon Gries. - //var t = Seconds.None - //val ps = time { - - var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - - val ra: RunningAnalysis = All val referenceDependencies: List[FPCFAnalysisScheduler] = List( EagerL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, LazyInterProceduralEscapeAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyLxTypeImmutabilityAnalysis_new + LazyFieldLocalityAnalysis ) val fieldDependencies: List[FPCFAnalysisScheduler] = List( LazyL0ReferenceImmutabilityAnalysis, @@ -179,7 +174,7 @@ object Immutability { LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis ) - val allImmAnalysisDepencies: List[FPCFAnalysisScheduler] = + val allImmAnalysisDependencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, @@ -194,66 +189,49 @@ object Immutability { LazyFieldLocalityAnalysis ) var dependencies: List[FPCFAnalysisScheduler] = List.empty - if (ra == References) + if (analysis == RunningAnalyses.References) dependencies = referenceDependencies - if (ra == Fields) + if (analysis == RunningAnalyses.Fields) dependencies = fieldDependencies - if (ra == Classes) + if (analysis == RunningAnalyses.Classes) dependencies = classDepencencies - if (ra == Types) + if (analysis == RunningAnalyses.Types) dependencies = typeDependencies - if (ra == All) - dependencies = allImmAnalysisDepencies - - //var timeResults: List[Seconds] = List.empty - val threadTimeResults: mutable.HashMap[Int, List[Seconds]] = mutable.HashMap.empty - var threads: List[Int] = List.empty - if (timeEvaluation) - threads = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24) - else - threads = List(0) - - for (thread ← threads) { - var timeResults: List[Seconds] = List.empty - for (i ← 1 to (if (timeEvaluation) 10 else 1)) { - import java.net.URL - val newProject: Project[URL] = project.recreate() - val analysesManager = newProject.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) - - analysesManager.project.recreate() - time { - propertyStore = analysesManager - .runAll( - dependencies - ) - ._1 - //val numThreads = 18 - newProject.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.ParTasksManagerConfig.MaxThreads = thread - // FIXME: this property store is broken - org.opalj.fpcf.par.PKECPropertyStore(context: _*) - } - } - ) - //new PKECPropertyStore(context, project.logContext) - //org.opalj.fpcf.par.ParTasksManagerConfig.MaxThreads = 8 - //var context: Map[Class[_], AnyRef] = Map.empty - - propertyStore.waitOnPhaseCompletion() - - } { t ⇒ - analysisTime = t.toSeconds + if (analysis == RunningAnalyses.All) + dependencies = allImmAnalysisDependencies + + L2PurityAnalysis_new.setRater(Some(SystemOutLoggingAllExceptionRater)) + + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + + project.getOrCreateProjectInformationKeyInitializationData( + PropertyStoreKey, + (context: List[PropertyStoreContext[AnyRef]]) ⇒ { + import org.opalj.log.LogContext + implicit val lg: LogContext = project.logContext + 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(context: _*) } - timeResults = analysisTime :: timeResults } - threadTimeResults.put(thread, timeResults) + ) + + time { + propertyStore = analysesManager + .runAll( + dependencies + ) + ._1 + propertyStore.waitOnPhaseCompletion() + + } { t ⇒ + analysisTime = t.toSeconds } val stringBuilderResults: StringBuilder = new StringBuilder() @@ -261,9 +239,8 @@ object Immutability { val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet - println("all fields in project class files: "+allfieldsInProjectClassFiles.size) //References - val mutableReferences = propertyStore + var mutableReferences = propertyStore .finalEntities(MutableReference) .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) .toList @@ -287,38 +264,41 @@ object Immutability { .entities( eps ⇒ //allfieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field]) && eps.isFinal && (eps.asFinal.p match { - case ImmutableReference(_) ⇒ true - case _ ⇒ false + case ImmutableReference ⇒ true + case _ ⇒ false }) ) .toList .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) - stringBuilderResults.append( - s""" - | mutable References: - | ${mutableReferences.mkString("|| mutable Reference \n")} - | - | lazy initalized not thread safe and not deterministic references: - | ${notThreadSafeOrNotDeterministicLazyInitialization.mkString("|| no ts & n dt ref\n")} - | - | lazy initialized not thread safe but deterministic references: - | ${lazyInitializedReferencesNotThreadSafeButDeterministic.mkString("|| li no ts b dt\n")} - | - | lazy initialized thread safe reference: - | ${lazyInitializedReferencesThreadSafe.mkString("|| li thread safe\n")} - | - | immutable Reference: - | ${immutableReferences.mkString("|| immutable References \n")} - | - |""".stripMargin - ) + //Test.... + mutableReferences = mutableReferences ++ notThreadSafeOrNotDeterministicLazyInitialization + // for comparison reasons + if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.References) { + stringBuilderResults.append(s""" +| mutable References: +| ${mutableReferences.mkString("|| mutable Reference \n")} +| +| lazy initalized not thread safe and not deterministic references: +| ${notThreadSafeOrNotDeterministicLazyInitialization.sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString).mkString("|| no ts & n dt ref\n")} +| +| lazy initialized not thread safe but deterministic references: +| ${lazyInitializedReferencesNotThreadSafeButDeterministic.mkString("|| li no ts b dt\n")} +| +| lazy initialized thread safe reference: +| ${lazyInitializedReferencesThreadSafe.mkString("|| li thread safe\n")} +| +| immutable Reference: +| ${immutableReferences.mkString("|| immutable References \n")} +| +|""".stripMargin) + } //Fields val mutableFields = propertyStore .finalEntities(MutableField) .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList + .toList.sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) val shallowImmutableFields = propertyStore .finalEntities(ShallowImmutableField) .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) @@ -333,24 +313,27 @@ object Immutability { .finalEntities(DeepImmutableField) .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) .toList - stringBuilderResults.append( - s""" + if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.Fields) { + stringBuilderResults.append( + s""" | 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")} | |""".stripMargin - ) + ) - //Classes + } + //Classes val mutableClasses = propertyStore .finalEntities(MutableClass) .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) @@ -379,8 +362,26 @@ object Immutability { .filter(!deepImmutableClassesInterfaces.toSet.contains(_)) .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - //Types + if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.Classes) { + stringBuilderResults.append( + s""" + | mutable classes: + | ${mutableClasses.mkString(" || mutable class \n")} + | + | shallow immutable classes: + | ${shallowImmutableClasses.mkString(" || shallow immutable classes \n")} + | + | dependent immutable classes: + | ${dependentImmutableClasses.mkString(" || dependent immutable classes \n")} + | + | deep immutable classes: + | ${deepImmutableClasses.mkString(" || deep immutable classes \n")} + | + |""".stripMargin + ) + } + //Types val allProjectClassFilesIterator = project.allProjectClassFiles val types = allProjectClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType).toSet @@ -409,86 +410,132 @@ object Immutability { types.contains(x.asInstanceOf[ObjectType]) }) .toList + if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.Types) { + stringBuilderResults.append( + s""" + | mutable types: + | ${mutableTypes.mkString(" || mutable types \n")} + | + | shallow immutable types: + | ${shallowImmutableTypes.mkString(" || shallow immutable types \n")} + | + | dependent immutable types: + | ${dependentImmutableTypes.mkString(" || dependent immutable types \n")} + | + | deep immutable types: + | ${deepImmutableTypes.mkString(" || deep immutable types\n")} + |""".stripMargin + ) + } + val stringBuilderAmounts: StringBuilder = new StringBuilder - if (ra == References || ra == All) { + if (analysis == RunningAnalyses.References || analysis == RunningAnalyses.All) { stringBuilderAmounts.append( s""" - | mutable References: ${mutableReferences.size} - | lazy initialized not thread safe or not deterministic ref.: ${notThreadSafeOrNotDeterministicLazyInitialization.size} - | lazy initialization not thread safe but deterministic: ${lazyInitializedReferencesNotThreadSafeButDeterministic.size} - | lazy initialization thread safe: ${lazyInitializedReferencesThreadSafe.size} - | immutable references: ${immutableReferences.size} - |""".stripMargin + | mutable References: ${mutableReferences.size} + | lazy initialized not thread safe or not deterministic ref.: ${notThreadSafeOrNotDeterministicLazyInitialization.size} + | lazy initialization not thread safe but deterministic: ${lazyInitializedReferencesNotThreadSafeButDeterministic.size} + | lazy initialization thread safe: ${lazyInitializedReferencesThreadSafe.size} + | immutable references: ${immutableReferences.size} + | references: ${allfieldsInProjectClassFiles.size} + |""".stripMargin ) } - if (ra == Fields || ra == All) { + if (analysis == RunningAnalyses.Fields || analysis == RunningAnalyses.All) { stringBuilderAmounts.append( s""" - | mutable fields: ${mutableFields.size} - | shallow immutable fields: ${shallowImmutableFields.size} - | depenent immutable fields: ${dependentImmutableFields.size} - | deep immutable fields: ${deepImmutableFields.size} - |""".stripMargin + | mutable fields: ${mutableFields.size} + | shallow immutable fields: ${shallowImmutableFields.size} + | dependent immutable fields: ${dependentImmutableFields.size} + | deep immutable fields: ${deepImmutableFields.size} + | fields: ${allfieldsInProjectClassFiles.size} + |""".stripMargin ) } - if (ra == Classes || ra == All) { + if (analysis == RunningAnalyses.Classes || analysis == RunningAnalyses.All) { stringBuilderAmounts.append( s""" - | mutable classes: ${mutableClasses.size} - | shallow immutable classes: ${shallowImmutableClasses.size} - | depenent immutable classes: ${dependentImmutableClasses.size} - | deep immutable classes: ${deepImmutableClasses.size} - |""".stripMargin + | mutable classes: ${mutableClasses.size} + | shallow immutable classes: ${shallowImmutableClasses.size} + | dependent immutable classes: ${dependentImmutableClasses.size} + | deep immutable classes: ${deepImmutableClasses.size} + | classes: ${allProjectClassFilesIterator.size} + | + |""".stripMargin ) } - if (ra == Types || ra == All) + if (analysis == RunningAnalyses.Types || analysis == RunningAnalyses.All) stringBuilderAmounts.append( s""" - | mutable types: ${mutableTypes.size} - | shallow immutable types: ${shallowImmutableTypes.size} - | dependent immutable types: ${dependentImmutableTypes.size} - | deep immutable types: ${deepImmutableTypes.size} - | - |""".stripMargin + | mutable types: ${mutableTypes.size} + | shallow immutable types: ${shallowImmutableTypes.size} + | dependent immutable types: ${dependentImmutableTypes.size} + | deep immutable types: ${deepImmutableTypes.size} + | + |""".stripMargin ) stringBuilderAmounts.append( s""" - |took $analysisTime seconds - |""".stripMargin + | running ${analysis.toString} analysis + |took $analysisTime seconds + | with $numThreads threads + |""".stripMargin ) - - /*(o._1, o._2.iterator.fold(0.0, - {(x:Double,y:Seconds)⇒x+y.timeSpan})))*/ - - threadTimeResults.map(x ⇒ (x._1, x._2.fold(Seconds.None)((x, y) ⇒ x + y).timeSpan / threadTimeResults.size)) - + // ${stringBuilderResults.toString()} println( - s""" ${stringBuilderResults.toString()} - | - | ${stringBuilderAmounts.toString()} - | - | Time Results: - | ${threadTimeResults.map(x ⇒ s"""${x._1} Thread :: as average ${x._2} seconds""")} - | - |"""".stripMargin + s""" +| +| ${stringBuilderAmounts.toString()} +| +| Time Results: +| +| $numThreads Threads :: took $analysisTime +| +|""".stripMargin ) + println("resultsfolder: "+resultsFolder) + // + /*val l = mutableReferences.filter(x ⇒ !mutableFields.toSet.contains(x)) + println( + s""" + | mut reference not contained in mutable fields: + | ${l.head} + | + | + | field imm: + | ${ + import org.opalj.br.fpcf.properties.FieldImmutability + import org.opalj.br.fpcf.properties.ReferenceImmutability + propertyStore(l.head, ReferenceImmutability.key).toString+"/n"+ + propertyStore(l.head, FieldImmutability.key).toString + } + |""".stripMargin + + )*/ val calendar = Calendar.getInstance() - val file = new File( - s"${resultsFolder.toAbsolutePath.toString}/wholeImmResult_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) - file.createNewFile() - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(s""" ${stringBuilderResults.toString()} - | - | ${stringBuilderAmounts.toString()} - |"""".stripMargin) - bw.close() + if (resultsFolder != null) { + val file = new File( + s"${if (resultsFolder != null) resultsFolder.toAbsolutePath.toString else "."}"+ + s""+ + s"/${analysis.toString}_${calendar.get(Calendar.YEAR)}_"+ + s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ + s"${calendar.get(Calendar.MILLISECOND)}_${numThreads}Threads.txt" + ) + file.createNewFile() + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(s""" ${stringBuilderResults.toString()} + | + | ${stringBuilderAmounts.toString()} + |"""".stripMargin) + bw.close() + } + println(s"propertyStore: ${propertyStore.getClass.toString()}") + println(s"jdk folder: $JRELibraryFolder") + BasicReport( stringBuilderAmounts.toString() ) @@ -496,40 +543,30 @@ object Immutability { def main(args: Array[String]): Unit = { def usage: String = { - "Usage: java …ImmutabilityAnalysisEvaluation \n"+ - "-cp OR -JDK\n"+ - "[-analysis ]\n"+ - "[-threads ]\n"+ - "[-resultFolder ]\n" /*+ - "[-projectDir ]\n"+ - "[-libDir ]\n"+ - "[-analysis (Default: L2, the most precise analysis configuration)]\n"+ - "[-fieldMutability (Default: Depends on analysis level)]\n"+ - "[-escape (Default: L1, the most precise configuration)]\n"+ - "[-domain ]\n"+ - "[-rater ]\n"+ - "[-callGraph (Default: RTA)]\n"+ - "[-eager] (supporting analyses are executed eagerly)\n"+ - "[-noJDK] (do not analyze any JDK methods)\n"+ - "[-individual] (reports the purity result for each method)\n"+ - "[-closedWorld] (uses closed world assumption, i.e. no class can be extended)\n"+ - "[-library] (assumes that the target is a library)\n"+ - "[-debug] (enable debug output from PropertyStore)\n"+ - "[-multi] (analyzes multiple projects in the subdirectories of -cp)\n"+ - "[-eval ]\n"+ - "[-j (0 for the sequential implementation)]\n"+ - "[-analysisName ]\n"+ - "[-schedulingStrategy ]\n"+ - "Example:\n\tjava …PurityAnalysisEvaluation -JDK -individual -closedWorld"*/ + s""" + | Usage: java …ImmutabilityAnalysisEvaluation + | -cp OR -JDK + | [-analysis ] + | [-threads ] + | [-resultFolder ] + | [-closedWorld] (uses closed world assumption, i.e. no class can be extended) + | [-noJDK] (running without the JDK) + |""".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 def readNextArg(): String = { - i += 1 + i = i + 1 if (i < args.length) { args(i) } else { @@ -538,62 +575,44 @@ object Immutability { } } - var analysis: RunningAnalysis = RunningAnalysis.All - //val numberOfThreads:Option[String] = None + var analysis: RunningAnalysis = RunningAnalyses.All while (i < args.length) { args(i) match { case "-analysis" ⇒ { val result = readNextArg() if (result == "All") - analysis = RunningAnalysis.All + analysis = RunningAnalyses.All else if (result == "References") - analysis = RunningAnalysis.References + analysis = RunningAnalyses.References else if (result == "Fields") - analysis = RunningAnalysis.Fields + analysis = RunningAnalyses.Fields else if (result == "Classes") - analysis = RunningAnalysis.Classes + analysis = RunningAnalyses.Classes else if (result == "Types") - analysis = RunningAnalysis.Fields + analysis = RunningAnalyses.Fields + else throw new IllegalArgumentException(s"unknown parameter: $result") } case "-threads" ⇒ numThreads = readNextArg().toInt case "-cp" ⇒ cp = new File(readNextArg()) case "-resultFolder" ⇒ - import java.nio.file.FileSystems - resultFolder = FileSystems.getDefault().getPath(readNextArg()) - case "-timeEvaluation" ⇒ timeEvaluation = true - /* - case "-projectDir" ⇒ projectDir = Some(readNextArg()) - case "-libDir" ⇒ libDir = Some(readNextArg()) - case "-analysis" ⇒ analysisName = Some(readNextArg()) - case "-fieldMutability" ⇒ fieldMutabilityAnalysisName = Some(readNextArg()) - case "-escape" ⇒ escapeAnalysisName = Some(readNextArg()) - case "-domain" ⇒ domainName = Some(readNextArg()) - case "-rater" ⇒ raterName = Some(readNextArg()) - case "-callGraph" ⇒ callGraphName = Some(readNextArg()) - case "-analysisName" ⇒ configurationName = Some(readNextArg()) - case "-schedulingStrategy" ⇒ schedulingStrategy = Some(readNextArg()) - case "-eager" ⇒ eager = true - case "-individual" ⇒ individual = true - case "-closedWorld" ⇒ cwa = true - case "-library" ⇒ isLibrary = true - case "-debug" ⇒ debug = true - case "-multi" ⇒ multiProjects = true - case "-eval" ⇒ evaluationDir = Some(new File(readNextArg())) - case "-j" ⇒ numThreads = readNextArg().toInt - case "-noJDK" ⇒ withoutJDK = true */ + 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 "-JDK" ⇒ - cp = JRELibraryFolder; //withoutJDK = true - + cp = new File(JRELibraryFolder.getAbsolutePath+"/rt.jar"); withoutJDK = true case unknown ⇒ Console.println(usage) throw new IllegalArgumentException(s"unknown parameter: $unknown") } i += 1 } - - evaluate(analysis, numThreads, cp, resultFolder, timeEvaluation) + evaluate(cp, analysis, numThreads, projectDir, libDir, resultFolder, timeEvaluation, threadEvaluation, isLibrary, withoutJDK, closedWorldAssumption) } } From 36ee2efd7aae0ca14c9568648aa970123af6ab73 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 31 Jul 2020 11:30:53 +0200 Subject: [PATCH 233/327] current status, still WIP --- .../L0FieldImmutabilityAnalysis.scala | 379 ++++++++---------- .../LxClassImmutabilityAnalysis_new.scala | 25 +- .../LxTypeImmutabilityAnalysis_new.scala | 44 +- ...bstractReferenceImmutabilityAnalysis.scala | 27 +- ...mutabilityAnalysisLazyInitialization.scala | 188 +++------ .../L0ReferenceImmutabilityAnalysis.scala | 27 +- 6 files changed, 283 insertions(+), 407 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index b26f77eedf..28af080d86 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -68,42 +68,36 @@ import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.Stmt case class State(f: Field) { - var field: Field = f - var typeImmutability: Option[Boolean] = Some(true) - var referenceImmutability: Option[Boolean] = None - var referenceNotEscapes: Boolean = true - var dependentImmutability: Option[DependentImmutabilityKind] = Some( - DependentImmutabilityKind.dependent - ) + var typeIsImmutable: Option[Boolean] = Some(true) + var referenceIsImmutable: Option[Boolean] = None + var noEscapePossibilityViaReference: Boolean = true + var dependentImmutability: Option[DependentImmutabilityKind] = Some(DependentImmutabilityKind.dependent) var genericTypeSetNotDeepImmutable = false var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - - def hasDependees: Boolean = { - dependencies.nonEmpty - } - - def dependees: Set[EOptionP[Entity, Property]] = { - dependencies - } } +/** + * Enum that describes the different kinds of dependent immutable fields: + * [[DependentImmutabilityKind.dependent]] Shallow or mutable types could still exist + * [[DependentImmutabilityKind.notShallowOrMutable]] There are no shallow or mutable types + * [[DependentImmutabilityKind.onlyDeepImmutable]] There are no generic parameters left. + * All are replaced with deep immutable types. + */ object DependentImmutabilityKind extends Enumeration { type DependentImmutabilityKind = Value - val dependent, notShallowOrMutable, onlyDeepImmutable = Value + val notShallowOrMutable, dependent, onlyDeepImmutable = Value } /** * Analyses that determines the immutability of org.opalj.br.Field - * Because it depends on the Field Immutability Lattice it combines the immutability of the fields reference and - * it's type. Thus needs the information of the reference of the field from the [[L0ReferenceImmutabilityAnalysis]] - * and the information of the type immutability determined by the type immutability analysis. + * The information of the reference of the field from the [[L0ReferenceImmutabilityAnalysis]] + * and the information of the type immutability determined by the [[LxTypeImmutabilityAnalysis_new]] * * @author Tobias Peter Roth */ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { - final val typeExtensibility = project.get(TypeExtensibilityKey) final val closedPackages = project.get(ClosedPackagesKey) final val fieldAccessInformation = project.get(FieldAccessInformationKey) @@ -122,21 +116,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) private[analyses] def determineFieldImmutability( field: Field ): ProperPropertyComputationResult = { - - if (field.name.contains("hash") && - field.classFile.thisType.simpleName.contains("AclEntry")) - println(s"field: ${field}") - var classFormalTypeParameters: Option[Set[String]] = None - def loadFormalTypeparameters() = { + def loadFormalTypeparameters(): Unit = { var result: Set[String] = Set.empty - //TODO + def CheckAttributeWithRegardToFormalTypeParameter: Attribute ⇒ Unit = { attribute ⇒ attribute match { case SourceFile(_) ⇒ case ClassSignature(typeParameters, _, _) ⇒ - for (parameter ← typeParameters.iterator) { + for (parameter ← typeParameters) { parameter match { case FormalTypeParameter(identifier, _, _) ⇒ result += identifier case _ ⇒ @@ -147,23 +136,22 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } //if the genericity is nested in an inner class + // collect the generic type parameters from the fields outer class if (field.classFile.outerType.isDefined) { val outerClassFile = project.classFile(field.classFile.outerType.get._1) if (outerClassFile.isDefined) { - outerClassFile.get.attributes.iterator.foreach( + outerClassFile.get.attributes.foreach( CheckAttributeWithRegardToFormalTypeParameter ) } } - - field.classFile.attributes.iterator.foreach( + // collect the generic type parameters from the fields class + field.classFile.attributes.foreach( CheckAttributeWithRegardToFormalTypeParameter ) - if (result.size > 0) { - println("result: "+result) + if (result.size > 0) classFormalTypeParameters = Some(result) - } } def isInClassesGenericTypeParameters(string: String): Boolean = @@ -172,123 +160,114 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def handleTypeImmutability(state: State) = { val objectType = field.fieldType.asFieldType if (objectType == ObjectType.Object) { - state.typeImmutability = Some(false) //handling generic fields + state.typeIsImmutable = Some(false) //handling generic fields } else if (objectType.isBaseType || objectType == ObjectType.String) { + // we hardcode here the strings deep immutability //state.typeImmutability = Some(true) // true is default } else if (objectType.isArrayType) { - state.typeImmutability = Some(false) + state.typeIsImmutable = Some(false) } else { - val result = propertyStore(objectType, TypeImmutability_new.key) - state.dependencies = state.dependencies.iterator.filter(_.e ne result.e).toSet - result match { - case FinalP(DeepImmutableType) ⇒ - case FinalP(DependentImmutableType) ⇒ { - state.typeImmutability = Some(false) - } + val typeImmutabilityPropertyStoreResult = + propertyStore(objectType, TypeImmutability_new.key) + state.dependencies = state.dependencies.iterator.filter(_.e ne typeImmutabilityPropertyStoreResult.e).toSet + typeImmutabilityPropertyStoreResult match { + case FinalP(DeepImmutableType) ⇒ // type is immutable is default + case FinalP(DependentImmutableType) ⇒ + state.typeIsImmutable = Some(false) + state.dependentImmutability = + Some(DependentImmutabilityKind.dependent) case FinalP(ShallowImmutableType | MutableType_new) ⇒ { - state.typeImmutability = Some(false) + state.typeIsImmutable = Some(false) state.dependentImmutability = None if (state.field.fieldType.isObjectType && state.field.fieldType.asObjectType != ObjectType.Object) { state.dependentImmutability = None //when the generic type is still final } } - case ep: InterimEP[e, p] ⇒ state.dependencies += ep - case epk @ _ ⇒ state.dependencies += epk + //case ep: InterimEP[e, p] ⇒ state.dependencies += ep + case epk ⇒ state.dependencies += epk } } } def hasGenericType(state: State): Unit = { - var flag_notShallow = true - var flag_onlyDeep = true + var noShallowOrMutableTypeInGenericTypeFound = true + var onlyDeepTypesInGenericTypeFound = true var genericFields: List[ObjectType] = List() - println("Attributes: "+state.field.asField.attributes) - state.field.asField.attributes.iterator.foreach( + var noRelevantAttributesFound = true + state.field.asField.attributes.foreach( _ match { case RuntimeInvisibleAnnotationTable(_) ⇒ case SourceFile(_) ⇒ case TypeVariableSignature(t) ⇒ - println("T: "+t) - flag_onlyDeep = false + noRelevantAttributesFound = false + onlyDeepTypesInGenericTypeFound = false if (!isInClassesGenericTypeParameters(t)) - flag_notShallow = false + noShallowOrMutableTypeInGenericTypeFound = false case ClassTypeSignature( packageIdentifier, SimpleClassTypeSignature(simpleName, typeArguments), _ ) ⇒ - typeArguments.iterator + noRelevantAttributesFound = false + typeArguments .foreach({ _ match { - case ProperTypeArgument( - variance, - TypeVariableSignature(identifier: String) - ) ⇒ - println("identifier: "+identifier) - flag_onlyDeep = false - if (!isInClassesGenericTypeParameters(identifier)) { - flag_notShallow = false - } - case ProperTypeArgument( - varianceIndicator, - ClassTypeSignature( - packageIdentifier1, - SimpleClassTypeSignature( - packageIdentifier2, - typeArguments2 - ), - _ - ) - ) ⇒ { - - val oPath = - packageIdentifier1 match { - case Some(packageIdentifier1) ⇒ packageIdentifier1 + packageIdentifier2 - case _ ⇒ packageIdentifier2 + case ProperTypeArgument(variance, TypeVariableSignature(identifier: String)) ⇒ + onlyDeepTypesInGenericTypeFound = false + if (!isInClassesGenericTypeParameters(identifier)) + noShallowOrMutableTypeInGenericTypeFound = false + case ProperTypeArgument(varianceIndicator, + ClassTypeSignature(outerPackageIdentifier, + SimpleClassTypeSignature(innerPackageIdentifier, typeArguments2), _)) ⇒ { + val objectPath = + outerPackageIdentifier match { + case Some(prepackageIdentifier) ⇒ + prepackageIdentifier + innerPackageIdentifier + case _ ⇒ innerPackageIdentifier } - genericFields = ObjectType(oPath) :: genericFields + genericFields = ObjectType(objectPath) :: genericFields } case _ ⇒ - flag_notShallow = false - flag_onlyDeep = false - state.typeImmutability = Some(false) + noShallowOrMutableTypeInGenericTypeFound = false + onlyDeepTypesInGenericTypeFound = false + state.typeIsImmutable = Some(false) } }) - case _ ⇒ - state.typeImmutability = Some(false) - flag_notShallow = false - flag_onlyDeep = false + state.typeIsImmutable = Some(false) + noShallowOrMutableTypeInGenericTypeFound = false + onlyDeepTypesInGenericTypeFound = false } ) genericFields.foreach(objectType ⇒ { - val result = propertyStore(objectType, TypeImmutability_new.key) - state.dependencies = state.dependencies.iterator.filter(_.e ne result.e).toSet - println("result: "+result) - result match { - case FinalP(DeepImmutableType) ⇒ //nothing to to here: default value is deep imm - case FinalP(ShallowImmutableType | DependentImmutableType | MutableType_new) ⇒ { - flag_notShallow = false - flag_onlyDeep = false - state.typeImmutability = Some(false) + val typeImmutabilityPropertyStoreResult = propertyStore(objectType, TypeImmutability_new.key) + state.dependencies = + state.dependencies.iterator.filter(_.e ne typeImmutabilityPropertyStoreResult.e).toSet + typeImmutabilityPropertyStoreResult match { + case FinalP(DeepImmutableType) ⇒ //nothing to do here: default value is deep immutable + case FinalP(DependentImmutableType) ⇒ + onlyDeepTypesInGenericTypeFound = false + state.typeIsImmutable = Some(false) + case FinalP(ShallowImmutableType | MutableType_new) ⇒ { + noShallowOrMutableTypeInGenericTypeFound = false + onlyDeepTypesInGenericTypeFound = false + state.typeIsImmutable = Some(false) } - case ep @ _ ⇒ + case ep ⇒ state.dependencies += ep } }) - if (state.field.asField.attributes.size == state.field.asField.attributes - .collect({ case x @ RuntimeInvisibleAnnotationTable(_) ⇒ x }) - .size) { - flag_notShallow = false - flag_onlyDeep = false + if (noRelevantAttributesFound) { + noShallowOrMutableTypeInGenericTypeFound = false + onlyDeepTypesInGenericTypeFound = false } if (state.dependentImmutability != None) { - if (flag_onlyDeep) + if (onlyDeepTypesInGenericTypeFound) state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) - else if (flag_notShallow) + else if (noShallowOrMutableTypeInGenericTypeFound) state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) } } @@ -313,7 +292,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def handleEscapeProperty( ep: EOptionP[DefinitionSite, EscapeProperty] )(implicit state: State): Boolean = { - println("ep: "+ep) ep match { case FinalP(NoEscape) ⇒ // | EscapeInCallee | EscapeViaReturn false @@ -362,20 +340,18 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (!fieldsUseSiteStmt.isMonitorEnter && !fieldsUseSiteStmt.isMonitorExit && !fieldsUseSiteStmt.isIf) { - println("x13") - println("fieldUseSiteStmt: "+fieldsUseSiteStmt) - state.referenceNotEscapes = false + state.noEscapePossibilityViaReference = false } } } else if (stmt.isExprStmt) { //is ignored } else { - println("x14") - state.referenceNotEscapes = false + state.noEscapePossibilityViaReference = false } } } + var seen: Set[Stmt[V]] = Set.empty def checkFieldWritesForEffImmutability(field: Field)(implicit state: State) = { def checkNonVirtualMethodCall( @@ -383,41 +359,77 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) nonVirtualMethodCall: NonVirtualMethodCall[V], tacCode: TACode[TACMethodParameter, V] ): Unit = { - println("check nonvirtualmethod call of method: "+method) - println(nonVirtualMethodCall) nonVirtualMethodCall.params.foreach( param ⇒ { - println("param: "+param) - //println("used by: " + para) param.asVar.definedBy.foreach( index ⇒ if (index < 0) { - println("x1") - state.referenceNotEscapes = false + state.noEscapePossibilityViaReference = false } else { - val definitionSides = definitionSites(method, index) - val stmt = tacCode.stmts(tacCode.pcToIndex(definitionSides.pc)) - if (stmt.isNonVirtualMethodCall) { - checkNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) - } else if (stmt.isPutField || stmt.isPutStatic) { - checkPuts(stmt, method, tacCode) + val paramDefinitionStmt = tacCode.stmts(index) + if (paramDefinitionStmt.isAssignment) { + val assignmentExpression = paramDefinitionStmt.asAssignment.expr + if (assignmentExpression.isGetField) { + val getField = assignmentExpression.asGetField + val field = getField.resolveField + val propertyStoreResult = propertyStore(field.get, FieldImmutability.key) + propertyStoreResult match { + case FinalP(DeepImmutableField) ⇒ //nothing to do here + case FinalP(_) ⇒ state.noEscapePossibilityViaReference = false + case ep ⇒ state.dependencies += ep + } + + } else if (assignmentExpression.isNew) { + //if (paramDefinitionStmt.isAssignment && //TODO..... + // paramDefinitionStmt.asAssignment.targetVar.isVar) { + for (i ← paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy) { + val stmt = tacCode.stmts(i) + if (stmt.isNonVirtualMethodCall) { + if (!seen.contains(stmt)) { + seen += stmt + checkNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) + } + } else + state.noEscapePossibilityViaReference = false + } + //} + } else + state.noEscapePossibilityViaReference = false + } else { - val propertyStoreResult = - propertyStore(definitionSides, EscapeProperty.key) - println("propertyStoreResult: "+propertyStoreResult) - if (handleEscapeProperty(propertyStoreResult)) { - println("x2") - state.referenceNotEscapes = false + val definitionSitesOfParam = definitionSites(method, index) + val stmt = tacCode.stmts(tacCode.pcToIndex(definitionSitesOfParam.pc)) + if (stmt.isNonVirtualMethodCall) { + if (!seen.contains(stmt)) { + seen += stmt + checkNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) + } + } else if (stmt.isPutField || stmt.isPutStatic) { + if (!seen.contains(stmt)) { + seen += stmt + checkPuts(stmt, method, tacCode) + } + + } else if (stmt.isArrayStore) { + //val arrayStore = stmt.asArrayStore + state.noEscapePossibilityViaReference = false //TODO handling that case more precise + } //else if // other cases that the purity analysis can not handle + else { + val propertyStoreResult = + propertyStore(definitionSitesOfParam, EscapeProperty.key) + if (handleEscapeProperty(propertyStoreResult)) { + state.noEscapePossibilityViaReference = false + } } - } + } //TODO go further } ) } ) } + def checkPuts(stmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { var putDefinitionSites: IntTrieSet = IntTrieSet.empty - println("check put: "+stmt) if (stmt.isPutField) { val putField = stmt.asPutField putDefinitionSites = putField.value.asVar.definedBy @@ -425,8 +437,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val putStatic = stmt.asPutStatic putDefinitionSites = putStatic.value.asVar.definedBy } else { - println("x3") - state.referenceNotEscapes = false + state.noEscapePossibilityViaReference = false } for { i ← putDefinitionSites @@ -442,8 +453,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall checkNonVirtualMethodCall(method, nonVirtualMethodCall, tacCode) } else { - println("x4") - state.referenceNotEscapes = false + state.noEscapePossibilityViaReference = false } //TODO andere Fälle bedenken } @@ -455,8 +465,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) EscapeProperty.key ) if (handleEscapeProperty(propertyStoreResult)) { - println("x5") - state.referenceNotEscapes = false + state.noEscapePossibilityViaReference = false } } else { val useSites = @@ -468,17 +477,14 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } else if (useSiteStmt.isPutStatic || useSiteStmt.isPutField) { //checkPuts(stmt, method, tacCode) if (useSiteStmt != stmt) { - println("x6") - state.referenceNotEscapes = false //TODO + state.noEscapePossibilityViaReference = false //TODO } } else if (useSiteStmt.isAssignment) { - println("x7") - state.referenceNotEscapes = false //TODO + state.noEscapePossibilityViaReference = false //TODO //val assignment = stmt.asAssignment } else { - println("x8") - state.referenceNotEscapes = false + state.noEscapePossibilityViaReference = false } } } @@ -486,17 +492,15 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) //TODO escape analyse für Object // else ; aktuelles putfield bedenken } else if (!definitionSiteAssignment.expr.isConst) { - println("x9") - state.referenceNotEscapes = false + state.noEscapePossibilityViaReference = false } } else { - println("x10") - state.referenceNotEscapes = false + state.noEscapePossibilityViaReference = false } } } // start of method - state.referenceNotEscapes = field.isPrivate + state.noEscapePossibilityViaReference = field.isPrivate for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method) @@ -506,43 +510,32 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val staticAddition = if (method.isStatic) 1 else 0 if (index > (-1 + staticAddition)) { val stmt = taCode.stmts(index) - checkPuts(stmt, method, taCode) + if (!seen.contains(stmt)) { + seen += stmt + checkPuts(stmt, method, taCode) + } + } else { - println("x11") - state.referenceNotEscapes = false + state.noEscapePossibilityViaReference = false } } } - if (state.referenceNotEscapes) + if (state.noEscapePossibilityViaReference) checkFieldWritesForEffImmutability(state.field) - if (state.referenceNotEscapes) + if (state.noEscapePossibilityViaReference) checkFieldReadsForEffImmutability(state.field) } def createResult(state: State): ProperPropertyComputationResult = { - /*if (field.name.contains("hash") && - state.field.classFile.thisType.simpleName.contains("AclEntry")) */ - //". == ObjectType("java/nio/file/attribute/AclEntry")) - println( - s""" - | ========================================================= - | create Result - | field: $field - | ref imm: ${state.referenceImmutability} - | type imm: ${state.typeImmutability} - | not escapes: ${state.referenceNotEscapes} - | dep imm: ${state.dependentImmutability} - |""".stripMargin - ) - state.referenceImmutability match { + state.referenceIsImmutable match { case Some(false) | None ⇒ Result(field, MutableField) case Some(true) ⇒ { - state.typeImmutability match { + state.typeIsImmutable match { case Some(true) ⇒ Result(field, DeepImmutableField) case Some(false) | None ⇒ { - if (state.referenceNotEscapes) + if (state.noEscapePossibilityViaReference) Result(field, DeepImmutableField) else state.dependentImmutability match { @@ -563,9 +556,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { state.dependencies = state.dependencies.iterator.filter(_.e ne eps.e).toSet - if (state.field.name.contains("hash") && state.field.classFile.thisType.simpleName.contains("AclEntry")) - println("continuation; eps: "+eps) - eps match { //(: @unchecked) case x: InterimEP[_, _] ⇒ { @@ -575,23 +565,21 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case FinalP(DeepImmutableType) ⇒ { //state.typeImmutability = Some(true) if (!state.dependentImmutability.isDefined) state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) - } case FinalEP(t, MutableType_new | ShallowImmutableType) ⇒ - state.typeImmutability = Some(false) + state.typeIsImmutable = Some(false) if (t != ObjectType.Object) { // in case of generic fields state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } case FinalEP(f, DependentImmutableType) ⇒ { - //println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") - state.typeImmutability = Some(false) + state.typeIsImmutable = Some(false) if (!state.dependentImmutability.isDefined) state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) } case FinalP( MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference ) ⇒ { - state.typeImmutability = Some(false) + state.typeIsImmutable = Some(false) return Result(field, MutableField); } case FinalP( @@ -599,17 +587,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference ) ⇒ { //TODO - state.referenceImmutability = Some(true) + state.referenceIsImmutable = Some(true) } - - case eps if eps.isFinal && eps.asEPK.pk == TACAI.key ⇒ - checkIfReferencedObjectCanEscape(state) - - case eps if eps.isFinal && eps.asEPK.pk == EscapeProperty.key ⇒ + case FinalP(DeepImmutableField) ⇒ // nothing to do + case eps if eps.isFinal && eps.asEPS.pk == FieldImmutability.key ⇒ //else case + state.noEscapePossibilityViaReference = false + case eps if eps.isFinal && eps.asEPS.pk == TACAI.key ⇒ checkIfReferencedObjectCanEscape(state) + case eps if eps.isFinal && eps.asEPS.pk == EscapeProperty.key ⇒ checkIfReferencedObjectCanEscape(state) if (handleEscapeProperty(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) { - println("x12") - state.referenceNotEscapes = false + state.noEscapePossibilityViaReference = false } //case _: FinalEP[_, TACAI] ⇒ @@ -634,11 +621,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) implicit val state: State = State(field) propertyStore(state.field, ReferenceImmutability.key) match { - case FinalP(ImmutableReference) ⇒ { - state.referenceImmutability = Some(true) - } + case FinalP(ImmutableReference) ⇒ state.referenceIsImmutable = Some(true) case FinalP(LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ - state.referenceImmutability = Some(true) + state.referenceIsImmutable = Some(true) case FinalP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference) ⇒ return Result(field, MutableField); case ep @ _ ⇒ { @@ -648,22 +633,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) loadFormalTypeparameters() handleTypeImmutability(state) + handleTypeImmutability(state) hasGenericType(state) - if (field.classFile.thisType.simpleName.contains("Generic_class2") || - field.classFile.thisType.simpleName.contains("Generic_class3")) - println( - s""" - | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - | ${field.classFile.thisType.simpleName} - | ${field.name} - | ref imm: ${state.referenceImmutability} - | typ imm: ${state.typeImmutability} - |""".stripMargin - ) //it is possible that the type immutability not already determined at this point - if (!state.referenceImmutability.isDefined || state.referenceImmutability.get) { - + if (!state.referenceIsImmutable.isDefined || state.referenceIsImmutable.get) { checkIfReferencedObjectCanEscape } @@ -678,7 +652,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) c(state) ) } - } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala index 9ec7c740f4..542db499ca 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala @@ -88,7 +88,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal */ @inline private[this] def createResultForAllSubtypes( t: ObjectType, - immutability: ClassImmutability_new //MutableObject + immutability: ClassImmutability_new ): MultiResult = { val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) val r = allSubtypes.map { st ⇒ @@ -320,27 +320,26 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal someEPS match { // Superclass related dependencies: // - case UBP(MutableClass) ⇒ // MutableObject) ⇒ - return Result(t, MutableClass); //MutableObjectByAnalysis); + case UBP(MutableClass) ⇒ + return Result(t, MutableClass); - case LBP(DeepImmutableClass) ⇒ //_:ImmutableObject) ⇒ // the super class + case LBP(DeepImmutableClass) ⇒ // the super class dependees -= SuperClassKey - case UBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is at most immutable container + case UBP(ShallowImmutableClass) ⇒ // super class is at most immutable container if (someEPS.isFinal) dependees -= SuperClassKey - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer - //dependees = dependees.filterNot(_._2.pk == TypeImmutability_new.key) //TypeImmutability.key) + maxLocalImmutability = ShallowImmutableClass case UBP(DependentImmutableClass) ⇒ if (someEPS.isFinal) dependees -= SuperClassKey maxLocalImmutability = DependentImmutableClass - case LBP(ShallowImmutableClass) ⇒ //ImmutableContainer) ⇒ // super class is a least immutable container - if (minLocalImmutability != ShallowImmutableClass && //ImmutableContainer && + case LBP(ShallowImmutableClass) ⇒ // super class is a least shallow immutable + if (minLocalImmutability != ShallowImmutableClass && !dependees.valuesIterator.exists(_.pk == FieldImmutability.key)) - minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible + minLocalImmutability = ShallowImmutableClass // Lift lower bound when possible - case LUBP(MutableClass, DeepImmutableClass) ⇒ //_: MutableObject, ImmutableObject) ⇒ // No information about superclass + case LUBP(MutableClass, DeepImmutableClass) ⇒ // No information about superclass case FinalEP(f, DependentImmutableField) ⇒ { if (hasShallowImmutableFields) { @@ -427,10 +426,6 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal Result(t, maxLocalImmutability) } else { - /* - * if (oldDependees == dependees) { - * Result(t, minLocalImmutability) - * } else**/ //For debugging purposes InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala index 886863f700..4af160822f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala @@ -107,7 +107,6 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP case epk ⇒ { InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) } - //InterimResult(t, MutableType, ImmutableType, Seq(epk), c) } } else { var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] @@ -116,12 +115,12 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP val resultToMatch2 = ps(t, ClassImmutability_new.key) resultToMatch2 match { - case FinalP(DeepImmutableClass) ⇒ //ImmutableObject) => - case FinalP(MutableClass) ⇒ //(_: MutableObject) => - return Result(t, MutableType_new); //MutableType); - case FinalP(ShallowImmutableClass) ⇒ //ImmutableContainer) => - joinedImmutability = ShallowImmutableType // ImmutableContainerType - maxImmutability = ShallowImmutableType //ImmutableContainerType + case FinalP(DeepImmutableClass) ⇒ + case FinalP(MutableClass) ⇒ + return Result(t, MutableType_new); + case FinalP(ShallowImmutableClass) ⇒ + joinedImmutability = ShallowImmutableType + maxImmutability = ShallowImmutableType case FinalP(DependentImmutableClass) ⇒ joinedImmutability = DependentImmutableType maxImmutability = DependentImmutableType @@ -138,13 +137,13 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP directSubtypes foreach { subtype ⇒ ps(subtype, TypeImmutability_new.key) match { - case FinalP(DeepImmutableType) ⇒ //ImmutableType) => - case UBP(MutableType_new) ⇒ //MutableType) => - return Result(t, MutableType_new); //MutableType); + case FinalP(DeepImmutableType) ⇒ + case UBP(MutableType_new) ⇒ + return Result(t, MutableType_new); - case FinalP(ShallowImmutableType) ⇒ //ImmutableContainerType) => - joinedImmutability = joinedImmutability.meet(ShallowImmutableType) //ImmutableContainerType) - maxImmutability = ShallowImmutableType //ImmutableContainerType + case FinalP(ShallowImmutableType) ⇒ + joinedImmutability = joinedImmutability.meet(ShallowImmutableType) + maxImmutability = ShallowImmutableType case FinalP(DependentImmutableType) ⇒ joinedImmutability = joinedImmutability.meet(DependentImmutableType) @@ -156,7 +155,7 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP dependencies += ((subtype, eps)) case epk ⇒ - joinedImmutability = MutableType_new //MutableType + joinedImmutability = MutableType_new dependencies += ((subtype, epk)) } } @@ -164,8 +163,8 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP if (dependencies.isEmpty) { Result(t, maxImmutability) } else if (joinedImmutability == maxImmutability) { - // E.g., as soon as one subtype is an ImmutableContainer, we are at most - // ImmutableContainer, even if all other subtype may even be immutable! + // 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 @@ -193,12 +192,12 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) } else { - joinedImmutability = MutableType_new //MutableType + joinedImmutability = MutableType_new continue = false } } if (joinedImmutability == maxImmutability) { - assert(maxImmutability == ShallowImmutableType) //ImmutableContainerType) + assert(maxImmutability == ShallowImmutableType) Result(t, maxImmutability) } else { InterimResult( @@ -213,19 +212,18 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP } (eps: @unchecked) match { - case FinalEP(e, DeepImmutableType | DeepImmutableClass) ⇒ //ImmutableType | ImmutableObject) => + case FinalEP(e, DeepImmutableType | DeepImmutableClass) ⇒ dependencies = dependencies - e nextResult() - case UBP(x) if (x == MutableType_new || x == MutableClass) ⇒ //MutableType | _: MutableObject) => + case UBP(x) if (x == MutableType_new || x == MutableClass) ⇒ Result(t, MutableType_new) //MutableType) - case FinalEP(e, x) if (x == ShallowImmutableType || x == ShallowImmutableClass) ⇒ //ImmutableContainerType | ImmutableContainer) => - maxImmutability = ShallowImmutableType //ImmutableContainerType + case FinalEP(e, x) if (x == ShallowImmutableType || x == ShallowImmutableClass) ⇒ + maxImmutability = ShallowImmutableType dependencies = dependencies - e nextResult() case FinalEP(e, DependentImmutableClass | DependentImmutableType) ⇒ { - //if (x == DependentImmutableClass() || x == DependentImmutableType) => { maxImmutability = DependentImmutableType dependencies = dependencies - e nextResult() diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala index 98cb410eea..dc0a5e5c65 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala @@ -41,10 +41,7 @@ import org.opalj.value.ValueInformation trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { - import scala.collection.mutable - type V = DUVar[ValueInformation] - final val typeExtensibility = project.get(TypeExtensibilityKey) final val closedPackages = project.get(ClosedPackagesKey) final val fieldAccessInformation = project.get(FieldAccessInformationKey) @@ -76,10 +73,6 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { } } - var stillCalculatedMethods: mutable.HashMap[Method, TACode[TACMethodParameter, V]] = - new mutable.HashMap[Method, TACode[TACMethodParameter, V]]() - //var stillCalculatedMethods: mutable.HashSet[Method] = new mutable.HashSet[Method] - /** * Returns the TACode for a method if available, registering dependencies as necessary. */ @@ -87,21 +80,17 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { method: Method, pcs: PCs )(implicit state: State): Option[TACode[TACMethodParameter, V]] = { - if (stillCalculatedMethods.contains(method)) - stillCalculatedMethods.get(method) - else - propertyStore(method, TACAI.key) match { - case finalEP: FinalEP[Method, TACAI] ⇒ - stillCalculatedMethods.put(method, finalEP.ub.tac.get) - finalEP.ub.tac - case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) // + alle pcs die schon existieren - None - } + propertyStore(method, TACAI.key) match { + case finalEP: FinalEP[Method, TACAI] ⇒ + finalEP.ub.tac + case epk ⇒ + state.tacDependees += method -> ((epk, pcs)) // + alle pcs die schon existieren + None + } } /** - * 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. */ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index 67898dfc57..f0a86b5707 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -46,7 +46,6 @@ import org.opalj.tac.UVar import org.opalj.tac.VirtualFunctionCall import org.opalj.tac.Throw import org.opalj.br.ObjectType - import scala.annotation.switch trait AbstractReferenceImmutabilityAnalysisLazyInitialization @@ -55,6 +54,12 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization import scala.collection.mutable + /** + * handles the lazy initialization determinations for the method + * methodUpdatesField + * Returns Some(true) if we have no thread safe or deterministic lazy initialization + * + */ def handleLazyInitialization( writeIndex: Int, defaultValue: Any, @@ -65,38 +70,23 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization tacCai: TACode[TACMethodParameter, V] )(implicit state: State): Option[Boolean] = { var result: Option[Boolean] = None - val dcl: ReferenceImmutability = isThreadSafeLazyInitialisation( - writeIndex, - defaultValue, - method, - code, - cfg, - pcToIndex, - tacCai - ) + val dcl: ReferenceImmutability = + isThreadSafeLazyInitialisation(writeIndex, defaultValue, method, code, cfg, pcToIndex, tacCai) dcl match { - case ir @ ImmutableReference ⇒ state.referenceImmutability = ir - case lits @ LazyInitializedThreadSafeReference ⇒ state.referenceImmutability = lits - case lintbd @ LazyInitializedNotThreadSafeButDeterministicReference ⇒ - state.referenceImmutability = lintbd + case ImmutableReference | + LazyInitializedThreadSafeReference | + LazyInitializedNotThreadSafeButDeterministicReference ⇒ + state.referenceImmutability = dcl case MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference ⇒ - val li = isLazyInitialization( - writeIndex, - defaultValue, - method, - code, - cfg, - pcToIndex - ) - if (dcl == MutableReference) { - if (!li) result = Some(true) - else state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference - } else if (dcl == LazyInitializedNotThreadSafeOrNotDeterministicReference) { - if (li) - state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference - else state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference - } //state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference - case _ ⇒ //TODO + val lazyInitialization = isLazyInitialization(writeIndex, defaultValue, method, code, cfg, pcToIndex) + if (lazyInitialization) + state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference + else if (dcl == MutableReference) { + if (!lazyInitialization) { + result = Some(true) + } else + state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference + } else state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference } result } @@ -129,15 +119,13 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization return false; } - //-------------------------------------------------------- //TODO reasoning if there is another way to do this val writes = fieldAccessInformation.writeAccesses(state.field) - if (writes.iterator.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) + if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) return false; // more than one write in the method - //---------------------------------------------------------------- val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.iterator.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { return false; // Reads outside the (single) lazy initialization method } @@ -150,13 +138,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization if (findGuardResult.isDefined) (findGuardResult.get._1, findGuardResult.get._2, findGuardResult.get._3) else return false; - /** - * findGuardResult match { - * case Some((guard, guarded, read))f ⇒ (guard, guarded, read) - * case None ⇒ return false; - * } * - */ - } // Detect only simple patterns where the lazily initialized value is returned immediately @@ -175,6 +156,11 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization true } + /** + * Determines if a given field is thread safe lazy initialized. + * E.g. in a synchronized method with a nonnull-check. + */ + def isThreadSafeLazyInitialisation( writeIndex: Int, defaultValue: Any, @@ -204,26 +190,26 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val guardedBB = cfg.bb(afterGuardRecognizedTheDefaultValueIndex) val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.iterator.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { return MutableReference } val writes = fieldAccessInformation.writeAccesses(state.field) - if (writes.iterator.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { + if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { return MutableReference }; if (method.returnType == state.field.fieldType && !checkThatTheValueOfTheFieldIsReturned(write, writeIndex, readIndex, code)) { return MutableReference; } - //result = checkWriteIsGuarded2(writeIndex, guardIndex, guardedIndex, method, code, cfg) //when the method is synchronized the monitor has not to be searched if (method.isSynchronized) { - if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { + if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || + (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { val resultCaughtsAndThrows = findCaughtsThrowsAndResults(tacCai, cfg) if (resultCaughtsAndThrows._1.size == resultCaughtsAndThrows._2.size && - resultCaughtsAndThrows._1.iterator.forall( + resultCaughtsAndThrows._1.forall( bbCatch ⇒ - resultCaughtsAndThrows._2.iterator.exists( + resultCaughtsAndThrows._2.exists( bbThrow ⇒ ( (domTree @@ -250,9 +236,9 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization )) { val resultCaughtsAndThrows = findCaughtsThrowsAndResults(tacCai, cfg) if (resultCaughtsAndThrows._1.size == resultCaughtsAndThrows._2.size && - resultCaughtsAndThrows._1.iterator.forall( + resultCaughtsAndThrows._1.forall( bbCatch ⇒ - resultCaughtsAndThrows._2.iterator.exists( + resultCaughtsAndThrows._2.exists( bbThrow ⇒ ( (domTree @@ -261,9 +247,10 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization bbThrow._2 == bbCatch._3 ) ) - )) { + )) LazyInitializedThreadSafeReference // result //DCL - } else LazyInitializedNotThreadSafeOrNotDeterministicReference + else + LazyInitializedNotThreadSafeOrNotDeterministicReference } else if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) LazyInitializedNotThreadSafeOrNotDeterministicReference else @@ -288,8 +275,10 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val intermdT = ce.exceptionType.get if (intermdT.isObjectType) intermdT.asObjectType - else ObjectType.Exception - } else ObjectType.Exception + else + ObjectType.Exception + } else + ObjectType.Exception exceptions = (ce.pc, et, ce.origins, curBB) :: exceptions case Throw.ASTID ⇒ @@ -297,7 +286,8 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val ds = if (t.exception.isVar) { val v = t.exception.asVar v.definedBy - } else IntTrieSet.empty + } else + IntTrieSet.empty throwers = (t.pc.toInt, ds, curBB) :: throwers case ReturnValue.ASTID ⇒ //case ReturnValue(pc, expr) ⇒ @@ -328,7 +318,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization state: State ): Boolean = { v.definedBy.iterator - //v.defSites .filter(i ⇒ { if (i > 0) { val stmt = tacCode.stmts(i) @@ -346,14 +335,13 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization //&& name == state.field.name case _ ⇒ false } - } else // (i == -1) + } else // (i <= -1) true - }) - .size == v.definedBy.size //v.defSites.size + .size == v.definedBy.size } - var monitorEnterqueuedBBs: Set[CFGNode] = startBB.predecessors + var monitorEnterQueuedBBs: Set[CFGNode] = startBB.predecessors var worklistMonitorEnter = getPredecessors(startBB, Set.empty) //find monitorenter @@ -363,8 +351,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization worklistMonitorEnter = worklistMonitorEnter.tail val startPC = curBB.startPC val endPC = curBB.endPC - //val cfStmt = code(startPC) //(endPC) - var flag = true for (i ← startPC to endPC) { (code(i).astID: @switch) match { @@ -375,13 +361,13 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization dclEnterBBs = curBB :: dclEnterBBs flag = false } - case x @ _ ⇒ + case _ ⇒ } } if (flag) { - val predecessor = getPredecessors(curBB, monitorEnterqueuedBBs) + val predecessor = getPredecessors(curBB, monitorEnterQueuedBBs) worklistMonitorEnter ++= predecessor - monitorEnterqueuedBBs ++= predecessor + monitorEnterQueuedBBs ++= predecessor } } @@ -389,8 +375,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization while (!worklistMonitorExit.isEmpty) { val curBB = worklistMonitorExit.head worklistMonitorExit = worklistMonitorExit.tail - - //val startPC = curBB.startPC val endPC = curBB.endPC val cfStmt = code(endPC) @@ -419,7 +403,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization else None } (result, (bbsEnter, bbsExit)) - } /** @@ -562,7 +545,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization while (worklist.nonEmpty) { val curBB = worklist.head worklist = worklist.tail - val startPC = curBB.startPC val endPC = curBB.endPC if (startPC == 0 || startPC == guardedIndex) @@ -574,14 +556,14 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization if (!lazyInitializerIsDeterministic(method, code)) { return false; } - }; + } // Exception thrown between guard and write (caught somewhere, but we don't care) if ((curBB ne startBB) & caughtExceptions.contains(endPC)) { if (!lazyInitializerIsDeterministic(method, code)) { return false; } - }; + } // Check all predecessors except for the one that contains the guarding if-Statement val predecessors = getPredecessors(curBB, enqueuedBBs).iterator.filterNot(_.endPC == guardIndex).toList @@ -590,66 +572,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization } true } - /* - def checkWriteIsGuarded2( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): ReferenceImmutability = { - - val startBB = cfg.bb(writeIndex).asBasicBlock - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - val abnormalReturnNode = cfg.abnormalReturnNode - val caughtExceptions = code.filter { stmt => - stmt.astID == CaughtException.ASTID - - }.toList flatMap { - import org.opalj.tac.fpcf.analyses.Statement - exception=> - exception.asCaughtException.origins.map { origin: Int => - if (isImmediateVMException(origin)) { - pcOfImmediateVMException(origin) - } else { - pcOfMethodExternalException(origin) - } - } - } - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - if (startPC == 0 || startPC == guardedIndex) - return LazyInitializedNotThreadSafeOrNotDeterministicReference; // Reached method start or wrong branch of guarding if-Statement - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) { - if (!lazyInitializerIsDeterministic(method, code)) { - return LazyInitializedNotThreadSafeOrNotDeterministicReference - } - }; - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.toSet.contains(endPC)) { - if (!lazyInitializerIsDeterministic(method, code)) { - return LazyInitializedNotThreadSafeOrNotDeterministicReference - } - }; - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = - getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - LazyInitializedThreadSafeReference - } - */ /** * Gets all predecessor BasicBlocks of a CFGNode. */ @@ -663,6 +586,9 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization result.toList } + /** + * Gets all successors BasicBlocks of a CFGNode + */ def getSuccessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { val result = node.successors.iterator flatMap ({ currentNode ⇒ if (currentNode.isBasicBlock) @@ -751,7 +677,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization ): Boolean = { // There is only a single method with reads aside from initializers (checked by // isLazilyInitialized), so we have to check only reads from that one method. - reads.iterator.filter(!_._1.isInitializer).toList.head._2.iterator forall { readPC: Int ⇒ + reads.iterator.filter(!_._1.isInitializer).toList.head._2 forall { readPC: Int ⇒ val index = pcToIndex(readPC) index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index 916e5dd7ca..0a6832d38e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -104,23 +104,19 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec field: Field ): ProperPropertyComputationResult = { - implicit val state: State = State(field) - // Fields are not final if they are read prematurely! - if (!field.isFinal && isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { - return Result(field, MutableReference) - }; - state.referenceImmutability = ImmutableReference - - // It still has to be determined of the referred object could escape if (field.isFinal) - return Result(field, ImmutableReference) - //return createResult(); + return Result(field, ImmutableReference); + if (field.isPublic) + return Result(field, MutableReference); + implicit val state: State = State(field) + state.referenceImmutability = ImmutableReference val thisType = field.classFile.thisType - if (field.isPublic && !field.isFinal) + // Fields are not final if they are read prematurely! + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { return Result(field, MutableReference) - //in cases of a public, package private or protected reference, the referenced object could escape + }; // 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 @@ -134,11 +130,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } else { Set(field.classFile) } - val classesHavingAccess: Iterator[ClassFile] = if (field.isProtected) { if (typeExtensibility(thisType).isYesOrUnknown) { - //state.notEscapes = false return Result(field, MutableReference); } val subclassesIterator: Iterator[ClassFile] = @@ -149,7 +143,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } else { initialClasses.iterator } - // If there are native methods, we give up if (classesHavingAccess.exists(_.methods.exists(_.isNative))) { //state.notEscapes = false @@ -261,7 +254,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (eps.isRefinable) state.tacDependees += method -> ((newEP, pcs)) //TODO tacai funktionen alle ausführen - isNotFinal = methodUpdatesField(method, newEP.ub.tac.get, pcs) + val tmp = method + if (tmp != method) + isNotFinal = methodUpdatesField(method, newEP.ub.tac.get, pcs) case Callees.key ⇒ isNotFinal = handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) case FieldPrematurelyRead.key ⇒ From 2ad80a099f8e1142ec75ce6ecf06314a58294006 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 31 Jul 2020 15:33:57 +0200 Subject: [PATCH 234/327] current status, still WIP --- .../ReferenceImmutabilityAnalysisDemo.scala | 4 +- .../org/opalj/support/info/Immutability.scala | 36 ++- .../fixtures/immutability/field/Escapers.java | 134 ++++++++++++ .../field/TrivialMutableClass.java | 18 -- .../field/privateFieldNotBlank_deep.java | 4 +- .../field/privateFieldNotBlank_shallow.java | 4 +- ...FinalFieldBlank_costructorEscape_deep.java | 4 +- ...alFieldBlank_costructorEscape_shallow.java | 4 +- .../field/privateFinalFieldNotBlank_deep.java | 13 -- .../privateFinalFieldNotBlank_shallow.java | 14 -- .../field/private_getterEscape_deep.java | 4 +- .../field/private_getterEscape_shallow.java | 4 +- .../field/privatefinal_getterEscape_deep.java | 4 +- .../privatefinal_getterEscape_shallow.java | 4 +- .../field/protectedClass_deep.java | 37 +++- .../field/protectedClass_shallow.java | 12 - .../reference/DeclaredFinalFields.java | 8 +- .../immutability/reference/Escapers.java | 59 ----- .../reference/LazyInitialization.java | 30 +-- .../reference/PrivateFieldUpdater.java | 4 +- ...rivateFinalArrayEscapesViaConstructor.java | 12 +- .../immutability/reference/Singleton.java | 7 +- .../immutability/reference/Template.java | 5 +- .../DCL.java | 44 ++-- .../S.java | 2 - .../SimpleLazyInstantiation.java | 6 +- .../WithMutableAndImmutableFieldType.java | 3 +- ...izedNotThreadSafeReferenceAnnotation.java} | 4 +- .../opalj/fpcf/FieldImmutabilityTests.scala | 2 +- .../fpcf/ReferenceImmutabilityTests.scala | 2 +- .../ReferenceImmutabilityMatcher.scala | 4 +- .../properties/ReferenceImmutability.scala | 6 +- .../src/main/scala/org/opalj/tac/Stmt.scala | 2 + .../L0FieldImmutabilityAnalysis.scala | 6 +- ...bstractReferenceImmutabilityAnalysis.scala | 6 +- ...mutabilityAnalysisLazyInitialization.scala | 205 ++++++++++++++---- .../L0ReferenceImmutabilityAnalysis.scala | 8 +- .../purity/AbstractPurityAnalysis_new.scala | 4 +- .../purity/L2PurityAnalysis_new.scala | 1 + 39 files changed, 450 insertions(+), 280 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/Escapers.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/TrivialMutableClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_deep.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_shallow.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Escapers.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/{LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation.java => LazyInitializedNotThreadSafeReferenceAnnotation.java} (84%) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala index f1682a0abb..634c83fab9 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala @@ -22,7 +22,7 @@ import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.fpcf import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis @@ -108,7 +108,7 @@ object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) val notThreadSafeOrNotDeterministicLazyInitialization = propertyStore - .finalEntities(LazyInitializedNotThreadSafeOrNotDeterministicReference) + .finalEntities(LazyInitializedNotThreadSafeReference) .toList .sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) 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 index d962444271..5686265298 100644 --- 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 @@ -21,7 +21,7 @@ import org.opalj.br.fpcf.properties.DeepImmutableField import org.opalj.br.fpcf.properties.DependentImmutableField import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.br.fpcf.properties.MutableField import org.opalj.br.fpcf.properties.MutableReference @@ -117,13 +117,18 @@ object Immutability { ) } - val project = + 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 referenceDependencies: List[FPCFAnalysisScheduler] = List( EagerL0ReferenceImmutabilityAnalysis, @@ -203,9 +208,14 @@ object Immutability { L2PurityAnalysis_new.setRater(Some(SystemOutLoggingAllExceptionRater)) var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None + val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + + time { + analysesManager.project.get(RTACallGraphKey) + } { t ⇒ callGraphTime = t.toSeconds } + + //val propertyStore = time { project.get(PropertyStoreKey) } { t ⇒ propertyStoreTime = t.toSeconds } project.getOrCreateProjectInformationKeyInitializationData( PropertyStoreKey, @@ -257,7 +267,7 @@ object Immutability { .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) val notThreadSafeOrNotDeterministicLazyInitialization = propertyStore - .finalEntities(LazyInitializedNotThreadSafeOrNotDeterministicReference) + .finalEntities(LazyInitializedNotThreadSafeReference) .toList .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) val immutableReferences = propertyStore @@ -476,12 +486,18 @@ object Immutability { |""".stripMargin ) + val totalTime = projectTime + callGraphTime + analysisTime // + propertyStoreTime + // $propertyStoreTime seconds propertyStoreTime stringBuilderAmounts.append( s""" - | running ${analysis.toString} analysis - |took $analysisTime seconds - | with $numThreads threads - |""".stripMargin + | running ${analysis.toString} analysis + | took $totalTime total time + | $projectTime seconds projectTime + | $callGraphTime seconds callGraphTime + | $analysisTime seconds analysisTime + | $analysisTime seconds + | with $numThreads threads + |""".stripMargin ) // ${stringBuilderResults.toString()} println( @@ -605,7 +621,7 @@ object Immutability { case "-isLibrary" ⇒ isLibrary = true case "-noJDK" ⇒ withoutJDK = true case "-JDK" ⇒ - cp = new File(JRELibraryFolder.getAbsolutePath+"/rt.jar"); withoutJDK = true + cp = JRELibraryFolder; withoutJDK = true case unknown ⇒ Console.println(usage) throw new IllegalArgumentException(s"unknown parameter: $unknown") diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/Escapers.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/Escapers.java new file mode 100644 index 0000000000..434920ab74 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/Escapers.java @@ -0,0 +1,134 @@ +package org.opalj.fpcf.fixtures.immutability.field; + +import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; +import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; + +public class Escapers{ + +} + +class TransitiveEscape1 { + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private TrivialMutableClass tmc = new TrivialMutableClass(); + + public void printTMC(){ + System.out.println(tmc.name); + } + + public TrivialMutableClass get(){ + TrivialMutableClass tmc1 = this.tmc; + return tmc1; + } +} + +class TransitiveEscape2 { + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private TrivialMutableClass tmc = new TrivialMutableClass(); + + public void printTMC(){ + System.out.println(tmc.name); + } + + public TrivialMutableClass get(){ + TrivialMutableClass tmc1 = this.tmc; + TrivialMutableClass tmc2 = tmc1; + return tmc2; + } +} +class OneThatNotEscapesAndOneWithDCL { + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private TrivialMutableClass tmc1 = new TrivialMutableClass(); + + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private TrivialMutableClass tmc2; + + public TrivialMutableClass set() { + TrivialMutableClass tmc11 = tmc1; + TrivialMutableClass tmc22 = tmc2; + if (tmc11 != null) { + synchronized (this) { + if (tmc22 == null) { + tmc2 = new TrivialMutableClass(); + } + } + } + return tmc2; + } +} +class GenericEscapes { + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private SimpleGenericClass sgc; + + public GenericEscapes(TrivialMutableClass tmc){ + sgc = new SimpleGenericClass(tmc); + } +} + + +@ShallowImmutableClassAnnotation("") +class GenericEscapesTransitive { + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private SimpleGenericClass gc1; + + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private TrivialMutableClass tmc; + + public GenericEscapesTransitive(TrivialMutableClass tmc){ + this.tmc = tmc; + gc1 = new SimpleGenericClass(this.tmc); + } +} + +class GenericNotEscapesMutualEscapeDependencyNotAbleToResolve{ + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private TrivialMutableClass tmc = new TrivialMutableClass(); + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private SimpleGenericClass sgc; + public GenericNotEscapesMutualEscapeDependencyNotAbleToResolve() { + + this.sgc = new SimpleGenericClass(this.tmc); + } +} + +class GenericNotEscapesMutualEscapeDependencyAbleToResolve{ + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private FinalEmptyClass fec = new FinalEmptyClass(); + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private SimpleGenericClass sgc; + public GenericNotEscapesMutualEscapeDependencyAbleToResolve() { + + this.sgc = new SimpleGenericClass(this.fec); + } +} + + + + + + + +@DependentImmutableClassAnnotation("") +class SimpleGenericClass { + @DependentImmutableFieldAnnotation(value = "", genericString = "T") + T t; + SimpleGenericClass(T t){ + this.t = t; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/TrivialMutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/TrivialMutableClass.java deleted file mode 100644 index 7dbab374f0..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/TrivialMutableClass.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.field; - - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; - -@MutableClassAnnotation("It has Mutable Fields") -public class TrivialMutableClass { - - @MutableFieldAnnotation("Because of Mutable Reference") - @MutableReferenceAnnotation("Because it is public") - public int n = 0; - - @MutableFieldAnnotation("Because of Mutable Reference") - @MutableReferenceAnnotation("Because it is public") - public String name = "name"; -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java index c3856793a1..d78125203f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java @@ -2,13 +2,13 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("Because of not final class") @DeepImmutableClassAnnotation("Because it has only Deep Immutable Field Types") public class privateFieldNotBlank_deep { @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceNotEscapesAnnotation("Effectively Immutable Reference") + @ImmutableReferenceAnnotation("Effectively Immutable Reference") private FinalEmptyClass name = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java index 17438376a7..a96a595112 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("Because of not final class") @@ -10,6 +10,6 @@ public class privateFieldNotBlank_shallow { @DeepImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") - @ImmutableReferenceNotEscapesAnnotation("Effectively Immutable Reference") + @ImmutableReferenceAnnotation("Effectively Immutable Reference") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java index b72f81b2aa..ecc426d142 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java @@ -2,13 +2,13 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; @DeepImmutableClassAnnotation("It has only Deep Immutable Fields") public class privateFinalFieldBlank_costructorEscape_deep { @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceEscapesAnnotation("Declared final Reference") + @ImmutableReferenceAnnotation("Declared final Reference") private final FinalEmptyClass fec; public privateFinalFieldBlank_costructorEscape_deep(FinalEmptyClass fec) { this.fec = fec; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java index 304f3b1404..7d1c430b2b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java @@ -2,13 +2,13 @@ import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; @ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") public class privateFinalFieldBlank_costructorEscape_shallow { @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") - @ImmutableReferenceEscapesAnnotation("Declared final Field") + @ImmutableReferenceAnnotation("Declared final Field") private final TrivialMutableClass tmc; public privateFinalFieldBlank_costructorEscape_shallow(TrivialMutableClass tmc) { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_deep.java deleted file mode 100644 index 8e86d0e607..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_deep.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.field; - - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; - -@DeepImmutableClassAnnotation("It has only Deep Immutable Fields") -public class privateFinalFieldNotBlank_deep { - @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceNotEscapesAnnotation("Declared final Field") - private final FinalEmptyClass fec = new FinalEmptyClass(); -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java deleted file mode 100644 index 20303eca61..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldNotBlank_shallow.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.field; - - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; - -@DeepImmutableClassAnnotation("It has only Shallow Immutable Fields") -public class privateFinalFieldNotBlank_shallow { - - @DeepImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") - @ImmutableReferenceNotEscapesAnnotation("Declared final Field") - private final TrivialMutableClass tmc = new TrivialMutableClass(); -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java index a08a4f6321..c59b4e1c82 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("Because of not final class") @@ -12,6 +12,6 @@ public FinalEmptyClass getFec() { return fec; } @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceEscapesAnnotation("It is effectively immutable") + @ImmutableReferenceAnnotation("It is effectively immutable") private FinalEmptyClass fec = new FinalEmptyClass(); } \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java index 01af50412b..9ed4ec4eb1 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; @MutableTypeAnnotation("Because of not final class") @@ -13,7 +13,7 @@ public TrivialMutableClass getTmc() { } @ShallowImmutableFieldAnnotation("Because of Immutable Reference and Mutable Field Type") - @ImmutableReferenceEscapesAnnotation("effectively immutable") + @ImmutableReferenceAnnotation("effectively immutable") private TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java index dea464dccd..4963d71a63 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; @DeepImmutableClassAnnotation("It has only Deep Immutable Fields") public class privatefinal_getterEscape_deep { @@ -11,7 +11,7 @@ public FinalEmptyClass getFec() { } @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceEscapesAnnotation("Declared final Field") + @ImmutableReferenceAnnotation("Declared final Field") private final FinalEmptyClass fec = new FinalEmptyClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java index fc0bb8db10..02713bd81a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java @@ -2,7 +2,7 @@ import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; @ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") public class privatefinal_getterEscape_shallow { @@ -11,6 +11,6 @@ public TrivialMutableClass getTmc() { } @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") - @ImmutableReferenceEscapesAnnotation("Declared final Reference") + @ImmutableReferenceAnnotation("Declared final Reference") private final TrivialMutableClass tmc = new TrivialMutableClass(); } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_deep.java index b9793fb839..40f124a8f3 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_deep.java @@ -1,12 +1,45 @@ package org.opalj.fpcf.fixtures.immutability.field; +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -@MutableClassAnnotation("It has Mutable Fields") + public class protectedClass_deep { @MutableFieldAnnotation("Because of Mutable Reference") @MutableReferenceAnnotation("Because it is declared as protected") - protected FinalEmptyClass fec = new FinalEmptyClass(); + protected FinalEmptyClass fec1 = new FinalEmptyClass(); + + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Because it is declared as protected") + protected TrivialMutableClass tmc1 = new TrivialMutableClass(); + + @DeepImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") + @ImmutableReferenceAnnotation("Declared final Field") + private final TrivialMutableClass tmc2 = new TrivialMutableClass(); + + @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") + @ImmutableReferenceAnnotation("Declared final Field") + private final FinalEmptyClass fec2 = new FinalEmptyClass(); +} + + + + + +@MutableClassAnnotation("It has Mutable Fields") +class TrivialMutableClass { + + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Because it is public") + public int n = 0; + + @MutableFieldAnnotation("Because of Mutable Reference") + @MutableReferenceAnnotation("Because it is public") + public String name = "name"; } + + diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_shallow.java deleted file mode 100644 index 2af008583c..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_shallow.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.field; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; - -@MutableClassAnnotation("It has Mutable Fields") -public class protectedClass_shallow { - @MutableFieldAnnotation("Because of Mutable Reference") - @MutableReferenceAnnotation("Because it is declared as protected") - protected TrivialMutableClass tmc = new TrivialMutableClass(); -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java index 0d352dcd67..cd16812cc3 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java @@ -28,7 +28,7 @@ */ package org.opalj.fpcf.fixtures.immutability.reference; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; /** @@ -54,13 +54,13 @@ public Super(){ */ public class DeclaredFinalFields extends Super { - @ImmutableReferenceNotEscapesAnnotation("Initialized directly") + @ImmutableReferenceAnnotation("Initialized directly") private final int a = 1; - @ImmutableReferenceNotEscapesAnnotation("Initialized through instance initializer") + @ImmutableReferenceAnnotation("Initialized through instance initializer") private final int b; - @ImmutableReferenceNotEscapesAnnotation("Initialized through constructor") + @ImmutableReferenceAnnotation("Initialized through constructor") private final int c; @MutableReferenceAnnotation(value = "Prematurely read through super constructor", prematurelyRead = true) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Escapers.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Escapers.java deleted file mode 100644 index 1c962f117f..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Escapers.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference; - -import org.opalj.fpcf.fixtures.immutability.classes.generic.TrivialMutableClass; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; - -public class Escapers{ - -} - -class TransitiveEscape1 { - @ImmutableReferenceEscapesAnnotation("") - private TrivialMutableClass tmc = new TrivialMutableClass(); - - public void printTMC(){ - System.out.println(tmc.name); - } - - public TrivialMutableClass get(){ - TrivialMutableClass tmc1 = this.tmc; - return tmc1; - } -} - -class TransitiveEscape2 { - @ImmutableReferenceEscapesAnnotation("") - private TrivialMutableClass tmc = new TrivialMutableClass(); - - public void printTMC(){ - System.out.println(tmc.name); - } - - public TrivialMutableClass get(){ - TrivialMutableClass tmc1 = this.tmc; - TrivialMutableClass tmc2 = tmc1; - return tmc2; - } -} -class NotEscape { - @ImmutableReferenceNotEscapesAnnotation("") - private TrivialMutableClass tmc1 = new TrivialMutableClass(); - - @LazyInitializedThreadSafeReferenceAnnotation("") - private TrivialMutableClass tmc2; - - public TrivialMutableClass set() { - TrivialMutableClass tmc11 = tmc1; - TrivialMutableClass tmc22 = tmc2; - if (tmc11 != null) { - synchronized (this) { - if (tmc22 == null) { - tmc2 = new TrivialMutableClass(); - } - } - } - return tmc2; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java index dd2d37ce1e..da0acbeed6 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java @@ -29,7 +29,10 @@ package org.opalj.fpcf.fixtures.immutability.reference; import org.opalj.fpcf.properties.field_mutability.LazyInitialized; -import org.opalj.fpcf.properties.reference_immutability.*; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; /** * Test classes for simple lazy initialization patterns and anti-patterns regarding reference immutability analysis. @@ -185,7 +188,7 @@ private final int sum(int a, int b) { class DeterministicCallWithParam { - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("Lazy initialization is not the same for different invocations") + @MutableReferenceAnnotation("Lazy initialization is not the same for different invocations") private int x; public int init(int z) { @@ -205,7 +208,7 @@ class DeterministicCallOnFinalField { @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Lazy initialization with call to deterministic method on final field") private int x; - @ImmutableReferenceEscapesAnnotation("Declared final field") + @ImmutableReferenceAnnotation("Declared final field") private final Inner inner; public DeterministicCallOnFinalField(int v) { @@ -266,9 +269,9 @@ public int init() { } } -class NondeterministicCall { +class NodeterministicCall { - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("Wrong lazy initialization with call to non-deterministic method") + @MutableReferenceAnnotation("Wrong lazy initialization with call to non-deterministic method") private int x; private final Object object = new Object(); @@ -335,7 +338,7 @@ 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. */ - @ImmutableReferenceEscapesAnnotation(value = "Field is never initialized, so it stays on its default value") + @ImmutableReferenceAnnotation(value = "Field is never initialized, so it stays on its default value") private int x; private int getZero() { @@ -352,24 +355,11 @@ public int init() { } } -class PossibleExceptionInInitialization { - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("Incorrect because lazy initialization is may not happen due to exception") - private int x; - - public int init(int i) { - int y = this.x; - if (y == 0) { - int z = 10 / i; - y = x = 5; - } - return y; - } -} class CaughtExceptionInInitialization { //TODO reasoning - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("Incorrect because lazy initialization is may not happen due to exception") + @MutableReferenceAnnotation("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/immutability/reference/PrivateFieldUpdater.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFieldUpdater.java index cd63085956..3513eb4956 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFieldUpdater.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFieldUpdater.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.reference; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; /** @@ -9,7 +9,7 @@ */ public class PrivateFieldUpdater { - @ImmutableReferenceEscapesAnnotation("only initialized by the constructor") + @ImmutableReferenceAnnotation("only initialized by the constructor") private String name; @MutableReferenceAnnotation("incremented whenever `this` object is passed to another `NonFinal` object") diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFinalArrayEscapesViaConstructor.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFinalArrayEscapesViaConstructor.java index 8a4a199e0b..0d58cea6f9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFinalArrayEscapesViaConstructor.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFinalArrayEscapesViaConstructor.java @@ -1,22 +1,22 @@ package org.opalj.fpcf.fixtures.immutability.reference; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; public class PrivateFinalArrayEscapesViaConstructor { - @ImmutableReferenceEscapesAnnotation("") + @ImmutableReferenceAnnotation("") private final char[] charArray; - @ImmutableReferenceEscapesAnnotation("") + @ImmutableReferenceAnnotation("") private final byte[] byteArray; - @ImmutableReferenceEscapesAnnotation("") + @ImmutableReferenceAnnotation("") private final int[] intArray; - @ImmutableReferenceEscapesAnnotation("") + @ImmutableReferenceAnnotation("") private final long[] longArray; - @ImmutableReferenceEscapesAnnotation("") + @ImmutableReferenceAnnotation("") private final Object[] objectArray; public PrivateFinalArrayEscapesViaConstructor(char[] charArray, byte[] byteArray, int[] intArray, diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java index afe0efe4b1..105a9d77c5 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java @@ -1,8 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.reference; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceEscapesAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceNotEscapesAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; public class Singleton { @@ -10,7 +9,7 @@ public class Singleton { @MutableReferenceAnnotation("written by static initializer after the field becomes (indirectly) readable") private String name; - @ImmutableReferenceNotEscapesAnnotation("only initialized once by the constructor") + @ImmutableReferenceAnnotation("only initialized once by the constructor") private Object mutex = new Object(); private Singleton() { @@ -25,7 +24,7 @@ public String getName() { // STATIC FUNCTIONALITY - @ImmutableReferenceEscapesAnnotation("only set in the static initializer") + @ImmutableReferenceAnnotation("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/reference/Template.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java index 006390b129..53111d6387 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java @@ -1,10 +1,9 @@ package org.opalj.fpcf.fixtures.immutability.reference; -import com.sun.org.apache.xalan.internal.xsltc.compiler.SyntaxTreeNode; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; public class Template { - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + @LazyInitializedNotThreadSafeReferenceAnnotation("") private Template _template; private Template _parent; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java index 86fb72c1a0..1a786503eb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java @@ -1,8 +1,8 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; import java.util.Random; @@ -41,6 +41,18 @@ public int getR(){ return r; } } +class NoDCLIntRandom { + + @MutableReferenceAnnotation("") + private int r; + + public int getR(){ + if(r==0){ + r = new Random().nextInt(); + } + return r; + } +} class DCLwithEarlyReturns { @LazyInitializedThreadSafeReferenceAnnotation("") @@ -195,7 +207,7 @@ public synchronized SimpleLockingSynchronizedFunction1 getInstance() { class NoDCL1 { - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + @MutableReferenceAnnotation("") NoDCL1 instance; public NoDCL1 NoDCL1(){ @@ -214,7 +226,7 @@ public NoDCL1 NoDCL1(){ class NoDCL2 { - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + @MutableReferenceAnnotation("") NoDCL2 instance; public NoDCL2 NoDCL1(){ @@ -234,7 +246,7 @@ public NoDCL2 NoDCL1(){ class NoDCL3 { - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + @MutableReferenceAnnotation("") NoDCL3 instance; public NoDCL3 NoDCL1(){ @@ -254,7 +266,7 @@ public NoDCL3 NoDCL1(){ class NoDCL4 { - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + @MutableReferenceAnnotation("") NoDCL4 instance; public NoDCL4 NoDCL1(){ @@ -280,7 +292,7 @@ public NoDCL4 NoDCL1(){ class NoDCL6 { - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + @MutableReferenceAnnotation("") NoDCL6 instance; public NoDCL6 NoDCL6() throws IndexOutOfBoundsException{ @@ -301,20 +313,6 @@ public NoDCL6 NoDCL6() throws IndexOutOfBoundsException{ } } -class NoDCLIntRandom { - - @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Random function is not deterministic") - private int r; - - public int getR(){ - if(r==0){ - r = new Random().nextInt(); - } - - return r; - } -} - class DoubleCheckedLockingClassWithStaticFields { @LazyInitializedThreadSafeReferenceAnnotation("") @@ -349,7 +347,7 @@ public Object[] getInstance() { class DoubleCheckedLockingClass19 { - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + @LazyInitializedNotThreadSafeReferenceAnnotation("") private DoubleCheckedLockingClass19 instance; public DoubleCheckedLockingClass19 getInstance() { @@ -365,7 +363,7 @@ public DoubleCheckedLockingClass19 getInstance() { } class ArrayLazyInitializationNotThreadSafe { - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("While the initialization phase there is no lock") + @LazyInitializedNotThreadSafeReferenceAnnotation("During the initialization phase there is no lock") int[] values; public int[] getValues(){ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/S.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/S.java index 4d17efa185..0700afb37a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/S.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/S.java @@ -1,7 +1,5 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; -//import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; - public class S { private final char value[]; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java index 4c12581277..a394de9568 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java @@ -1,10 +1,10 @@ package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; public class SimpleLazyInstantiation{ - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + @LazyInitializedNotThreadSafeReferenceAnnotation("") private static SimpleLazyInstantiation instance; public static SimpleLazyInstantiation init() { @@ -25,7 +25,7 @@ public int hashcode() { } class SimpleLazyObjectsInstantiation{ - @LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation("") + @LazyInitializedNotThreadSafeReferenceAnnotation("") private static SimpleLazyObjectsInstantiation instance; public static SimpleLazyObjectsInstantiation getInstance() { if(instance==null) 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 index fb4e2166c5..a8d58b3b59 100644 --- 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 @@ -6,7 +6,6 @@ import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -import org.opalj.fpcf.properties.type_mutability.MutableType; @DeepImmutableTypeAnnotation("has shallow and mutable fields") @DeepImmutableClassAnnotation("has shallow and immutable fields") @@ -21,7 +20,7 @@ public final class WithMutableAndImmutableFieldType { private SimpleMutableClass tmc = new SimpleMutableClass(); } -@MutableType("Class has no fields but is not final") +@MutableTypeAnnotation("Class has no fields but is not final") @DeepImmutableClassAnnotation("Class has no fields") class EmptyClass { } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeReferenceAnnotation.java similarity index 84% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeReferenceAnnotation.java index 687cfc13e0..e77dc2b30f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeReferenceAnnotation.java @@ -14,10 +14,10 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedNotThreadSafeOrNotDeterministicReferenceMatcher.class) +@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedNotThreadSafeReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface LazyInitializedNotThreadSafeOrNotDeterministicReferenceAnnotation { +public @interface LazyInitializedNotThreadSafeReferenceAnnotation { /** * A short reasoning of this property. 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 index 90125eb46e..714a311ed8 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -25,7 +25,7 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new class FieldImmutabilityTests extends PropertiesTest { override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") ///sandbox6") + List("org/opalj/fpcf/fixtures/immutability") ///sandbox") } override def init(p: Project[URL]): Unit = { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index 0ec7f5d7e5..2b33113a96 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -32,7 +32,7 @@ class ReferenceImmutabilityTests extends PropertiesTest { override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability/sandbox_dcoff") ///sandbox/reference_immutability_lazy_initialization/sandbox") + List("org/opalj/fpcf/fixtures/immutability") ///sandbox5") } override def init(p: Project[URL]): Unit = { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala index a5633a0af1..6cda7addb6 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala @@ -6,7 +6,7 @@ import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.br.fpcf.properties.ReferenceImmutability import org.opalj.fpcf.Entity @@ -64,6 +64,6 @@ class LazyInitializedThreadSafeReferenceMatcher extends ReferenceImmutabilityMat class LazyInitializedNotThreadSafeButDeterministicReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedNotThreadSafeButDeterministicReference) -class LazyInitializedNotThreadSafeOrNotDeterministicReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedNotThreadSafeOrNotDeterministicReference) +class LazyInitializedNotThreadSafeReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedNotThreadSafeReference) class ImmutableReferenceMatcher extends ReferenceImmutabilityMatcher(ImmutableReference) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala index 2c2c2c1b55..e7d8855d2c 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala @@ -65,7 +65,7 @@ case object LazyInitializedThreadSafeReference extends ReferenceImmutability { case _ ⇒ } def meet(other: ReferenceImmutability): ReferenceImmutability = - if (other == MutableReference || other == LazyInitializedNotThreadSafeOrNotDeterministicReference || + if (other == MutableReference || other == LazyInitializedNotThreadSafeReference || other == LazyInitializedNotThreadSafeButDeterministicReference) { other } else { @@ -81,14 +81,14 @@ case object LazyInitializedNotThreadSafeButDeterministicReference extends Refere } def meet(other: ReferenceImmutability): ReferenceImmutability = { - if (other == MutableReference || other == LazyInitializedNotThreadSafeOrNotDeterministicReference) { + if (other == MutableReference || other == LazyInitializedNotThreadSafeReference) { other } else { this } } } -case object LazyInitializedNotThreadSafeOrNotDeterministicReference extends ReferenceImmutability { +case object LazyInitializedNotThreadSafeReference extends ReferenceImmutability { def meet(other: ReferenceImmutability): properties.ReferenceImmutability = { if (other == MutableReference) { other 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 f446e5f8d3..dcaa5f8c50 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala @@ -80,6 +80,7 @@ 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 @@ -578,6 +579,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) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index 28af080d86..eea27e8052 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -27,7 +27,7 @@ import org.opalj.br.fpcf.properties.DependentImmutableType import org.opalj.br.fpcf.properties.FieldImmutability import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.br.fpcf.properties.MutableField import org.opalj.br.fpcf.properties.MutableReference @@ -577,7 +577,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) } case FinalP( - MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference + MutableReference | LazyInitializedNotThreadSafeReference ) ⇒ { state.typeIsImmutable = Some(false) return Result(field, MutableField); @@ -624,7 +624,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case FinalP(ImmutableReference) ⇒ state.referenceIsImmutable = Some(true) case FinalP(LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ state.referenceIsImmutable = Some(true) - case FinalP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference) ⇒ + case FinalP(MutableReference | LazyInitializedNotThreadSafeReference) ⇒ return Result(field, MutableField); case ep @ _ ⇒ { state.dependencies += ep diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala index dc0a5e5c65..c32c9eeb24 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala @@ -112,9 +112,11 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { )(implicit state: State): Boolean = { val propertyStoreResult = propertyStore(declaredMethods(method), Purity.key) - val result = (method.descriptor.parametersCount == 0 && !isNonDeterministic( + val resultIsNonDeterministic = !isNonDeterministic( propertyStoreResult - )) + ) + println("result is non Deterministic: "+resultIsNonDeterministic) + val result = (method.descriptor.parametersCount == 0 && resultIsNonDeterministic) result } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index f0a86b5707..1eecf96ec6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -17,7 +17,7 @@ import org.opalj.br.cfg.CFGNode import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.Purity @@ -71,22 +71,25 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization )(implicit state: State): Option[Boolean] = { var result: Option[Boolean] = None val dcl: ReferenceImmutability = - isThreadSafeLazyInitialisation(writeIndex, defaultValue, method, code, cfg, pcToIndex, tacCai) + isThreadSafeLazyInitialization(writeIndex, defaultValue, method, code, cfg, pcToIndex, tacCai) + println("dcl results: "+dcl) dcl match { case ImmutableReference | LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference ⇒ state.referenceImmutability = dcl - case MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference ⇒ + case MutableReference | LazyInitializedNotThreadSafeReference ⇒ val lazyInitialization = isLazyInitialization(writeIndex, defaultValue, method, code, cfg, pcToIndex) + println("result lazyInitialization: "+lazyInitialization) if (lazyInitialization) state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference else if (dcl == MutableReference) { - if (!lazyInitialization) { + /*if (!lazyInitialization) { result = Some(true) } else - state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference - } else state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference + state.referenceImmutability = LazyInitializedNotThreadSafeReference*/ + result = Some(true) + } else state.referenceImmutability = LazyInitializedNotThreadSafeReference } result } @@ -122,10 +125,13 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization //TODO reasoning if there is another way to do this val writes = fieldAccessInformation.writeAccesses(state.field) - if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) + if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) + println("false1") return false; // more than one write in the method + } val reads = fieldAccessInformation.readAccesses(state.field) if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + println("false2") return false; // Reads outside the (single) lazy initialization method } @@ -137,21 +143,27 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val findGuardResult = findGuard(method, writeIndex, defaultValue, code, cfg) if (findGuardResult.isDefined) (findGuardResult.get._1, findGuardResult.get._2, findGuardResult.get._3) - else return false; + else { println("false3"); return false; } } // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) - return false; + if (!checkImmediateReturn(write, writeIndex, readIndex, code)) { + println("false4") + return false + }; // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) - return false; + if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) { + println("false5") + return false + }; // Field reads (except for the guard) may only be executed if the field's value is not the // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) - return false; + if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) { + println("false6") + return false + }; true } @@ -161,19 +173,28 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization * E.g. in a synchronized method with a nonnull-check. */ - def isThreadSafeLazyInitialisation( + def isThreadSafeLazyInitialization( writeIndex: Int, defaultValue: Any, method: Method, code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]], pcToIndex: Array[Int], - tacCai: TACode[TACMethodParameter, V] + tacCode: TACode[TACMethodParameter, V] )(implicit state: State): ReferenceImmutability = { //var result: ReferenceImmutability = LazyInitializedThreadSafeReference val write = code(writeIndex).asFieldWriteAccessStmt val writeBB = cfg.bb(writeIndex).asBasicBlock val domTree = cfg.dominatorTree + val resultCaughtsAndThrows = findCaughtsThrowsAndResults(tacCode, cfg) + def noInterferingExceptions: Boolean = { + //resultCaughtsAndThrows._1.size == resultCaughtsAndThrows._2.size && + resultCaughtsAndThrows._1.forall(bbCatch ⇒ + resultCaughtsAndThrows._2.exists(bbThrow ⇒ + ((domTree.strictlyDominates(bbCatch._4.nodeId, bbThrow._3.nodeId) || //domination + (bbCatch._4 == bbThrow._3 && bbCatch._1 < bbThrow._1)) && // or equal and successor + bbThrow._2 == bbCatch._3))) + } val (guardIndex, guardedIndex, readIndex, afterGuardRecognizedTheDefaultValueIndex) = { val findGuardResult: (Option[(Int, Int, Int, CFGNode, Int)]) = @@ -204,27 +225,15 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization //when the method is synchronized the monitor has not to be searched if (method.isSynchronized) { if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || - (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { - val resultCaughtsAndThrows = findCaughtsThrowsAndResults(tacCai, cfg) - if (resultCaughtsAndThrows._1.size == resultCaughtsAndThrows._2.size && - resultCaughtsAndThrows._1.forall( - bbCatch ⇒ - resultCaughtsAndThrows._2.exists( - bbThrow ⇒ - ( - (domTree - .strictlyDominates(bbCatch._4.nodeId, bbThrow._3.nodeId) || //domination - (bbCatch._4 == bbThrow._3 && bbCatch._1 < bbThrow._1)) && // or equal and successor - bbThrow._2 == bbCatch._3 - ) - ) - )) { + (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { + + if (noInterferingExceptions) { LazyInitializedThreadSafeReference // result //DCL - } else LazyInitializedNotThreadSafeOrNotDeterministicReference - } else MutableReference; + } else MutableReference + } else MutableReference } else { val monitorResult: ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = - findMonitorCaughtsAndThrowsAndResult(writeIndex, defaultValue, code, cfg, tacCai) + findMonitorCaughtsAndThrowsAndResult(writeIndex, defaultValue, code, cfg, tacCode) if ((monitorResult._1._1.isDefined && monitorResult._1._2.isDefined && monitorResult._2._1.isDefined) && @@ -234,8 +243,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex) //&& //true case dominates Write )) { - val resultCaughtsAndThrows = findCaughtsThrowsAndResults(tacCai, cfg) - if (resultCaughtsAndThrows._1.size == resultCaughtsAndThrows._2.size && + if (noInterferingExceptions /*resultCaughtsAndThrows._1.size == resultCaughtsAndThrows._2.size && resultCaughtsAndThrows._1.forall( bbCatch ⇒ resultCaughtsAndThrows._2.exists( @@ -247,14 +255,36 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization bbThrow._2 == bbCatch._3 ) ) - )) + )*/ ) LazyInitializedThreadSafeReference // result //DCL else - LazyInitializedNotThreadSafeOrNotDeterministicReference - } else if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) - LazyInitializedNotThreadSafeOrNotDeterministicReference - else - MutableReference + MutableReference + } else { + /* + println( + s""" + | fieldtype is objecttype: ${state.field.fieldType.isObjectType} + | > -1 : ${write.value.asVar.definedBy.head > -1} + | checkWriteIsDeterministic: ${checkWriteIsDeterministic(code(write.value.asVar.definedBy.head).asAssignment, method, code)} + | + |""".stripMargin + ) */ + + if ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || + (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) && write.value.asVar.definedBy.size > 0 && + (state.field.fieldType.isObjectType || write.value.asVar.definedBy.head > -1 + && checkWriteIsDeterministic(code(write.value.asVar.definedBy.head).asAssignment, method, code))) { + println("aaaa") + //LazyInitializedNotThreadSafeReference + if (noInterferingExceptions) + LazyInitializedNotThreadSafeReference + else + MutableReference + //checkWriteIsGuarded2(writeIndex, guardIndex, guardedIndex, method, code, cfg) //, tacCode.stmts) + + } else + MutableReference + } } } @@ -573,6 +603,74 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization true } + def checkWriteIsGuarded2( + writeIndex: Int, + guardIndex: Int, + guardedIndex: Int, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + )(implicit state: State): ReferenceImmutability = { + val startBB = cfg.bb(writeIndex).asBasicBlock + var enqueuedBBs: Set[CFGNode] = Set(startBB) + var worklist: List[BasicBlock] = List(startBB.asBasicBlock) + val abnormalReturnNode = cfg.abnormalReturnNode + + val caughtExceptions = code.iterator + .filter { stmt ⇒ stmt.astID == CaughtException.ASTID } + .flatMap { exception ⇒ + exception.asCaughtException.origins.iterator.map { origin: Int ⇒ + if (isImmediateVMException(origin)) { + pcOfImmediateVMException(origin) + } else { + pcOfMethodExternalException(origin) + } + } + } + .toSet + + /*val caughtExceptions = code.filter { stmt => + stmt.astID == CaughtException.ASTID + }.toList flatMap { + exception=> + exception.asCaughtException.origins.flatMap { origin => + if (isImmediateVMException(origin)) { + pcOfImmediateVMException(origin) + } else { + pcOfMethodExternalException(origin) + } + } + } */ + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + val startPC = curBB.startPC + val endPC = curBB.endPC + if (startPC == 0 || startPC == guardedIndex) + return MutableReference; // Reached method start or wrong branch of guarding if-Statement + // Exception thrown between guard and write, which is ok for deterministic methods, + // but may be a problem otherwise as the initialization is not guaranteed to happen + // (or never happen). + if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) { + if (!lazyInitializerIsDeterministic(method, code)) { + return MutableReference + } + }; + // Exception thrown between guard and write (caught somewhere, but we don't care) + if ((curBB ne startBB) & caughtExceptions.toSet.contains(endPC)) { + if (!lazyInitializerIsDeterministic(method, code)) { + return MutableReference + } + }; + // Check all predecessors except for the one that contains the guarding if-Statement + val predecessors = + getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + LazyInitializedNotThreadSafeReference + } + /** * Gets all predecessor BasicBlocks of a CFGNode. */ @@ -610,6 +708,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization method: Method, code: Array[Stmt[V]] )(implicit state: State): Boolean = { + import org.opalj.tac.NewArray def isConstant(uvar: Expr[V]): Boolean = { val defSites = uvar.asVar.definedBy @@ -618,6 +717,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization else if (code(defSites.head).asAssignment.expr.isConst) true else { val expr = code(index).asAssignment.expr + println("expr is field read: "+expr.isFieldRead) expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { case Some(field) ⇒ isImmutableReference(propertyStore(field, ReferenceImmutability.key)) @@ -626,13 +726,26 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization }) } } - - defSites == SelfReferenceParameter || + println( + s""" + | defSites==SelfReferenceParameter: ${defSites == SelfReferenceParameter} + | defSites.size: ${defSites.size} + | isConstantDef: ${isConstantDef(defSites.head)} + | + |""".stripMargin + ) + + val result = defSites == SelfReferenceParameter || defSites.size == 1 && isConstantDef(defSites.head) + + println("result: "+result) + result } val value = origin.expr + println("value: "+value) + val isNonConstDeterministic = value.astID match { case GetStatic.ASTID | GetField.ASTID ⇒ value.asFieldRead.resolveField(p) match { @@ -652,6 +765,9 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) true } + case NewArray.ASTID ⇒ + value.asNewArray.counts.forall(isConstant(_)) + 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 @@ -887,5 +1003,4 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization } false } - } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index 0a6832d38e..15d0f9e923 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -23,7 +23,7 @@ import org.opalj.br.fpcf.properties.EscapeViaReturn import org.opalj.br.fpcf.properties.FieldPrematurelyRead import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.NoEscape @@ -188,12 +188,12 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val pc = state.lazyInitInvocation.get._2 if (callees.isIncompleteCallSite(pc)) { - state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference //TODO //MutableReference //NonFinalFieldByAnalysis + state.referenceImmutability = MutableReference //LazyInitializedNotThreadSafeReference //TODO //MutableReference //NonFinalFieldByAnalysis true } else { val targets = callees.callees(pc) if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { - state.referenceImmutability = LazyInitializedNotThreadSafeOrNotDeterministicReference //MutableReference //NonFinalFieldByAnalysis + state.referenceImmutability = MutableReference //LazyInitializedNotThreadSafeReference //MutableReference //NonFinalFieldByAnalysis true } else false } @@ -266,7 +266,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) val nonDeterministicResult = isNonDeterministic(newEP) //if (!r) state.referenceImmutability = LazyInitializedReference - if (state.referenceImmutability != LazyInitializedNotThreadSafeOrNotDeterministicReference && + if (state.referenceImmutability != LazyInitializedNotThreadSafeReference && state.referenceImmutability != LazyInitializedThreadSafeReference) { // both dont need determinism isNotFinal = nonDeterministicResult } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala index bc3814af36..c6f5f29425 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala @@ -23,7 +23,7 @@ import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.ImpureByAnalysis import org.opalj.br.fpcf.properties.ImpureByLackOfInformation import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeOrNotDeterministicReference +import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.Pure @@ -452,7 +452,7 @@ trait AbstractPurityAnalysis_new extends FPCFAnalysis { //case LBP(LazyInitializedReference) ⇒ case LBP(ImmutableReference | LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ //_: FinalField) ⇒ // Final fields don't impede purity - case FinalP(MutableReference | LazyInitializedNotThreadSafeOrNotDeterministicReference) ⇒ + case FinalP(MutableReference | LazyInitializedNotThreadSafeReference) ⇒ //_: FinalEP[Field, ReferenceImmutability] ⇒ //FieldMutability] ⇒ // Mutable field if (objRef.isDefined) { if (state.ubPurity.isDeterministic) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala index a97441f95f..b7fe324d2d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala @@ -1024,6 +1024,7 @@ object LazyL2PurityAnalysis_new extends L2PurityAnalysisScheduler_new with FPCFL override def register( p: SomeProject, ps: PropertyStore, analysis: InitializationData ): FPCFAnalysis = { + val analysis = new L2PurityAnalysis_new(p) ps.registerLazyPropertyComputation(Purity.key, analysis.doDeterminePurity) analysis } From b972fec2322fed99bee447733894f885b2108430 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 7 Aug 2020 20:34:46 +0200 Subject: [PATCH 235/327] current status, still WIP, code not fully revised, one test remaining --- .../org/opalj/support/info/Immutability.scala | 16 +- .../{field => fields}/ArrayAndString.java | 4 +- .../ClassWithStaticFields.java | 2 +- .../{field => fields}/Escapers.java | 2 +- .../{field => fields}/FinalEmptyClass.java | 2 +- .../privateFieldNotBlank_deep.java | 2 +- .../privateFieldNotBlank_shallow.java | 2 +- ...FinalFieldBlank_costructorEscape_deep.java | 2 +- ...alFieldBlank_costructorEscape_shallow.java | 2 +- .../private_getterEscape_deep.java | 2 +- .../private_getterEscape_shallow.java | 2 +- .../private_setter_deep.java | 2 +- .../private_setter_shallow.java | 2 +- .../privatefinal_getterEscape_deep.java | 2 +- .../privatefinal_getterEscape_shallow.java | 2 +- .../protectedClass_deep.java | 3 +- .../immutability/reference/Template.java | 4 +- .../opalj/fpcf/FieldImmutabilityTests.scala | 4 +- .../fpcf/ReferenceImmutabilityTests.scala | 2 +- .../src/main/scala/org/opalj/tac/Stmt.scala | 2 + .../L0FieldImmutabilityAnalysis.scala | 335 +++++++--- ...bstractReferenceImmutabilityAnalysis.scala | 27 +- ...mutabilityAnalysisLazyInitialization.scala | 574 +++++++++++------- .../L0ReferenceImmutabilityAnalysis.scala | 53 +- 24 files changed, 669 insertions(+), 381 deletions(-) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/ArrayAndString.java (82%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/ClassWithStaticFields.java (93%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/Escapers.java (98%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/FinalEmptyClass.java (86%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/privateFieldNotBlank_deep.java (93%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/privateFieldNotBlank_shallow.java (93%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/privateFinalFieldBlank_costructorEscape_deep.java (92%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/privateFinalFieldBlank_costructorEscape_shallow.java (92%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/private_getterEscape_deep.java (93%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/private_getterEscape_shallow.java (93%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/private_setter_deep.java (93%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/private_setter_shallow.java (93%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/privatefinal_getterEscape_deep.java (92%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/privatefinal_getterEscape_shallow.java (92%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{field => fields}/protectedClass_deep.java (92%) 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 index 5686265298..14ceac5ed0 100644 --- 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 @@ -250,7 +250,7 @@ object Immutability { val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet //References - var mutableReferences = propertyStore + val mutableReferences = propertyStore .finalEntities(MutableReference) .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) .toList @@ -266,7 +266,7 @@ object Immutability { .toList .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) - val notThreadSafeOrNotDeterministicLazyInitialization = propertyStore + val notThreadSafeLazyInitialization = propertyStore .finalEntities(LazyInitializedNotThreadSafeReference) .toList .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) @@ -282,7 +282,7 @@ object Immutability { .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) //Test.... - mutableReferences = mutableReferences ++ notThreadSafeOrNotDeterministicLazyInitialization + //mutableReferences = mutableReferences ++ notThreadSafeLazyInitialization // for comparison reasons if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.References) { stringBuilderResults.append(s""" @@ -290,7 +290,7 @@ object Immutability { | ${mutableReferences.mkString("|| mutable Reference \n")} | | lazy initalized not thread safe and not deterministic references: -| ${notThreadSafeOrNotDeterministicLazyInitialization.sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString).mkString("|| no ts & n dt ref\n")} +| ${notThreadSafeLazyInitialization.sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString).mkString("|| no ts & n dt ref\n")} | | lazy initialized not thread safe but deterministic references: | ${lazyInitializedReferencesNotThreadSafeButDeterministic.mkString("|| li no ts b dt\n")} @@ -444,7 +444,7 @@ object Immutability { stringBuilderAmounts.append( s""" | mutable References: ${mutableReferences.size} - | lazy initialized not thread safe or not deterministic ref.: ${notThreadSafeOrNotDeterministicLazyInitialization.size} + | lazy initialized not thread safe ref.: ${notThreadSafeLazyInitialization.size} | lazy initialization not thread safe but deterministic: ${lazyInitializedReferencesNotThreadSafeButDeterministic.size} | lazy initialization thread safe: ${lazyInitializedReferencesThreadSafe.size} | immutable references: ${immutableReferences.size} @@ -537,9 +537,9 @@ object Immutability { s"${if (resultsFolder != null) resultsFolder.toAbsolutePath.toString else "."}"+ s""+ s"/${analysis.toString}_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ + s"${(calendar.get(Calendar.MONTH) + 1)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}_${numThreads}Threads.txt" + s"${calendar.get(Calendar.SECOND)}_${numThreads}Threads.txt" ) file.createNewFile() val bw = new BufferedWriter(new FileWriter(file)) @@ -606,7 +606,7 @@ object Immutability { else if (result == "Classes") analysis = RunningAnalyses.Classes else if (result == "Types") - analysis = RunningAnalyses.Fields + analysis = RunningAnalyses.Types else throw new IllegalArgumentException(s"unknown parameter: $result") } case "-threads" ⇒ numThreads = readNextArg().toInt diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ArrayAndString.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayAndString.java similarity index 82% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ArrayAndString.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayAndString.java index 8ff0d12f75..7a6eb14ab0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ArrayAndString.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayAndString.java @@ -1,8 +1,6 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; -import org.opalj.br.fpcf.properties.DependentImmutableField; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; public class ArrayAndString { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ClassWithStaticFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithStaticFields.java similarity index 93% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ClassWithStaticFields.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithStaticFields.java index d2676062d9..1352ff31b0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/ClassWithStaticFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithStaticFields.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/Escapers.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/Escapers.java similarity index 98% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/Escapers.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/Escapers.java index 434920ab74..d1e5ad004e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/Escapers.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/Escapers.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/FinalEmptyClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/FinalEmptyClass.java similarity index 86% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/FinalEmptyClass.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/FinalEmptyClass.java index 5aae58f878..c8bbbac54c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/FinalEmptyClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/FinalEmptyClass.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_deep.java similarity index 93% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_deep.java index d78125203f..c3af344bc4 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_deep.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_shallow.java similarity index 93% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_shallow.java index a96a595112..a16d5fc7b6 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFieldNotBlank_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_shallow.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_deep.java similarity index 92% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_deep.java index ecc426d142..df3941d3f4 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_deep.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_shallow.java similarity index 92% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_shallow.java index 7d1c430b2b..ab78b53a3a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privateFinalFieldBlank_costructorEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_shallow.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_deep.java similarity index 93% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_deep.java index c59b4e1c82..d1bd253dfb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_deep.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_shallow.java similarity index 93% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_shallow.java index 9ed4ec4eb1..87d093b1f3 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_shallow.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_deep.java similarity index 93% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_deep.java index 4aae64534c..b0844417bb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_deep.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_shallow.java similarity index 93% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_shallow.java index 385fdcd5a6..a82821aa8b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/private_setter_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_shallow.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_deep.java similarity index 92% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_deep.java index 4963d71a63..1f0e03aaf5 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_deep.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_shallow.java similarity index 92% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_shallow.java index 02713bd81a..1b2b14f107 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/privatefinal_getterEscape_shallow.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_shallow.java @@ -1,4 +1,4 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/protectedClass_deep.java similarity index 92% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_deep.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/protectedClass_deep.java index 40f124a8f3..2191d20935 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field/protectedClass_deep.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/protectedClass_deep.java @@ -1,6 +1,5 @@ -package org.opalj.fpcf.fixtures.immutability.field; +package org.opalj.fpcf.fixtures.immutability.fields; -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java index 53111d6387..bd8309afea 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java @@ -1,9 +1,9 @@ package org.opalj.fpcf.fixtures.immutability.reference; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; +//import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; public class Template { - @LazyInitializedNotThreadSafeReferenceAnnotation("") + //@LazyInitializedNotThreadSafeReferenceAnnotation("") private Template _template; private Template _parent; 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 index 714a311ed8..483afe6a7f 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -24,8 +24,10 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new */ class FieldImmutabilityTests extends PropertiesTest { + override def withRT = true + override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") ///sandbox") + List("org/opalj/fpcf/fixtures/immutability") } override def init(p: Project[URL]): Unit = { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala index 2b33113a96..868a4c4f67 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala @@ -32,7 +32,7 @@ class ReferenceImmutabilityTests extends PropertiesTest { override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") ///sandbox5") + List("org/opalj/fpcf/fixtures/immutability") } override def init(p: Project[URL]): Unit = { 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 dcaa5f8c50..e2497b4ea3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala @@ -90,6 +90,7 @@ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { def isMonitorExit: Boolean = false def isPutStatic: Boolean = false def isPutField: Boolean = false + def isNop: Boolean = false } /** @@ -486,6 +487,7 @@ 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 diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index eea27e8052..81d8ddb1c6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -66,6 +66,8 @@ import org.opalj.fpcf.InterimUBP import org.opalj.tac.common.DefinitionSite import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.Stmt +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.DeclaredMethodsKey case class State(f: Field) { var field: Field = f @@ -98,10 +100,12 @@ object DependentImmutabilityKind extends Enumeration { */ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { + 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) def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { entity match { @@ -119,7 +123,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var classFormalTypeParameters: Option[Set[String]] = None def loadFormalTypeparameters(): Unit = { var result: Set[String] = Set.empty - def CheckAttributeWithRegardToFormalTypeParameter: Attribute ⇒ Unit = { attribute ⇒ attribute match { @@ -135,8 +138,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } - //if the genericity is nested in an inner class - // collect the generic type parameters from the fields outer class + /** + * If the genericity is nested in an inner class + * collecting the generic type parameters from the fields outer class + */ if (field.classFile.outerType.isDefined) { val outerClassFile = project.classFile(field.classFile.outerType.get._1) if (outerClassFile.isDefined) { @@ -145,7 +150,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) } } - // collect the generic type parameters from the fields class + + /** + * Collecting the generic type parameters from the fields class + */ field.classFile.attributes.foreach( CheckAttributeWithRegardToFormalTypeParameter ) @@ -154,24 +162,35 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) classFormalTypeParameters = Some(result) } + /** + * Returns, if a generic parameter like e.g. 'T' is in the classes or the first outer classes Signature + * @param string The generic type parameter that should be looked for + */ def isInClassesGenericTypeParameters(string: String): Boolean = classFormalTypeParameters.isDefined && classFormalTypeParameters.get.contains(string) - def handleTypeImmutability(state: State) = { + /** + * Checks the immutability of a fields type. Returns the Result and registers the dependencies if necessary. + * @param state + */ + def handleTypeImmutability(implicit state: State): Unit = { val objectType = field.fieldType.asFieldType if (objectType == ObjectType.Object) { state.typeIsImmutable = Some(false) //handling generic fields } else if (objectType.isBaseType || objectType == ObjectType.String) { // we hardcode here the strings deep immutability + // base types are by design deep immutable //state.typeImmutability = Some(true) // true is default } else if (objectType.isArrayType) { + // Because the entries of an array can be reassigned we hardcode it as not being deep immutable state.typeIsImmutable = Some(false) } else { val typeImmutabilityPropertyStoreResult = propertyStore(objectType, TypeImmutability_new.key) - state.dependencies = state.dependencies.iterator.filter(_.e ne typeImmutabilityPropertyStoreResult.e).toSet + state.dependencies = + state.dependencies.iterator.filter(_.e ne typeImmutabilityPropertyStoreResult.e).toSet typeImmutabilityPropertyStoreResult match { - case FinalP(DeepImmutableType) ⇒ // type is immutable is default + case FinalP(DeepImmutableType) ⇒ // type being deep immutable is default case FinalP(DependentImmutableType) ⇒ state.typeIsImmutable = Some(false) state.dependentImmutability = @@ -181,10 +200,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.dependentImmutability = None if (state.field.fieldType.isObjectType && state.field.fieldType.asObjectType != ObjectType.Object) { - state.dependentImmutability = None //when the generic type is still final + state.dependentImmutability = None //state we are less then dependent immutable } } - //case ep: InterimEP[e, p] ⇒ state.dependencies += ep case epk ⇒ state.dependencies += epk } } @@ -258,7 +276,8 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.dependencies += ep } }) - + //Prevents keeping the case of keeping the default values of these + // flags only because of ne revelant attribute was found if (noRelevantAttributesFound) { noShallowOrMutableTypeInGenericTypeFound = false onlyDeepTypesInGenericTypeFound = false @@ -319,6 +338,19 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } + /*def isDeterministic( + eop: EOptionP[DeclaredMethod, Purity] + )(implicit state: State): Boolean = eop match { + case FinalP(p: Purity) ⇒ + println("final p is det: "+p.isDeterministic); p.isDeterministic + case LBP(p: Purity) if p.isDeterministic ⇒ + true + case EUBP(e, p: Purity) if !p.isDeterministic ⇒ + false + case _ ⇒ + state.dependencies += eop + true + } */ def checkIfReferencedObjectCanEscape(implicit state: State): Unit = { def checkFieldReadsForEffImmutability(field: Field)(implicit state: State) = { @@ -329,32 +361,62 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) pc ← pcs } { val index = taCode.pcToIndex(pc) - val stmt = taCode.stmts(index) - if (stmt.isAssignment) { - val assignment = stmt.asAssignment - val useSites = assignment.targetVar.usedBy - for { - index ← useSites - } { - val fieldsUseSiteStmt = taCode.stmts(index) - if (!fieldsUseSiteStmt.isMonitorEnter && - !fieldsUseSiteStmt.isMonitorExit && - !fieldsUseSiteStmt.isIf) { + if (index > -1) { + val stmt = taCode.stmts(index) + if (stmt.isAssignment) { + val assignment = stmt.asAssignment + val useSites = assignment.targetVar.usedBy + val propStoreResult = propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) + val handleEscapePropertyResult = handleEscapeProperty(propStoreResult) + if (handleEscapePropertyResult) { + state.noEscapePossibilityViaReference = false } + for { + index ← useSites + } { + val fieldsUseSiteStmt = taCode.stmts(index) + + if (!fieldsUseSiteStmt.isMonitorEnter && + !fieldsUseSiteStmt.isMonitorExit && + !fieldsUseSiteStmt.isIf) { + + state.noEscapePossibilityViaReference = false + } + } + } else if (stmt.isExprStmt) { + //is ignored + // The value is only read but not assigned to another one + } else { + + state.noEscapePossibilityViaReference = false } - } else if (stmt.isExprStmt) { - //is ignored } else { + state.noEscapePossibilityViaReference = false } } } var seen: Set[Stmt[V]] = Set.empty - def checkFieldWritesForEffImmutability(field: Field)(implicit state: State) = { + def checkFieldWritesForEffImmutability(field: Field)(implicit state: State): Unit = { + import org.opalj.tac.StaticFunctionCall + + def handleStaticFunctionCall( + staticFunctionCall: StaticFunctionCall[V], + tacCode: TACode[TACMethodParameter, V] + ): Unit = { + if (staticFunctionCall.params. + exists(p ⇒ + p.asVar.definedBy.size != 1 || + p.asVar.definedBy.head < 0 || + !tacCode.stmts(p.asVar.definedBy.head).asAssignment.expr.isConst)) { + + state.noEscapePossibilityViaReference = false + } + } - def checkNonVirtualMethodCall( + def handleNonVirtualMethodCall( method: Method, nonVirtualMethodCall: NonVirtualMethodCall[V], tacCode: TACode[TACMethodParameter, V] @@ -364,6 +426,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) param.asVar.definedBy.foreach( index ⇒ if (index < 0) { + state.noEscapePossibilityViaReference = false } else { val paramDefinitionStmt = tacCode.stmts(index) @@ -387,14 +450,18 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (stmt.isNonVirtualMethodCall) { if (!seen.contains(stmt)) { seen += stmt - checkNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) + handleNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) } - } else + } else { + state.noEscapePossibilityViaReference = false + } } //} - } else + } else { + state.noEscapePossibilityViaReference = false + } } else { val definitionSitesOfParam = definitionSites(method, index) @@ -402,7 +469,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (stmt.isNonVirtualMethodCall) { if (!seen.contains(stmt)) { seen += stmt - checkNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) + handleNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) } } else if (stmt.isPutField || stmt.isPutStatic) { if (!seen.contains(stmt)) { @@ -412,12 +479,14 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } else if (stmt.isArrayStore) { //val arrayStore = stmt.asArrayStore + state.noEscapePossibilityViaReference = false //TODO handling that case more precise } //else if // other cases that the purity analysis can not handle else { val propertyStoreResult = propertyStore(definitionSitesOfParam, EscapeProperty.key) if (handleEscapeProperty(propertyStoreResult)) { + state.noEscapePossibilityViaReference = false } } @@ -428,78 +497,149 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) } - def checkPuts(stmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { + def checkPuts(putStmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { + import org.opalj.Yes + import org.opalj.tac.Expr var putDefinitionSites: IntTrieSet = IntTrieSet.empty - if (stmt.isPutField) { - val putField = stmt.asPutField + var value: Expr[V] = null + if (putStmt.isPutField) { + val putField = putStmt.asPutField putDefinitionSites = putField.value.asVar.definedBy - } else if (stmt.isPutStatic) { - val putStatic = stmt.asPutStatic + value = putField.value + } else if (putStmt.isPutStatic) { + val putStatic = putStmt.asPutStatic putDefinitionSites = putStatic.value.asVar.definedBy + value = putStatic.value } else { + state.noEscapePossibilityViaReference = false } - for { - i ← putDefinitionSites - } { - if (i > 0) { - val definitionSiteStatement = tacCode.stmts(i) - val definitionSiteAssignment = definitionSiteStatement.asAssignment - if (definitionSiteAssignment.expr.isVar) { - val definitionSiteVar = definitionSiteAssignment.expr.asVar - for (definitionSiteVarUseSite ← definitionSiteVar.usedBy.iterator) { - val definitionSiteVarUseSiteStmt = tacCode.stmts(definitionSiteVarUseSite) - if (definitionSiteVarUseSiteStmt.isNonVirtualMethodCall) { - val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall - checkNonVirtualMethodCall(method, nonVirtualMethodCall, tacCode) + val index = value.asVar.definedBy.head + if (value.asVar.value.isArrayValue == Yes) { + tacCode.stmts(index).asAssignment.targetVar.usedBy.foreach(x ⇒ { + val arrayStmt = tacCode.stmts(x) + if (arrayStmt != putStmt) + if (arrayStmt.isArrayStore) { + val arrayStore = arrayStmt.asArrayStore + val arrayStoreIndex = arrayStore.index + val isArrayIndexConst = + tacCode.stmts(arrayStoreIndex.asVar.definedBy.head).asAssignment.expr.isConst + val assignedValue = arrayStore.value + val valueAssignment = tacCode.stmts(assignedValue.asVar.definedBy.head).asAssignment + val assignedExpr = valueAssignment.expr + if (!isArrayIndexConst) { + state.noEscapePossibilityViaReference = false + } else if (assignedExpr.isStaticFunctionCall) { + handleStaticFunctionCall(assignedExpr.asStaticFunctionCall, tacCode) + } else if (assignedExpr.isNew) { + //val newExpression = assignedExpr.asNew + valueAssignment.targetVar.asVar.usedBy.foreach(index ⇒ { + val tmpStmt = tacCode.stmts(index) + if (tmpStmt.isArrayStore) { + // can be ingored + } else if (tmpStmt.isNonVirtualMethodCall) { + val nonVirtualMethodcall = tmpStmt.asNonVirtualMethodCall + if (!seen.contains(tmpStmt)) { + seen += tmpStmt + handleNonVirtualMethodCall(method, nonVirtualMethodcall, tacCode) + } //nothing to do in the else case. Stmt was still handled + } else { + + state.noEscapePossibilityViaReference = false + } + }) } else { + state.noEscapePossibilityViaReference = false } - //TODO andere Fälle bedenken + } else { + + state.noEscapePossibilityViaReference = false } - } else if (definitionSiteAssignment.expr.isNew) { - if (!method.isConstructor) { - val propertyStoreResult = - propertyStore( - definitionSites(method, definitionSiteAssignment.pc), - EscapeProperty.key - ) - if (handleEscapeProperty(propertyStoreResult)) { - state.noEscapePossibilityViaReference = false + }) + } else + for { + i ← putDefinitionSites + } { + if (i > 0) { + val definitionSiteStatement = tacCode.stmts(i) + val definitionSiteAssignment = definitionSiteStatement.asAssignment + if (definitionSiteAssignment.expr.isStaticFunctionCall) { + handleStaticFunctionCall(definitionSiteAssignment.expr.asStaticFunctionCall, tacCode) + } else if (definitionSiteAssignment.expr.isVar) { + val definitionSiteVar = definitionSiteAssignment.expr.asVar + for (definitionSiteVarUseSite ← definitionSiteVar.usedBy.iterator) { + val definitionSiteVarUseSiteStmt = tacCode.stmts(definitionSiteVarUseSite) + if (definitionSiteVarUseSiteStmt.isNonVirtualMethodCall) { + val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall + handleNonVirtualMethodCall(method, nonVirtualMethodCall, tacCode) + } else { + + state.noEscapePossibilityViaReference = false + } + //TODO andere Fälle bedenken } - } else { - val useSites = - definitionSiteAssignment.targetVar.usedBy - for (i ← useSites) { - val useSiteStmt = tacCode.stmts(i) - if (useSiteStmt.isNonVirtualMethodCall) { - checkNonVirtualMethodCall(method, useSiteStmt.asNonVirtualMethodCall, tacCode) - } else if (useSiteStmt.isPutStatic || useSiteStmt.isPutField) { - //checkPuts(stmt, method, tacCode) - if (useSiteStmt != stmt) { - state.noEscapePossibilityViaReference = false //TODO + } else if (definitionSiteAssignment.expr.isNew) { + if (!method.isConstructor) { + definitionSiteAssignment.targetVar.asVar.usedBy.foreach(x ⇒ { + val tmpStmt = tacCode.stmts(x) + if (tmpStmt.isPutStatic || tmpStmt.isPutField) { + // can be ingored //TODO use seen + } else if (tmpStmt.isNonVirtualMethodCall) { + if (!seen.contains(tmpStmt)) + handleNonVirtualMethodCall(method, tmpStmt.asNonVirtualMethodCall, tacCode) + } else { + + state.noEscapePossibilityViaReference = false } + }) + /*val propertyStoreResult = { + propertyStore( + definitionSites(method, definitionSiteAssignment.pc), + EscapeProperty.key + ) - } else if (useSiteStmt.isAssignment) { - state.noEscapePossibilityViaReference = false //TODO - //val assignment = stmt.asAssignment - } else { + } + println("propertystoreresult: " + propertyStoreResult) + if (handleEscapeProperty(propertyStoreResult)) { state.noEscapePossibilityViaReference = false + }*/ + } else { + val useSites = + definitionSiteAssignment.targetVar.usedBy + for (i ← useSites) { + val useSiteStmt = tacCode.stmts(i) + if (useSiteStmt.isNonVirtualMethodCall) { + handleNonVirtualMethodCall(method, useSiteStmt.asNonVirtualMethodCall, tacCode) + } else if (useSiteStmt.isPutStatic || useSiteStmt.isPutField) { + if (!seen.contains(useSiteStmt)) { + seen += useSiteStmt + checkPuts(useSiteStmt, method, tacCode) + } + } else if (useSiteStmt.isAssignment) { + + state.noEscapePossibilityViaReference = false //TODO + //val assignment = stmt.asAssignment + } else { + + state.noEscapePossibilityViaReference = false + } } } + //TODO alle Fälle abdecken + //TODO escape analyse für Object + // else ; aktuelles putfield bedenken + } else if (!definitionSiteAssignment.expr.isConst) { + + state.noEscapePossibilityViaReference = false } - //TODO alle Fälle abdecken - //TODO escape analyse für Object - // else ; aktuelles putfield bedenken - } else if (!definitionSiteAssignment.expr.isConst) { + } else { + state.noEscapePossibilityViaReference = false } - } else { - state.noEscapePossibilityViaReference = false } - } } - // start of method + // start of method check field writes state.noEscapePossibilityViaReference = field.isPrivate for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) @@ -516,17 +656,35 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } else { + state.noEscapePossibilityViaReference = false } } } - if (state.noEscapePossibilityViaReference) - checkFieldWritesForEffImmutability(state.field) - if (state.noEscapePossibilityViaReference) + if (state.noEscapePossibilityViaReference) { + checkFieldReadsForEffImmutability(state.field) + } + if (state.noEscapePossibilityViaReference) { + + checkFieldWritesForEffImmutability(state.field) + } + } def createResult(state: State): ProperPropertyComputationResult = { + /* + println( + s""" + | create result field: ${state.field} + | ref imm: ${state.referenceIsImmutable} + | type imm: ${state.typeIsImmutable} + | dep imm: ${state.dependentImmutability} + | not escape: ${state.noEscapePossibilityViaReference} + | + |""".stripMargin + ) */ + state.referenceIsImmutable match { case Some(false) | None ⇒ Result(field, MutableField) @@ -555,7 +713,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { state.dependencies = state.dependencies.iterator.filter(_.e ne eps.e).toSet - eps match { //(: @unchecked) case x: InterimEP[_, _] ⇒ { @@ -620,7 +777,8 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } implicit val state: State = State(field) - propertyStore(state.field, ReferenceImmutability.key) match { + val referenceImmutabilityPropertyStoreResult = propertyStore(state.field, ReferenceImmutability.key) + referenceImmutabilityPropertyStoreResult match { case FinalP(ImmutableReference) ⇒ state.referenceIsImmutable = Some(true) case FinalP(LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ state.referenceIsImmutable = Some(true) @@ -630,12 +788,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.dependencies += ep } } - loadFormalTypeparameters() handleTypeImmutability(state) - handleTypeImmutability(state) hasGenericType(state) - //it is possible that the type immutability not already determined at this point if (!state.referenceIsImmutable.isDefined || state.referenceIsImmutable.get) { checkIfReferencedObjectCanEscape @@ -657,8 +812,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - import org.opalj.tac.fpcf.properties.TACAI - final override def uses: Set[PropertyBounds] = Set( PropertyBounds.finalP(TACAI), PropertyBounds.ub(EscapeProperty), @@ -680,7 +833,7 @@ object EagerL0FieldImmutabilityAnalysis final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L0FieldImmutabilityAnalysis(p) - val fields = p.allProjectClassFiles.toIterator.flatMap { _.fields } + val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) //TODO p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) analysis } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala index c32c9eeb24..32dfee8694 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala @@ -39,6 +39,9 @@ import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.properties.TACAI import org.opalj.value.ValueInformation +/** + * Encompasses the base function used in the [[L0ReferenceImmutabilityAnalysis]] + */ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { type V = DUVar[ValueInformation] @@ -49,17 +52,16 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { implicit final val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) case class State( - field: Field, - var referenceImmutability: ReferenceImmutability = ImmutableReference, - 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = 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_new]] = Set.empty, - var lazyInitializerIsDeterministicFlag: Boolean = false + field: Field, + var referenceImmutability: ReferenceImmutability = ImmutableReference, + 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = 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_new]] = Set.empty ) { def hasDependees: Boolean = { prematurelyReadDependee.isDefined || purityDependees.nonEmpty || @@ -84,7 +86,7 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { case finalEP: FinalEP[Method, TACAI] ⇒ finalEP.ub.tac case epk ⇒ - state.tacDependees += method -> ((epk, pcs)) // + alle pcs die schon existieren + state.tacDependees += method -> ((epk, pcs)) None } } @@ -115,7 +117,6 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { val resultIsNonDeterministic = !isNonDeterministic( propertyStoreResult ) - println("result is non Deterministic: "+resultIsNonDeterministic) val result = (method.descriptor.parametersCount == 0 && resultIsNonDeterministic) result } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index 1eecf96ec6..867045d782 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -52,8 +52,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization extends AbstractReferenceImmutabilityAnalysis with FPCFAnalysis { - import scala.collection.mutable - /** * handles the lazy initialization determinations for the method * methodUpdatesField @@ -72,24 +70,25 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization var result: Option[Boolean] = None val dcl: ReferenceImmutability = isThreadSafeLazyInitialization(writeIndex, defaultValue, method, code, cfg, pcToIndex, tacCai) - println("dcl results: "+dcl) dcl match { case ImmutableReference | LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference ⇒ state.referenceImmutability = dcl case MutableReference | LazyInitializedNotThreadSafeReference ⇒ - val lazyInitialization = isLazyInitialization(writeIndex, defaultValue, method, code, cfg, pcToIndex) + /*val lazyInitialization = isLazyInitialization(writeIndex, defaultValue, method, code, cfg, pcToIndex) println("result lazyInitialization: "+lazyInitialization) if (lazyInitialization) state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference - else if (dcl == MutableReference) { - /*if (!lazyInitialization) { + else if (dcl == MutableReference) { */ + /*if (!lazyInitialization) { result = Some(true) } else state.referenceImmutability = LazyInitializedNotThreadSafeReference*/ + state.referenceImmutability = dcl + if (dcl == MutableReference) result = Some(true) - } else state.referenceImmutability = LazyInitializedNotThreadSafeReference + //} else state.referenceImmutability = LazyInitializedNotThreadSafeReference } result } @@ -111,7 +110,8 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization method: Method, code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] + pcToIndex: Array[Int], + tacCode: TACode[TACMethodParameter, V] )(implicit state: State): Boolean = { val write = code(writeIndex).asFieldWriteAccessStmt @@ -126,12 +126,10 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val writes = fieldAccessInformation.writeAccesses(state.field) if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) - println("false1") return false; // more than one write in the method } val reads = fieldAccessInformation.readAccesses(state.field) if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - println("false2") return false; // Reads outside the (single) lazy initialization method } @@ -140,31 +138,27 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization // first statement that is executed after the if-Statement if the field's value was not the // default value val (guardIndex, guardedIndex, readIndex) = { - val findGuardResult = findGuard(method, writeIndex, defaultValue, code, cfg) + val findGuardResult = findGuard(method, writeIndex, defaultValue, code, cfg, tacCode) if (findGuardResult.isDefined) (findGuardResult.get._1, findGuardResult.get._2, findGuardResult.get._3) - else { println("false3"); return false; } + else return false; } // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code)) { - println("false4") - return false + if (!checkImmediateReturn(write, writeIndex, readIndex, code, tacCode)) { + return false; }; // The value written must be computed deterministically and the writes guarded correctly if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) { - println("false5") - return false + return false; }; // Field reads (except for the guard) may only be executed if the field's value is not the // default value if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) { - println("false6") return false - }; - + } true } @@ -187,6 +181,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val writeBB = cfg.bb(writeIndex).asBasicBlock val domTree = cfg.dominatorTree val resultCaughtsAndThrows = findCaughtsThrowsAndResults(tacCode, cfg) + def noInterferingExceptions: Boolean = { //resultCaughtsAndThrows._1.size == resultCaughtsAndThrows._2.size && resultCaughtsAndThrows._1.forall(bbCatch ⇒ @@ -198,7 +193,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val (guardIndex, guardedIndex, readIndex, afterGuardRecognizedTheDefaultValueIndex) = { val findGuardResult: (Option[(Int, Int, Int, CFGNode, Int)]) = - findGuard(method, writeIndex, defaultValue, code, cfg) + findGuard(method, writeIndex, defaultValue, code, cfg, tacCode) if (findGuardResult.isDefined) ( findGuardResult.get._1, @@ -206,20 +201,26 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization findGuardResult.get._3, findGuardResult.get._5 ) - else return MutableReference; + else { + println("mut1") + return MutableReference; + } } val guardedBB = cfg.bb(afterGuardRecognizedTheDefaultValueIndex) val reads = fieldAccessInformation.readAccesses(state.field) if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return MutableReference + println("mut2") + return MutableReference; } val writes = fieldAccessInformation.writeAccesses(state.field) if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { - return MutableReference - }; + println("mut3") + return MutableReference; + } if (method.returnType == state.field.fieldType && - !checkThatTheValueOfTheFieldIsReturned(write, writeIndex, readIndex, code)) { + !checkThatTheValueOfTheFieldIsReturned(write, writeIndex, readIndex, code, tacCode)) { + println("mut4") return MutableReference; } //when the method is synchronized the monitor has not to be searched @@ -229,8 +230,14 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization if (noInterferingExceptions) { LazyInitializedThreadSafeReference // result //DCL - } else MutableReference - } else MutableReference + } else { + println("mut5") + MutableReference + } + } else { + println("mut6") + MutableReference + } } else { val monitorResult: ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = findMonitorCaughtsAndThrowsAndResult(writeIndex, defaultValue, code, cfg, tacCode) @@ -243,47 +250,69 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex) //&& //true case dominates Write )) { - if (noInterferingExceptions /*resultCaughtsAndThrows._1.size == resultCaughtsAndThrows._2.size && - resultCaughtsAndThrows._1.forall( - bbCatch ⇒ - resultCaughtsAndThrows._2.exists( - bbThrow ⇒ - ( - (domTree - .strictlyDominates(bbCatch._4.nodeId, bbThrow._3.nodeId) || //domination - (bbCatch._4 == bbThrow._3 && bbCatch._1 < bbThrow._1)) && // or equal and successor - bbThrow._2 == bbCatch._3 - ) - ) - )*/ ) + if (noInterferingExceptions) LazyInitializedThreadSafeReference // result //DCL - else + else { + println("mut7") MutableReference + } } else { /* - println( - s""" - | fieldtype is objecttype: ${state.field.fieldType.isObjectType} - | > -1 : ${write.value.asVar.definedBy.head > -1} - | checkWriteIsDeterministic: ${checkWriteIsDeterministic(code(write.value.asVar.definedBy.head).asAssignment, method, code)} - | - |""".stripMargin - ) */ + println("xxx0") + println("defined by: "+write.value.asVar.definedBy.head) + println("def by inner: " + tacCode.stmts(write.value.asVar.definedBy.filter(n⇒ n>0).head)) + println("write: "+write) + var f: Expr[V] = null + if (write.isPutField) + f = write.asPutField.value + if (write.isPutStatic) { + f = write.asPutStatic.value + + } + println("field: "+f) + + if(write.value.asVar.definedBy.size>1) + tacCode.stmts(write.value.asVar.definedBy.filter(n⇒ n>0).head).asAssignment.expr + */ + /* val propertyStoreResultFieldImmutability = propertyStore(f, FieldImmutability.key) +println("propertystore result field imm: "+propertyStoreResultFieldImmutability) +state.fieldDependees = state.ldDependees.filter(_.e ne propertyStoreResultFieldImmutability.e) +println("propertystore result: "+propertyStoreResultFieldImmutability) */ + /*val fieldIsDeepImmutable = + propertyStoreResultFieldImmutability match { + case FinalP(DeepImmutableField) ⇒ true + case FinalP(_) ⇒ false + case ep ⇒ + state.fieldDependees = state.fieldDependees + ep + true + } */ + /*if(write.value.asVar.definedBy.head>0) { + val f = write.value.asVar.definedBy.head +}*/ if ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) && write.value.asVar.definedBy.size > 0 && - (state.field.fieldType.isObjectType || write.value.asVar.definedBy.head > -1 - && checkWriteIsDeterministic(code(write.value.asVar.definedBy.head).asAssignment, method, code))) { - println("aaaa") + (( //IsDeepImmutable //state.field.isFinal ||write.value.asVar.definedBy.head > -1 + !write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.isEmpty && + checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code) + ))) { //LazyInitializedNotThreadSafeReference - if (noInterferingExceptions) - LazyInitializedNotThreadSafeReference - else + if (noInterferingExceptions) { + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + LazyInitializedNotThreadSafeReference + } else + LazyInitializedNotThreadSafeButDeterministicReference + } else { + println("mut8") MutableReference + } //checkWriteIsGuarded2(writeIndex, guardIndex, guardedIndex, method, code, cfg) //, tacCode.stmts) - } else + } else { + println("mut9") MutableReference + } } } } @@ -296,33 +325,35 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization var throwers: List[(Int, IntTrieSet, CFGNode)] = List.empty var resultNode: Option[CFGNode] = None for (stmt ← tacCode.stmts) { - val curBB = cfg.bb(tacCode.pcToIndex(stmt.pc)) - (stmt.astID: @switch) match { - case CaughtException.ASTID ⇒ - val ce = stmt.asCaughtException - val et = - if (ce.exceptionType.isDefined) { - val intermdT = ce.exceptionType.get - if (intermdT.isObjectType) - intermdT.asObjectType - else + if (!stmt.isNop) { + val curBB = cfg.bb(tacCode.pcToIndex(stmt.pc)) + (stmt.astID: @switch) match { + case CaughtException.ASTID ⇒ + val ce = stmt.asCaughtException + val et = + if (ce.exceptionType.isDefined) { + val intermdT = ce.exceptionType.get + if (intermdT.isObjectType) + intermdT.asObjectType + else + ObjectType.Exception + } else ObjectType.Exception + exceptions = (ce.pc, et, ce.origins, curBB) :: exceptions + + case Throw.ASTID ⇒ + val t = stmt.asThrow + val ds = if (t.exception.isVar) { + val v = t.exception.asVar + v.definedBy } else - ObjectType.Exception - exceptions = (ce.pc, et, ce.origins, curBB) :: exceptions - - case Throw.ASTID ⇒ - val t = stmt.asThrow - val ds = if (t.exception.isVar) { - val v = t.exception.asVar - v.definedBy - } else - IntTrieSet.empty - throwers = (t.pc.toInt, ds, curBB) :: throwers - case ReturnValue.ASTID ⇒ - //case ReturnValue(pc, expr) ⇒ - resultNode = Some(curBB) - case _ ⇒ + IntTrieSet.empty + throwers = (t.pc.toInt, ds, curBB) :: throwers + case ReturnValue.ASTID ⇒ + //case ReturnValue(pc, expr) ⇒ + resultNode = Some(curBB) + case _ ⇒ + } } } (exceptions, throwers, resultNode) @@ -440,102 +471,101 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization * first statement executed if the field does not have its default value and the index of the * field read used for the guard. */ - var savedGuards: mutable.HashMap[(Int, Method), ((Option[(Int, Int, Int, CFGNode, Int)]))] = - new mutable.HashMap[(Int, Method), ((Option[(Int, Int, Int, CFGNode, Int)]))]() + // var savedGuards: mutable.HashMap[(Int, Method), ((Option[(Int, Int, Int, CFGNode, Int)]))] = + // new mutable.HashMap[(Int, Method), ((Option[(Int, Int, Int, CFGNode, Int)]))]() def findGuard( method: Method, fieldWrite: Int, defaultValue: Any, code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] + cfg: CFG[Stmt[V], TACStmts[V]], + tacCode: TACode[TACMethodParameter, V] )(implicit state: State): (Option[(Int, Int, Int, CFGNode, Int)]) = { - if (1 == 2 + 3) //savedGuards.contains(fieldWrite)) - savedGuards.get((fieldWrite, method)).get - else { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int, CFGNode, Int)] = None - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID ⇒ - val ifStmt = cfStmt.asIf - //ifStmt.condition match { - if (ifStmt.condition.equals(EQ) && curBB != startBB && isGuard( - ifStmt, - defaultValue, - code - )) { - //case EQ - //if => - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1, curBB, ifStmt.targetStmt)) - } - } else if (ifStmt.condition.equals(NE) && curBB != startBB && isGuard( - ifStmt, - defaultValue, - code - )) { - //case NE - //if => - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None - } else { - result = Some((endPC, ifStmt.targetStmt, curBB, endPC + 1)) - } + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) + + var result: Option[(Int, Int, CFGNode, Int)] = None + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + + val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID ⇒ + val ifStmt = cfStmt.asIf + //ifStmt.condition match { + if (ifStmt.condition.equals(EQ) && curBB != startBB && isGuard( + ifStmt, + defaultValue, + code, + tacCode + )) { + //case EQ + //if => + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != endPC + 1) + return None; } else { - // Otherwise, we have to ensure that a guard is present for all predecessors - //case _ => - if (startPC == 0) return None; - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors + result = Some((endPC, endPC + 1, curBB, ifStmt.targetStmt)) } - //} - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ + } else if (ifStmt.condition.equals(NE) && curBB != startBB && isGuard( + ifStmt, + defaultValue, + code, + tacCode + )) { + //case NE + //if => + if (result.isDefined) { + if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) + return None + } else { + result = Some((endPC, ifStmt.targetStmt, curBB, endPC + 1)) + } + } else { + // Otherwise, we have to ensure that a guard is present for all predecessors + //case _ => if (startPC == 0) return None; - val predecessors = getPredecessors(curBB, enqueuedBBs) worklist ++= predecessors enqueuedBBs ++= predecessors - } - } - - val finalResult: Option[(Int, Int, Int, CFGNode, Int)] = - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy - val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ - use == result.get._1 || use == result.get._2 } - if (definitions.size == 1 && fieldReadUsedCorrectly) - Some((result.get._1, result.get._2, definitions.head, result.get._3, result.get._4)); // Found proper guard - else None - } else None - savedGuards.put((fieldWrite, method), finalResult) - finalResult + //} + + // Otherwise, we have to ensure that a guard is present for all predecessors + case _ ⇒ + if (startPC == 0) return None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } } + + val finalResult: Option[(Int, Int, Int, CFGNode, Int)] = + if (result.isDefined) { + // 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.get._1).asIf + val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr + val definitions = expr.asVar.definedBy + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ + use == result.get._1 || use == result.get._2 + } + if (definitions.size == 1 && fieldReadUsedCorrectly) + Some((result.get._1, result.get._2, definitions.head, result.get._3, result.get._4)); // Found proper guard + else None + } else None + finalResult + } /** @@ -630,17 +660,17 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization .toSet /*val caughtExceptions = code.filter { stmt => - stmt.astID == CaughtException.ASTID - }.toList flatMap { - exception=> - exception.asCaughtException.origins.flatMap { origin => - if (isImmediateVMException(origin)) { - pcOfImmediateVMException(origin) - } else { - pcOfMethodExternalException(origin) - } - } - } */ +stmt.astID == CaughtException.ASTID +}.toList flatMap { +exception=> +exception.asCaughtException.origins.flatMap { origin => + if (isImmediateVMException(origin)) { + pcOfImmediateVMException(origin) + } else { + pcOfMethodExternalException(origin) + } +} +} */ while (worklist.nonEmpty) { val curBB = worklist.head worklist = worklist.tail @@ -717,40 +747,26 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization else if (code(defSites.head).asAssignment.expr.isConst) true else { val expr = code(index).asAssignment.expr - println("expr is field read: "+expr.isFieldRead) expr.isFieldRead && (expr.asFieldRead.resolveField(p) match { case Some(field) ⇒ - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + state.field == field || isImmutableReference(propertyStore(field, ReferenceImmutability.key)) case _ ⇒ // Unknown field false }) } } - println( - s""" - | defSites==SelfReferenceParameter: ${defSites == SelfReferenceParameter} - | defSites.size: ${defSites.size} - | isConstantDef: ${isConstantDef(defSites.head)} - | - |""".stripMargin - ) val result = defSites == SelfReferenceParameter || defSites.size == 1 && isConstantDef(defSites.head) - - println("result: "+result) result } val value = origin.expr - - println("value: "+value) - val isNonConstDeterministic = value.astID match { case GetStatic.ASTID | GetField.ASTID ⇒ value.asFieldRead.resolveField(p) match { case Some(field) ⇒ - isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + state.field == field || isImmutableReference(propertyStore(field, ReferenceImmutability.key)) case _ ⇒ // Unknown field false } @@ -766,16 +782,35 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization true } case NewArray.ASTID ⇒ - value.asNewArray.counts.forall(isConstant(_)) + //TODO after holiday origin.targetVar.usedBy.iterator.filter(i ⇒ code(i).isArrayStore).map(i ⇒ code(i).asArrayStore). + //TODO foreach(arrayStore ⇒ println("arraystore: "+arrayStore)) + origin.targetVar.usedBy.iterator.filter(i ⇒ code(i).isArrayStore).map(i ⇒ code(i).asArrayStore). + forall(arrayStore ⇒ isConstant(arrayStore.value)) + + case _ if value.isNew ⇒ { + val nonVirtualFunctionCallIndex = + origin.asAssignment.targetVar.usedBy.iterator.filter(i ⇒ code(i).isNonVirtualMethodCall).toList.head + origin.asAssignment.targetVar.usedBy.size == 2 && + code(nonVirtualFunctionCallIndex).asNonVirtualMethodCall.params.forall(isConstant(_)) + } 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, code) } - - value.isConst || isNonConstDeterministic + println( + s""" + | value.isConst : ${value.isConst} + | isNonConstDeterminstic: $isNonConstDeterministic + | + |""".stripMargin + ) + + val result = value.isConst || isNonConstDeterministic + println("check write is deterministic result: "+result) + result } /** @@ -842,16 +877,89 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization * 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])(implicit state: State): Boolean = { - val field = (expr.astID: @switch) match { - case GetField.ASTID ⇒ - val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) None - else expr.asGetField.resolveField(project) - case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project) - case _ ⇒ None - } - field.contains(state.field) + + def isReadOfCurrentField(expr: Expr[V], tacCode: TACode[TACMethodParameter, V])(implicit state: State): Boolean = { + var seen: Set[Expr[V]] = Set.empty + def _isReadOfCurrentField(expr: Expr[V], tacCode: TACode[TACMethodParameter, V])(implicit state: State): Boolean = { + + if (seen.contains(expr)) { + return false + }; + seen += expr + import org.opalj.tac.Compare + + //println("params: "+expr.asVirtualFunctionCall.params.mkString(", ")) + //expr + //val field:Option[Field] = + (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 Compare.ASTID ⇒ { //........................................... + val leftExpr = expr.asCompare.left + val rightExpr = expr.asCompare.right + val leftDefinitionIndex = leftExpr.asVar.definedBy.filter(i ⇒ i != expr.asCompare.pc).head + val rightDefinitionIndex = rightExpr.asVar.definedBy.filter(i ⇒ i != expr.asCompare.pc).head + if (leftDefinitionIndex < 0 || rightDefinitionIndex < 0) + return false; + + val definitionStmtLeft = tacCode.stmts(leftDefinitionIndex) + val definitionStmtRight = tacCode.stmts(rightDefinitionIndex) + /* + println( + s""" + | leftExpr: $leftExpr + + | rightExpr: $rightExpr + + + t: $definitionStmtLeft + + : $definitionStmtRight + + | + |""".stripMargin + ) */ + if (definitionStmtLeft.asAssignment.expr.isGetField || + definitionStmtLeft.asAssignment.expr.isGetStatic || + definitionStmtLeft.asAssignment.expr.isVirtualFunctionCall) { + if (definitionStmtRight.asAssignment.expr.isConst) { //TODO ggf konservativer + _isReadOfCurrentField(definitionStmtLeft.asAssignment.expr, tacCode) + } else + false + } else { + if (definitionStmtLeft.asAssignment.expr.isConst) //TODO siehe oben + _isReadOfCurrentField(definitionStmtRight.asAssignment.expr, tacCode) + else + false + } + + } + case VirtualFunctionCall.ASTID ⇒ { + val virtualFunctionCall = expr.asVirtualFunctionCall + val receiverDefSites = virtualFunctionCall.receiver.asVar.definedBy + //TODO better check of these functions + for { + defSite ← receiverDefSites + } { + if (defSite >= 0) + if (_isReadOfCurrentField(tacCode.stmts(defSite).asAssignment.expr, tacCode)) { //nothing to do + } else { + return false; + } + else { + return false; + } + + } + true + } + case _ ⇒ false + } + } + _isReadOfCurrentField(expr, tacCode) } /** @@ -861,13 +969,15 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization def isGuard( ifStmt: If[V], defaultValue: Any, - code: Array[Stmt[V]] + code: Array[Stmt[V]], + tacCode: TACode[TACMethodParameter, V] )(implicit state: State): Boolean = { - + //println("check if is guard: "+ifStmt+", \n"+defaultValue) /** * Checks if an expression is an IntConst or FloatConst with the corresponding default value. */ def isDefaultConst(expr: Expr[V]): Boolean = { + if (expr.isVar) { val defSites = expr.asVar.definedBy val head = defSites.head @@ -875,7 +985,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization } else { expr.isIntConst && defaultValue == expr.asIntConst.value || expr.isFloatConst && defaultValue == expr.asFloatConst.value || - defaultValue == null //TODO ?? + expr.isDoubleConst && defaultValue == expr.asDoubleConst || expr.isNullExpr && defaultValue == null } } @@ -883,18 +993,46 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization * Checks whether the non-constant expression of the if-Statement is a read of the current * field. */ - def isGuardInternal(expr: V): Boolean = { + def isGuardInternal(expr: V, tacCode: TACode[TACMethodParameter, V]): Boolean = { + expr.definedBy forall { index ⇒ if (index < 0) false // If the value is from a parameter, this can not be the guard - else isReadOfCurrentField(code(index).asAssignment.expr) + else { + if (code(index).asAssignment.expr.isVirtualFunctionCall) { + //in case of Integer etc.... .initValue() + val virtualFunctionCall = code(index).asAssignment.expr.asVirtualFunctionCall + val callTargets = virtualFunctionCall.resolveCallTargets(state.field.classFile.thisType) + callTargets.foreach( + method ⇒ { + val propertyStorePurityResult = propertyStore(declaredMethods(method), Purity.key) + if (virtualFunctionCall.params.exists(!_.isConst) && + isNonDeterministic(propertyStorePurityResult)) + return false + } + ) + if (callTargets.isEmpty || virtualFunctionCall.params.exists(!_.isConst)) + return false; + + val receiverDefSite = virtualFunctionCall.receiver.asVar.definedBy.head + + receiverDefSite >= 0 && isReadOfCurrentField( + code(receiverDefSite).asAssignment.expr, tacCode + ) + } else { + isReadOfCurrentField(code(index).asAssignment.expr, tacCode) + } + + } } } - if (isDefaultConst(ifStmt.leftExpr) && ifStmt.rightExpr.isVar) { - isGuardInternal(ifStmt.rightExpr.asVar) - } else if (isDefaultConst(ifStmt.rightExpr) && ifStmt.leftExpr.isVar) { - isGuardInternal(ifStmt.leftExpr.asVar) - } else false + if ((ifStmt.rightExpr.isVar) && isDefaultConst(ifStmt.leftExpr)) { + isGuardInternal(ifStmt.rightExpr.asVar, tacCode) + } else if (ifStmt.leftExpr.isVar && isDefaultConst(ifStmt.rightExpr)) { + isGuardInternal(ifStmt.leftExpr.asVar, tacCode) + } else { + false + } } def checkWrites( @@ -933,7 +1071,8 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization write: FieldWriteAccessStmt[V], writeIndex: Int, readIndex: Int, - code: Array[Stmt[V]] + code: Array[Stmt[V]], + tacCode: TACode[TACMethodParameter, V] )(implicit state: State): Boolean = { var index = writeIndex + 1 @@ -943,7 +1082,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val stmt = code(index) (stmt.astID: @switch) match { case Assignment.ASTID ⇒ - if (isReadOfCurrentField(stmt.asAssignment.expr)) + if (isReadOfCurrentField(stmt.asAssignment.expr, tacCode)) load = index else return false; // No field read or a field read of a different field @@ -969,7 +1108,8 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization write: FieldWriteAccessStmt[V], writeIndex: Int, readIndex: Int, - code: Array[Stmt[V]] + code: Array[Stmt[V]], + tacCode: TACode[TACMethodParameter, V] )(implicit state: State): Boolean = { var index = writeIndex + 1 @@ -979,7 +1119,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val stmt = code(index) (stmt.astID: @switch) match { case a @ Assignment.ASTID ⇒ - if (isReadOfCurrentField(stmt.asAssignment.expr)) { + if (isReadOfCurrentField(stmt.asAssignment.expr, tacCode)) { load = index } // No field read or a field read of a different field diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index 15d0f9e923..5164264be8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -23,7 +23,6 @@ import org.opalj.br.fpcf.properties.EscapeViaReturn import org.opalj.br.fpcf.properties.FieldPrematurelyRead import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference import org.opalj.br.fpcf.properties.MutableReference import org.opalj.br.fpcf.properties.NoEscape @@ -149,7 +148,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (!field.isFinal) return Result(field, MutableReference) } - for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method, pcs) @@ -204,14 +202,28 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * values. */ def getDefaultValue()(implicit state: State): Option[Any] = { + import org.opalj.br.CharType + import org.opalj.br.DoubleType + import org.opalj.br.LongType + import org.opalj.br.ObjectType state.field.fieldType match { + case ObjectType.Integer + | ObjectType.Float + | ObjectType.Long + | ObjectType.Short + | ObjectType.Byte + | ObjectType.Double ⇒ Some(0) case FloatType ⇒ Some(0.0f) + case DoubleType ⇒ Some(0.0d) + case LongType ⇒ Some(0.0) + case CharType ⇒ Some(0) case IntegerType ⇒ Some(0) case _: ReferenceType ⇒ Some(null) case BooleanType ⇒ Some(false) case ByteType ⇒ Some(0) case ShortType ⇒ Some(0) + case _ ⇒ None } } @@ -266,40 +278,19 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) val nonDeterministicResult = isNonDeterministic(newEP) //if (!r) state.referenceImmutability = LazyInitializedReference - if (state.referenceImmutability != LazyInitializedNotThreadSafeReference && - state.referenceImmutability != LazyInitializedThreadSafeReference) { // both dont need determinism - isNotFinal = nonDeterministicResult - } + //if (state.referenceImmutability != LazyInitializedNotThreadSafeReference && + // state.referenceImmutability != LazyInitializedThreadSafeReference) { // both dont need determinism + isNotFinal = nonDeterministicResult + //} case ReferenceImmutability.key ⇒ val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] state.referenceImmutabilityDependees = state.referenceImmutabilityDependees.filter(_.e ne newEP.e) isNotFinal = !isImmutableReference(newEP) - /*case TypeImmutability_new.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] - state.typeDependees = state.typeDependees.filter(_.e ne newEP.e) - newEP match { - case FinalP(DependentImmutableType) ⇒ - case FinalP(_) ⇒ - case ep ⇒ state.typeDependees += ep - } */ } if (isNotFinal) state.referenceImmutability = MutableReference - - /*println("is not final: " + isNotFinal) - if (!state.field.isFinal && { - state.referenceImmutability match { - case ImmutableReference | - LazyInitializedThreadSafeReference | - LazyInitializedNotThreadSafeButDeterministicReference => //OrNotDeterministicReference ⇒ - false - case _ ⇒ true - } - }) { - Result(state.field, MutableReference) //Result(state.field, NonFinalFieldByAnalysis) - } else */ createResult() } @@ -320,7 +311,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec else 0 } - //println("stmts: "+stmts.mkString(", \n")) for (pc ← pcs.iterator) { val index = taCode.pcToIndex(pc) if (index > (-1 + staticAddition)) { //TODO unnötig @@ -461,12 +451,15 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { + import org.opalj.br.fpcf.properties.FieldImmutability + final override def uses: Set[PropertyBounds] = Set( PropertyBounds.lub(Purity), PropertyBounds.lub(FieldPrematurelyRead), PropertyBounds.finalP(TACAI), PropertyBounds.ub(EscapeProperty), - PropertyBounds.ub(ReferenceImmutability) + PropertyBounds.ub(ReferenceImmutability), + PropertyBounds.ub(FieldImmutability) ) final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) @@ -482,7 +475,7 @@ object EagerL0ReferenceImmutabilityAnalysis final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allProjectClassFiles.flatMap(_.fields) + val fields = p.allFields //p.allProjectClassFiles.flatMap(_.fields) //TODO allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) analysis } From c60745f41d373008421a68ca121ab664f687f006 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 17 Aug 2020 18:58:18 +0200 Subject: [PATCH 236/327] revised array handling, still wip. Not fully reviewed yet --- .../immutability/fields/ArrayClasses.java | 176 +++++++ .../fields/EffectivelyImmutableFields.java | 492 ++++++++++++++++++ .../immutability/fields/MethodCalls.java | 84 +++ .../DoubleCheckedLocking.java | 322 ++++++++++++ .../src/main/scala/org/opalj/tac/Expr.scala | 2 + .../L0FieldImmutabilityAnalysis.scala | 149 ++++-- ...mutabilityAnalysisLazyInitialization.scala | 122 +++-- .../L0ReferenceImmutabilityAnalysis.scala | 2 +- 8 files changed, 1255 insertions(+), 94 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayClasses.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/EffectivelyImmutableFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/MethodCalls.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLocking.java 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..c75009b61f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayClasses.java @@ -0,0 +1,176 @@ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class ArrayClasses { + + @DeepImmutableFieldAnnotation("The elements of the array can not escape") + @ImmutableReferenceAnnotation("Array is eager initialized") + private Object[] zzz = new Object[]{1, 2, 3}; + + @DeepImmutableFieldAnnotation("The elements of the array can not escape") + @ImmutableReferenceAnnotation("Array is initialized in the constructor") + private Object[] a; + + public ArrayClasses() { + a = new Object[]{5, 6, 7, 8}; + } + + @ShallowImmutableFieldAnnotation("The elements of the array are manipulated after initialization and can escape.") + @ImmutableReferenceAnnotation("The array is eager initialized.") + private Object[] b = new Object[]{1, 2, 3, 4, 5}; + + public Object[] getB() { + b[2] = 2; + return b; + } + + @MutableFieldAnnotation("Array has a mutable reference.") + @MutableReferenceAnnotation("The array is initalized always when the InitC function is called") + private Object[] c; + + public void InitC() { + c = new Object[]{1, 2, 3}; + } + + + @ShallowImmutableFieldAnnotation("The elements of the array can escape.") + @ImmutableReferenceAnnotation("The array is eager initialized.") + private Object[] d = new Object[]{1, 2, 3, 4, 5,}; + + public Object[] getD() { + return d; + } + + + @ShallowImmutableFieldAnnotation("The elements of the array can escape.") + @LazyInitializedThreadSafeReferenceAnnotation("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; + } + + + @MutableFieldAnnotation("The reference is seen as mutable.") + @LazyInitializedNotThreadSafeReferenceAnnotation("The array is initialized lazily but not thread safe.") + private Object[] f; + + public void getF() { + if (f == null) { + f = new Object[]{1, 2, 3}; + } + } + + + @ShallowImmutableFieldAnnotation("One element of the array is written after initialization.") + @LazyInitializedThreadSafeReferenceAnnotation("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; + } + + + @DeepImmutableFieldAnnotation("The elements of the array can not escape.") + @ImmutableReferenceAnnotation("The array is initialized eagerly.") + private Object[] h = new Object[]{new Object(), new Object(), new Object()}; + + + @ShallowImmutableFieldAnnotation("The elements of the array can escape.") + @LazyInitializedThreadSafeReferenceAnnotation("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; + } + + + @DeepImmutableFieldAnnotation("The elements of the array can not escape.") + @LazyInitializedThreadSafeReferenceAnnotation("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; + } + + + @DeepImmutableFieldAnnotation("The elements of the array can not escape") + @ImmutableReferenceAnnotation("The array is not initialized.") + private Object[] k; + + + @MutableFieldAnnotation("The reference is seen as mutable.") + @LazyInitializedNotThreadSafeReferenceAnnotation("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; + } + + + @MutableFieldAnnotation("The reference is seen as mutable.") + @LazyInitializedNotThreadSafeReferenceAnnotation("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; + } + + + @ShallowImmutableFieldAnnotation("The elements of the array can escape.") + @LazyInitializedThreadSafeReferenceAnnotation("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; + } + + @ShallowImmutableFieldAnnotation("One element of the array can escape") + @LazyInitializedThreadSafeReferenceAnnotation("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]; + } + + @DeepImmutableFieldAnnotation("The elements of the array can escape, but have a deep immutable reference.") + @LazyInitializedThreadSafeReferenceAnnotation("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/EffectivelyImmutableFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/EffectivelyImmutableFields.java new file mode 100644 index 0000000000..8d72d075a9 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/EffectivelyImmutableFields.java @@ -0,0 +1,492 @@ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; + +import java.util.*; + +public class EffectivelyImmutableFields { + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private int n1 = 5; + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private int n2; + + public synchronized void initN2(){ + if(n2==0) + n2 = 5; + } + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private int n3; + + public synchronized int getN3(){ + if(n3==0) + n3 = 5; + return n3; + } + + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Integer nO = 5; + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Integer nO2; + + public synchronized void initNO2(){ + if(nO2==0) + nO2 = 5; //TODO test again + } + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Integer nO3; + + public synchronized Integer getNO3(){ + if(nO3==0) + nO3 = 5; + return nO3; + } + + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private double d = 5d; + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private double d2; + + public synchronized void initD2(){ + if(d2==0d) + d2 = 5d; + } + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private double d3; + + public synchronized double getD3(){ + if(d3==0d) + d3 = 5; + return d3; + } + + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Double dO = 5d; + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Double dO2; + + public synchronized void initDO2(){ + if(dO2==0) + dO2 = 5d; + } + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Double dO3; + + public synchronized Double getDO3(){ + if(dO3==0) + dO3 = 5d; + return dO3; + } + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private float f = 5; + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private float f2; + + public synchronized void initF2(){ + if(f2==0) + f2 = 5f; + } + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private float f3; + + public synchronized float getf3(){ + if(f3==0) + f3 = 5f; + return f3; + } + + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Float fO = 5f; + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Float fO2; + + public synchronized void initFO2(){ + if(fO2==0) + fO2 = 5f; + } + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private float fO3; + + public synchronized Float getfO3(){ + if(fO3==0) + fO3 = 5f; + return fO3; + } + + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private byte b = 5; + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private byte b2; + + public synchronized void initB2(){ + if(b2==0) + b2 = 5; + } + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private byte b3; + + public synchronized byte b3(){ + if(b3==0) + b3 = 5; + return b3; + } + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Byte b0 = 5; + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Byte bO2; + + public synchronized void initBO2(){ + if(bO2==0) + bO2 = 5; + } + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Byte bO3; + + public synchronized Byte bO3(){ + if(bO3==0) + bO3 = 5; + return bO3; + } + + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private char c = 'a'; + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private char c2; + + public synchronized void initC2(){ + if(c2 == '\u0000') + c2 = 'a'; + } + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private char c3; + + public synchronized char c3(){ + if(c3 == '\u0000') + c3 = 5; + return c3; + } + + + +//------------------------------------------------------------------------------------------------------ + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private long l = 5; + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private long l2; + + public synchronized void initL2(){ + if(l2 == 0l) + l2 = 5l; + } + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private long l3; + + public synchronized long l3(){ + if(l3 == 0l) + l3 = 5; + return l3; + } + + + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Long lO = 5l; + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Long lO2; + + public synchronized void initLO2(){ + if(lO2 == 0l) + lO2 = 5l; + } + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Long lO3; + + public synchronized Long lO3(){ + if(lO3 == 0l) + lO3 = 5l; + return lO3; + } + +//-------------------------------------------------------------------------------------------------------- + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private String s = "abc"; + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private String s2; + + public synchronized void initS2(){ + if(s2 == null) + s2 = "abc"; + } + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private String s3; + + public synchronized String getS3(){ + if(s3 == null) + s3 = "abc"; + return s3; + } + + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Object o = new Object(); + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Object o2; + + public synchronized void initO2(){ + if(o2 == null) + o2 = new Object(); + } + + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Object o3; + + public synchronized Object getO3(){ + if(o3 == null) + o3 = new Object(); + return o3; + } + + + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private List linkedList = new LinkedList(); + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private List linkedList2; + + public synchronized void initLinkedList2(){ + if(linkedList2 == null) + linkedList2 = new LinkedList(); + } + + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private List linkedList3; + + public synchronized void initLinkedList3(){ + if(linkedList3 == null) + linkedList3 = new LinkedList(); + linkedList3.add(new Object()); + } + + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Object linkedList4; + + public synchronized Object getLinkedList4(){ + if(linkedList4 == null) + linkedList4 = new Object(); + return linkedList4; + } + + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private List arrayList = new ArrayList(); + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private List arrayList2; + + public synchronized void initArrayList2(){ + if(arrayList2 == null) + arrayList2 = new ArrayList(); + } + + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private List arrayList3; + + public synchronized void initArrayList3(){ + if(arrayList3 == null) + arrayList3 = new ArrayList(); + arrayList3.add(new Object()); + } + + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private List arrayList4; + + public synchronized List getArrayList4(){ + if(arrayList4 == null) + arrayList4 = new ArrayList(); + return arrayList4; + } + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private Set set = new HashSet(); + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Set set2; + + public synchronized void initSet2(){ + if(set2 == null) + set2 = new HashSet(); + } + + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Set set3; + + public synchronized void initSet3(){ + if(set3 == null) + set3 = new HashSet(); + set3.add(new Object()); + } + + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private Set set4; + + public synchronized Set getSet4(){ + if(set4 == null) + set4 = new HashSet(); + return set4; + } + + + + + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private HashMap hashMap = new HashMap(); + + @DeepImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private HashMap hashMap2; + + public synchronized void initHashMap2(){ + if(hashMap2 == null) + hashMap2 = new HashMap(); + } + + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private HashMap hashMap3; + + public synchronized void initHashMap3(){ + if(hashMap3 == null) + hashMap3 = new HashMap(); + hashMap3.put(new Object(), new Object()); + } + + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private HashMap hashMap4; + + public synchronized HashMap getHashMap4(){ + if(hashMap4 == null) + hashMap4 = new HashMap(); + return hashMap4; + } + +} 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..4d609098b6 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/MethodCalls.java @@ -0,0 +1,84 @@ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class MethodCalls { + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private TestMutable tm1; + + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private TestMutable tm2; + + @MutableFieldAnnotation("") + @LazyInitializedNotThreadSafeReferenceAnnotation("") + private TestMutable tm3; + + @MutableReferenceAnnotation("") + @MutableFieldAnnotation("") + private TestMutable tm4; + + @ShallowImmutableFieldAnnotation("") + private TestMutable tm5; + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private TestMutable tm6 = new TestMutable(); + + public synchronized void getTM1(){ + if(tm1==null){ + tm1= new TestMutable(); + } + tm1.nop(); + } + + public synchronized TestMutable getTM2(){ + if(tm2==null){ + tm2= new TestMutable(); + } + return tm2; + } + + public void getTm3() { + if(tm3==null){ + tm3 = new TestMutable(); + } + } + + public synchronized TestMutable getTm4() { + if(tm4==null){ + tm4 = new TestMutable(); + } + return tm4; + } + public synchronized TestMutable getTm42() { + if(tm4==null){ + tm4 = new TestMutable(); + } + return tm4; + } + public synchronized void getTm5() { + if(tm5==null){ + tm5 = new TestMutable(); + } + tm5.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/reference_immutability_lazy_initialization/DoubleCheckedLocking.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLocking.java new file mode 100644 index 0000000000..ce1f8353cc --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLocking.java @@ -0,0 +1,322 @@ +package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; + +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class DoubleCheckedLocking { +} + +class DoubleCheckedLockingClass1{ + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass1 instance; + public DoubleCheckedLockingClass1 getInstance() { + if(instance==null){ + synchronized(this) { + if(instance==null){ + instance = new DoubleCheckedLockingClass1(); + } + } + } + return instance; + } +} + +class DoubleCheckedLockingClass2 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass2 instance; + private DoubleCheckedLockingClass2 instance2 = new DoubleCheckedLockingClass2(); + public DoubleCheckedLockingClass2 getInstance() { + if(instance==null){ + synchronized(this) { + if(instance==null){ + instance = new DoubleCheckedLockingClass2(); + } + } + } + return instance; + } +} + +class DoubleCheckedLockingClass3 { + + @LazyInitializedNotThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass3 instance; + public DoubleCheckedLockingClass3 getInstance() { + if(instance==null){ + synchronized(DoubleCheckedLockingClass3.class) { + instance = new DoubleCheckedLockingClass3(); + } + } + return instance; + } +} + +class DoubleCheckedLockingClass4 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass4 instance; + public DoubleCheckedLockingClass4 getInstance() { + synchronized(DoubleCheckedLockingClass4.class) { + if(instance==null){ + instance = new DoubleCheckedLockingClass4(); + } + } + return instance; + } +} + +class DoubleCheckedLockingClass5 { + + @LazyInitializedNotThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass5 instance; + public DoubleCheckedLockingClass5 getInstance() { + if(instance==null){ + if(instance==null){ + instance = new DoubleCheckedLockingClass5(); + } + } + return instance; + } +} + +class DoubleCheckedLockingClass6 { + + @LazyInitializedNotThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass6 instance; + public DoubleCheckedLockingClass6 getInstance() { + if(instance==null){ + synchronized(this) { + if(instance==null){ + //instance = new DoubleCheckedLockingClass6(); + } + instance = new DoubleCheckedLockingClass6(); + } + + } + return instance; + } +} + +class DoubleCheckedLockingClass7 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass7 instance; + public DoubleCheckedLockingClass7 getInstance() { + if(instance==null){ + synchronized(this) { + if(instance==null){ + instance = new DoubleCheckedLockingClass7(); + } + + } + instance = new DoubleCheckedLockingClass7(); + } + return instance; + } +} + +class DoubleCheckedLockingClass8 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass8 instance; + public DoubleCheckedLockingClass8 getInstance() { + if(instance==null){ + synchronized(this) { + if(instance==null){ + instance = new DoubleCheckedLockingClass8(); + } + } + } + instance = new DoubleCheckedLockingClass8(); + return instance; + } +} + +class DoubleCheckedLockingClass9 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass9 instance; + + public DoubleCheckedLockingClass9 getInstance() { + if (instance != null) { + synchronized (this) { + if (instance != null) { + instance = new DoubleCheckedLockingClass9(); + } + } + } + return instance; + } +} + +class DoubleCheckedLockingClass10 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass10 instance; + + public DoubleCheckedLockingClass10 getInstance() { + if (instance == null) { + synchronized (this) { + if (instance == null) { + } + } + } + instance = new DoubleCheckedLockingClass10(); + return instance; + } +} + +class DoubleCheckedLockingClass11 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass11 instance; + public DoubleCheckedLockingClass11 getInstance() { + for(int i=0; i<1000; i++){} + if(instance==null){ + for(int i=0; i<1000; i++){} + synchronized(this) { + for(int i=0; i<1000; i++){} + if(instance==null){ + for(int i=0; i<1000; i++){} + instance = new DoubleCheckedLockingClass11(); + } + } + } + return instance; + } +} + +class DoubleCheckedLockingClass12 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass12 instance; + public DoubleCheckedLockingClass12 getInstance() { + if(instance==null){ + } + synchronized(this) { + if(instance==null){ + instance = new DoubleCheckedLockingClass12(); + } + } + return instance; + } +} + +class DoubleCheckedLockingClass13 { + + @LazyInitializedNotThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass13 instance; + public DoubleCheckedLockingClass13 getInstance() { + if(instance==null){ + } + synchronized(this) { + } + if(instance==null){ + instance = new DoubleCheckedLockingClass13(); + } + return instance; + } +} + +class DoubleCheckedLockingClass14 { + + @MutableReferenceAnnotation("") + private DoubleCheckedLockingClass14 instance; + public DoubleCheckedLockingClass14 getInstance() { + if(instance==null){ + } + synchronized(this) { + } + if(instance==null){ + + } + instance = new DoubleCheckedLockingClass14(); + return instance; + } +} + +class DoubleCheckedLockingClass15 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass15 instance; + public DoubleCheckedLockingClass15 getInstance() { + if(instance==null){ + synchronized(this) { + if(instance==null){ + if(instance==null) { + instance = new DoubleCheckedLockingClass15(); + } + } + } + } + return instance; + } +} + +class DoubleCheckedLockingClass16 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass16 instance; + public DoubleCheckedLockingClass16 getInstance() { + if(instance==null){ + if(instance==null){ + synchronized(this) { + if(instance==null) { + instance = new DoubleCheckedLockingClass16(); + } + } + } + } + return instance; + } +} + +class DoubleCheckedLockingClass17 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass17 instance; + public DoubleCheckedLockingClass17 getInstance() { + if(instance==null){ + if(instance==null) { + if (instance == null) { + synchronized (this) { + if (instance == null) { + if (instance == null) { + instance = new DoubleCheckedLockingClass17(); + } + } + } + } + } + } + return instance; + } +} + +class DoubleCheckedLockingClass18 { + + @LazyInitializedThreadSafeReferenceAnnotation("") + private DoubleCheckedLockingClass18 instance; + private boolean lock = true; + public DoubleCheckedLockingClass18 getInstance() { + if(instance==null && lock){ + synchronized(this) { + if(instance==null && lock){ + instance = new DoubleCheckedLockingClass18(); + } + } + } + return instance; + } +} + + + + + + + + + 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..d658edcce5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Expr.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Expr.scala @@ -116,6 +116,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 @@ -544,6 +545,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/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index 81d8ddb1c6..a409f966c3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -369,17 +369,44 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val propStoreResult = propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) val handleEscapePropertyResult = handleEscapeProperty(propStoreResult) if (handleEscapePropertyResult) { - + println("escapes1") state.noEscapePossibilityViaReference = false } for { index ← useSites } { val fieldsUseSiteStmt = taCode.stmts(index) - - if (!fieldsUseSiteStmt.isMonitorEnter && + println("useSiteStmt: "+fieldsUseSiteStmt) + if (fieldsUseSiteStmt.isAssignment) { + val assignment = fieldsUseSiteStmt.asAssignment + if (assignment.expr.isArrayLoad) { + import org.opalj.value.ASArrayValue + val arrayLoad = assignment.expr.asArrayLoad + + //arrayLoad.arrayRef.asVar.value.isArrayValue. + if (arrayLoad.arrayRef.asVar.value.toCanonicalForm.isInstanceOf[ASArrayValue]) { + val innerArrayType = arrayLoad.arrayRef.asVar.value.toCanonicalForm.asInstanceOf[ASArrayValue].theUpperTypeBound.componentType + if (innerArrayType.isObjectType) { + val propertyStoreResultInnerArrayType = propertyStore(innerArrayType, TypeImmutability_new.key) + println("propertyStoreResult: "+propertyStoreResultInnerArrayType) + propertyStoreResultInnerArrayType match { + case FinalP(DeepImmutableType) ⇒ //nothing to to + case FinalP(_) ⇒ state.noEscapePossibilityViaReference = false + case ep ⇒ state.dependencies += ep + } + } else if (innerArrayType.isArrayType) { + state.noEscapePossibilityViaReference = false // to be sound + } else {} // primitive type -> nothing to do + + //println("array ref: "+) + // ASArrayValue(typ, isNull = , isPrecise = ) + //println("array value: "+arrayLoad.arrayRef.asVar.value.asArrayValue) + } else state.noEscapePossibilityViaReference = false + } else state.noEscapePossibilityViaReference = false + } else if (!fieldsUseSiteStmt.isMonitorEnter && !fieldsUseSiteStmt.isMonitorExit && !fieldsUseSiteStmt.isIf) { + println("escapes2") state.noEscapePossibilityViaReference = false } @@ -388,11 +415,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) //is ignored // The value is only read but not assigned to another one } else { - + println("escapes3") state.noEscapePossibilityViaReference = false } } else { - + println("escapes4") state.noEscapePossibilityViaReference = false } } @@ -411,7 +438,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) p.asVar.definedBy.size != 1 || p.asVar.definedBy.head < 0 || !tacCode.stmts(p.asVar.definedBy.head).asAssignment.expr.isConst)) { - + println("escapes5") state.noEscapePossibilityViaReference = false } } @@ -426,7 +453,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) param.asVar.definedBy.foreach( index ⇒ if (index < 0) { - + println("escapes6") state.noEscapePossibilityViaReference = false } else { val paramDefinitionStmt = tacCode.stmts(index) @@ -438,8 +465,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val propertyStoreResult = propertyStore(field.get, FieldImmutability.key) propertyStoreResult match { case FinalP(DeepImmutableField) ⇒ //nothing to do here - case FinalP(_) ⇒ state.noEscapePossibilityViaReference = false - case ep ⇒ state.dependencies += ep + case FinalP(_) ⇒ + println("escapes7"); state.noEscapePossibilityViaReference = false + case ep ⇒ state.dependencies += ep } } else if (assignmentExpression.isNew) { @@ -453,13 +481,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) handleNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) } } else { - + println("escapes8") state.noEscapePossibilityViaReference = false } } //} + } else if (assignmentExpression.isConst) { + //nothing to do } else { - + println("Assignmentexpr: "+assignmentExpression) + println("escapes9") state.noEscapePossibilityViaReference = false } @@ -479,14 +510,14 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } else if (stmt.isArrayStore) { //val arrayStore = stmt.asArrayStore - + println("escapes10") state.noEscapePossibilityViaReference = false //TODO handling that case more precise } //else if // other cases that the purity analysis can not handle else { val propertyStoreResult = propertyStore(definitionSitesOfParam, EscapeProperty.key) if (handleEscapeProperty(propertyStoreResult)) { - + println("escapes11") state.noEscapePossibilityViaReference = false } } @@ -511,52 +542,57 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) putDefinitionSites = putStatic.value.asVar.definedBy value = putStatic.value } else { - + println("escapes12") state.noEscapePossibilityViaReference = false } val index = value.asVar.definedBy.head if (value.asVar.value.isArrayValue == Yes) { - tacCode.stmts(index).asAssignment.targetVar.usedBy.foreach(x ⇒ { - val arrayStmt = tacCode.stmts(x) - if (arrayStmt != putStmt) - if (arrayStmt.isArrayStore) { - val arrayStore = arrayStmt.asArrayStore - val arrayStoreIndex = arrayStore.index - val isArrayIndexConst = - tacCode.stmts(arrayStoreIndex.asVar.definedBy.head).asAssignment.expr.isConst - val assignedValue = arrayStore.value - val valueAssignment = tacCode.stmts(assignedValue.asVar.definedBy.head).asAssignment - val assignedExpr = valueAssignment.expr - if (!isArrayIndexConst) { - state.noEscapePossibilityViaReference = false - } else if (assignedExpr.isStaticFunctionCall) { - handleStaticFunctionCall(assignedExpr.asStaticFunctionCall, tacCode) - } else if (assignedExpr.isNew) { - //val newExpression = assignedExpr.asNew - valueAssignment.targetVar.asVar.usedBy.foreach(index ⇒ { - val tmpStmt = tacCode.stmts(index) - if (tmpStmt.isArrayStore) { - // can be ingored - } else if (tmpStmt.isNonVirtualMethodCall) { - val nonVirtualMethodcall = tmpStmt.asNonVirtualMethodCall - if (!seen.contains(tmpStmt)) { - seen += tmpStmt - handleNonVirtualMethodCall(method, nonVirtualMethodcall, tacCode) - } //nothing to do in the else case. Stmt was still handled + if (index >= 0) { + tacCode.stmts(index).asAssignment.targetVar.usedBy.foreach(x ⇒ { + val arrayStmt = tacCode.stmts(x) + if (arrayStmt != putStmt) + if (arrayStmt.isArrayStore) { + val arrayStore = arrayStmt.asArrayStore + val arrayStoreIndex = arrayStore.index + val isArrayIndexConst = + tacCode.stmts(arrayStoreIndex.asVar.definedBy.head).asAssignment.expr.isConst + val assignedValue = arrayStore.value + if (assignedValue.asVar.definedBy.head >= 0) { + val valueAssignment = tacCode.stmts(assignedValue.asVar.definedBy.head).asAssignment + val assignedExpr = valueAssignment.expr + if (!isArrayIndexConst) { + println("escapes13") + state.noEscapePossibilityViaReference = false + } else if (assignedExpr.isStaticFunctionCall) { + handleStaticFunctionCall(assignedExpr.asStaticFunctionCall, tacCode) + } else if (assignedExpr.isNew) { + //val newExpression = assignedExpr.asNew + valueAssignment.targetVar.asVar.usedBy.foreach(index ⇒ { + val tmpStmt = tacCode.stmts(index) + if (tmpStmt.isArrayStore) { + // can be ingored + } else if (tmpStmt.isNonVirtualMethodCall) { + val nonVirtualMethodcall = tmpStmt.asNonVirtualMethodCall + if (!seen.contains(tmpStmt)) { + seen += tmpStmt + handleNonVirtualMethodCall(method, nonVirtualMethodcall, tacCode) + } //nothing to do in the else case. Stmt was still handled + } else { + println("escapes14") + state.noEscapePossibilityViaReference = false + } + }) } else { - + println("escapes15") state.noEscapePossibilityViaReference = false } - }) + } } else { - + println("escapes16") state.noEscapePossibilityViaReference = false } - } else { - - state.noEscapePossibilityViaReference = false - } - }) + }) + } else state.noEscapePossibilityViaReference = false } else for { i ← putDefinitionSites @@ -574,7 +610,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall handleNonVirtualMethodCall(method, nonVirtualMethodCall, tacCode) } else { - + println("escapes17") state.noEscapePossibilityViaReference = false } //TODO andere Fälle bedenken @@ -589,7 +625,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (!seen.contains(tmpStmt)) handleNonVirtualMethodCall(method, tmpStmt.asNonVirtualMethodCall, tacCode) } else { - + println("escapes18") state.noEscapePossibilityViaReference = false } }) @@ -617,11 +653,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) checkPuts(useSiteStmt, method, tacCode) } } else if (useSiteStmt.isAssignment) { - + println("escapes19") state.noEscapePossibilityViaReference = false //TODO //val assignment = stmt.asAssignment } else { - + println("escapes20") state.noEscapePossibilityViaReference = false } } @@ -630,16 +666,17 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) //TODO escape analyse für Object // else ; aktuelles putfield bedenken } else if (!definitionSiteAssignment.expr.isConst) { - + println("escapes21") state.noEscapePossibilityViaReference = false } } else { - + println("escapes22") state.noEscapePossibilityViaReference = false } } } // start of method check field writes + println("escapes23") state.noEscapePossibilityViaReference = field.isPrivate for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) @@ -656,7 +693,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } else { - + println("escapes24") state.noEscapePossibilityViaReference = false } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index 867045d782..a82fa4a75f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -288,15 +288,32 @@ println("propertystore result: "+propertyStoreResultFieldImmutability) */ } */ /*if(write.value.asVar.definedBy.head>0) { val f = write.value.asVar.definedBy.head -}*/ +}*/ /**/ + /*println( + s""" + | (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) : ${(domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId))} + | (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex): ${(guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)} + | write.value.asVar.definedBy.size > 0: ${write.value.asVar.definedBy.size > 0} + | checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code): ${checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code)} + | write.value.asVar.definedBy: ${write.value.asVar.definedBy.map(code(_)).mkString(", \n")} + |""".stripMargin + ) */ if ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || - (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) && write.value.asVar.definedBy.size > 0 && + (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) && + write.value.asVar.definedBy.size >= 0 && (( //IsDeepImmutable //state.field.isFinal ||write.value.asVar.definedBy.head > -1 !write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.isEmpty && checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code) ))) { //LazyInitializedNotThreadSafeReference + println("-----------------------------------------------------------------------------Aui") + println( + s""" + | noInterferingExceptions: $noInterferingExceptions + | comp type: ${state.field.fieldType.computationalType} + |""".stripMargin + ) if (noInterferingExceptions) { if (state.field.fieldType.computationalType != ComputationalTypeInt && state.field.fieldType.computationalType != ComputationalTypeFloat) { @@ -762,53 +779,84 @@ exception.asCaughtException.origins.flatMap { origin => } val value = origin.expr - val isNonConstDeterministic = value.astID match { - case GetStatic.ASTID | GetField.ASTID ⇒ - value.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - state.field == field || isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ ⇒ // Unknown field + println("value.astID: "+value.astID) + def isNonConstDeterministic(value: Expr[V]): Boolean = { //val isNonConstDeterministic = + println("value: "+value) + value.astID match { + //case ⇒ + case GetStatic.ASTID | GetField.ASTID ⇒ + value.asFieldRead.resolveField(p) match { + case Some(field) ⇒ + state.field == field || isImmutableReference(propertyStore(field, ReferenceImmutability.key)) + case _ ⇒ // Unknown field + false + } + 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.exists(!isConstant(_))) { false + } else { + state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) + true + } + case NewArray.ASTID ⇒ + //TODO after holiday origin.targetVar.usedBy.iterator.filter(i ⇒ code(i).isArrayStore).map(i ⇒ code(i).asArrayStore). + //TODO foreach(arrayStore ⇒ println("arraystore: "+arrayStore)) + /*println("used bys: ") + println("target var: "+origin.targetVar) + println("ub: "+origin.targetVar.usedBy) + origin.targetVar.usedBy.foreach(i ⇒ println(code(i))) + origin.targetVar.usedBy.iterator.filter(i ⇒ code(i).isArrayStore).map(i ⇒ code(i).asArrayStore). + foreach(arrayStore ⇒ println( + s""" + | arraystorevalue: ${arrayStore.value} + | isConst: ${isConstant(arrayStore.value)} + | isNonConstDeterministic: ${isNonConstDeterministic(arrayStore.value): Boolean} + |""".stripMargin + )) + + origin.targetVar.usedBy.iterator.filter(i ⇒ code(i).isArrayStore).map(i ⇒ code(i).asArrayStore). + forall(arrayStore ⇒ isNonConstDeterministic(arrayStore.value)) */ + true // + case _ if value.isVar ⇒ { + val varValue = value.asVar + println("value.asVar: def by") + varValue.definedBy.forall(i ⇒ + + i >= 0 && code(i).isAssignment && isNonConstDeterministic(code(i).asAssignment.expr)) + // println(code(i))) + //varValue.definedBy.forall(i ⇒isNonConstDeterministic(code(i))) + //false } - 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.exists(!isConstant(_))) { - false - } else { - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true + /* case Assignment.ASTID ⇒ { + + }*/ + case _ if value.isNew ⇒ { + println(origin.asAssignment.targetVar.usedBy.map(code(_)).mkString(", \n")) + val nonVirtualFunctionCallIndex = + origin.asAssignment.targetVar.usedBy.iterator.filter(i ⇒ code(i).isNonVirtualMethodCall).toList.head + origin.asAssignment.targetVar.usedBy.size == 2 && + code(nonVirtualFunctionCallIndex).asNonVirtualMethodCall.params.forall(isConstant(_)) } - case NewArray.ASTID ⇒ - //TODO after holiday origin.targetVar.usedBy.iterator.filter(i ⇒ code(i).isArrayStore).map(i ⇒ code(i).asArrayStore). - //TODO foreach(arrayStore ⇒ println("arraystore: "+arrayStore)) - origin.targetVar.usedBy.iterator.filter(i ⇒ code(i).isArrayStore).map(i ⇒ code(i).asArrayStore). - forall(arrayStore ⇒ isConstant(arrayStore.value)) - - case _ if value.isNew ⇒ { - - val nonVirtualFunctionCallIndex = - origin.asAssignment.targetVar.usedBy.iterator.filter(i ⇒ code(i).isNonVirtualMethodCall).toList.head - origin.asAssignment.targetVar.usedBy.size == 2 && - code(nonVirtualFunctionCallIndex).asNonVirtualMethodCall.params.forall(isConstant(_)) + 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, code) } - 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, code) } println( s""" | value.isConst : ${value.isConst} - | isNonConstDeterminstic: $isNonConstDeterministic + | isNonConstDeterminstic: ${isNonConstDeterministic(value)} | |""".stripMargin ) - val result = value.isConst || isNonConstDeterministic + val result = value.isConst || isNonConstDeterministic(value) println("check write is deterministic result: "+result) result } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index 5164264be8..37648f55b5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -475,7 +475,7 @@ object EagerL0ReferenceImmutabilityAnalysis final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allFields //p.allProjectClassFiles.flatMap(_.fields) //TODO allFields + val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) //TODO allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) analysis } From 35b1cd67ada4148d8aa6836b2440609f63a698b7 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 19 Aug 2020 17:54:06 +0200 Subject: [PATCH 237/327] extending with is-properties --- OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala | 2 ++ 1 file changed, 2 insertions(+) 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 e2497b4ea3..d1f6bdec5c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala @@ -91,6 +91,7 @@ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { def isPutStatic: Boolean = false def isPutField: Boolean = false def isNop: Boolean = false + def isReturnValue: Boolean = false } /** @@ -402,6 +403,7 @@ 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) From 39699a3b35ab4f3ba37663002fda0395713ab0fe Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 19 Aug 2020 17:54:56 +0200 Subject: [PATCH 238/327] begin of revise, still wip --- .../L0FieldImmutabilityAnalysis.scala | 416 ++++++++-------- .../LxClassImmutabilityAnalysis_new.scala | 2 +- ...bstractReferenceImmutabilityAnalysis.scala | 8 +- ...mutabilityAnalysisLazyInitialization.scala | 446 ++---------------- .../L0ReferenceImmutabilityAnalysis.scala | 28 +- 5 files changed, 266 insertions(+), 634 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index a409f966c3..a8b11cf702 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -68,6 +68,10 @@ import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.Stmt import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.tac.StaticFunctionCall +import org.opalj.value.ASArrayValue +import org.opalj.Yes +import org.opalj.tac.Expr case class State(f: Field) { var field: Field = f @@ -80,11 +84,11 @@ case class State(f: Field) { } /** - * Enum that describes the different kinds of dependent immutable fields: + * Describes the different kinds of dependent immutable fields: * [[DependentImmutabilityKind.dependent]] Shallow or mutable types could still exist * [[DependentImmutabilityKind.notShallowOrMutable]] There are no shallow or mutable types * [[DependentImmutabilityKind.onlyDeepImmutable]] There are no generic parameters left. - * All are replaced with deep immutable types. + * All have been replaced with deep immutable types. */ object DependentImmutabilityKind extends Enumeration { type DependentImmutabilityKind = Value @@ -120,45 +124,54 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) private[analyses] def determineFieldImmutability( field: Field ): ProperPropertyComputationResult = { + + //stores the formal type parameters of the fields class or outer class var classFormalTypeParameters: Option[Set[String]] = None - def loadFormalTypeparameters(): Unit = { + + /** + * Loads th + */ + def loadFormalTypeParameter(): Unit = { var result: Set[String] = Set.empty - def CheckAttributeWithRegardToFormalTypeParameter: Attribute ⇒ Unit = { + + /** + * + * Extract the formal type parameter if it exists of a class attribute + */ + def collectFormalTypeParameterFromClassAttribute: Attribute ⇒ Unit = { attribute ⇒ attribute match { - case SourceFile(_) ⇒ - case ClassSignature(typeParameters, _, _) ⇒ - for (parameter ← typeParameters) { - parameter match { + case ClassSignature(typeParameters, _, _) ⇒ { + typeParameters.foreach( + _ match { case FormalTypeParameter(identifier, _, _) ⇒ result += identifier case _ ⇒ } - } + ) + } case _ ⇒ } } /** * If the genericity is nested in an inner class - * collecting the generic type parameters from the fields outer class + * collect the generic type parameters from the fields outer class */ if (field.classFile.outerType.isDefined) { val outerClassFile = project.classFile(field.classFile.outerType.get._1) if (outerClassFile.isDefined) { outerClassFile.get.attributes.foreach( - CheckAttributeWithRegardToFormalTypeParameter + collectFormalTypeParameterFromClassAttribute ) } } /** - * Collecting the generic type parameters from the fields class + * Collect the generic type parameters from the fields class */ - field.classFile.attributes.foreach( - CheckAttributeWithRegardToFormalTypeParameter - ) + field.classFile.attributes.foreach(collectFormalTypeParameterFromClassAttribute) - if (result.size > 0) + if (result.nonEmpty) classFormalTypeParameters = Some(result) } @@ -173,16 +186,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) * Checks the immutability of a fields type. Returns the Result and registers the dependencies if necessary. * @param state */ - def handleTypeImmutability(implicit state: State): Unit = { + def handleTypeImmutability()(implicit state: State): Unit = { val objectType = field.fieldType.asFieldType if (objectType == ObjectType.Object) { state.typeIsImmutable = Some(false) //handling generic fields } else if (objectType.isBaseType || objectType == ObjectType.String) { - // we hardcode here the strings deep immutability + // we state here the strings deep immutability // base types are by design deep immutable //state.typeImmutability = Some(true) // true is default } else if (objectType.isArrayType) { - // Because the entries of an array can be reassigned we hardcode it as not being deep immutable + // Because the entries of an array can be reassigned we state it as not being deep immutable state.typeIsImmutable = Some(false) } else { val typeImmutabilityPropertyStoreResult = @@ -190,28 +203,23 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.dependencies = state.dependencies.iterator.filter(_.e ne typeImmutabilityPropertyStoreResult.e).toSet typeImmutabilityPropertyStoreResult match { - case FinalP(DeepImmutableType) ⇒ // type being deep immutable is default + case FinalP(DeepImmutableType) ⇒ // deep immutable type is set as default case FinalP(DependentImmutableType) ⇒ state.typeIsImmutable = Some(false) state.dependentImmutability = Some(DependentImmutabilityKind.dependent) - case FinalP(ShallowImmutableType | MutableType_new) ⇒ { + case FinalP(ShallowImmutableType | MutableType_new) ⇒ state.typeIsImmutable = Some(false) state.dependentImmutability = None - if (state.field.fieldType.isObjectType && - state.field.fieldType.asObjectType != ObjectType.Object) { - state.dependentImmutability = None //state we are less then dependent immutable - } - } case epk ⇒ state.dependencies += epk } } } - def hasGenericType(state: State): Unit = { - var noShallowOrMutableTypeInGenericTypeFound = true - var onlyDeepTypesInGenericTypeFound = true - var genericFields: List[ObjectType] = List() + def hasGenericType()(implicit state: State): Unit = { + var noShallowOrMutableTypesInGenericTypeFound = true + var onlyDeepImmutableTypesInGenericTypeFound = true + var genericParameters: List[ObjectType] = List() var noRelevantAttributesFound = true state.field.asField.attributes.foreach( _ match { @@ -219,9 +227,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case SourceFile(_) ⇒ case TypeVariableSignature(t) ⇒ noRelevantAttributesFound = false - onlyDeepTypesInGenericTypeFound = false + onlyDeepImmutableTypesInGenericTypeFound = false if (!isInClassesGenericTypeParameters(t)) - noShallowOrMutableTypeInGenericTypeFound = false + noShallowOrMutableTypesInGenericTypeFound = false case ClassTypeSignature( packageIdentifier, SimpleClassTypeSignature(simpleName, typeArguments), @@ -232,9 +240,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) .foreach({ _ match { case ProperTypeArgument(variance, TypeVariableSignature(identifier: String)) ⇒ - onlyDeepTypesInGenericTypeFound = false + onlyDeepImmutableTypesInGenericTypeFound = false if (!isInClassesGenericTypeParameters(identifier)) - noShallowOrMutableTypeInGenericTypeFound = false + noShallowOrMutableTypesInGenericTypeFound = false case ProperTypeArgument(varianceIndicator, ClassTypeSignature(outerPackageIdentifier, SimpleClassTypeSignature(innerPackageIdentifier, typeArguments2), _)) ⇒ { @@ -244,32 +252,32 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) prepackageIdentifier + innerPackageIdentifier case _ ⇒ innerPackageIdentifier } - genericFields = ObjectType(objectPath) :: genericFields + genericParameters ::= ObjectType(objectPath) } case _ ⇒ - noShallowOrMutableTypeInGenericTypeFound = false - onlyDeepTypesInGenericTypeFound = false + noShallowOrMutableTypesInGenericTypeFound = false + onlyDeepImmutableTypesInGenericTypeFound = false state.typeIsImmutable = Some(false) } }) case _ ⇒ state.typeIsImmutable = Some(false) - noShallowOrMutableTypeInGenericTypeFound = false - onlyDeepTypesInGenericTypeFound = false + noShallowOrMutableTypesInGenericTypeFound = false + onlyDeepImmutableTypesInGenericTypeFound = false } ) - genericFields.foreach(objectType ⇒ { + genericParameters.foreach(objectType ⇒ { val typeImmutabilityPropertyStoreResult = propertyStore(objectType, TypeImmutability_new.key) state.dependencies = state.dependencies.iterator.filter(_.e ne typeImmutabilityPropertyStoreResult.e).toSet typeImmutabilityPropertyStoreResult match { case FinalP(DeepImmutableType) ⇒ //nothing to do here: default value is deep immutable case FinalP(DependentImmutableType) ⇒ - onlyDeepTypesInGenericTypeFound = false + onlyDeepImmutableTypesInGenericTypeFound = false state.typeIsImmutable = Some(false) case FinalP(ShallowImmutableType | MutableType_new) ⇒ { - noShallowOrMutableTypeInGenericTypeFound = false - onlyDeepTypesInGenericTypeFound = false + noShallowOrMutableTypesInGenericTypeFound = false + onlyDeepImmutableTypesInGenericTypeFound = false state.typeIsImmutable = Some(false) } case ep ⇒ @@ -277,20 +285,24 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } }) //Prevents keeping the case of keeping the default values of these - // flags only because of ne revelant attribute was found - if (noRelevantAttributesFound) { - noShallowOrMutableTypeInGenericTypeFound = false - onlyDeepTypesInGenericTypeFound = false - } - - if (state.dependentImmutability != None) { - if (onlyDeepTypesInGenericTypeFound) - state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) - else if (noShallowOrMutableTypeInGenericTypeFound) - state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) + // flags only because of no relevant attribute has been found + if (!noRelevantAttributesFound) { + /** + * call the above defined function + */ + if (state.dependentImmutability.isDefined) { + if (onlyDeepImmutableTypesInGenericTypeFound) + state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) + else if (noShallowOrMutableTypesInGenericTypeFound) + state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) + } } } + /** + * Returns the taccode to a given method when it still exists. + * Otherwise collect dependencies. + */ def getTACAI( method: Method )(implicit state: State): Option[TACode[TACMethodParameter, V]] = { @@ -312,136 +324,117 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ep: EOptionP[DefinitionSite, EscapeProperty] )(implicit state: State): Boolean = { ep match { - case FinalP(NoEscape) ⇒ // | EscapeInCallee | EscapeViaReturn - false - - case FinalP(EscapeInCallee | EscapeViaReturn) ⇒ true - case FinalP(AtMost(_)) ⇒ - true - - case _: FinalEP[DefinitionSite, EscapeProperty] ⇒ - true // Escape state is worse than via return - - case InterimUBP(NoEscape) ⇒ // | EscapeInCallee | EscapeViaReturn) ⇒ - state.dependencies += ep + case FinalP(NoEscape) ⇒ false + case InterimUBP(NoEscape) ⇒ false - + case FinalP(EscapeInCallee | EscapeViaReturn) ⇒ true + case FinalP(AtMost(_)) ⇒ true + case _: FinalEP[DefinitionSite, EscapeProperty] ⇒ true // Escape state is worse than via return case InterimUBP(AtMost(_)) ⇒ true - case _: InterimEP[DefinitionSite, EscapeProperty] ⇒ true // Escape state is worse than via return - case _ ⇒ state.dependencies += ep false } } - /*def isDeterministic( - eop: EOptionP[DeclaredMethod, Purity] - )(implicit state: State): Boolean = eop match { - case FinalP(p: Purity) ⇒ - println("final p is det: "+p.isDeterministic); p.isDeterministic - case LBP(p: Purity) if p.isDeterministic ⇒ - true - case EUBP(e, p: Purity) if !p.isDeterministic ⇒ - false - case _ ⇒ - state.dependencies += eop - true - } */ - def checkIfReferencedObjectCanEscape(implicit state: State): Unit = { - - def checkFieldReadsForEffImmutability(field: Field)(implicit state: State) = { + /** + * Determine if the referenced object can escape either via field reads or writes. + */ + def determineEscapePossibilityOfReferencedObjectOrValue(implicit state: State): Unit = { + /** + * Determine if the referenced object can escape via field reads. + */ + def determineEscapePossibilityViaFieldReads(implicit state: State): Unit = { for { - (method, pcs) ← fieldAccessInformation.readAccesses(field) + (method, pcs) ← fieldAccessInformation.readAccesses(state.field) taCode ← getTACAI(method) pc ← pcs } { val index = taCode.pcToIndex(pc) - if (index > -1) { + if (index >= 0) { val stmt = taCode.stmts(index) if (stmt.isAssignment) { val assignment = stmt.asAssignment - val useSites = assignment.targetVar.usedBy - val propStoreResult = propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) - val handleEscapePropertyResult = handleEscapeProperty(propStoreResult) - if (handleEscapePropertyResult) { - println("escapes1") + if (handleEscapeProperty( + propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) + )) { state.noEscapePossibilityViaReference = false } for { - index ← useSites + useSite ← assignment.targetVar.usedBy } { - val fieldsUseSiteStmt = taCode.stmts(index) - println("useSiteStmt: "+fieldsUseSiteStmt) + val fieldsUseSiteStmt = taCode.stmts(useSite) if (fieldsUseSiteStmt.isAssignment) { val assignment = fieldsUseSiteStmt.asAssignment if (assignment.expr.isArrayLoad) { - import org.opalj.value.ASArrayValue val arrayLoad = assignment.expr.asArrayLoad - - //arrayLoad.arrayRef.asVar.value.isArrayValue. - if (arrayLoad.arrayRef.asVar.value.toCanonicalForm.isInstanceOf[ASArrayValue]) { - val innerArrayType = arrayLoad.arrayRef.asVar.value.toCanonicalForm.asInstanceOf[ASArrayValue].theUpperTypeBound.componentType - if (innerArrayType.isObjectType) { - val propertyStoreResultInnerArrayType = propertyStore(innerArrayType, TypeImmutability_new.key) - println("propertyStoreResult: "+propertyStoreResultInnerArrayType) - propertyStoreResultInnerArrayType match { - case FinalP(DeepImmutableType) ⇒ //nothing to to - case FinalP(_) ⇒ state.noEscapePossibilityViaReference = false - case ep ⇒ state.dependencies += ep - } - } else if (innerArrayType.isArrayType) { - state.noEscapePossibilityViaReference = false // to be sound - } else {} // primitive type -> nothing to do - - //println("array ref: "+) - // ASArrayValue(typ, isNull = , isPrecise = ) - //println("array value: "+arrayLoad.arrayRef.asVar.value.asArrayValue) - } else state.noEscapePossibilityViaReference = false + arrayLoad.arrayRef.asVar.value.toCanonicalForm match { + case value: ASArrayValue ⇒ + val innerArrayType = value.theUpperTypeBound.componentType + if (innerArrayType.isObjectType) { + val propertyStoreResultInnerArrayType = + propertyStore(innerArrayType, TypeImmutability_new.key) + propertyStoreResultInnerArrayType match { + case FinalP(DeepImmutableType) ⇒ //nothing to to + case FinalP(_) ⇒ + state.noEscapePossibilityViaReference = false + case ep ⇒ + state.dependencies += ep + } + } else if (innerArrayType.isArrayType) { + state.noEscapePossibilityViaReference = false // to be sound + } else if (innerArrayType.isBaseType) { + // nothing to do, because it can not be mutated + } else state.noEscapePossibilityViaReference + case _ ⇒ state.noEscapePossibilityViaReference = false + } } else state.noEscapePossibilityViaReference = false - } else if (!fieldsUseSiteStmt.isMonitorEnter && - !fieldsUseSiteStmt.isMonitorExit && - !fieldsUseSiteStmt.isIf) { - println("escapes2") - + } else if (fieldsUseSiteStmt.isMonitorEnter || + fieldsUseSiteStmt.isMonitorExit || + fieldsUseSiteStmt.isIf) { + //nothing to do + } else { state.noEscapePossibilityViaReference = false } } } else if (stmt.isExprStmt) { - //is ignored - // The value is only read but not assigned to another one + //this case can be ignored because the value is only read but not assigned to another one } else { - println("escapes3") state.noEscapePossibilityViaReference = false } } else { - println("escapes4") state.noEscapePossibilityViaReference = false } } } - var seen: Set[Stmt[V]] = Set.empty + /** + * Determine if the referenced object can escape via field writes + */ def checkFieldWritesForEffImmutability(field: Field)(implicit state: State): Unit = { - import org.opalj.tac.StaticFunctionCall + //Needed because of cyclic calls of the functions to prevent infinite cycles + var seen: Set[Stmt[V]] = Set.empty + /** + * Checks if the parameters of a static function call are no parameters from an outer + * function and are constants + */ def handleStaticFunctionCall( staticFunctionCall: StaticFunctionCall[V], tacCode: TACode[TACMethodParameter, V] ): Unit = { - if (staticFunctionCall.params. - exists(p ⇒ - p.asVar.definedBy.size != 1 || - p.asVar.definedBy.head < 0 || - !tacCode.stmts(p.asVar.definedBy.head).asAssignment.expr.isConst)) { - println("escapes5") + if (staticFunctionCall.params.exists(p ⇒ + p.asVar.definedBy.size != 1 || + p.asVar.definedBy.head < 0 || + !tacCode.stmts(p.asVar.definedBy.head).asAssignment.expr.isConst)) { state.noEscapePossibilityViaReference = false } } + //TODO hier weitermachen def handleNonVirtualMethodCall( method: Method, @@ -453,7 +446,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) param.asVar.definedBy.foreach( index ⇒ if (index < 0) { - println("escapes6") state.noEscapePossibilityViaReference = false } else { val paramDefinitionStmt = tacCode.stmts(index) @@ -466,13 +458,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) propertyStoreResult match { case FinalP(DeepImmutableField) ⇒ //nothing to do here case FinalP(_) ⇒ - println("escapes7"); state.noEscapePossibilityViaReference = false + state.noEscapePossibilityViaReference = false case ep ⇒ state.dependencies += ep } - } else if (assignmentExpression.isNew) { - //if (paramDefinitionStmt.isAssignment && //TODO..... - // paramDefinitionStmt.asAssignment.targetVar.isVar) { for (i ← paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy) { val stmt = tacCode.stmts(i) if (stmt.isNonVirtualMethodCall) { @@ -481,19 +470,14 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) handleNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) } } else { - println("escapes8") state.noEscapePossibilityViaReference = false } } - //} } else if (assignmentExpression.isConst) { //nothing to do } else { - println("Assignmentexpr: "+assignmentExpression) - println("escapes9") state.noEscapePossibilityViaReference = false } - } else { val definitionSitesOfParam = definitionSites(method, index) val stmt = tacCode.stmts(tacCode.pcToIndex(definitionSitesOfParam.pc)) @@ -505,19 +489,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } else if (stmt.isPutField || stmt.isPutStatic) { if (!seen.contains(stmt)) { seen += stmt - checkPuts(stmt, method, tacCode) + handlePut(stmt, method, tacCode) } - } else if (stmt.isArrayStore) { //val arrayStore = stmt.asArrayStore - println("escapes10") state.noEscapePossibilityViaReference = false //TODO handling that case more precise } //else if // other cases that the purity analysis can not handle else { val propertyStoreResult = propertyStore(definitionSitesOfParam, EscapeProperty.key) if (handleEscapeProperty(propertyStoreResult)) { - println("escapes11") state.noEscapePossibilityViaReference = false } } @@ -528,27 +509,25 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) } - def checkPuts(putStmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { - import org.opalj.Yes - import org.opalj.tac.Expr + def handlePut(putStmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { + var putDefinitionSites: IntTrieSet = IntTrieSet.empty - var value: Expr[V] = null + var putValue: Expr[V] = null if (putStmt.isPutField) { val putField = putStmt.asPutField putDefinitionSites = putField.value.asVar.definedBy - value = putField.value + putValue = putField.value } else if (putStmt.isPutStatic) { val putStatic = putStmt.asPutStatic putDefinitionSites = putStatic.value.asVar.definedBy - value = putStatic.value + putValue = putStatic.value } else { - println("escapes12") state.noEscapePossibilityViaReference = false } - val index = value.asVar.definedBy.head - if (value.asVar.value.isArrayValue == Yes) { - if (index >= 0) { - tacCode.stmts(index).asAssignment.targetVar.usedBy.foreach(x ⇒ { + val putValueDefinedByIndex = putValue.asVar.definedBy.head + if (putValue.asVar.value.isArrayValue == Yes) { + if (putValueDefinedByIndex >= 0) { + tacCode.stmts(putValueDefinedByIndex).asAssignment.targetVar.usedBy.foreach(x ⇒ { val arrayStmt = tacCode.stmts(x) if (arrayStmt != putStmt) if (arrayStmt.isArrayStore) { @@ -560,13 +539,29 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (assignedValue.asVar.definedBy.head >= 0) { val valueAssignment = tacCode.stmts(assignedValue.asVar.definedBy.head).asAssignment val assignedExpr = valueAssignment.expr + val useSites = valueAssignment.targetVar.usedBy.map(tacCode.stmts(_)) + for (useSite ← useSites) { + if (useSite.isNonVirtualMethodCall) { + val nonVirtualMethodCall = useSite.asNonVirtualMethodCall + nonVirtualMethodCall.params.foreach(param ⇒ { + if (!param.isConst && + !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst) + state.noEscapePossibilityViaReference = false + }) + } else if (useSite == arrayStore) { + //nothing to do + } else if (useSite.isReturnValue) { + //assigned array-element escapes + state.noEscapePossibilityViaReference = false + } else { + state.noEscapePossibilityViaReference = false + } + } if (!isArrayIndexConst) { - println("escapes13") state.noEscapePossibilityViaReference = false } else if (assignedExpr.isStaticFunctionCall) { handleStaticFunctionCall(assignedExpr.asStaticFunctionCall, tacCode) } else if (assignedExpr.isNew) { - //val newExpression = assignedExpr.asNew valueAssignment.targetVar.asVar.usedBy.foreach(index ⇒ { val tmpStmt = tacCode.stmts(index) if (tmpStmt.isArrayStore) { @@ -576,19 +571,18 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (!seen.contains(tmpStmt)) { seen += tmpStmt handleNonVirtualMethodCall(method, nonVirtualMethodcall, tacCode) - } //nothing to do in the else case. Stmt was still handled + } //nothing to do in the else case. Stmt has still been handled } else { - println("escapes14") state.noEscapePossibilityViaReference = false } }) } else { - println("escapes15") state.noEscapePossibilityViaReference = false } + } else { + state.noEscapePossibilityViaReference = false } } else { - println("escapes16") state.noEscapePossibilityViaReference = false } }) @@ -610,7 +604,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall handleNonVirtualMethodCall(method, nonVirtualMethodCall, tacCode) } else { - println("escapes17") + state.noEscapePossibilityViaReference = false } //TODO andere Fälle bedenken @@ -625,21 +619,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (!seen.contains(tmpStmt)) handleNonVirtualMethodCall(method, tmpStmt.asNonVirtualMethodCall, tacCode) } else { - println("escapes18") + state.noEscapePossibilityViaReference = false } }) - /*val propertyStoreResult = { - propertyStore( - definitionSites(method, definitionSiteAssignment.pc), - EscapeProperty.key - ) - - } - println("propertystoreresult: " + propertyStoreResult) - if (handleEscapeProperty(propertyStoreResult)) { - state.noEscapePossibilityViaReference = false - }*/ } else { val useSites = definitionSiteAssignment.targetVar.usedBy @@ -650,14 +633,12 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } else if (useSiteStmt.isPutStatic || useSiteStmt.isPutField) { if (!seen.contains(useSiteStmt)) { seen += useSiteStmt - checkPuts(useSiteStmt, method, tacCode) + handlePut(useSiteStmt, method, tacCode) } } else if (useSiteStmt.isAssignment) { - println("escapes19") state.noEscapePossibilityViaReference = false //TODO //val assignment = stmt.asAssignment } else { - println("escapes20") state.noEscapePossibilityViaReference = false } } @@ -666,17 +647,17 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) //TODO escape analyse für Object // else ; aktuelles putfield bedenken } else if (!definitionSiteAssignment.expr.isConst) { - println("escapes21") state.noEscapePossibilityViaReference = false } } else { - println("escapes22") state.noEscapePossibilityViaReference = false } } } - // start of method check field writes - println("escapes23") + + /** + * Begin of method check field writes + */ state.noEscapePossibilityViaReference = field.isPrivate for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) @@ -689,39 +670,24 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val stmt = taCode.stmts(index) if (!seen.contains(stmt)) { seen += stmt - checkPuts(stmt, method, taCode) + handlePut(stmt, method, taCode) } - } else { - println("escapes24") + state.noEscapePossibilityViaReference = false } } } - if (state.noEscapePossibilityViaReference) { - - checkFieldReadsForEffImmutability(state.field) - } - if (state.noEscapePossibilityViaReference) { - + /** + * Begin of method determineEscapePossibilityOfReferencedObjectOrValue + */ + if (state.noEscapePossibilityViaReference) + determineEscapePossibilityViaFieldReads(state) + if (state.noEscapePossibilityViaReference) checkFieldWritesForEffImmutability(state.field) - } - } def createResult(state: State): ProperPropertyComputationResult = { - /* - println( - s""" - | create result field: ${state.field} - | ref imm: ${state.referenceIsImmutable} - | type imm: ${state.typeIsImmutable} - | dep imm: ${state.dependentImmutability} - | not escape: ${state.noEscapePossibilityViaReference} - | - |""".stripMargin - ) */ - state.referenceIsImmutable match { case Some(false) | None ⇒ Result(field, MutableField) @@ -751,13 +717,12 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { state.dependencies = state.dependencies.iterator.filter(_.e ne eps.e).toSet eps match { //(: @unchecked) - case x: InterimEP[_, _] ⇒ { state.dependencies += eps InterimResult(field, MutableField, DeepImmutableField, state.dependencies, c(state)) } case FinalP(DeepImmutableType) ⇒ { //state.typeImmutability = Some(true) - if (!state.dependentImmutability.isDefined) + if (state.dependentImmutability.isEmpty) state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) } case FinalEP(t, MutableType_new | ShallowImmutableType) ⇒ @@ -767,7 +732,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case FinalEP(f, DependentImmutableType) ⇒ { state.typeIsImmutable = Some(false) - if (!state.dependentImmutability.isDefined) + if (state.dependentImmutability.isEmpty) state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) } case FinalP( @@ -784,23 +749,17 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.referenceIsImmutable = Some(true) } case FinalP(DeepImmutableField) ⇒ // nothing to do - case eps if eps.isFinal && eps.asEPS.pk == FieldImmutability.key ⇒ //else case + case FinalP(DependentImmutableField | ShallowImmutableField | MutableField) ⇒ state.noEscapePossibilityViaReference = false - case eps if eps.isFinal && eps.asEPS.pk == TACAI.key ⇒ checkIfReferencedObjectCanEscape(state) + case eps if eps.isFinal && eps.asEPS.pk == TACAI.key ⇒ + determineEscapePossibilityOfReferencedObjectOrValue(state) case eps if eps.isFinal && eps.asEPS.pk == EscapeProperty.key ⇒ - checkIfReferencedObjectCanEscape(state) + determineEscapePossibilityOfReferencedObjectOrValue(state) if (handleEscapeProperty(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) { state.noEscapePossibilityViaReference = false } - - //case _: FinalEP[_, TACAI] ⇒ - // checkIfReferencedObjectCanEscape(state) - //TODO hier weiter machen - - //case EPS(,Te)⇒ - - case ep ⇒ - state.dependencies = state.dependencies + ep + case eps ⇒ + state.dependencies += eps } if (state.dependencies.isEmpty) createResult(state) else @@ -813,6 +772,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) } + /** + * Begin of determine field immutability function + */ implicit val state: State = State(field) val referenceImmutabilityPropertyStoreResult = propertyStore(state.field, ReferenceImmutability.key) referenceImmutabilityPropertyStoreResult match { @@ -825,12 +787,12 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.dependencies += ep } } - loadFormalTypeparameters() - handleTypeImmutability(state) - hasGenericType(state) + loadFormalTypeParameter() + handleTypeImmutability() + hasGenericType() //it is possible that the type immutability not already determined at this point - if (!state.referenceIsImmutable.isDefined || state.referenceIsImmutable.get) { - checkIfReferencedObjectCanEscape + if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) { + determineEscapePossibilityOfReferencedObjectOrValue } if (state.dependencies.isEmpty) { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala index 542db499ca..72b5692c09 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala @@ -110,7 +110,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case Some(scf) ⇒ nextComputations ::= ( ( - determineClassImmutability_new(t, cfMutability, cfMutabilityIsFinal, false) _, + determineClassImmutability_new(t, cfMutability, cfMutabilityIsFinal, lazyComputation = false) _, scf ) ) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala index 32dfee8694..c40627e66e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala @@ -40,7 +40,7 @@ import org.opalj.tac.fpcf.properties.TACAI import org.opalj.value.ValueInformation /** - * Encompasses the base function used in the [[L0ReferenceImmutabilityAnalysis]] + * Encompasses the base functions used in the [[L0ReferenceImmutabilityAnalysis]] */ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { @@ -112,17 +112,15 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { method: Method, code: Array[Stmt[V]] )(implicit state: State): Boolean = { - val propertyStoreResult = propertyStore(declaredMethods(method), Purity.key) val resultIsNonDeterministic = !isNonDeterministic( propertyStoreResult ) - val result = (method.descriptor.parametersCount == 0 && resultIsNonDeterministic) - result + method.descriptor.parametersCount == 0 && resultIsNonDeterministic } /** - * Checkes if the field the value assigned to a (potentially) lazily initialized field is final, + * Checks if 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( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index a82fa4a75f..26e7793d0a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -15,7 +15,6 @@ import org.opalj.br.cfg.BasicBlock import org.opalj.br.cfg.CFG import org.opalj.br.cfg.CFGNode import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.properties.ImmutableReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference @@ -47,6 +46,8 @@ import org.opalj.tac.VirtualFunctionCall import org.opalj.tac.Throw import org.opalj.br.ObjectType import scala.annotation.switch +import org.opalj.tac.NewArray +import org.opalj.tac.Compare trait AbstractReferenceImmutabilityAnalysisLazyInitialization extends AbstractReferenceImmutabilityAnalysis @@ -67,99 +68,12 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization pcToIndex: Array[Int], tacCai: TACode[TACMethodParameter, V] )(implicit state: State): Option[Boolean] = { - var result: Option[Boolean] = None - val dcl: ReferenceImmutability = + val doubleCheckedLockingResult: ReferenceImmutability = isThreadSafeLazyInitialization(writeIndex, defaultValue, method, code, cfg, pcToIndex, tacCai) - dcl match { - case ImmutableReference | - LazyInitializedThreadSafeReference | - LazyInitializedNotThreadSafeButDeterministicReference ⇒ - state.referenceImmutability = dcl - case MutableReference | LazyInitializedNotThreadSafeReference ⇒ - /*val lazyInitialization = isLazyInitialization(writeIndex, defaultValue, method, code, cfg, pcToIndex) - println("result lazyInitialization: "+lazyInitialization) - if (lazyInitialization) - state.referenceImmutability = LazyInitializedNotThreadSafeButDeterministicReference - else if (dcl == MutableReference) { */ - /*if (!lazyInitialization) { - result = Some(true) - } else - state.referenceImmutability = LazyInitializedNotThreadSafeReference*/ - state.referenceImmutability = dcl - if (dcl == MutableReference) - result = Some(true) - //} else state.referenceImmutability = LazyInitializedNotThreadSafeReference - } - result - } - - /** - * Checks whether a field write may be a lazy initialization. - * - * We only consider simple cases of lazy initialization where the single field write is guarded - * so it is executed only if the field still has its default value and where the written value - * is guaranteed to be the same even if the write is executed multiple times (may happen because - * of the initializing method being executed more than once on concurrent threads). - * - * Also, all field reads must be used only for a guard or their uses have to be guarded - * themselves. - */ - def isLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int], - tacCode: TACode[TACMethodParameter, V] - )(implicit state: State): Boolean = { - val write = code(writeIndex).asFieldWriteAccessStmt - - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - // Only handle lazy initialization of ints and floats as they are guaranteed to be - // written atomically - return false; - } - - //TODO reasoning if there is another way to do this - val writes = fieldAccessInformation.writeAccesses(state.field) - - if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { //filter(mAndPCs ⇒ (mAndPCs._1 eq method)) - return false; // more than one write in the method - } - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return false; // Reads outside the (single) lazy initialization method - } - - // There must be a guarding if-Statement - // The guardIndex is the index of the if-Statement, the guardedIndex is the index of the - // first statement that is executed after the if-Statement if the field's value was not the - // default value - val (guardIndex, guardedIndex, readIndex) = { - val findGuardResult = findGuard(method, writeIndex, defaultValue, code, cfg, tacCode) - if (findGuardResult.isDefined) - (findGuardResult.get._1, findGuardResult.get._2, findGuardResult.get._3) - else return false; - } - - // Detect only simple patterns where the lazily initialized value is returned immediately - if (!checkImmediateReturn(write, writeIndex, readIndex, code, tacCode)) { - return false; - }; - - // The value written must be computed deterministically and the writes guarded correctly - if (!checkWrites(write, writeIndex, guardIndex, guardedIndex, method, code, cfg)) { - return false; - }; - - // Field reads (except for the guard) may only be executed if the field's value is not the - // default value - if (!checkReads(reads, readIndex, guardedIndex, writeIndex, cfg, pcToIndex)) { - return false - } - true + state.referenceImmutability = doubleCheckedLockingResult + if (doubleCheckedLockingResult == MutableReference) + Some(true) + else None } /** @@ -176,14 +90,12 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization pcToIndex: Array[Int], tacCode: TACode[TACMethodParameter, V] )(implicit state: State): ReferenceImmutability = { - //var result: ReferenceImmutability = LazyInitializedThreadSafeReference val write = code(writeIndex).asFieldWriteAccessStmt val writeBB = cfg.bb(writeIndex).asBasicBlock val domTree = cfg.dominatorTree val resultCaughtsAndThrows = findCaughtsThrowsAndResults(tacCode, cfg) def noInterferingExceptions: Boolean = { - //resultCaughtsAndThrows._1.size == resultCaughtsAndThrows._2.size && resultCaughtsAndThrows._1.forall(bbCatch ⇒ resultCaughtsAndThrows._2.exists(bbThrow ⇒ ((domTree.strictlyDominates(bbCatch._4.nodeId, bbThrow._3.nodeId) || //domination @@ -202,7 +114,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization findGuardResult.get._5 ) else { - println("mut1") return MutableReference; } } @@ -210,38 +121,31 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization val reads = fieldAccessInformation.readAccesses(state.field) if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - println("mut2") return MutableReference; } val writes = fieldAccessInformation.writeAccesses(state.field) if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { - println("mut3") return MutableReference; } if (method.returnType == state.field.fieldType && !checkThatTheValueOfTheFieldIsReturned(write, writeIndex, readIndex, code, tacCode)) { - println("mut4") return MutableReference; } //when the method is synchronized the monitor has not to be searched if (method.isSynchronized) { if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { - if (noInterferingExceptions) { LazyInitializedThreadSafeReference // result //DCL } else { - println("mut5") MutableReference } } else { - println("mut6") MutableReference } } else { val monitorResult: ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = findMonitorCaughtsAndThrowsAndResult(writeIndex, defaultValue, code, cfg, tacCode) - if ((monitorResult._1._1.isDefined && monitorResult._1._2.isDefined && monitorResult._2._1.isDefined) && ( @@ -253,67 +157,16 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization if (noInterferingExceptions) LazyInitializedThreadSafeReference // result //DCL else { - println("mut7") MutableReference } } else { - /* - println("xxx0") - println("defined by: "+write.value.asVar.definedBy.head) - println("def by inner: " + tacCode.stmts(write.value.asVar.definedBy.filter(n⇒ n>0).head)) - println("write: "+write) - var f: Expr[V] = null - if (write.isPutField) - f = write.asPutField.value - if (write.isPutStatic) { - f = write.asPutStatic.value - - } - println("field: "+f) - - if(write.value.asVar.definedBy.size>1) - tacCode.stmts(write.value.asVar.definedBy.filter(n⇒ n>0).head).asAssignment.expr - */ - /* val propertyStoreResultFieldImmutability = propertyStore(f, FieldImmutability.key) -println("propertystore result field imm: "+propertyStoreResultFieldImmutability) -state.fieldDependees = state.ldDependees.filter(_.e ne propertyStoreResultFieldImmutability.e) -println("propertystore result: "+propertyStoreResultFieldImmutability) */ - /*val fieldIsDeepImmutable = - propertyStoreResultFieldImmutability match { - case FinalP(DeepImmutableField) ⇒ true - case FinalP(_) ⇒ false - case ep ⇒ - state.fieldDependees = state.fieldDependees + ep - true - } */ - /*if(write.value.asVar.definedBy.head>0) { - val f = write.value.asVar.definedBy.head -}*/ /**/ - /*println( - s""" - | (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) : ${(domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId))} - | (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex): ${(guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)} - | write.value.asVar.definedBy.size > 0: ${write.value.asVar.definedBy.size > 0} - | checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code): ${checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code)} - | write.value.asVar.definedBy: ${write.value.asVar.definedBy.map(code(_)).mkString(", \n")} - |""".stripMargin - ) */ - if ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) && write.value.asVar.definedBy.size >= 0 && (( //IsDeepImmutable //state.field.isFinal ||write.value.asVar.definedBy.head > -1 - !write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.isEmpty && + write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.nonEmpty && checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code) ))) { - //LazyInitializedNotThreadSafeReference - println("-----------------------------------------------------------------------------Aui") - println( - s""" - | noInterferingExceptions: $noInterferingExceptions - | comp type: ${state.field.fieldType.computationalType} - |""".stripMargin - ) if (noInterferingExceptions) { if (state.field.fieldType.computationalType != ComputationalTypeInt && state.field.fieldType.computationalType != ComputationalTypeFloat) { @@ -321,13 +174,9 @@ println("propertystore result: "+propertyStoreResultFieldImmutability) */ } else LazyInitializedNotThreadSafeButDeterministicReference } else { - println("mut8") MutableReference } - //checkWriteIsGuarded2(writeIndex, guardIndex, guardedIndex, method, code, cfg) //, tacCode.stmts) - } else { - println("mut9") MutableReference } } @@ -339,41 +188,39 @@ println("propertystore result: "+propertyStoreResultFieldImmutability) */ cfg: CFG[Stmt[V], TACStmts[V]] ): (List[(Int, ObjectType, IntTrieSet, CFGNode)], List[(Int, IntTrieSet, CFGNode)], Option[CFGNode]) = { var exceptions: List[(Int, ObjectType, IntTrieSet, CFGNode)] = List.empty - var throwers: List[(Int, IntTrieSet, CFGNode)] = List.empty - var resultNode: Option[CFGNode] = None + var throwStatements: List[(Int, IntTrieSet, CFGNode)] = List.empty + var returnNode: Option[CFGNode] = None for (stmt ← tacCode.stmts) { if (!stmt.isNop) { - val curBB = cfg.bb(tacCode.pcToIndex(stmt.pc)) + val currentBB = cfg.bb(tacCode.pcToIndex(stmt.pc)) (stmt.astID: @switch) match { case CaughtException.ASTID ⇒ - val ce = stmt.asCaughtException - val et = - if (ce.exceptionType.isDefined) { - val intermdT = ce.exceptionType.get - if (intermdT.isObjectType) - intermdT.asObjectType + val caughtException = stmt.asCaughtException + val exceptionType = + if (caughtException.exceptionType.isDefined) { + val intermediateExceptionType = caughtException.exceptionType.get + if (intermediateExceptionType.isObjectType) + intermediateExceptionType.asObjectType else ObjectType.Exception } else ObjectType.Exception - exceptions = (ce.pc, et, ce.origins, curBB) :: exceptions - + exceptions = (caughtException.pc, exceptionType, caughtException.origins, currentBB) :: exceptions case Throw.ASTID ⇒ - val t = stmt.asThrow - val ds = if (t.exception.isVar) { - val v = t.exception.asVar - v.definedBy - } else - IntTrieSet.empty - throwers = (t.pc.toInt, ds, curBB) :: throwers + val throwStatement = stmt.asThrow + val throwStatementDefinedBys = + if (throwStatement.exception.isVar) { + throwStatement.exception.asVar.definedBy + } else + IntTrieSet.empty + throwStatements = (throwStatement.pc, throwStatementDefinedBys, currentBB) :: throwStatements case ReturnValue.ASTID ⇒ - //case ReturnValue(pc, expr) ⇒ - resultNode = Some(curBB) + returnNode = Some(currentBB) case _ ⇒ } } } - (exceptions, throwers, resultNode) + (exceptions, throwStatements, returnNode) } def findMonitorCaughtsAndThrowsAndResult( @@ -389,7 +236,7 @@ println("propertystore result: "+propertyStoreResultFieldImmutability) */ var dclExitBBs: List[CFGNode] = List.empty val startBB = cfg.bb(fieldWrite) var MonitorExitqueuedBBs: Set[CFGNode] = startBB.successors - var worklistMonitorExit = getSuccessors(startBB, Set.empty).toList + var worklistMonitorExit = getSuccessors(startBB, Set.empty) def checkMonitor(pc: PC, v: V, curBB: CFGNode)( implicit @@ -410,7 +257,6 @@ println("propertystore result: "+propertyStoreResultFieldImmutability) */ ) ⇒ classType == state.field.classFile.thisType - //&& name == state.field.name case _ ⇒ false } } else // (i <= -1) @@ -423,7 +269,7 @@ println("propertystore result: "+propertyStoreResultFieldImmutability) */ var worklistMonitorEnter = getPredecessors(startBB, Set.empty) //find monitorenter - while (!worklistMonitorEnter.isEmpty) { + while (worklistMonitorEnter.nonEmpty) { val curBB = worklistMonitorEnter.head worklistMonitorEnter = worklistMonitorEnter.tail @@ -450,7 +296,7 @@ println("propertystore result: "+propertyStoreResultFieldImmutability) */ } //find monitorexit - while (!worklistMonitorExit.isEmpty) { + while (worklistMonitorExit.nonEmpty) { val curBB = worklistMonitorExit.head worklistMonitorExit = worklistMonitorExit.tail val endPC = curBB.endPC @@ -471,12 +317,12 @@ println("propertystore result: "+propertyStoreResultFieldImmutability) */ } val bbsEnter = { - if (dclEnterBBs.size >= 1) + if (dclEnterBBs.nonEmpty) Some(dclEnterBBs.head) else None } val bbsExit = { - if (dclExitBBs.size >= 1) + if (dclExitBBs.nonEmpty) Some(dclExitBBs.head) else None } @@ -573,16 +419,17 @@ println("propertystore result: "+propertyStoreResultFieldImmutability) */ val ifStmt = code(result.get._1).asIf val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr val definitions = expr.asVar.definedBy - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + if (definitions.head < 0) + return None; + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy //TODO ... val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ use == result.get._1 || use == result.get._2 } - if (definitions.size == 1 && fieldReadUsedCorrectly) + if (definitions.size == 1 && definitions.head >= 0 && fieldReadUsedCorrectly) Some((result.get._1, result.get._2, definitions.head, result.get._3, result.get._4)); // Found proper guard else None } else None finalResult - } /** @@ -607,7 +454,6 @@ println("propertystore result: "+propertyStoreResultFieldImmutability) */ val caughtExceptions = code.iterator .filter { stmt ⇒ stmt.astID == CaughtException.ASTID - } .flatMap { exception ⇒ exception.asCaughtException.origins.iterator.map { origin: Int ⇒ @@ -650,74 +496,6 @@ println("propertystore result: "+propertyStoreResultFieldImmutability) */ true } - def checkWriteIsGuarded2( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): ReferenceImmutability = { - val startBB = cfg.bb(writeIndex).asBasicBlock - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - val abnormalReturnNode = cfg.abnormalReturnNode - - val caughtExceptions = code.iterator - .filter { stmt ⇒ stmt.astID == CaughtException.ASTID } - .flatMap { exception ⇒ - exception.asCaughtException.origins.iterator.map { origin: Int ⇒ - if (isImmediateVMException(origin)) { - pcOfImmediateVMException(origin) - } else { - pcOfMethodExternalException(origin) - } - } - } - .toSet - - /*val caughtExceptions = code.filter { stmt => -stmt.astID == CaughtException.ASTID -}.toList flatMap { -exception=> -exception.asCaughtException.origins.flatMap { origin => - if (isImmediateVMException(origin)) { - pcOfImmediateVMException(origin) - } else { - pcOfMethodExternalException(origin) - } -} -} */ - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - val startPC = curBB.startPC - val endPC = curBB.endPC - if (startPC == 0 || startPC == guardedIndex) - return MutableReference; // Reached method start or wrong branch of guarding if-Statement - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) { - if (!lazyInitializerIsDeterministic(method, code)) { - return MutableReference - } - }; - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.toSet.contains(endPC)) { - if (!lazyInitializerIsDeterministic(method, code)) { - return MutableReference - } - }; - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = - getPredecessors(curBB, enqueuedBBs).filterNot(_.endPC == guardIndex) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - LazyInitializedNotThreadSafeReference - } - /** * Gets all predecessor BasicBlocks of a CFGNode. */ @@ -755,7 +533,7 @@ exception.asCaughtException.origins.flatMap { origin => method: Method, code: Array[Stmt[V]] )(implicit state: State): Boolean = { - import org.opalj.tac.NewArray + def isConstant(uvar: Expr[V]): Boolean = { val defSites = uvar.asVar.definedBy @@ -779,9 +557,9 @@ exception.asCaughtException.origins.flatMap { origin => } val value = origin.expr - println("value.astID: "+value.astID) + def isNonConstDeterministic(value: Expr[V]): Boolean = { //val isNonConstDeterministic = - println("value: "+value) + value.astID match { //case ⇒ case GetStatic.ASTID | GetField.ASTID ⇒ @@ -803,43 +581,17 @@ exception.asCaughtException.origins.flatMap { origin => true } case NewArray.ASTID ⇒ - //TODO after holiday origin.targetVar.usedBy.iterator.filter(i ⇒ code(i).isArrayStore).map(i ⇒ code(i).asArrayStore). - //TODO foreach(arrayStore ⇒ println("arraystore: "+arrayStore)) - /*println("used bys: ") - println("target var: "+origin.targetVar) - println("ub: "+origin.targetVar.usedBy) - origin.targetVar.usedBy.foreach(i ⇒ println(code(i))) - origin.targetVar.usedBy.iterator.filter(i ⇒ code(i).isArrayStore).map(i ⇒ code(i).asArrayStore). - foreach(arrayStore ⇒ println( - s""" - | arraystorevalue: ${arrayStore.value} - | isConst: ${isConstant(arrayStore.value)} - | isNonConstDeterministic: ${isNonConstDeterministic(arrayStore.value): Boolean} - |""".stripMargin - )) - - origin.targetVar.usedBy.iterator.filter(i ⇒ code(i).isArrayStore).map(i ⇒ code(i).asArrayStore). - forall(arrayStore ⇒ isNonConstDeterministic(arrayStore.value)) */ - true // + true //TODO look at it case _ if value.isVar ⇒ { val varValue = value.asVar - println("value.asVar: def by") varValue.definedBy.forall(i ⇒ - i >= 0 && code(i).isAssignment && isNonConstDeterministic(code(i).asAssignment.expr)) - // println(code(i))) - //varValue.definedBy.forall(i ⇒isNonConstDeterministic(code(i))) - //false } - /* case Assignment.ASTID ⇒ { - - }*/ case _ if value.isNew ⇒ { - println(origin.asAssignment.targetVar.usedBy.map(code(_)).mkString(", \n")) val nonVirtualFunctionCallIndex = origin.asAssignment.targetVar.usedBy.iterator.filter(i ⇒ code(i).isNonVirtualMethodCall).toList.head origin.asAssignment.targetVar.usedBy.size == 2 && - code(nonVirtualFunctionCallIndex).asNonVirtualMethodCall.params.forall(isConstant(_)) + code(nonVirtualFunctionCallIndex).asNonVirtualMethodCall.params.forall(isConstant) } case _ ⇒ // The value neither is a constant nor originates from a call, but if the @@ -848,16 +600,8 @@ exception.asCaughtException.origins.flatMap { origin => lazyInitializerIsDeterministic(method, code) } } - println( - s""" - | value.isConst : ${value.isConst} - | isNonConstDeterminstic: ${isNonConstDeterministic(value)} - | - |""".stripMargin - ) - val result = value.isConst || isNonConstDeterministic(value) - println("check write is deterministic result: "+result) + result } @@ -927,25 +671,21 @@ exception.asCaughtException.origins.flatMap { origin => */ def isReadOfCurrentField(expr: Expr[V], tacCode: TACode[TACMethodParameter, V])(implicit state: State): Boolean = { - var seen: Set[Expr[V]] = Set.empty + var seenExpressions: Set[Expr[V]] = Set.empty def _isReadOfCurrentField(expr: Expr[V], tacCode: TACode[TACMethodParameter, V])(implicit state: State): Boolean = { - if (seen.contains(expr)) { + if (seenExpressions.contains(expr)) { return false }; - seen += expr - import org.opalj.tac.Compare + seenExpressions += expr - //println("params: "+expr.asVirtualFunctionCall.params.mkString(", ")) - //expr - //val field:Option[Field] = (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 Compare.ASTID ⇒ { //........................................... + case Compare.ASTID ⇒ { val leftExpr = expr.asCompare.left val rightExpr = expr.asCompare.right val leftDefinitionIndex = leftExpr.asVar.definedBy.filter(i ⇒ i != expr.asCompare.pc).head @@ -955,21 +695,7 @@ exception.asCaughtException.origins.flatMap { origin => val definitionStmtLeft = tacCode.stmts(leftDefinitionIndex) val definitionStmtRight = tacCode.stmts(rightDefinitionIndex) - /* - println( - s""" - | leftExpr: $leftExpr - - | rightExpr: $rightExpr - - t: $definitionStmtLeft - - : $definitionStmtRight - - | - |""".stripMargin - ) */ if (definitionStmtLeft.asAssignment.expr.isGetField || definitionStmtLeft.asAssignment.expr.isGetStatic || definitionStmtLeft.asAssignment.expr.isVirtualFunctionCall) { @@ -992,15 +718,14 @@ exception.asCaughtException.origins.flatMap { origin => for { defSite ← receiverDefSites } { - if (defSite >= 0) + if (defSite >= 0) { if (_isReadOfCurrentField(tacCode.stmts(defSite).asAssignment.expr, tacCode)) { //nothing to do } else { return false; } - else { + } else { return false; } - } true } @@ -1020,10 +745,12 @@ exception.asCaughtException.origins.flatMap { origin => code: Array[Stmt[V]], tacCode: TACode[TACMethodParameter, V] )(implicit state: State): Boolean = { - //println("check if is guard: "+ifStmt+", \n"+defaultValue) + import scala.annotation.tailrec + /** * Checks if an expression is an IntConst or FloatConst with the corresponding default value. */ + @tailrec def isDefaultConst(expr: Expr[V]): Boolean = { if (expr.isVar) { @@ -1069,7 +796,6 @@ exception.asCaughtException.origins.flatMap { origin => } else { isReadOfCurrentField(code(index).asAssignment.expr, tacCode) } - } } } @@ -1081,75 +807,7 @@ exception.asCaughtException.origins.flatMap { origin => } else { false } - } - - def checkWrites( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val definitions = write.value.asVar.definedBy - val isDeterministic = - if (definitions.size == 1) { - // The value written must be computed deterministically - checkWriteIsDeterministic(code(definitions.head).asAssignment, method, code) - } else { - // More than one definition site for the value might lead to differences between - // invocations, but not if this method has no parameters and is deterministic - // (in this case, the definition reaching the write will always be the same) - method.descriptor.parametersCount == 0 && - !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) - } - val checkWriteIsGuardedResult = - checkWriteIsGuarded(writeIndex, guardIndex, guardedIndex, method, code, cfg) - // The field write must be guarded correctly - isDeterministic && checkWriteIsGuardedResult - - } - - /** - * Checks if the field write for a lazy initialization is immediately followed by a return of - * the written value (possibly loaded by another field read). - */ - def checkImmediateReturn( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]], - tacCode: TACode[TACMethodParameter, V] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 - - var load = -1 - - while (index < code.length) { - val stmt = code(index) - (stmt.astID: @switch) match { - case Assignment.ASTID ⇒ - if (isReadOfCurrentField(stmt.asAssignment.expr, tacCode)) - load = index - else - return false; // No field read or a field read of a different field - case ReturnValue.ASTID ⇒ - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) - return true; // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) - return true; // return of field value loaded by field read - else - return false; // return of different value - case _ ⇒ return false; // neither a field read nor a return - } - index += 1 - } - false + // true //TODO check } def checkThatTheValueOfTheFieldIsReturned( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index 37648f55b5..4390aa06e4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -84,13 +84,18 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec with AbstractReferenceImmutabilityAnalysis with FPCFAnalysis { - def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = + def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = { entity match { - case field: Field ⇒ determineReferenceImmutability(field) + case field: Field ⇒ { + //if (!field.isPublic || !field.isProtected) + // return Result(field, MutableReference) + determineReferenceImmutability(field) + } case _ ⇒ val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" throw new IllegalArgumentException(m) } + } /** * Analyzes the immutability fields references. @@ -105,10 +110,14 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (field.isFinal) return Result(field, ImmutableReference); + if (field.isPublic) return Result(field, MutableReference); implicit val state: State = State(field) + + // if (field.isPublic && field.isPackagePrivate) { + state.referenceImmutability = ImmutableReference val thisType = field.classFile.thisType @@ -148,11 +157,13 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (!field.isFinal) return Result(field, MutableReference) } + for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method, pcs) } { - if (methodUpdatesField(method, taCode, pcs)) { + /*val tmp = method*/ + if ( /*tmp == method ||*/ methodUpdatesField(method, taCode, pcs)) { return Result(field, MutableReference); } } @@ -160,6 +171,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) handleCalls(calleesEOP) } + + // } + createResult() } @@ -256,7 +270,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec eps.pk match { case EscapeProperty.key ⇒ val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] - state.escapeDependees = state.escapeDependees.filter(_.e ne newEP.e) + state.escapeDependees = state.escapeDependees.iterator.filter(_.e ne newEP.e).toSet isNotFinal = handleEscapeProperty(newEP) case TACAI.key ⇒ val newEP = eps.asInstanceOf[EOptionP[Method, TACAI]] @@ -275,7 +289,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec isNotFinal = isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) case Purity.key ⇒ val newEP = eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]] - state.purityDependees = state.purityDependees.filter(_.e ne newEP.e) + state.purityDependees = state.purityDependees.iterator.filter(_.e ne newEP.e).toSet val nonDeterministicResult = isNonDeterministic(newEP) //if (!r) state.referenceImmutability = LazyInitializedReference //if (state.referenceImmutability != LazyInitializedNotThreadSafeReference && @@ -286,7 +300,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec case ReferenceImmutability.key ⇒ val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] state.referenceImmutabilityDependees = - state.referenceImmutabilityDependees.filter(_.e ne newEP.e) + state.referenceImmutabilityDependees.iterator.filter(_.e ne newEP.e).toSet isNotFinal = !isImmutableReference(newEP) } if (isNotFinal) @@ -475,7 +489,7 @@ object EagerL0ReferenceImmutabilityAnalysis final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) //TODO allFields + val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) //TODO p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) analysis } From bc99756b52da1bd48b2c7223ff4bba1d9d861cb1 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 19 Aug 2020 17:56:28 +0200 Subject: [PATCH 239/327] revised and added tests --- .../Generic_class1.java | 4 +- .../fields/ArrayWithOneEscapingObject.java | 46 +++++++++++++++++++ .../ConstructorWithEscapingParameters.java | 31 +++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayWithOneEscapingObject.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ConstructorWithEscapingParameters.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java index 95724c65a9..060e5e62b2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java @@ -276,6 +276,6 @@ public TestTest(Generic_class1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ } -class SimpleMutableClass { public int n = 0;} -final class FinalEmptyClass {} +//class SimpleMutableClass { public int n = 0;} +//final class FinalEmptyClass {} 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..ca224ade2a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayWithOneEscapingObject.java @@ -0,0 +1,46 @@ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class ArrayWithOneEscapingObject { + @MutableFieldAnnotation("Reference of the field is mutable") + @MutableReferenceAnnotation("Field is public") + public Object o = new Object(); + + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("Reference is only initialized once") + private Object[] array1 = new Object[]{o, new Object(), new Object()}; //TODO + + + @ShallowImmutableFieldAnnotation("Field is initialized with an Shallow immutable field") + @ImmutableReferenceAnnotation("Field is only initialized once.") + private Object[] array2; + + public ArrayWithOneEscapingObject() { + array2 = new Object[]{o}; + } + + @ShallowImmutableFieldAnnotation("Field is initialized with a shallow immutable field.") + @LazyInitializedThreadSafeReferenceAnnotation("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}; + } + + @ShallowImmutableFieldAnnotation("An array element escapes") + @LazyInitializedThreadSafeReferenceAnnotation("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/ConstructorWithEscapingParameters.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ConstructorWithEscapingParameters.java new file mode 100644 index 0000000000..257f69f8ed --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ConstructorWithEscapingParameters.java @@ -0,0 +1,31 @@ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; + +public class ConstructorWithEscapingParameters { + + @ShallowImmutableFieldAnnotation("The field is init") + @ImmutableReferenceAnnotation("The field is only assigned in the constructor.") + private TrivialClass tc1; + + @DeepImmutableFieldAnnotation("The construtor pararameter of the assigned object not escape") + @ImmutableReferenceAnnotation("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; + } +} From e69dd229d5487df3e22f80ea481cba4a36680171 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 20 Aug 2020 10:11:05 +0200 Subject: [PATCH 240/327] renaming of classes for compatibility purposes --- .../Generic_class1.java | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java index 060e5e62b2..e77ee42648 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java @@ -46,7 +46,7 @@ public Generic_class1(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ @ShallowImmutableTypeAnnotation("") @ShallowImmutableClassAnnotation("") -final class Generic_class1_extString { +final class Generic_class1_extString { @ShallowImmutableFieldAnnotation("") private T1 t1; @@ -91,14 +91,14 @@ final class Generic_class2 { @ImmutableReferenceAnnotation("") @DependentImmutableFieldAnnotation(value = "", genericString = "") - private Generic_class1 gc; + private Generic_class1 gc; - public Generic_class2(T1 t1, T2 t2, T3 t3, FinalEmptyClass fec1, FinalEmptyClass fec2){ + public Generic_class2(T1 t1, T2 t2, T3 t3, FinalClassWithoutFields fec1, FinalClassWithoutFields fec2){ this.t1 = t1; this.t2 = t2; this.t3 = t3; - gc = new Generic_class1(fec1, fec2, t1,t2,t3); + gc = new Generic_class1(fec1, fec2, t1,t2,t3); } } @@ -113,11 +113,11 @@ final class Generic_class3 { @ImmutableReferenceAnnotation("") @DependentImmutableFieldAnnotation(value = "", genericString = "") - private Generic_class2 gc; + private Generic_class2 gc; - public Generic_class3(T1 t1, FinalEmptyClass fec1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4){ + public Generic_class3(T1 t1, FinalClassWithoutFields fec1, FinalClassWithoutFields fec2, FinalClassWithoutFields fec3, FinalClassWithoutFields fec4){ this.t1 = t1; - gc = new Generic_class2(t1, fec1, fec2, fec3, fec4); + gc = new Generic_class2(t1, fec1, fec2, fec3, fec4); } } @@ -127,10 +127,10 @@ final class Generic_class4_deep { @ImmutableReferenceAnnotation("") @DeepImmutableFieldAnnotation("") - private Generic_class3 gc; + private Generic_class3 gc; - public Generic_class4_deep(FinalEmptyClass fec1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4, FinalEmptyClass fec5){ - gc = new Generic_class3(fec1, fec2, fec3, fec4, fec5); + public Generic_class4_deep(FinalClassWithoutFields fec1, FinalClassWithoutFields fec2, FinalClassWithoutFields fec3, FinalClassWithoutFields fec4, FinalClassWithoutFields fec5){ + gc = new Generic_class3(fec1, fec2, fec3, fec4, fec5); } } @@ -140,10 +140,10 @@ final class Generic_class4_shallow { @ImmutableReferenceAnnotation("") @ShallowImmutableFieldAnnotation("") - private Generic_class3 gc; + private Generic_class3 gc; - public Generic_class4_shallow(SimpleMutableClass tmc1, FinalEmptyClass fec2, FinalEmptyClass fec3, FinalEmptyClass fec4, FinalEmptyClass fec5){ - gc = new Generic_class3(tmc1, fec2, fec3, fec4, fec5); + public Generic_class4_shallow(ClassWithOneMutableField tmc1, FinalClassWithoutFields fec2, FinalClassWithoutFields fec3, FinalClassWithoutFields fec4, FinalClassWithoutFields fec5){ + gc = new Generic_class3(tmc1, fec2, fec3, fec4, fec5); } } @@ -152,9 +152,9 @@ public Generic_class4_shallow(SimpleMutableClass tmc1, FinalEmptyClass fec2, Fin final class DeepGeneric { @DeepImmutableFieldAnnotation("") @ImmutableReferenceAnnotation("") - private Generic_class1 gc1; + private Generic_class1 gc1; - public DeepGeneric(Generic_class1 gc1){ + public DeepGeneric(Generic_class1 gc1){ this.gc1 = gc1; } @@ -177,17 +177,17 @@ class One { private D d; @MutableFieldAnnotation("") @MutableReferenceAnnotation("") - public SimpleMutableClass tmc; + public ClassWithOneMutableField tmc; @ShallowImmutableFieldAnnotation("") @ImmutableReferenceAnnotation("") - private Generic_class1 gc1; - public One(A a, B b, C c, D d, SimpleMutableClass tmc){ + private Generic_class1 gc1; + public One(A a, B b, C c, D d, ClassWithOneMutableField tmc){ this.a = a; this.b = b; this.c = c; this.d = d; this.tmc = tmc; - this.gc1 = new Generic_class1(this.a,this.b,this.c,this.d, this.tmc); + this.gc1 = new Generic_class1(this.a,this.b,this.c,this.d, this.tmc); } } @@ -209,16 +209,16 @@ class OneVirgin { @MutableFieldAnnotation("") @MutableReferenceAnnotation("") - public SimpleMutableClass tmc; + public ClassWithOneMutableField tmc; - Generic_class1 gc1; - public OneVirgin(A a, B b, C c, D d, SimpleMutableClass e){ + Generic_class1 gc1; + public OneVirgin(A a, B b, C c, D d, ClassWithOneMutableField e){ this.a = a; this.b = b; this.c = c; this.d = d; this.tmc = tmc; - this.gc1 = new Generic_class1(this.a, this.b, this.c, this.d, this.tmc); + this.gc1 = new Generic_class1(this.a, this.b, this.c, this.d, this.tmc); } @@ -230,10 +230,10 @@ class Two { @ShallowImmutableFieldAnnotation("") @ImmutableReferenceAnnotation("") - private Generic_class1, B, B, B, SimpleMutableClass> gc1; + private Generic_class1, B, B, B, ClassWithOneMutableField> gc1; - public Two(A a, B b, SimpleMutableClass tmc, Generic_class1 gc1) { - this.gc1 = new Generic_class1, B, B, B, SimpleMutableClass>(gc1,b,b,b,tmc); + public Two(A a, B b, ClassWithOneMutableField tmc, Generic_class1 gc1) { + this.gc1 = new Generic_class1, B, B, B, ClassWithOneMutableField>(gc1,b,b,b,tmc); } } @@ -249,7 +249,7 @@ public TwoVirgin(A a, B b, C c, Generic_class1 gc1) @DependentImmutableTypeAnnotation("") @DependentImmutableClassAnnotation("") -final class TestTest { +final class TestTest { @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") private Generic_class1 t1; @@ -276,6 +276,6 @@ public TestTest(Generic_class1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ } -//class SimpleMutableClass { public int n = 0;} -//final class FinalEmptyClass {} +class ClassWithOneMutableField { public int n = 0;} +final class FinalClassWithoutFields {} From 37e4fc53a4c676a005c5669ff765a171b7069e8c Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 26 Aug 2020 19:01:23 +0200 Subject: [PATCH 241/327] revised, still wip --- .../L0FieldImmutabilityAnalysis.scala | 172 ++++++++++-------- .../LxClassImmutabilityAnalysis_new.scala | 3 +- .../LxTypeImmutabilityAnalysis_new.scala | 21 ++- ...mutabilityAnalysisLazyInitialization.scala | 155 ++-------------- .../L0ReferenceImmutabilityAnalysis.scala | 53 ++---- 5 files changed, 138 insertions(+), 266 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index a8b11cf702..290ae76273 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -69,16 +69,16 @@ import org.opalj.tac.Stmt import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.tac.StaticFunctionCall -import org.opalj.value.ASArrayValue import org.opalj.Yes import org.opalj.tac.Expr +import org.opalj.value.ASArrayValue case class State(f: Field) { var field: Field = f var typeIsImmutable: Option[Boolean] = Some(true) var referenceIsImmutable: Option[Boolean] = None var noEscapePossibilityViaReference: Boolean = true - var dependentImmutability: Option[DependentImmutabilityKind] = Some(DependentImmutabilityKind.dependent) + var dependentImmutability: Option[DependentImmutabilityKind] = Some(DependentImmutabilityKind.onlyDeepImmutable) var genericTypeSetNotDeepImmutable = false var dependencies: Set[EOptionP[Entity, Property]] = Set.empty } @@ -129,7 +129,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var classFormalTypeParameters: Option[Set[String]] = None /** - * Loads th + * Loads the formal typeparameters from the classes and outer class signature */ def loadFormalTypeParameter(): Unit = { var result: Set[String] = Set.empty @@ -198,16 +198,12 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) // Because the entries of an array can be reassigned we state it as not being deep immutable state.typeIsImmutable = Some(false) } else { - val typeImmutabilityPropertyStoreResult = - propertyStore(objectType, TypeImmutability_new.key) - state.dependencies = - state.dependencies.iterator.filter(_.e ne typeImmutabilityPropertyStoreResult.e).toSet - typeImmutabilityPropertyStoreResult match { + propertyStore(objectType, TypeImmutability_new.key) match { case FinalP(DeepImmutableType) ⇒ // deep immutable type is set as default case FinalP(DependentImmutableType) ⇒ state.typeIsImmutable = Some(false) - state.dependentImmutability = - Some(DependentImmutabilityKind.dependent) + /*state.dependentImmutability = + Some(DependentImmutabilityKind.dependent)*/ case FinalP(ShallowImmutableType | MutableType_new) ⇒ state.typeIsImmutable = Some(false) state.dependentImmutability = None @@ -230,11 +226,14 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) onlyDeepImmutableTypesInGenericTypeFound = false if (!isInClassesGenericTypeParameters(t)) noShallowOrMutableTypesInGenericTypeFound = false + + //state.typeIsImmutable = Some(false) case ClassTypeSignature( packageIdentifier, SimpleClassTypeSignature(simpleName, typeArguments), _ ) ⇒ + //state.typeIsImmutable = Some(false) noRelevantAttributesFound = false typeArguments .foreach({ @@ -257,20 +256,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case _ ⇒ noShallowOrMutableTypesInGenericTypeFound = false onlyDeepImmutableTypesInGenericTypeFound = false - state.typeIsImmutable = Some(false) } }) case _ ⇒ - state.typeIsImmutable = Some(false) + //state.typeIsImmutable = Some(false) noShallowOrMutableTypesInGenericTypeFound = false onlyDeepImmutableTypesInGenericTypeFound = false } ) genericParameters.foreach(objectType ⇒ { - val typeImmutabilityPropertyStoreResult = propertyStore(objectType, TypeImmutability_new.key) - state.dependencies = - state.dependencies.iterator.filter(_.e ne typeImmutabilityPropertyStoreResult.e).toSet - typeImmutabilityPropertyStoreResult match { + propertyStore(objectType, TypeImmutability_new.key) match { case FinalP(DeepImmutableType) ⇒ //nothing to do here: default value is deep immutable case FinalP(DependentImmutableType) ⇒ onlyDeepImmutableTypesInGenericTypeFound = false @@ -284,23 +279,25 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.dependencies += ep } }) - //Prevents keeping the case of keeping the default values of these + //Prevents the case of keeping the default values of these // flags only because of no relevant attribute has been found if (!noRelevantAttributesFound) { /** - * call the above defined function + * The above defined functions are called */ if (state.dependentImmutability.isDefined) { - if (onlyDeepImmutableTypesInGenericTypeFound) - state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) - else if (noShallowOrMutableTypesInGenericTypeFound) + if (onlyDeepImmutableTypesInGenericTypeFound) { + //nothing to do... + //state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) + } else if (noShallowOrMutableTypesInGenericTypeFound) state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) + else state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } - } + } else state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } /** - * Returns the taccode to a given method when it still exists. + * Returns the TAC to a given method when it still exists. * Otherwise collect dependencies. */ def getTACAI( @@ -354,17 +351,20 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) taCode ← getTACAI(method) pc ← pcs } { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = taCode.stmts(index) + val readIndex = taCode.pcToIndex(pc) + // This if-statement is necessary, because there are -1 elements in the array + //TODO verify if this is a bug + + //This if-statement is necessary because readAccesses can also be simple Expression-statements + if (readIndex != -1) { + val stmt = taCode.stmts(readIndex) if (stmt.isAssignment) { val assignment = stmt.asAssignment if (handleEscapeProperty( propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) )) { state.noEscapePossibilityViaReference = false - } - for { + } else for { useSite ← assignment.targetVar.usedBy } { val fieldsUseSiteStmt = taCode.stmts(useSite) @@ -375,20 +375,19 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) arrayLoad.arrayRef.asVar.value.toCanonicalForm match { case value: ASArrayValue ⇒ val innerArrayType = value.theUpperTypeBound.componentType - if (innerArrayType.isObjectType) { - val propertyStoreResultInnerArrayType = - propertyStore(innerArrayType, TypeImmutability_new.key) - propertyStoreResultInnerArrayType match { + if (innerArrayType.isBaseType) { + // nothing to do, because it can not be mutated + } else if (innerArrayType.isArrayType) { + state.noEscapePossibilityViaReference = false // to be sound + } else if (innerArrayType.isObjectType) { + //If a deep immutable object escapes, it can not be mutated + propertyStore(innerArrayType, TypeImmutability_new.key) match { case FinalP(DeepImmutableType) ⇒ //nothing to to case FinalP(_) ⇒ state.noEscapePossibilityViaReference = false case ep ⇒ state.dependencies += ep } - } else if (innerArrayType.isArrayType) { - state.noEscapePossibilityViaReference = false // to be sound - } else if (innerArrayType.isBaseType) { - // nothing to do, because it can not be mutated } else state.noEscapePossibilityViaReference case _ ⇒ state.noEscapePossibilityViaReference = false } @@ -402,12 +401,14 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } } else if (stmt.isExprStmt) { - //this case can be ignored because the value is only read but not assigned to another one + //nothing to do here, because the value is only read but not assigned to another one } else { state.noEscapePossibilityViaReference = false } } else { - state.noEscapePossibilityViaReference = false + //nothing to do + // -1 means nothing + //state.noEscapePossibilityViaReference = false } } } @@ -416,7 +417,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) * Determine if the referenced object can escape via field writes */ def checkFieldWritesForEffImmutability(field: Field)(implicit state: State): Unit = { - //Needed because of cyclic calls of the functions to prevent infinite cycles + //Needed because of cyclic calls of the functions - to prevent infinite cycles var seen: Set[Stmt[V]] = Set.empty /** @@ -434,8 +435,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.noEscapePossibilityViaReference = false } } - //TODO hier weitermachen + /** + * Checks if the referenced object or elements from it can escape via the nonvirtualmethod-call + */ def handleNonVirtualMethodCall( method: Method, nonVirtualMethodCall: NonVirtualMethodCall[V], @@ -444,26 +447,37 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) nonVirtualMethodCall.params.foreach( param ⇒ { param.asVar.definedBy.foreach( - index ⇒ - if (index < 0) { + paramDefinedByIndex ⇒ + if (paramDefinedByIndex < 0) { state.noEscapePossibilityViaReference = false } else { - val paramDefinitionStmt = tacCode.stmts(index) + val paramDefinitionStmt = tacCode.stmts(paramDefinedByIndex) if (paramDefinitionStmt.isAssignment) { val assignmentExpression = paramDefinitionStmt.asAssignment.expr - if (assignmentExpression.isGetField) { - val getField = assignmentExpression.asGetField - val field = getField.resolveField - val propertyStoreResult = propertyStore(field.get, FieldImmutability.key) - propertyStoreResult match { - case FinalP(DeepImmutableField) ⇒ //nothing to do here + if (assignmentExpression.isGetField || + assignmentExpression.isGetStatic) { + var field: Option[Field] = None + if (assignmentExpression.isGetField) + field = assignmentExpression.asGetField.resolveField + else if (assignmentExpression.isGetStatic) + field = assignmentExpression.asGetStatic.resolveField + propertyStore(field.get, FieldImmutability.key) match { + case FinalP(DeepImmutableField) ⇒ //nothing to do here case FinalP(_) ⇒ state.noEscapePossibilityViaReference = false case ep ⇒ state.dependencies += ep } + } else if (assignmentExpression.isVirtualFunctionCall) { + val virtualFunctionCall = assignmentExpression.asVirtualFunctionCall + virtualFunctionCall.params.exists( + param ⇒ param.asVar.definedBy.head < 0 || + !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst + ) + } else if (assignmentExpression.isStaticFunctionCall) { + handleStaticFunctionCall(assignmentExpression.asStaticFunctionCall, tacCode) } else if (assignmentExpression.isNew) { - for (i ← paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy) { - val stmt = tacCode.stmts(i) + for (usedSiteIndex ← paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy) { + val stmt = tacCode.stmts(usedSiteIndex) if (stmt.isNonVirtualMethodCall) { if (!seen.contains(stmt)) { seen += stmt @@ -479,7 +493,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.noEscapePossibilityViaReference = false } } else { - val definitionSitesOfParam = definitionSites(method, index) + val definitionSitesOfParam = definitionSites(method, paramDefinedByIndex) val stmt = tacCode.stmts(tacCode.pcToIndex(definitionSitesOfParam.pc)) if (stmt.isNonVirtualMethodCall) { if (!seen.contains(stmt)) { @@ -492,13 +506,12 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) handlePut(stmt, method, tacCode) } } else if (stmt.isArrayStore) { - //val arrayStore = stmt.asArrayStore state.noEscapePossibilityViaReference = false //TODO handling that case more precise } //else if // other cases that the purity analysis can not handle else { - val propertyStoreResult = + if (handleEscapeProperty( propertyStore(definitionSitesOfParam, EscapeProperty.key) - if (handleEscapeProperty(propertyStoreResult)) { + )) { state.noEscapePossibilityViaReference = false } } @@ -509,8 +522,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) } + /** + * Checks if a reference object can escape via a given putfield or putstatic + */ def handlePut(putStmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { - var putDefinitionSites: IntTrieSet = IntTrieSet.empty var putValue: Expr[V] = null if (putStmt.isPutField) { @@ -526,7 +541,8 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } val putValueDefinedByIndex = putValue.asVar.definedBy.head if (putValue.asVar.value.isArrayValue == Yes) { - if (putValueDefinedByIndex >= 0) { + + if (putValueDefinedByIndex >= 0) { //necessary tacCode.stmts(putValueDefinedByIndex).asAssignment.targetVar.usedBy.foreach(x ⇒ { val arrayStmt = tacCode.stmts(x) if (arrayStmt != putStmt) @@ -545,6 +561,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val nonVirtualMethodCall = useSite.asNonVirtualMethodCall nonVirtualMethodCall.params.foreach(param ⇒ { if (!param.isConst && + param.asVar.definedBy.head > -1 && !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst) state.noEscapePossibilityViaReference = false }) @@ -591,20 +608,19 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) for { i ← putDefinitionSites } { - if (i > 0) { + if (i > 0) { //necessary val definitionSiteStatement = tacCode.stmts(i) val definitionSiteAssignment = definitionSiteStatement.asAssignment if (definitionSiteAssignment.expr.isStaticFunctionCall) { handleStaticFunctionCall(definitionSiteAssignment.expr.asStaticFunctionCall, tacCode) } else if (definitionSiteAssignment.expr.isVar) { val definitionSiteVar = definitionSiteAssignment.expr.asVar - for (definitionSiteVarUseSite ← definitionSiteVar.usedBy.iterator) { + for (definitionSiteVarUseSite ← definitionSiteVar.usedBy) { val definitionSiteVarUseSiteStmt = tacCode.stmts(definitionSiteVarUseSite) if (definitionSiteVarUseSiteStmt.isNonVirtualMethodCall) { val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall handleNonVirtualMethodCall(method, nonVirtualMethodCall, tacCode) } else { - state.noEscapePossibilityViaReference = false } //TODO andere Fälle bedenken @@ -614,12 +630,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) definitionSiteAssignment.targetVar.asVar.usedBy.foreach(x ⇒ { val tmpStmt = tacCode.stmts(x) if (tmpStmt.isPutStatic || tmpStmt.isPutField) { - // can be ingored //TODO use seen + if (!seen.contains(tmpStmt)) { + seen += tmpStmt + handlePut(tmpStmt, method, tacCode) + } } else if (tmpStmt.isNonVirtualMethodCall) { - if (!seen.contains(tmpStmt)) + if (!seen.contains(tmpStmt)) { + seen += tmpStmt handleNonVirtualMethodCall(method, tmpStmt.asNonVirtualMethodCall, tacCode) + } } else { - state.noEscapePossibilityViaReference = false } }) @@ -637,7 +657,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } else if (useSiteStmt.isAssignment) { state.noEscapePossibilityViaReference = false //TODO - //val assignment = stmt.asAssignment } else { state.noEscapePossibilityViaReference = false } @@ -645,7 +664,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } //TODO alle Fälle abdecken //TODO escape analyse für Object - // else ; aktuelles putfield bedenken } else if (!definitionSiteAssignment.expr.isConst) { state.noEscapePossibilityViaReference = false } @@ -673,7 +691,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) handlePut(stmt, method, taCode) } } else { - state.noEscapePossibilityViaReference = false } } @@ -687,7 +704,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) checkFieldWritesForEffImmutability(state.field) } + /** + * If there are no dependencies left, this method can be called to create the result. + */ def createResult(state: State): ProperPropertyComputationResult = { + state.referenceIsImmutable match { case Some(false) | None ⇒ Result(field, MutableField) @@ -716,12 +737,12 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { state.dependencies = state.dependencies.iterator.filter(_.e ne eps.e).toSet - eps match { //(: @unchecked) - case x: InterimEP[_, _] ⇒ { + eps match { + case _: InterimEP[_, _] ⇒ { state.dependencies += eps InterimResult(field, MutableField, DeepImmutableField, state.dependencies, c(state)) } - case FinalP(DeepImmutableType) ⇒ { //state.typeImmutability = Some(true) + case FinalP(DeepImmutableType) ⇒ { if (state.dependentImmutability.isEmpty) state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) } @@ -730,22 +751,27 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (t != ObjectType.Object) { // in case of generic fields state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } - case FinalEP(f, DependentImmutableType) ⇒ { + if (field.fieldType.isArrayType && t == field.fieldType.asArrayType.componentType) + state.noEscapePossibilityViaReference = false + case FinalEP(t, DependentImmutableType) ⇒ { state.typeIsImmutable = Some(false) if (state.dependentImmutability.isEmpty) state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) + if (field.fieldType.isArrayType && t == field.fieldType.asArrayType.componentType) + state.noEscapePossibilityViaReference = false } case FinalP( MutableReference | LazyInitializedNotThreadSafeReference ) ⇒ { state.typeIsImmutable = Some(false) + state.referenceIsImmutable = Some(false) return Result(field, MutableField); } case FinalP( ImmutableReference | LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference - ) ⇒ { //TODO + ) ⇒ { state.referenceIsImmutable = Some(true) } case FinalP(DeepImmutableField) ⇒ // nothing to do @@ -790,7 +816,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) loadFormalTypeParameter() handleTypeImmutability() hasGenericType() - //it is possible that the type immutability not already determined at this point + //!! attention it is possible that the type immutability has not already been determined at this point if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) { determineEscapePossibilityOfReferencedObjectOrValue } @@ -832,7 +858,7 @@ object EagerL0FieldImmutabilityAnalysis final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L0FieldImmutabilityAnalysis(p) - val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) //TODO p.allFields + val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) // p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) analysis } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala index 72b5692c09..55842970f5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala @@ -549,7 +549,7 @@ object EagerLxClassImmutabilityAnalysis_new ps.scheduleEagerComputationsForEntities(cfs)( analysis.determineClassImmutability_new( superClassType = null, - FinalEP(ObjectType.Object, DeepImmutableClass), //ImmutableObject), + FinalEP(ObjectType.Object, DeepImmutableClass), superClassMutabilityIsFinal = true, lazyComputation = false ) @@ -560,7 +560,6 @@ object EagerLxClassImmutabilityAnalysis_new /** * Scheduler to run the immutability analysis lazily. - * @author Tobias Peter Roth * @author Michael Eichberg */ object LazyLxClassImmutabilityAnalysis_new diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala index 4af160822f..582adb3cdb 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala @@ -66,13 +66,15 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP typeExtensibility: ObjectType ⇒ Answer )( t: ObjectType - ): ProperPropertyComputationResult = { - typeExtensibility(t) match { - case Yes | Unknown ⇒ - Result(t, MutableType_new) // MutableType) - case No ⇒ step2(t) + ): ProperPropertyComputationResult = + { + val te = typeExtensibility(t) + te match { + case Yes | Unknown ⇒ + Result(t, MutableType_new) // MutableType) + case No ⇒ step2(t) + } } - } def step2(t: ObjectType): ProperPropertyComputationResult = { val directSubtypes = classHierarchy.directSubtypesOf(t) @@ -270,9 +272,10 @@ object EagerLxTypeImmutabilityAnalysis_new override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val typeExtensibility = project.get(TypeExtensibilityKey) val analysis = new LxTypeImmutabilityAnalysis_new(project) - val allProjectClassFilesIterator = project.allProjectClassFiles - val types = allProjectClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) - + //val allProjectClassFilesIterator = project.allProjectClassFiles + //val types = allProjectClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) + // + val types = project.allClassFiles.iterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) ps.scheduleEagerComputationsForEntities(types) { analysis.step1(typeExtensibility) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index 26e7793d0a..822161dbe9 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -3,14 +3,10 @@ package org.opalj.tac.fpcf.analyses.immutability.reference import org.opalj.RelationalOperators.EQ import org.opalj.RelationalOperators.NE -import org.opalj.ai.isImmediateVMException -import org.opalj.ai.pcOfImmediateVMException -import org.opalj.ai.pcOfMethodExternalException import org.opalj.br.ComputationalTypeFloat import org.opalj.br.ComputationalTypeInt import org.opalj.br.Method import org.opalj.br.PC -import org.opalj.br.PCs import org.opalj.br.cfg.BasicBlock import org.opalj.br.cfg.CFG import org.opalj.br.cfg.CFGNode @@ -118,7 +114,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization } } val guardedBB = cfg.bb(afterGuardRecognizedTheDefaultValueIndex) - val reads = fieldAccessInformation.readAccesses(state.field) if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { return MutableReference; @@ -145,7 +140,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization } } else { val monitorResult: ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = - findMonitorCaughtsAndThrowsAndResult(writeIndex, defaultValue, code, cfg, tacCode) + findMonitors(writeIndex, defaultValue, code, cfg, tacCode) if ((monitorResult._1._1.isDefined && monitorResult._1._2.isDefined && monitorResult._2._1.isDefined) && ( @@ -223,7 +218,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization (exceptions, throwStatements, returnNode) } - def findMonitorCaughtsAndThrowsAndResult( + def findMonitors( fieldWrite: Int, defaultValue: Any, code: Array[Stmt[V]], @@ -244,7 +239,7 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization ): Boolean = { v.definedBy.iterator .filter(i ⇒ { - if (i > 0) { + if (i >= 0) { val stmt = tacCode.stmts(i) stmt match { case Assignment(pc1, DVar(useSites, value), cc @ ClassConst(_, constant)) ⇒ { @@ -432,70 +427,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization finalResult } - /** - * Checks if the field write is only executed if the field's value was still the default value. - * Also, no exceptions may be thrown between the guarding if-Statement of a lazy initialization - * and the field write. - */ - def checkWriteIsGuarded( - writeIndex: Int, - guardIndex: Int, - guardedIndex: Int, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): Boolean = { - val startBB = cfg.bb(writeIndex).asBasicBlock - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - val abnormalReturnNode = cfg.abnormalReturnNode - - val caughtExceptions = code.iterator - .filter { stmt ⇒ - stmt.astID == CaughtException.ASTID - } - .flatMap { exception ⇒ - exception.asCaughtException.origins.iterator.map { origin: Int ⇒ - if (isImmediateVMException(origin)) { - pcOfImmediateVMException(origin) - } else { - pcOfMethodExternalException(origin) - } - } - } - .toSet - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - val startPC = curBB.startPC - val endPC = curBB.endPC - if (startPC == 0 || startPC == guardedIndex) - return false; // Reached method start or wrong branch of guarding if-Statement - // Exception thrown between guard and write, which is ok for deterministic methods, - // but may be a problem otherwise as the initialization is not guaranteed to happen - // (or never happen). - if ((curBB ne startBB) && abnormalReturnNode.predecessors.contains(curBB)) { - if (!lazyInitializerIsDeterministic(method, code)) { - return false; - } - } - // Exception thrown between guard and write (caught somewhere, but we don't care) - if ((curBB ne startBB) & caughtExceptions.contains(endPC)) { - if (!lazyInitializerIsDeterministic(method, code)) { - return false; - } - - } - // Check all predecessors except for the one that contains the guarding if-Statement - val predecessors = - getPredecessors(curBB, enqueuedBBs).iterator.filterNot(_.endPC == guardIndex).toList - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - true - } - /** * Gets all predecessor BasicBlocks of a CFGNode. */ @@ -605,71 +536,10 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization result } - /** - * Checks that all non-dead field reads that are not used for the guarding if-Statement of a - * lazy initialization are only executed if the field did not have its default value or after - * the (single) field write. - */ - def checkReads( - reads: Seq[(Method, PCs)], - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int] - ): Boolean = { - // There is only a single method with reads aside from initializers (checked by - // isLazilyInitialized), so we have to check only reads from that one method. - reads.iterator.filter(!_._1.isInitializer).toList.head._2 forall { readPC: Int ⇒ - val index = pcToIndex(readPC) - index != -1 || index == readIndex || checkRead(index, guardedIndex, writeIndex, cfg) - } - } - - /** - * Checks that a field read is only executed if the field did not have its default value or - * after the (single) field write. - */ - def checkRead( - readIndex: Int, - guardedIndex: Int, - writeIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Boolean = { - val startBB = cfg.bb(readIndex).asBasicBlock - val writeBB = cfg.bb(writeIndex) - - var enqueuedBBs: Set[CFGNode] = Set(startBB) - var worklist: List[BasicBlock] = List(startBB.asBasicBlock) - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - - if (startPC == 0) - return false; // Reached the start of the method but not the guard or field write - - if ((curBB eq writeBB) && writeIndex > readIndex) - return false; // In the basic block of the write, but before the write - - if (startPC != guardedIndex && // Did not reach the guard - (curBB ne writeBB) /* Not the basic block of the write */ ) { - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - true - } - /** * 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])(implicit state: State): Boolean = { var seenExpressions: Set[Expr[V]] = Set.empty def _isReadOfCurrentField(expr: Expr[V], tacCode: TACode[TACMethodParameter, V])(implicit state: State): Boolean = { @@ -714,7 +584,6 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization case VirtualFunctionCall.ASTID ⇒ { val virtualFunctionCall = expr.asVirtualFunctionCall val receiverDefSites = virtualFunctionCall.receiver.asVar.definedBy - //TODO better check of these functions for { defSite ← receiverDefSites } { @@ -807,9 +676,12 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization } else { false } - // true //TODO check } + /** + * + * Checks that the value of the field is returned. + */ def checkThatTheValueOfTheFieldIsReturned( write: FieldWriteAccessStmt[V], writeIndex: Int, @@ -834,15 +706,14 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization if (returnValueDefs.size == 2 && returnValueDefs.contains(write.value.asVar.definedBy.head) && returnValueDefs.contains(readIndex)) { - return true - }; // direct return of the written value + return true; + } // direct return of the written value else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || returnValueDefs == IntTrieSet(readIndex, load))) { - return true - }; // return of field value loaded by field read - else { - return false - }; // return of different value + return true; + } // return of field value loaded by field read + else + return false; // return of different value case _ ⇒ ; } index += 1 diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index 4390aa06e4..31c23fce3e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -52,6 +52,10 @@ import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.SelfReferenceParameter import scala.annotation.switch import org.opalj.br.ReferenceType +import org.opalj.br.CharType +import org.opalj.br.DoubleType +import org.opalj.br.LongType +import org.opalj.br.ObjectType /** * @@ -87,8 +91,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = { entity match { case field: Field ⇒ { - //if (!field.isPublic || !field.isProtected) - // return Result(field, MutableReference) determineReferenceImmutability(field) } case _ ⇒ @@ -107,17 +109,11 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec private[analyses] def determineReferenceImmutability( field: Field ): ProperPropertyComputationResult = { - if (field.isFinal) return Result(field, ImmutableReference); - if (field.isPublic) return Result(field, MutableReference); - implicit val state: State = State(field) - - // if (field.isPublic && field.isPackagePrivate) { - state.referenceImmutability = ImmutableReference val thisType = field.classFile.thisType @@ -125,7 +121,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { return Result(field, MutableReference) }; - // 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 @@ -153,17 +148,14 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } // If there are native methods, we give up if (classesHavingAccess.exists(_.methods.exists(_.isNative))) { - //state.notEscapes = false if (!field.isFinal) return Result(field, MutableReference) } - for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method, pcs) } { - /*val tmp = method*/ - if ( /*tmp == method ||*/ methodUpdatesField(method, taCode, pcs)) { + if (methodUpdatesField(method, taCode, pcs)) { return Result(field, MutableReference); } } @@ -171,9 +163,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) handleCalls(calleesEOP) } - - // } - createResult() } @@ -199,13 +188,12 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec def handleCallees(callees: Callees)(implicit state: State): Boolean = { val pc = state.lazyInitInvocation.get._2 if (callees.isIncompleteCallSite(pc)) { - - state.referenceImmutability = MutableReference //LazyInitializedNotThreadSafeReference //TODO //MutableReference //NonFinalFieldByAnalysis + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis true } else { val targets = callees.callees(pc) if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { - state.referenceImmutability = MutableReference //LazyInitializedNotThreadSafeReference //MutableReference //NonFinalFieldByAnalysis + state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis true } else false } @@ -216,10 +204,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * values. */ def getDefaultValue()(implicit state: State): Option[Any] = { - import org.opalj.br.CharType - import org.opalj.br.DoubleType - import org.opalj.br.LongType - import org.opalj.br.ObjectType state.field.fieldType match { case ObjectType.Integer @@ -319,15 +303,9 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec )(implicit state: State): Boolean = { val field = state.field val stmts = taCode.stmts - val staticAddition = { - if (method.isStatic) - 1 - else - 0 - } for (pc ← pcs.iterator) { val index = taCode.pcToIndex(pc) - if (index > (-1 + staticAddition)) { //TODO unnötig + if (index > -1) { //TODO actually, unnecessary but required because there are '-1' val stmt = stmts(index) if (stmt.pc == pc) { (stmt.astID: @switch) match { @@ -360,7 +338,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec // A field written outside an initializer must be lazily // initialized or it is non-final - val result = handleLazyInitialization( index, defaultValue.get, @@ -389,8 +366,6 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } } - //TODO assignment statement ... - //case Assignment. case _ ⇒ throw new RuntimeException("unexpected field access"); } } else { @@ -402,7 +377,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } /** - * f + * * Checks whether the object reference of a PutField does escape (except for being returned). */ def referenceHasEscaped( @@ -410,18 +385,16 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec stmts: Array[Stmt[V]], method: Method )(implicit state: State): Boolean = { - ref.definedBy.forall { defSite ⇒ - if (defSite < 0) true // Must be locally created + if (defSite < 0) true + // Must be locally created else { val definition = stmts(defSite).asAssignment // Must either be null or freshly allocated if (definition.expr.isNullExpr) false else if (!definition.expr.isNew) true else { - val escape = - propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) - handleEscapeProperty(escape) + handleEscapeProperty(propertyStore(definitionSites(method, definition.pc), EscapeProperty.key)) } } } @@ -489,7 +462,7 @@ object EagerL0ReferenceImmutabilityAnalysis final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) //TODO p.allFields + val fields = p.allProjectClassFiles.flatMap(_.fields) //p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) analysis } From e105acff6c60ad4ffb8375fcb0c69c02cb753f40 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 26 Aug 2020 19:02:34 +0200 Subject: [PATCH 242/327] revised tests --- .../immutability/classes/generic/Nested.java | 2 +- .../immutability/fields/Escapers.java | 2 +- .../immutability/fields/MethodCalls.java | 56 ++++++++++++------- .../DCL.java | 6 +- 4 files changed, 42 insertions(+), 24 deletions(-) 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 index a929b8dbd8..b319e0ec0c 100644 --- 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 @@ -25,7 +25,7 @@ public Inner(GenericClass gc){ } } -class GenericClass { +final class GenericClass { @DependentImmutableFieldAnnotation(value = "", genericString = "T") private T t; public GenericClass(T t){ 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 index d1e5ad004e..f6307a7dd9 100644 --- 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 @@ -127,7 +127,7 @@ public GenericNotEscapesMutualEscapeDependencyAbleToResolve() { @DependentImmutableClassAnnotation("") class SimpleGenericClass { @DependentImmutableFieldAnnotation(value = "", genericString = "T") - T t; + private T t; SimpleGenericClass(T t){ this.t = t; } 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 index 4d609098b6..01a4f73cc8 100644 --- 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 @@ -13,25 +13,6 @@ public class MethodCalls { @LazyInitializedThreadSafeReferenceAnnotation("") private TestMutable tm1; - @ShallowImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private TestMutable tm2; - - @MutableFieldAnnotation("") - @LazyInitializedNotThreadSafeReferenceAnnotation("") - private TestMutable tm3; - - @MutableReferenceAnnotation("") - @MutableFieldAnnotation("") - private TestMutable tm4; - - @ShallowImmutableFieldAnnotation("") - private TestMutable tm5; - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private TestMutable tm6 = new TestMutable(); - public synchronized void getTM1(){ if(tm1==null){ tm1= new TestMutable(); @@ -39,6 +20,10 @@ public synchronized void getTM1(){ tm1.nop(); } + @ShallowImmutableFieldAnnotation("") + @LazyInitializedThreadSafeReferenceAnnotation("") + private TestMutable tm2; + public synchronized TestMutable getTM2(){ if(tm2==null){ tm2= new TestMutable(); @@ -46,30 +31,63 @@ public synchronized TestMutable getTM2(){ return tm2; } + @MutableFieldAnnotation("") + @LazyInitializedNotThreadSafeReferenceAnnotation("") + private TestMutable tm3; + public void getTm3() { if(tm3==null){ tm3 = new TestMutable(); } } + @MutableReferenceAnnotation("") + @MutableFieldAnnotation("") + 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; } + + @ShallowImmutableFieldAnnotation("") + private TestMutable tm5; + public synchronized void getTm5() { if(tm5==null){ tm5 = new TestMutable(); } tm5.nop(); } + + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private TestMutable tm6 = new TestMutable(); + + @ShallowImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private TestMutable tm7 = new TestMutable(); + + public void foo(){ + tm7.nop(); + } + + + + + + + + } class TestMutable{ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java index 1a786503eb..e7a93bbfd9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java @@ -141,7 +141,7 @@ synchronized public static org.omg.CORBA.TypeCode type () class DCL5 { @LazyInitializedThreadSafeReferenceAnnotation("") - DCL5 instance; + private DCL5 instance; public DCL5 DCL5(){ if(instance==null){ @@ -364,7 +364,7 @@ public DoubleCheckedLockingClass19 getInstance() { class ArrayLazyInitializationNotThreadSafe { @LazyInitializedNotThreadSafeReferenceAnnotation("During the initialization phase there is no lock") - int[] values; + private int[] values; public int[] getValues(){ if(values==null){ @@ -376,7 +376,7 @@ public int[] getValues(){ class ArrayLazyInitializationThreadSafe { @LazyInitializedThreadSafeReferenceAnnotation("it is a lock via the synchronized method") - int[] values; + private int[] values; public synchronized int[] getValues(){ if(values==null){ From 471533e0226366388a24fd5403315b0f51d4f048 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 31 Aug 2020 20:28:23 +0200 Subject: [PATCH 243/327] running on all fields --- .../reference/L0ReferenceImmutabilityAnalysis.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala index 31c23fce3e..7e75969c18 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala @@ -462,7 +462,7 @@ object EagerL0ReferenceImmutabilityAnalysis final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L0ReferenceImmutabilityAnalysis(p) - val fields = p.allProjectClassFiles.flatMap(_.fields) //p.allFields + val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) //p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) analysis } From 2ea6b9b6660193820aedb8ad266316e80a5510df Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 31 Aug 2020 20:29:34 +0200 Subject: [PATCH 244/327] revised logic and time consumption, still wip --- .../L0FieldImmutabilityAnalysis.scala | 387 ++++++++++++------ 1 file changed, 259 insertions(+), 128 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index 290ae76273..710931431d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -71,7 +71,6 @@ import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.tac.StaticFunctionCall import org.opalj.Yes import org.opalj.tac.Expr -import org.opalj.value.ASArrayValue case class State(f: Field) { var field: Field = f @@ -81,6 +80,8 @@ case class State(f: Field) { var dependentImmutability: Option[DependentImmutabilityKind] = Some(DependentImmutabilityKind.onlyDeepImmutable) var genericTypeSetNotDeepImmutable = false var dependencies: Set[EOptionP[Entity, Property]] = Set.empty + var innerArrayTypes: Set[ObjectType] = Set.empty + var escapesStillDetermined = false } /** @@ -198,17 +199,20 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) // Because the entries of an array can be reassigned we state it as not being deep immutable state.typeIsImmutable = Some(false) } else { - propertyStore(objectType, TypeImmutability_new.key) match { + val result = propertyStore(objectType, TypeImmutability_new.key) + // println("result1: "+result) + result match { case FinalP(DeepImmutableType) ⇒ // deep immutable type is set as default case FinalP(DependentImmutableType) ⇒ state.typeIsImmutable = Some(false) - /*state.dependentImmutability = - Some(DependentImmutabilityKind.dependent)*/ - case FinalP(ShallowImmutableType | MutableType_new) ⇒ + case FinalEP(t, ShallowImmutableType | MutableType_new) ⇒ state.typeIsImmutable = Some(false) - state.dependentImmutability = None + //println("field type: "+field.fieldType) + if (field.fieldType != ObjectType.Object) + state.dependentImmutability = Some(DependentImmutabilityKind.dependent) case epk ⇒ state.dependencies += epk } + // println("depp imm: "+state.dependentImmutability) } } @@ -265,7 +269,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } ) genericParameters.foreach(objectType ⇒ { - propertyStore(objectType, TypeImmutability_new.key) match { + val result = propertyStore(objectType, TypeImmutability_new.key) + + // println("result: "+result) + result match { case FinalP(DeepImmutableType) ⇒ //nothing to do here: default value is deep immutable case FinalP(DependentImmutableType) ⇒ onlyDeepImmutableTypesInGenericTypeFound = false @@ -279,6 +286,15 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.dependencies += ep } }) + /* println( + s""" + | onlyDeep Imm types in generics found: $onlyDeepImmutableTypesInGenericTypeFound + | no shallow or mutable types in generics found: $noShallowOrMutableTypesInGenericTypeFound + | no relevant attributes: $noRelevantAttributesFound + | dep imm ${state.dependentImmutability} + |""".stripMargin + ) */ + //Prevents the case of keeping the default values of these // flags only because of no relevant attribute has been found if (!noRelevantAttributesFound) { @@ -289,7 +305,8 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (onlyDeepImmutableTypesInGenericTypeFound) { //nothing to do... //state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) - } else if (noShallowOrMutableTypesInGenericTypeFound) + } else if (noShallowOrMutableTypesInGenericTypeFound && + state.dependentImmutability != Some(DependentImmutabilityKind.dependent)) state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) else state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } @@ -340,83 +357,113 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) /** * Determine if the referenced object can escape either via field reads or writes. */ - def determineEscapePossibilityOfReferencedObjectOrValue(implicit state: State): Unit = { + def determineEscapePossibilityOfReferencedObjectOrValue()(implicit state: State): Unit = { + state.escapesStillDetermined = true /** * Determine if the referenced object can escape via field reads. */ - def determineEscapePossibilityViaFieldReads(implicit state: State): Unit = { - for { - (method, pcs) ← fieldAccessInformation.readAccesses(state.field) - taCode ← getTACAI(method) - pc ← pcs - } { - val readIndex = taCode.pcToIndex(pc) - // This if-statement is necessary, because there are -1 elements in the array - //TODO verify if this is a bug - - //This if-statement is necessary because readAccesses can also be simple Expression-statements - if (readIndex != -1) { - val stmt = taCode.stmts(readIndex) - if (stmt.isAssignment) { - val assignment = stmt.asAssignment - if (handleEscapeProperty( - propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) - )) { - state.noEscapePossibilityViaReference = false - } else for { - useSite ← assignment.targetVar.usedBy - } { - val fieldsUseSiteStmt = taCode.stmts(useSite) - if (fieldsUseSiteStmt.isAssignment) { - val assignment = fieldsUseSiteStmt.asAssignment - 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) { - // nothing to do, because it can not be mutated - } else if (innerArrayType.isArrayType) { - state.noEscapePossibilityViaReference = false // to be sound - } else if (innerArrayType.isObjectType) { - //If a deep immutable object escapes, it can not be mutated - propertyStore(innerArrayType, TypeImmutability_new.key) match { - case FinalP(DeepImmutableType) ⇒ //nothing to to - case FinalP(_) ⇒ + def determineEscapePossibilityViaFieldReads()(implicit state: State): Unit = { + val reads = fieldAccessInformation.readAccesses(state.field) + + reads.foreach(read ⇒ { + val method = read._1 + val pcs = read._2 + val taCodeOption = getTACAI(method) + if (taCodeOption.isDefined) { + val taCode = taCodeOption.get + pcs.foreach( + 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 + + if (handleEscapeProperty( + propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) + )) { + state.noEscapePossibilityViaReference = false + return ; + } else for { + useSite ← assignment.targetVar.usedBy + } { + val fieldsUseSiteStmt = taCode.stmts(useSite) + if (fieldsUseSiteStmt.isAssignment) { + val assignment = fieldsUseSiteStmt.asAssignment + if (assignment.expr.isArrayLoad) { + import org.opalj.value.ASArrayValue + val arrayLoad = assignment.expr.asArrayLoad + arrayLoad.arrayRef.asVar.value.toCanonicalForm match { + case value: ASArrayValue ⇒ + val innerArrayType = value.theUpperTypeBound.componentType + if (innerArrayType.isBaseType) { + // nothing to do, because it can not be mutated + } else if (innerArrayType.isArrayType) { + state.noEscapePossibilityViaReference = false // to be sound + return ; + } else if (innerArrayType.isObjectType) { + //If a deep immutable object escapes, it can not be mutated + propertyStore(innerArrayType, TypeImmutability_new.key) match { + case FinalP(DeepImmutableType) ⇒ //nothing to to + case FinalP(_) ⇒ + state.noEscapePossibilityViaReference = false + return ; + case ep ⇒ { + state.innerArrayTypes += innerArrayType.asObjectType + state.dependencies += ep + } + } + } else { + state.noEscapePossibilityViaReference = false + return ; + } + case _ ⇒ { state.noEscapePossibilityViaReference = false - case ep ⇒ - state.dependencies += ep + return ; + } } - } else state.noEscapePossibilityViaReference - case _ ⇒ state.noEscapePossibilityViaReference = false - } - } else state.noEscapePossibilityViaReference = false - } else if (fieldsUseSiteStmt.isMonitorEnter || - fieldsUseSiteStmt.isMonitorExit || - fieldsUseSiteStmt.isIf) { - //nothing to do + } else { + state.noEscapePossibilityViaReference = false + return ; + } + } else if (fieldsUseSiteStmt.isMonitorEnter || + fieldsUseSiteStmt.isMonitorExit || + fieldsUseSiteStmt.isIf) { + //nothing to do + } else { + state.noEscapePossibilityViaReference = false + return ; + } + } //xxx + } else if (stmt.isExprStmt) { + //nothing to do here, because the value is only read but not assigned to another one + } else { + state.noEscapePossibilityViaReference = false + return ; + } } else { - state.noEscapePossibilityViaReference = false + //nothing to do + // -1 means NOTHING as a placeholder } + //} + + /*-------------------------------------------------------------*/ } - } else if (stmt.isExprStmt) { - //nothing to do here, because the value is only read but not assigned to another one - } else { - state.noEscapePossibilityViaReference = false - } + ) } else { - //nothing to do - // -1 means nothing - //state.noEscapePossibilityViaReference = false } - } + + }) + } /** * Determine if the referenced object can escape via field writes */ - def checkFieldWritesForEffImmutability(field: Field)(implicit state: State): Unit = { + def checkFieldWritesForEffImmutability()(implicit state: State): Unit = { //Needed because of cyclic calls of the functions - to prevent infinite cycles var seen: Set[Stmt[V]] = Set.empty @@ -433,6 +480,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) p.asVar.definedBy.head < 0 || !tacCode.stmts(p.asVar.definedBy.head).asAssignment.expr.isConst)) { state.noEscapePossibilityViaReference = false + return ; } } @@ -450,23 +498,27 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) paramDefinedByIndex ⇒ if (paramDefinedByIndex < 0) { state.noEscapePossibilityViaReference = false + return ; } else { val paramDefinitionStmt = tacCode.stmts(paramDefinedByIndex) if (paramDefinitionStmt.isAssignment) { val assignmentExpression = paramDefinitionStmt.asAssignment.expr if (assignmentExpression.isGetField || assignmentExpression.isGetStatic) { - var field: Option[Field] = None + var assignedField: Option[Field] = None if (assignmentExpression.isGetField) - field = assignmentExpression.asGetField.resolveField + assignedField = assignmentExpression.asGetField.resolveField else if (assignmentExpression.isGetStatic) - field = assignmentExpression.asGetStatic.resolveField - propertyStore(field.get, FieldImmutability.key) match { - case FinalP(DeepImmutableField) ⇒ //nothing to do here - case FinalP(_) ⇒ - state.noEscapePossibilityViaReference = false - case ep ⇒ state.dependencies += ep - } + assignedField = assignmentExpression.asGetStatic.resolveField + + if (assignedField.isDefined && assignedField.get != state.field) + propertyStore(assignedField.get, FieldImmutability.key) match { + case FinalP(DeepImmutableField) ⇒ //nothing to do here + case FinalP(_) ⇒ + state.noEscapePossibilityViaReference = false + return ; + case ep ⇒ state.dependencies += ep + } } else if (assignmentExpression.isVirtualFunctionCall) { val virtualFunctionCall = assignmentExpression.asVirtualFunctionCall virtualFunctionCall.params.exists( @@ -475,6 +527,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) ) } else if (assignmentExpression.isStaticFunctionCall) { handleStaticFunctionCall(assignmentExpression.asStaticFunctionCall, tacCode) + return ; } else if (assignmentExpression.isNew) { for (usedSiteIndex ← paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy) { val stmt = tacCode.stmts(usedSiteIndex) @@ -485,12 +538,14 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } else { state.noEscapePossibilityViaReference = false + return ; } } } else if (assignmentExpression.isConst) { //nothing to do } else { state.noEscapePossibilityViaReference = false + return ; } } else { val definitionSitesOfParam = definitionSites(method, paramDefinedByIndex) @@ -504,15 +559,19 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (!seen.contains(stmt)) { seen += stmt handlePut(stmt, method, tacCode) + return ; } + return ; } else if (stmt.isArrayStore) { state.noEscapePossibilityViaReference = false //TODO handling that case more precise + return ; } //else if // other cases that the purity analysis can not handle else { if (handleEscapeProperty( propertyStore(definitionSitesOfParam, EscapeProperty.key) )) { state.noEscapePossibilityViaReference = false + return ; } } } //TODO go further @@ -538,6 +597,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) putValue = putStatic.value } else { state.noEscapePossibilityViaReference = false + return ; } val putValueDefinedByIndex = putValue.asVar.definedBy.head if (putValue.asVar.value.isArrayValue == Yes) { @@ -562,20 +622,25 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) nonVirtualMethodCall.params.foreach(param ⇒ { if (!param.isConst && param.asVar.definedBy.head > -1 && - !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst) + !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst) { state.noEscapePossibilityViaReference = false + return ; + } }) } else if (useSite == arrayStore) { //nothing to do } else if (useSite.isReturnValue) { //assigned array-element escapes state.noEscapePossibilityViaReference = false + return ; } else { state.noEscapePossibilityViaReference = false + return ; } } if (!isArrayIndexConst) { state.noEscapePossibilityViaReference = false + return ; } else if (assignedExpr.isStaticFunctionCall) { handleStaticFunctionCall(assignedExpr.asStaticFunctionCall, tacCode) } else if (assignedExpr.isNew) { @@ -591,19 +656,26 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } //nothing to do in the else case. Stmt has still been handled } else { state.noEscapePossibilityViaReference = false + return ; } }) } else { state.noEscapePossibilityViaReference = false + return ; } } else { state.noEscapePossibilityViaReference = false + return ; } } else { state.noEscapePossibilityViaReference = false + return ; } }) - } else state.noEscapePossibilityViaReference = false + } else { + state.noEscapePossibilityViaReference = false + return ; + } } else for { i ← putDefinitionSites @@ -622,6 +694,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) handleNonVirtualMethodCall(method, nonVirtualMethodCall, tacCode) } else { state.noEscapePossibilityViaReference = false + return ; } //TODO andere Fälle bedenken } @@ -641,6 +714,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } else { state.noEscapePossibilityViaReference = false + return ; } }) } else { @@ -657,8 +731,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } else if (useSiteStmt.isAssignment) { state.noEscapePossibilityViaReference = false //TODO + return ; } else { state.noEscapePossibilityViaReference = false + return ; } } } @@ -666,9 +742,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) //TODO escape analyse für Object } else if (!definitionSiteAssignment.expr.isConst) { state.noEscapePossibilityViaReference = false + return ; } } else { state.noEscapePossibilityViaReference = false + return ; } } } @@ -676,62 +754,98 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) /** * Begin of method check field writes */ - state.noEscapePossibilityViaReference = field.isPrivate - for { - (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode ← getTACAI(method) - pc ← pcs - } { - val index = taCode.pcToIndex(pc) - val staticAddition = if (method.isStatic) 1 else 0 - if (index > (-1 + staticAddition)) { - val stmt = taCode.stmts(index) - if (!seen.contains(stmt)) { - seen += stmt - handlePut(stmt, method, taCode) - } + state.noEscapePossibilityViaReference = state.field.isPrivate + + val writeAccesses = fieldAccessInformation.writeAccesses(state.field) + writeAccesses.foreach(writeAccess ⇒ { + val method = writeAccess._1 + val pcs = writeAccess._2 + val tacCodeOption = getTACAI(method) + if (tacCodeOption.isDefined) { + pcs.foreach( + pc ⇒ { + val taCode = tacCodeOption.get + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = taCode.stmts(index) + if (!seen.contains(stmt)) { + seen += stmt + handlePut(stmt, method, taCode) + } + } else { + state.noEscapePossibilityViaReference = false + return ; + } + } + ) } else { - state.noEscapePossibilityViaReference = false + //state.escapesStillDetermined = false + //return ; } - } + }) } /** * Begin of method determineEscapePossibilityOfReferencedObjectOrValue */ if (state.noEscapePossibilityViaReference) - determineEscapePossibilityViaFieldReads(state) + checkFieldWritesForEffImmutability() if (state.noEscapePossibilityViaReference) - checkFieldWritesForEffImmutability(state.field) + determineEscapePossibilityViaFieldReads() } /** * If there are no dependencies left, this method can be called to create the result. */ def createResult(state: State): ProperPropertyComputationResult = { - - state.referenceIsImmutable match { - case Some(false) | None ⇒ - Result(field, MutableField) - case Some(true) ⇒ { - state.typeIsImmutable match { - case Some(true) ⇒ - Result(field, DeepImmutableField) - case Some(false) | None ⇒ { - if (state.noEscapePossibilityViaReference) + /*println( + s""" + | field $field + | ref imm: ${state.referenceIsImmutable} + | type imm: ${state.typeIsImmutable} + | dep imm: ${state.dependentImmutability} + | no esc: ${state.noEscapePossibilityViaReference} + | dependencies empty: ${state.dependencies.isEmpty} + | escape still det: ${state.escapesStillDetermined} + |""".stripMargin + ) */ + + if (state.dependencies.isEmpty) { + if (!state.escapesStillDetermined) + state.noEscapePossibilityViaReference = false + state.referenceIsImmutable match { + case Some(false) | None ⇒ + Result(field, MutableField) + case Some(true) ⇒ { + state.typeIsImmutable match { + case Some(true) ⇒ Result(field, DeepImmutableField) - else - state.dependentImmutability match { - case Some(DependentImmutabilityKind.notShallowOrMutable) ⇒ - Result(field, DependentImmutableField) - case Some(DependentImmutabilityKind.onlyDeepImmutable) ⇒ - Result(field, DeepImmutableField) - case _ ⇒ { - Result(field, ShallowImmutableField) + Result(field, DeepImmutableField) + case Some(false) | None ⇒ { + if (state.noEscapePossibilityViaReference) { + Result(field, DeepImmutableField) + } else { + state.dependentImmutability match { + case Some(DependentImmutabilityKind.notShallowOrMutable) ⇒ + Result(field, DependentImmutableField) + case Some(DependentImmutabilityKind.onlyDeepImmutable) ⇒ + Result(field, DeepImmutableField) + case _ ⇒ { + Result(field, ShallowImmutableField) + } } } + } } } } + } else { + InterimResult( + field, + MutableField, + DeepImmutableField, + state.dependencies, + c(state) + ) } } @@ -742,24 +856,23 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.dependencies += eps InterimResult(field, MutableField, DeepImmutableField, state.dependencies, c(state)) } - case FinalP(DeepImmutableType) ⇒ { - if (state.dependentImmutability.isEmpty) - state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) - } + case FinalP(DeepImmutableType) ⇒ //nothing to do -> is default case FinalEP(t, MutableType_new | ShallowImmutableType) ⇒ state.typeIsImmutable = Some(false) if (t != ObjectType.Object) { // in case of generic fields state.dependentImmutability = Some(DependentImmutabilityKind.dependent) } - if (field.fieldType.isArrayType && t == field.fieldType.asArrayType.componentType) + if (t.isInstanceOf[ObjectType] && state.innerArrayTypes.contains(t.asInstanceOf[ObjectType])) state.noEscapePossibilityViaReference = false case FinalEP(t, DependentImmutableType) ⇒ { state.typeIsImmutable = Some(false) - if (state.dependentImmutability.isEmpty) + if (t != field.fieldType && state.dependentImmutability == Some(DependentImmutabilityKind.onlyDeepImmutable)) state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) - if (field.fieldType.isArrayType && t == field.fieldType.asArrayType.componentType) + if (t.isInstanceOf[ObjectType] && state.innerArrayTypes.contains(t.asInstanceOf[ObjectType])) state.noEscapePossibilityViaReference = false } + //TODO wip + case FinalP( MutableReference | LazyInitializedNotThreadSafeReference ) ⇒ { @@ -778,15 +891,14 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case FinalP(DependentImmutableField | ShallowImmutableField | MutableField) ⇒ state.noEscapePossibilityViaReference = false case eps if eps.isFinal && eps.asEPS.pk == TACAI.key ⇒ - determineEscapePossibilityOfReferencedObjectOrValue(state) + determineEscapePossibilityOfReferencedObjectOrValue()(state) case eps if eps.isFinal && eps.asEPS.pk == EscapeProperty.key ⇒ - determineEscapePossibilityOfReferencedObjectOrValue(state) - if (handleEscapeProperty(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) { + if (handleEscapeProperty(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) state.noEscapePossibilityViaReference = false - } case eps ⇒ state.dependencies += eps } + if (state.dependencies.isEmpty) createResult(state) else InterimResult( @@ -814,14 +926,33 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } loadFormalTypeParameter() + loadFormalTypeParameter() handleTypeImmutability() hasGenericType() //!! attention it is possible that the type immutability has not already been determined at this point if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) { - determineEscapePossibilityOfReferencedObjectOrValue + determineEscapePossibilityOfReferencedObjectOrValue() } if (state.dependencies.isEmpty) { + /* Test */ + /*if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) { //(state.referenceIsImmutable.get && !state.typeIsImmutable.get) { + determineEscapePossibilityOfReferencedObjectOrValue + if (state.dependencies.isEmpty) + createResult(state) + else { + InterimResult( + field, + MutableField, + DeepImmutableField, + state.dependencies, + c(state) + ) + } + } else { + //state.noEscapePossibilityViaReference = false + createResult(state) + } */ createResult(state) } else { InterimResult( From fb9554ef2def267c13d913b2a2e67a6d4b0cb59f Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 31 Aug 2020 20:31:18 +0200 Subject: [PATCH 245/327] added guards for correct property handling --- .../immutability/LxClassImmutabilityAnalysis_new.scala | 9 ++++++--- .../immutability/LxTypeImmutabilityAnalysis_new.scala | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala index 55842970f5..b5194c930f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala @@ -24,13 +24,13 @@ 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.EPK + 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.InterimE + import org.opalj.fpcf.InterimResult import org.opalj.fpcf.LBP import org.opalj.fpcf.LUBP @@ -232,6 +232,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal fieldsPropertyStoreInformation.foreach( f ⇒ { + import org.opalj.fpcf.EPK + import org.opalj.fpcf.InterimE f match { case FinalP(MutableField) ⇒ { if (lazyComputation) @@ -332,7 +334,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case UBP(DependentImmutableClass) ⇒ if (someEPS.isFinal) dependees -= SuperClassKey - maxLocalImmutability = DependentImmutableClass + if (maxLocalImmutability != ShallowImmutableClass) + maxLocalImmutability = DependentImmutableClass case LBP(ShallowImmutableClass) ⇒ // super class is a least shallow immutable if (minLocalImmutability != ShallowImmutableClass && diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala index 582adb3cdb..38a198c6cf 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala @@ -125,7 +125,8 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP maxImmutability = ShallowImmutableType case FinalP(DependentImmutableClass) ⇒ joinedImmutability = DependentImmutableType - maxImmutability = DependentImmutableType + if (maxImmutability != ShallowImmutableType) + maxImmutability = DependentImmutableType case eps @ InterimLUBP(lb, ub) ⇒ joinedImmutability = lb.correspondingTypeImmutability @@ -149,7 +150,8 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP case FinalP(DependentImmutableType) ⇒ joinedImmutability = joinedImmutability.meet(DependentImmutableType) - maxImmutability = DependentImmutableType + if (maxImmutability != ShallowImmutableType) + maxImmutability = DependentImmutableType case eps @ InterimLUBP(subtypeLB, subtypeUB) ⇒ joinedImmutability = joinedImmutability.meet(subtypeLB) @@ -226,7 +228,8 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP dependencies = dependencies - e nextResult() case FinalEP(e, DependentImmutableClass | DependentImmutableType) ⇒ { - maxImmutability = DependentImmutableType + if (maxImmutability != ShallowImmutableType) + maxImmutability = DependentImmutableType dependencies = dependencies - e nextResult() } From b5154175fb0a4d7484b512cbceeee979d72810cf Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 31 Aug 2020 20:34:08 +0200 Subject: [PATCH 246/327] set closed package key and type extensibility key conservatively before executing the type immutability tests --- .../FieldImmutabilityAnalysisDemo.scala | 12 +++--- .../org/opalj/support/info/Immutability.scala | 29 ++++--------- .../opalj/fpcf/TypeImmutabilityTests.scala | 41 +++++++++++++++++-- 3 files changed, 51 insertions(+), 31 deletions(-) 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 index ab432232f9..fed49703af 100644 --- 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 @@ -1,11 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.analyses -import java.io.BufferedWriter -import java.io.FileWriter -import java.io.File import java.net.URL -import java.util.Calendar import org.opalj.br.Field import org.opalj.br.analyses.BasicReport @@ -54,6 +50,7 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { + import org.opalj.bytecode.JRELibraryFolder var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None val analysesManager = project.get(FPCFAnalysesManagerKey) @@ -135,7 +132,7 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { | took : $analysisTime seconds |""".stripMargin ) - val calendar = Calendar.getInstance() + /*val calendar = Calendar.getInstance() val file = new File( s"C:/MA/results/fieldImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ @@ -145,10 +142,11 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val bw = new BufferedWriter(new FileWriter(file)) bw.write(sb.toString()) - bw.close() - + bw.close() */ + println("xxx: "+JRELibraryFolder) s""" | took : $analysisTime seconds |""".stripMargin + } } 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 index 14ceac5ed0..b4d94373c7 100644 --- 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 @@ -86,6 +86,7 @@ object Immutability { isLibrary: Boolean, closedWorldAssumption: Boolean ): BasicReport = { + import org.opalj.ai.fpcf.properties.AIDomainFactoryKey OPALLogger.updateLogger(GlobalLogContext, DevNullLogger) @@ -215,6 +216,13 @@ object Immutability { analysesManager.project.get(RTACallGraphKey) } { t ⇒ callGraphTime = t.toSeconds } + analysesManager.project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + import java.net.URL + + import org.opalj.ai.domain + //Set[Class[_ <: AnyRef]](classOf[domain.l0.BaseDomainWithDefUse[URL]]) + Set[Class[_ <: AnyRef]](classOf[domain.l1.DefaultDomainWithCFGAndDefUse[URL]]) + } //val propertyStore = time { project.get(PropertyStoreKey) } { t ⇒ propertyStoreTime = t.toSeconds } project.getOrCreateProjectInformationKeyInitializationData( @@ -499,7 +507,6 @@ object Immutability { | with $numThreads threads |""".stripMargin ) - // ${stringBuilderResults.toString()} println( s""" | @@ -512,24 +519,6 @@ object Immutability { |""".stripMargin ) println("resultsfolder: "+resultsFolder) - // - /*val l = mutableReferences.filter(x ⇒ !mutableFields.toSet.contains(x)) - println( - s""" - | mut reference not contained in mutable fields: - | ${l.head} - | - | - | field imm: - | ${ - import org.opalj.br.fpcf.properties.FieldImmutability - import org.opalj.br.fpcf.properties.ReferenceImmutability - propertyStore(l.head, ReferenceImmutability.key).toString+"/n"+ - propertyStore(l.head, FieldImmutability.key).toString - } - |""".stripMargin - - )*/ val calendar = Calendar.getInstance() if (resultsFolder != null) { @@ -628,7 +617,7 @@ object Immutability { } i += 1 } - evaluate(cp, analysis, numThreads, projectDir, libDir, resultFolder, timeEvaluation, threadEvaluation, isLibrary, withoutJDK, closedWorldAssumption) + evaluate(cp, analysis, numThreads, projectDir, libDir, resultFolder, timeEvaluation, threadEvaluation, withoutJDK, isLibrary, closedWorldAssumption) } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala index 1beef6ed8c..c2a072466f 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala @@ -3,8 +3,10 @@ package org.opalj.fpcf import java.net.URL +import com.typesafe.config.Config +import com.typesafe.config.ConfigValueFactory +import org.opalj.BaseConfig 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 @@ -18,6 +20,9 @@ import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.cg.InitialEntryPointsKey +import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey /** * Tests the Type Immutability Analysis with the new lattice @@ -27,13 +32,42 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new class TypeImmutabilityTests extends PropertiesTest { override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability/sandbox") + List("org/opalj/fpcf/fixtures/immutability") + } + + override def createConfig(): Config = { + import com.typesafe.config.ConfigValueFactory.fromAnyRef + 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( + InitialInstantiatedTypesKey.ConfigKeyPrefix+ + "AllInstantiatedTypesFinder.projectClassesOnly", + ConfigValueFactory.fromAnyRef(true) + ) + .withValue( + "org.opalj.br.analyses.cg.ClosedPackagesKey", + fromAnyRef("org.opalj.br.analyses.cg.OpenCodeBase") + ) + .withValue("org.opalj.br.analyses.cg.ClassExtensibilityKey", ConfigValueFactory.fromAnyRef( + "org.opalj.br.analyses.cg.ConfiguredExtensibleClasses" + )) } override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) } + p.get(RTACallGraphKey) } @@ -45,7 +79,6 @@ class TypeImmutabilityTests extends PropertiesTest { * } */ describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { - println(1) val as = executeAnalyses( Set( LazyUnsoundPrematurelyReadFieldsAnalysis, @@ -67,6 +100,6 @@ class TypeImmutabilityTests extends PropertiesTest { // fieldsWithAnnotations(as.project), classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), Set("TypeImmutability_new") - ) //TODO class files ... with annotation + ) } } From d9b16d16cfc32350624960b8c7efadceba2e2510 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 10 Sep 2020 09:18:49 +0200 Subject: [PATCH 247/327] added concrete type immutability determination in cases where we know the concrete type --- .../L0FieldImmutabilityAnalysis.scala | 948 +++++++++--------- 1 file changed, 474 insertions(+), 474 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala index 710931431d..2324a9035f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala @@ -71,17 +71,35 @@ import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.tac.StaticFunctionCall import org.opalj.Yes import org.opalj.tac.Expr +import org.opalj.br.PCs +import org.opalj.br.fpcf.properties.ClassImmutability_new +import org.opalj.value.ASArrayValue +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 + +case class State( + field: Field, + var typeIsImmutable: Option[Boolean] = Some(true), + var referenceIsImmutable: Option[Boolean] = None, + var noEscapePossibilityViaReference: Boolean = true, + var dependentImmutability: Option[DependentImmutabilityKind] = Some(DependentImmutabilityKind.onlyDeepImmutable), + var genericTypeSetNotDeepImmutable: Boolean = false, + 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 escapesStillDetermined: Boolean = false, + var concreteClassTypeIsKnown: Boolean = false, + var totalAmountOfFieldWrites: Int = -1 +) { + def hasDependees: Boolean = { + !dependees.isEmpty || tacDependees.nonEmpty + } -case class State(f: Field) { - var field: Field = f - var typeIsImmutable: Option[Boolean] = Some(true) - var referenceIsImmutable: Option[Boolean] = None - var noEscapePossibilityViaReference: Boolean = true - var dependentImmutability: Option[DependentImmutabilityKind] = Some(DependentImmutabilityKind.onlyDeepImmutable) - var genericTypeSetNotDeepImmutable = false - var dependencies: Set[EOptionP[Entity, Property]] = Set.empty - var innerArrayTypes: Set[ObjectType] = Set.empty - var escapesStillDetermined = false + def getDependees: Traversable[EOptionP[Entity, Property]] = { + dependees ++ tacDependees.valuesIterator.map(_._1) + } } /** @@ -125,7 +143,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) private[analyses] def determineFieldImmutability( field: Field ): ProperPropertyComputationResult = { - //stores the formal type parameters of the fields class or outer class var classFormalTypeParameters: Option[Set[String]] = None @@ -200,19 +217,16 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.typeIsImmutable = Some(false) } else { val result = propertyStore(objectType, TypeImmutability_new.key) - // println("result1: "+result) result match { case FinalP(DeepImmutableType) ⇒ // deep immutable type is set as default case FinalP(DependentImmutableType) ⇒ state.typeIsImmutable = Some(false) case FinalEP(t, ShallowImmutableType | MutableType_new) ⇒ state.typeIsImmutable = Some(false) - //println("field type: "+field.fieldType) if (field.fieldType != ObjectType.Object) state.dependentImmutability = Some(DependentImmutabilityKind.dependent) - case epk ⇒ state.dependencies += epk + case epk ⇒ state.dependees += epk } - // println("depp imm: "+state.dependentImmutability) } } @@ -230,14 +244,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) onlyDeepImmutableTypesInGenericTypeFound = false if (!isInClassesGenericTypeParameters(t)) noShallowOrMutableTypesInGenericTypeFound = false - - //state.typeIsImmutable = Some(false) case ClassTypeSignature( packageIdentifier, SimpleClassTypeSignature(simpleName, typeArguments), _ ) ⇒ - //state.typeIsImmutable = Some(false) noRelevantAttributesFound = false typeArguments .foreach({ @@ -263,7 +274,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } }) case _ ⇒ - //state.typeIsImmutable = Some(false) noShallowOrMutableTypesInGenericTypeFound = false onlyDeepImmutableTypesInGenericTypeFound = false } @@ -271,7 +281,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) genericParameters.foreach(objectType ⇒ { val result = propertyStore(objectType, TypeImmutability_new.key) - // println("result: "+result) result match { case FinalP(DeepImmutableType) ⇒ //nothing to do here: default value is deep immutable case FinalP(DependentImmutableType) ⇒ @@ -283,17 +292,9 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.typeIsImmutable = Some(false) } case ep ⇒ - state.dependencies += ep + state.dependees += ep } }) - /* println( - s""" - | onlyDeep Imm types in generics found: $onlyDeepImmutableTypesInGenericTypeFound - | no shallow or mutable types in generics found: $noShallowOrMutableTypesInGenericTypeFound - | no relevant attributes: $noRelevantAttributesFound - | dep imm ${state.dependentImmutability} - |""".stripMargin - ) */ //Prevents the case of keeping the default values of these // flags only because of no relevant attribute has been found @@ -314,17 +315,32 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } /** - * Returns the TAC to a given method when it still exists. - * Otherwise collect dependencies. + * Returns the TACode for a method if available, registering dependencies as necessary. */ def getTACAI( - method: Method + method: Method, + pcs: PCs, + readOrWrite: String )(implicit state: State): Option[TACode[TACMethodParameter, V]] = { + if (readOrWrite != "read" && readOrWrite != "write") + throw new Exception propertyStore(method, TACAI.key) match { case finalEP: FinalEP[Method, TACAI] ⇒ finalEP.ub.tac case epk ⇒ - state.dependencies += epk + var reads = IntTrieSet.empty + var writes = IntTrieSet.empty + if (state.tacDependees.contains(method)) { + reads = state.tacDependees(method)._2._1 + writes = state.tacDependees(method)._2._2 + } + if (readOrWrite == "read") { + state.tacDependees += method -> ((epk, (reads ++ pcs, writes))) + } + if (readOrWrite == "writes") { + state.tacDependees += method -> ((epk, (reads, writes ++ pcs))) + state.tacDependees += method -> ((epk, (reads, writes ++ pcs))) + } None } } @@ -349,7 +365,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case _: InterimEP[DefinitionSite, EscapeProperty] ⇒ true // Escape state is worse than via return case _ ⇒ - state.dependencies += ep + state.dependees += ep false } } @@ -359,310 +375,339 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) */ def determineEscapePossibilityOfReferencedObjectOrValue()(implicit state: State): Unit = { state.escapesStillDetermined = true + state.noEscapePossibilityViaReference = state.field.isPrivate - /** - * Determine if the referenced object can escape via field reads. - */ - def determineEscapePossibilityViaFieldReads()(implicit state: State): Unit = { - val reads = fieldAccessInformation.readAccesses(state.field) - - reads.foreach(read ⇒ { - val method = read._1 - val pcs = read._2 - val taCodeOption = getTACAI(method) - if (taCodeOption.isDefined) { - val taCode = taCodeOption.get - pcs.foreach( - 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 - - if (handleEscapeProperty( - propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) - )) { - state.noEscapePossibilityViaReference = false - return ; - } else for { - useSite ← assignment.targetVar.usedBy - } { - val fieldsUseSiteStmt = taCode.stmts(useSite) - if (fieldsUseSiteStmt.isAssignment) { - val assignment = fieldsUseSiteStmt.asAssignment - if (assignment.expr.isArrayLoad) { - import org.opalj.value.ASArrayValue - val arrayLoad = assignment.expr.asArrayLoad - arrayLoad.arrayRef.asVar.value.toCanonicalForm match { - case value: ASArrayValue ⇒ - val innerArrayType = value.theUpperTypeBound.componentType - if (innerArrayType.isBaseType) { - // nothing to do, because it can not be mutated - } else if (innerArrayType.isArrayType) { - state.noEscapePossibilityViaReference = false // to be sound - return ; - } else if (innerArrayType.isObjectType) { - //If a deep immutable object escapes, it can not be mutated - propertyStore(innerArrayType, TypeImmutability_new.key) match { - case FinalP(DeepImmutableType) ⇒ //nothing to to - case FinalP(_) ⇒ - state.noEscapePossibilityViaReference = false - return ; - case ep ⇒ { - state.innerArrayTypes += innerArrayType.asObjectType - state.dependencies += ep - } - } - } else { + if (state.noEscapePossibilityViaReference) { + val writes = fieldAccessInformation.writeAccesses(state.field) + state.totalAmountOfFieldWrites = writes.map(x ⇒ x._2.size).fold(0)(_ + _) + writes.foreach(writeAccess ⇒ { + val method = writeAccess._1 + val pcs = writeAccess._2 + checkFieldWritesForEffImmutability(method, pcs) + }) + if (state.noEscapePossibilityViaReference) { + val reads = fieldAccessInformation.readAccesses(state.field) + reads.foreach(read ⇒ { + val method = read._1 + val pcs = read._2 + determineEscapePossibilityViaFieldReads(method, pcs) + }) + } + } + } + + /** + * Determine if the referenced object can escape via field reads. + */ + def determineEscapePossibilityViaFieldReads(method: Method, pcs: PCs)(implicit state: State): Unit = { + + val taCodeOption = getTACAI(method, pcs, "read") + if (taCodeOption.isDefined) { + val taCode = taCodeOption.get + pcs.foreach( + 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 + + if (handleEscapeProperty( + propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) + )) { + state.noEscapePossibilityViaReference = false + return ; + } else for { + useSite ← assignment.targetVar.usedBy + } { + val fieldsUseSiteStmt = taCode.stmts(useSite) + if (fieldsUseSiteStmt.isAssignment) { + val assignment = fieldsUseSiteStmt.asAssignment + 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) { + // nothing to do, because it can not be mutated + } else if (innerArrayType.isArrayType) { + state.noEscapePossibilityViaReference = false // to be sound + return ; + } else if (innerArrayType.isObjectType) { + //If a deep immutable object escapes, it can not be mutated + propertyStore(innerArrayType, TypeImmutability_new.key) match { + case FinalP(DeepImmutableType) ⇒ //nothing to to + case FinalP(_) ⇒ state.noEscapePossibilityViaReference = false return ; + case ep ⇒ { + state.innerArrayTypes += innerArrayType.asObjectType + state.dependees += ep } - case _ ⇒ { - state.noEscapePossibilityViaReference = false - return ; } + } else { + state.noEscapePossibilityViaReference = false + return ; } - } else { + case _ ⇒ { state.noEscapePossibilityViaReference = false return ; } - } else if (fieldsUseSiteStmt.isMonitorEnter || - fieldsUseSiteStmt.isMonitorExit || - fieldsUseSiteStmt.isIf) { - //nothing to do - } else { - state.noEscapePossibilityViaReference = false - return ; } - } //xxx - } else if (stmt.isExprStmt) { - //nothing to do here, because the value is only read but not assigned to another one + } else { + state.noEscapePossibilityViaReference = false + return ; + } + } else if (fieldsUseSiteStmt.isMonitorEnter || + fieldsUseSiteStmt.isMonitorExit || + fieldsUseSiteStmt.isIf) { + //nothing to do } else { state.noEscapePossibilityViaReference = false return ; } - } else { - //nothing to do - // -1 means NOTHING as a placeholder } - //} - - /*-------------------------------------------------------------*/ + } else if (stmt.isExprStmt) { + //nothing to do here, because the value is only read but not assigned to another one + } else { + state.noEscapePossibilityViaReference = false + return ; } - ) - } else { + } else { + //nothing to do + // -1 means NOTHING as a placeholder + } } - - }) - + ) } + } + + /** + * Determine if the referenced object can escape via field writes + */ + def checkFieldWritesForEffImmutability(method: Method, pcs: PCs)(implicit state: State): Unit = { + //Needed because of cyclic calls of the functions - to prevent infinite cycles + var seen: Set[Stmt[V]] = Set.empty /** - * Determine if the referenced object can escape via field writes + * Checks if the parameters of a static function call are no parameters from an outer + * function and are constants */ - def checkFieldWritesForEffImmutability()(implicit state: State): Unit = { - //Needed because of cyclic calls of the functions - to prevent infinite cycles - var seen: Set[Stmt[V]] = Set.empty + def handleStaticFunctionCall( + staticFunctionCall: StaticFunctionCall[V], + tacCode: TACode[TACMethodParameter, V] + ): Unit = { + if (staticFunctionCall.params.exists(p ⇒ + p.asVar.definedBy.size != 1 || + p.asVar.definedBy.head < 0 || + !tacCode.stmts(p.asVar.definedBy.head).asAssignment.expr.isConst)) { + state.noEscapePossibilityViaReference = false + return ; + } + } - /** - * Checks if the parameters of a static function call are no parameters from an outer - * function and are constants - */ - def handleStaticFunctionCall( - staticFunctionCall: StaticFunctionCall[V], - tacCode: TACode[TACMethodParameter, V] - ): Unit = { - if (staticFunctionCall.params.exists(p ⇒ - p.asVar.definedBy.size != 1 || - p.asVar.definedBy.head < 0 || - !tacCode.stmts(p.asVar.definedBy.head).asAssignment.expr.isConst)) { - state.noEscapePossibilityViaReference = false - return ; - } + def handleKnownClassType(objectType: ObjectType)(implicit state: State): Unit = { + state.concreteClassTypeIsKnown = true + + val propertyStoreResult = propertyStore(objectType, ClassImmutability_new.key) + propertyStoreResult match { + case FinalP(DeepImmutableClass) ⇒ state.typeIsImmutable = Some(true) + case FinalP(_) ⇒ state.typeIsImmutable = Some(false) + case eps ⇒ state.dependees += eps } + } - /** - * Checks if the referenced object or elements from it can escape via the nonvirtualmethod-call - */ - def handleNonVirtualMethodCall( - method: Method, - nonVirtualMethodCall: NonVirtualMethodCall[V], - tacCode: TACode[TACMethodParameter, V] - ): Unit = { - nonVirtualMethodCall.params.foreach( - param ⇒ { - param.asVar.definedBy.foreach( - paramDefinedByIndex ⇒ - if (paramDefinedByIndex < 0) { - state.noEscapePossibilityViaReference = false - return ; - } else { - val paramDefinitionStmt = tacCode.stmts(paramDefinedByIndex) - if (paramDefinitionStmt.isAssignment) { - val assignmentExpression = paramDefinitionStmt.asAssignment.expr - if (assignmentExpression.isGetField || - assignmentExpression.isGetStatic) { - var assignedField: Option[Field] = None - if (assignmentExpression.isGetField) - assignedField = assignmentExpression.asGetField.resolveField - else if (assignmentExpression.isGetStatic) - assignedField = assignmentExpression.asGetStatic.resolveField - - if (assignedField.isDefined && assignedField.get != state.field) - propertyStore(assignedField.get, FieldImmutability.key) match { - case FinalP(DeepImmutableField) ⇒ //nothing to do here - case FinalP(_) ⇒ - state.noEscapePossibilityViaReference = false - return ; - case ep ⇒ state.dependencies += ep - } - } else if (assignmentExpression.isVirtualFunctionCall) { - val virtualFunctionCall = assignmentExpression.asVirtualFunctionCall - virtualFunctionCall.params.exists( - param ⇒ param.asVar.definedBy.head < 0 || - !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst - ) - } else if (assignmentExpression.isStaticFunctionCall) { - handleStaticFunctionCall(assignmentExpression.asStaticFunctionCall, tacCode) - return ; - } else if (assignmentExpression.isNew) { - for (usedSiteIndex ← paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy) { - val stmt = tacCode.stmts(usedSiteIndex) - if (stmt.isNonVirtualMethodCall) { - if (!seen.contains(stmt)) { - seen += stmt - handleNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) - } - } else { + /** + * Checks if the referenced object or elements from it can escape via the nonvirtualmethod-call + */ + def handleNonVirtualMethodCall( + method: Method, + nonVirtualMethodCall: NonVirtualMethodCall[V], + tacCode: TACode[TACMethodParameter, V] + ): Unit = { + nonVirtualMethodCall.params.foreach( + param ⇒ { + param.asVar.definedBy.foreach( + paramDefinedByIndex ⇒ + if (paramDefinedByIndex < 0) { + state.noEscapePossibilityViaReference = false + return ; + } else { + val paramDefinitionStmt = tacCode.stmts(paramDefinedByIndex) + if (paramDefinitionStmt.isAssignment) { + val assignmentExpression = paramDefinitionStmt.asAssignment.expr + if (assignmentExpression.isGetField || + assignmentExpression.isGetStatic) { + var assignedField: Option[Field] = None + if (assignmentExpression.isGetField) + assignedField = assignmentExpression.asGetField.resolveField + else if (assignmentExpression.isGetStatic) + assignedField = assignmentExpression.asGetStatic.resolveField + + if (assignedField.isDefined && assignedField.get != state.field) + propertyStore(assignedField.get, FieldImmutability.key) match { + case FinalP(DeepImmutableField) ⇒ //nothing to do here + case FinalP(_) ⇒ state.noEscapePossibilityViaReference = false return ; - } - } - } else if (assignmentExpression.isConst) { - //nothing to do - } else { - state.noEscapePossibilityViaReference = false - return ; - } - } else { - val definitionSitesOfParam = definitionSites(method, paramDefinedByIndex) - val stmt = tacCode.stmts(tacCode.pcToIndex(definitionSitesOfParam.pc)) - if (stmt.isNonVirtualMethodCall) { - if (!seen.contains(stmt)) { - seen += stmt - handleNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) - } - } else if (stmt.isPutField || stmt.isPutStatic) { - if (!seen.contains(stmt)) { - seen += stmt - handlePut(stmt, method, tacCode) - return ; - } - return ; - } else if (stmt.isArrayStore) { - state.noEscapePossibilityViaReference = false //TODO handling that case more precise - return ; - } //else if // other cases that the purity analysis can not handle - else { - if (handleEscapeProperty( - propertyStore(definitionSitesOfParam, EscapeProperty.key) - )) { - state.noEscapePossibilityViaReference = false - return ; + case ep ⇒ state.dependees += ep } - } - } //TODO go further - } - ) - } - ) - } + } else if (assignmentExpression.isVirtualFunctionCall) { + val virtualFunctionCall = assignmentExpression.asVirtualFunctionCall + virtualFunctionCall.params.exists( + param ⇒ param.asVar.definedBy.head < 0 || + !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst + ) + } else if (assignmentExpression.isStaticFunctionCall) { + handleStaticFunctionCall(assignmentExpression.asStaticFunctionCall, tacCode) + return ; + } else if (assignmentExpression.isNew) { + val newStmt = assignmentExpression.asNew + if (field.fieldType.isObjectType && - /** - * Checks if a reference object can escape via a given putfield or putstatic - */ - def handlePut(putStmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { - var putDefinitionSites: IntTrieSet = IntTrieSet.empty - var putValue: Expr[V] = null - if (putStmt.isPutField) { - val putField = putStmt.asPutField - putDefinitionSites = putField.value.asVar.definedBy - putValue = putField.value - } else if (putStmt.isPutStatic) { - val putStatic = putStmt.asPutStatic - putDefinitionSites = putStatic.value.asVar.definedBy - putValue = putStatic.value - } else { - state.noEscapePossibilityViaReference = false - return ; - } - val putValueDefinedByIndex = putValue.asVar.definedBy.head - if (putValue.asVar.value.isArrayValue == Yes) { - - if (putValueDefinedByIndex >= 0) { //necessary - tacCode.stmts(putValueDefinedByIndex).asAssignment.targetVar.usedBy.foreach(x ⇒ { - val arrayStmt = tacCode.stmts(x) - if (arrayStmt != putStmt) - if (arrayStmt.isArrayStore) { - val arrayStore = arrayStmt.asArrayStore - val arrayStoreIndex = arrayStore.index - val isArrayIndexConst = - tacCode.stmts(arrayStoreIndex.asVar.definedBy.head).asAssignment.expr.isConst - val assignedValue = arrayStore.value - if (assignedValue.asVar.definedBy.head >= 0) { - val valueAssignment = tacCode.stmts(assignedValue.asVar.definedBy.head).asAssignment - val assignedExpr = valueAssignment.expr - val useSites = valueAssignment.targetVar.usedBy.map(tacCode.stmts(_)) - for (useSite ← useSites) { - if (useSite.isNonVirtualMethodCall) { - val nonVirtualMethodCall = useSite.asNonVirtualMethodCall - nonVirtualMethodCall.params.foreach(param ⇒ { - if (!param.isConst && - param.asVar.definedBy.head > -1 && - !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst) { - state.noEscapePossibilityViaReference = false - return ; - } - }) - } else if (useSite == arrayStore) { - //nothing to do - } else if (useSite.isReturnValue) { - //assigned array-element escapes - state.noEscapePossibilityViaReference = false - return ; + newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && + state.totalAmountOfFieldWrites == 1) { + handleKnownClassType(newStmt.tpe.mostPreciseObjectType) + } + for (usedSiteIndex ← paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy) { + val stmt = tacCode.stmts(usedSiteIndex) + if (stmt.isNonVirtualMethodCall) { + if (!seen.contains(stmt)) { + seen += stmt + handleNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) + } } else { state.noEscapePossibilityViaReference = false return ; } } - if (!isArrayIndexConst) { + } else if (assignmentExpression.isConst) { + //nothing to do + } else { + state.noEscapePossibilityViaReference = false + return ; + } + } else { + val definitionSitesOfParam = definitionSites(method, paramDefinedByIndex) + val stmt = tacCode.stmts(tacCode.pcToIndex(definitionSitesOfParam.pc)) + if (stmt.isNonVirtualMethodCall) { + if (!seen.contains(stmt)) { + seen += stmt + handleNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) + } + } else if (stmt.isPutField || stmt.isPutStatic) { + if (!seen.contains(stmt)) { + seen += stmt + handlePut(stmt, method, tacCode) + return ; + } + return ; + } else if (stmt.isArrayStore) { + state.noEscapePossibilityViaReference = false //TODO handling that case more precise + return ; + } //else if // other cases that the purity analysis can not handle + else { + if (handleEscapeProperty( + propertyStore(definitionSitesOfParam, EscapeProperty.key) + )) { state.noEscapePossibilityViaReference = false return ; - } else if (assignedExpr.isStaticFunctionCall) { - handleStaticFunctionCall(assignedExpr.asStaticFunctionCall, tacCode) - } else if (assignedExpr.isNew) { - valueAssignment.targetVar.asVar.usedBy.foreach(index ⇒ { - val tmpStmt = tacCode.stmts(index) - if (tmpStmt.isArrayStore) { - // can be ingored - } else if (tmpStmt.isNonVirtualMethodCall) { - val nonVirtualMethodcall = tmpStmt.asNonVirtualMethodCall - if (!seen.contains(tmpStmt)) { - seen += tmpStmt - handleNonVirtualMethodCall(method, nonVirtualMethodcall, tacCode) - } //nothing to do in the else case. Stmt has still been handled - } else { + } + } + } //TODO go further + } + ) + } + ) + } + + /** + * Checks if a reference object can escape via a given putfield or putstatic + */ + def handlePut(putStmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { + var putDefinitionSites: IntTrieSet = IntTrieSet.empty + var putValue: Expr[V] = null + if (putStmt.isPutField) { + val putField = putStmt.asPutField + putDefinitionSites = putField.value.asVar.definedBy + putValue = putField.value + } else if (putStmt.isPutStatic) { + val putStatic = putStmt.asPutStatic + putDefinitionSites = putStatic.value.asVar.definedBy + putValue = putStatic.value + } else { + state.noEscapePossibilityViaReference = false + return ; + } + + val putValueDefinedByIndex = putValue.asVar.definedBy.head + if (putValue.asVar.value.isArrayValue == Yes) { + + if (putValueDefinedByIndex >= 0) { //necessary + tacCode.stmts(putValueDefinedByIndex).asAssignment.targetVar.usedBy.foreach(x ⇒ { + val arrayStmt = tacCode.stmts(x) + if (arrayStmt != putStmt) + if (arrayStmt.isArrayStore) { + val arrayStore = arrayStmt.asArrayStore + val arrayStoreIndex = arrayStore.index + val isArrayIndexConst = + tacCode.stmts(arrayStoreIndex.asVar.definedBy.head).asAssignment.expr.isConst + val assignedValue = arrayStore.value + if (assignedValue.asVar.definedBy.head >= 0) { + val valueAssignment = tacCode.stmts(assignedValue.asVar.definedBy.head).asAssignment + val assignedExpr = valueAssignment.expr + val useSites = valueAssignment.targetVar.usedBy.map(tacCode.stmts(_)) + for (useSite ← useSites) { + if (useSite.isNonVirtualMethodCall) { + val nonVirtualMethodCall = useSite.asNonVirtualMethodCall + nonVirtualMethodCall.params.foreach(param ⇒ { + if (!param.isConst && + param.asVar.definedBy.head > -1 && + !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst) { state.noEscapePossibilityViaReference = false return ; } }) + } else if (useSite == arrayStore) { + //nothing to do + } else if (useSite.isReturnValue) { + //assigned array-element escapes + state.noEscapePossibilityViaReference = false + return ; } else { state.noEscapePossibilityViaReference = false return ; } + } + if (!isArrayIndexConst) { + state.noEscapePossibilityViaReference = false + return ; + } else if (assignedExpr.isStaticFunctionCall) { + handleStaticFunctionCall(assignedExpr.asStaticFunctionCall, tacCode) + } else if (assignedExpr.isNew) { + val newStmt = assignedExpr.asNew + if (field.fieldType.isObjectType && + newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && + state.totalAmountOfFieldWrites == 1) { + handleKnownClassType(newStmt.tpe.mostPreciseObjectType) + } + valueAssignment.targetVar.asVar.usedBy.foreach(index ⇒ { + val tmpStmt = tacCode.stmts(index) + if (tmpStmt.isArrayStore) { + // can be ingored + } else if (tmpStmt.isNonVirtualMethodCall) { + val nonVirtualMethodcall = tmpStmt.asNonVirtualMethodCall + if (!seen.contains(tmpStmt)) { + seen += tmpStmt + handleNonVirtualMethodCall(method, nonVirtualMethodcall, tacCode) + } //nothing to do in the else case. Stmt has still been handled + } else { + state.noEscapePossibilityViaReference = false + return ; + } + }) } else { state.noEscapePossibilityViaReference = false return ; @@ -671,145 +716,133 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.noEscapePossibilityViaReference = false return ; } - }) - } else { - state.noEscapePossibilityViaReference = false - return ; - } - } else - for { - i ← putDefinitionSites - } { - if (i > 0) { //necessary - val definitionSiteStatement = tacCode.stmts(i) - val definitionSiteAssignment = definitionSiteStatement.asAssignment - if (definitionSiteAssignment.expr.isStaticFunctionCall) { - handleStaticFunctionCall(definitionSiteAssignment.expr.asStaticFunctionCall, tacCode) - } else if (definitionSiteAssignment.expr.isVar) { - val definitionSiteVar = definitionSiteAssignment.expr.asVar - for (definitionSiteVarUseSite ← definitionSiteVar.usedBy) { - val definitionSiteVarUseSiteStmt = tacCode.stmts(definitionSiteVarUseSite) - if (definitionSiteVarUseSiteStmt.isNonVirtualMethodCall) { - val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall - handleNonVirtualMethodCall(method, nonVirtualMethodCall, tacCode) + } else { + state.noEscapePossibilityViaReference = false + return ; + } + }) + } else { + state.noEscapePossibilityViaReference = false + return ; + } + } else + for { + i ← putDefinitionSites + } { + if (i > 0) { //necessary + val definitionSiteStatement = tacCode.stmts(i) + val definitionSiteAssignment = definitionSiteStatement.asAssignment + if (definitionSiteAssignment.expr.isStaticFunctionCall) { + handleStaticFunctionCall(definitionSiteAssignment.expr.asStaticFunctionCall, tacCode) + } else if (definitionSiteAssignment.expr.isVar) { + val definitionSiteVar = definitionSiteAssignment.expr.asVar + for (definitionSiteVarUseSite ← definitionSiteVar.usedBy) { + val definitionSiteVarUseSiteStmt = tacCode.stmts(definitionSiteVarUseSite) + if (definitionSiteVarUseSiteStmt.isNonVirtualMethodCall) { + val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall + handleNonVirtualMethodCall(method, nonVirtualMethodCall, tacCode) + } else { + state.noEscapePossibilityViaReference = false + return ; + } + //TODO andere Fälle bedenken + } + } else if (definitionSiteAssignment.expr.isNew) { + val newStmt = definitionSiteAssignment.expr.asNew + if (field.fieldType.isObjectType && + + newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && + state.totalAmountOfFieldWrites == 1) { + handleKnownClassType(newStmt.tpe.mostPreciseObjectType) + } + if (!method.isConstructor) { + definitionSiteAssignment.targetVar.asVar.usedBy.foreach(x ⇒ { + val tmpStmt = tacCode.stmts(x) + if (tmpStmt.isPutStatic || tmpStmt.isPutField) { + if (!seen.contains(tmpStmt)) { + seen += tmpStmt + handlePut(tmpStmt, method, tacCode) + } + } else if (tmpStmt.isNonVirtualMethodCall) { + if (!seen.contains(tmpStmt)) { + seen += tmpStmt + handleNonVirtualMethodCall(method, tmpStmt.asNonVirtualMethodCall, tacCode) + } } else { state.noEscapePossibilityViaReference = false return ; } - //TODO andere Fälle bedenken - } - } else if (definitionSiteAssignment.expr.isNew) { - if (!method.isConstructor) { - definitionSiteAssignment.targetVar.asVar.usedBy.foreach(x ⇒ { - val tmpStmt = tacCode.stmts(x) - if (tmpStmt.isPutStatic || tmpStmt.isPutField) { - if (!seen.contains(tmpStmt)) { - seen += tmpStmt - handlePut(tmpStmt, method, tacCode) - } - } else if (tmpStmt.isNonVirtualMethodCall) { - if (!seen.contains(tmpStmt)) { - seen += tmpStmt - handleNonVirtualMethodCall(method, tmpStmt.asNonVirtualMethodCall, tacCode) - } - } else { - state.noEscapePossibilityViaReference = false - return ; - } - }) - } else { - val useSites = - definitionSiteAssignment.targetVar.usedBy - for (i ← useSites) { - val useSiteStmt = tacCode.stmts(i) - if (useSiteStmt.isNonVirtualMethodCall) { - handleNonVirtualMethodCall(method, useSiteStmt.asNonVirtualMethodCall, tacCode) - } else if (useSiteStmt.isPutStatic || useSiteStmt.isPutField) { - if (!seen.contains(useSiteStmt)) { - seen += useSiteStmt - handlePut(useSiteStmt, method, tacCode) - } - } else if (useSiteStmt.isAssignment) { - state.noEscapePossibilityViaReference = false //TODO - return ; - } else { - state.noEscapePossibilityViaReference = false - return ; + }) + } else { + val useSites = + definitionSiteAssignment.targetVar.usedBy + for (i ← useSites) { + val useSiteStmt = tacCode.stmts(i) + if (useSiteStmt.isNonVirtualMethodCall) { + handleNonVirtualMethodCall(method, useSiteStmt.asNonVirtualMethodCall, tacCode) + } else if (useSiteStmt.isPutStatic || useSiteStmt.isPutField) { + if (!seen.contains(useSiteStmt)) { + seen += useSiteStmt + handlePut(useSiteStmt, method, tacCode) } + } else if (useSiteStmt.isAssignment) { + state.noEscapePossibilityViaReference = false //TODO + return ; + } else { + state.noEscapePossibilityViaReference = false + return ; } } - //TODO alle Fälle abdecken - //TODO escape analyse für Object - } else if (!definitionSiteAssignment.expr.isConst) { - state.noEscapePossibilityViaReference = false - return ; } - } else { + //TODO alle Fälle abdecken + } else if (!definitionSiteAssignment.expr.isConst) { state.noEscapePossibilityViaReference = false return ; } + } else { + state.noEscapePossibilityViaReference = false + return ; } - } - - /** - * Begin of method check field writes - */ - state.noEscapePossibilityViaReference = state.field.isPrivate - - val writeAccesses = fieldAccessInformation.writeAccesses(state.field) - writeAccesses.foreach(writeAccess ⇒ { - val method = writeAccess._1 - val pcs = writeAccess._2 - val tacCodeOption = getTACAI(method) - if (tacCodeOption.isDefined) { - pcs.foreach( - pc ⇒ { - val taCode = tacCodeOption.get - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = taCode.stmts(index) - if (!seen.contains(stmt)) { - seen += stmt - handlePut(stmt, method, taCode) - } - } else { - state.noEscapePossibilityViaReference = false - return ; - } - } - ) - } else { - //state.escapesStillDetermined = false - //return ; } - }) } + /** - * Begin of method determineEscapePossibilityOfReferencedObjectOrValue + * Begin of method check field writes */ - if (state.noEscapePossibilityViaReference) - checkFieldWritesForEffImmutability() - if (state.noEscapePossibilityViaReference) - determineEscapePossibilityViaFieldReads() + val tacCodeOption = getTACAI(method, pcs, "write") + if (tacCodeOption.isDefined) { + val taCode = tacCodeOption.get + pcs.foreach( + pc ⇒ { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = taCode.stmts(index) + if (!seen.contains(stmt)) { + seen += stmt + handlePut(stmt, method, taCode) + } + } else { + state.noEscapePossibilityViaReference = false + return ; + } + } + ) + } } /** * If there are no dependencies left, this method can be called to create the result. */ def createResult(state: State): ProperPropertyComputationResult = { - /*println( - s""" - | field $field - | ref imm: ${state.referenceIsImmutable} - | type imm: ${state.typeIsImmutable} - | dep imm: ${state.dependentImmutability} - | no esc: ${state.noEscapePossibilityViaReference} - | dependencies empty: ${state.dependencies.isEmpty} - | escape still det: ${state.escapesStillDetermined} - |""".stripMargin - ) */ - - if (state.dependencies.isEmpty) { + if (state.hasDependees) { + InterimResult( + field, + MutableField, + DeepImmutableField, + state.getDependees, + c(state) + ) + } else { if (!state.escapesStillDetermined) state.noEscapePossibilityViaReference = false state.referenceIsImmutable match { @@ -838,24 +871,19 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } } - } else { - InterimResult( - field, - MutableField, - DeepImmutableField, - state.dependencies, - c(state) - ) } + } def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { - state.dependencies = state.dependencies.iterator.filter(_.e ne eps.e).toSet + + if (eps.asEPS.pk != TACAI.key) + state.dependees = state.dependees.iterator.filter(_.e ne eps.e).toSet eps match { - case _: InterimEP[_, _] ⇒ { - state.dependencies += eps - InterimResult(field, MutableField, DeepImmutableField, state.dependencies, c(state)) - } + /*case _: InterimEP[_, _] ⇒ { + state.dependees += eps + InterimResult(field, MutableField, DeepImmutableField, state.dependees, c(state)) + }*/ case FinalP(DeepImmutableType) ⇒ //nothing to do -> is default case FinalEP(t, MutableType_new | ShallowImmutableType) ⇒ state.typeIsImmutable = Some(false) @@ -871,8 +899,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (t.isInstanceOf[ObjectType] && state.innerArrayTypes.contains(t.asInstanceOf[ObjectType])) state.noEscapePossibilityViaReference = false } - //TODO wip - case FinalP( MutableReference | LazyInitializedNotThreadSafeReference ) ⇒ { @@ -890,24 +916,27 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case FinalP(DeepImmutableField) ⇒ // nothing to do case FinalP(DependentImmutableField | ShallowImmutableField | MutableField) ⇒ state.noEscapePossibilityViaReference = false - case eps if eps.isFinal && eps.asEPS.pk == TACAI.key ⇒ - determineEscapePossibilityOfReferencedObjectOrValue()(state) + case eps if eps.asEPS.pk == TACAI.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Method, TACAI]] + val method = newEP.e + state.tacDependees -= method + val pcs = state.tacDependees(method)._2 + if (eps.isFinal) { + checkFieldWritesForEffImmutability(method, pcs._2)(state) + determineEscapePossibilityViaFieldReads(method, pcs._1)(state) + } else { + state.tacDependees += method -> ((newEP, pcs)) + } + case eps if eps.isFinal && eps.asEPS.pk == EscapeProperty.key ⇒ if (handleEscapeProperty(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) state.noEscapePossibilityViaReference = false + case FinalP(DeepImmutableClass) ⇒ state.typeIsImmutable = Some(true) + case FinalP(ShallowImmutableClass | DependentImmutableClass | MutableClass) ⇒ state.typeIsImmutable = Some(false) case eps ⇒ - state.dependencies += eps + state.dependees += eps } - - if (state.dependencies.isEmpty) createResult(state) - else - InterimResult( - field, - MutableField, - DeepImmutableField, - state.dependencies, - c(state) - ) + createResult(state) } /** @@ -922,47 +951,18 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case FinalP(MutableReference | LazyInitializedNotThreadSafeReference) ⇒ return Result(field, MutableField); case ep @ _ ⇒ { - state.dependencies += ep + state.dependees += ep } } loadFormalTypeParameter() - loadFormalTypeParameter() - handleTypeImmutability() + hasGenericType() - //!! attention it is possible that the type immutability has not already been determined at this point if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) { determineEscapePossibilityOfReferencedObjectOrValue() } - - if (state.dependencies.isEmpty) { - /* Test */ - /*if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) { //(state.referenceIsImmutable.get && !state.typeIsImmutable.get) { - determineEscapePossibilityOfReferencedObjectOrValue - if (state.dependencies.isEmpty) - createResult(state) - else { - InterimResult( - field, - MutableField, - DeepImmutableField, - state.dependencies, - c(state) - ) - } - } else { - //state.noEscapePossibilityViaReference = false - createResult(state) - } */ - createResult(state) - } else { - InterimResult( - field, - MutableField, - DeepImmutableField, - state.dependencies, - c(state) - ) - } + if (!state.concreteClassTypeIsKnown) + handleTypeImmutability() + createResult(state) } } From c00fabac650ed853d7e401031762cfe69cfc8ea6 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 10 Sep 2020 09:20:51 +0200 Subject: [PATCH 248/327] added lazy initialization handling for the case of a guard with mutliple arguments --- ...mutabilityAnalysisLazyInitialization.scala | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala index 822161dbe9..2178118ac8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala @@ -114,6 +114,11 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization } } val guardedBB = cfg.bb(afterGuardRecognizedTheDefaultValueIndex) + val elseBB = cfg.bb(guardedIndex) + + if (isTransitivePredecessorsOf(elseBB, writeBB, Set.empty)) //succsrs.toSet.contains(writeBB)) + return MutableReference; + val reads = fieldAccessInformation.readAccesses(state.field) if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { return MutableReference; @@ -440,6 +445,23 @@ trait AbstractReferenceImmutabilityAnalysisLazyInitialization result.toList } + def isTransitivePredecessorsOf(possiblePredecessor: CFGNode, node: CFGNode, visited: Set[CFGNode]): Boolean = { + var tmpVisited = visited + node + if (possiblePredecessor == node) + true + else { + node.predecessors.foreach( + currentNode ⇒ { + if (!tmpVisited.contains(currentNode)) + if (isTransitivePredecessorsOf(possiblePredecessor, currentNode, tmpVisited)) + return true; + tmpVisited += currentNode + } + ) + false + } + } + /** * Gets all successors BasicBlocks of a CFGNode */ From 9248b28ce96355bc700fc67b76f98f419005d126 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 10 Sep 2020 09:22:05 +0200 Subject: [PATCH 249/327] added multiple information in the test output like the amount of class interfaces --- .../main/scala/org/opalj/support/info/Immutability.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 index b4d94373c7..0ef9e8fb46 100644 --- 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 @@ -395,6 +395,8 @@ object Immutability { | deep immutable classes: | ${deepImmutableClasses.mkString(" || deep immutable classes \n")} | + | deep immutable interfaces: + | ${deepImmutableClassesInterfaces.mkString(" || deep immutable interfaces \n")} |""".stripMargin ) } @@ -478,6 +480,7 @@ object Immutability { | shallow immutable classes: ${shallowImmutableClasses.size} | dependent immutable classes: ${dependentImmutableClasses.size} | deep immutable classes: ${deepImmutableClasses.size} + | deep immutable interfaces: ${deepImmutableClassesInterfaces.size} | classes: ${allProjectClassFilesIterator.size} | |""".stripMargin @@ -490,7 +493,7 @@ object Immutability { | shallow immutable types: ${shallowImmutableTypes.size} | dependent immutable types: ${dependentImmutableTypes.size} | deep immutable types: ${deepImmutableTypes.size} - | + | types: ${allProjectClassTypes.size} |""".stripMargin ) @@ -535,6 +538,8 @@ object Immutability { bw.write(s""" ${stringBuilderResults.toString()} | | ${stringBuilderAmounts.toString()} + | + | jdk folder: $JRELibraryFolder |"""".stripMargin) bw.close() } From 61462d3f17ed5d1dcfc3fd4a8de832a91eb9ffe4 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 10 Sep 2020 09:23:10 +0200 Subject: [PATCH 250/327] added more test cases --- .../PossibleExceptionInInitialization.java | 50 ++++++++++ .../GenericEscapesTransitive.java | 32 +++++++ .../fields/ArrayInitialization.java | 93 +++++++++++++++++++ .../fields/EffectivelyImmutableFields.java | 4 +- 4 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/PossibleExceptionInInitialization.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericEscapesTransitive.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayInitialization.java diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/PossibleExceptionInInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/PossibleExceptionInInitialization.java new file mode 100644 index 0000000000..ed5e38a5a2 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/PossibleExceptionInInitialization.java @@ -0,0 +1,50 @@ +package org.opalj.fpcf.fixtures.immutability.classes; + +import org.opalj.fpcf.properties.field_mutability.NonFinal; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +class PossibleExceptionInInitialization { + + @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Incorrect because lazy initialization is may not happen due to exception") + private int x; + + public int init(int i) { + int y = this.x; + if (y == 0) { + int z = 10 / i; + y = x = 5; + } + return y; + } +} + +class CaughtExceptionInInitialization { + + @MutableReferenceAnnotation("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; + } + } + //Test + //com.sun.corba.se.impl.io.FVDCodeBaseImp + //com.sun.xml.internal.bind.v2.model.impl.Messages + //com.sun.xml.internal.bind.v2.model.impl.Messages + //com.sun.jmx.defaults.JmxProperties + //com.sun.jmx.defaults + //com.sun.xml.internal.bind.v2.model.impl.ModelBuilder + //javax.management.loading.MLet + + //com.sun.corba.se.impl.io.FVDCodeBaseImpl + // +} \ No newline at end of file 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..831a7e1a82 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericEscapesTransitive.java @@ -0,0 +1,32 @@ +package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; + +import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; + + +@DeepImmutableTypeAnnotation("") +@DeepImmutableClassAnnotation("") +final class ClassWithGenericField_shallow { + @DeepImmutableFieldAnnotation("") + @ImmutableReferenceAnnotation("") + private SimpleGenericClass gc = + new SimpleGenericClass + (new SimpleMutableClass(), new FinalEmptyClass(), new FinalEmptyClass()); +} + +class FinalEmptyClass{} +class SimpleMutableClass{ public int n = 10;} + +class SimpleGenericClass { + A a; + B b; + C c; + SimpleGenericClass(A a, B b, C c){ + this.a = a; + this.b = b; + this.c = c; + } +} + 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..07b24418d2 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayInitialization.java @@ -0,0 +1,93 @@ +package org.opalj.fpcf.fixtures.immutability.fields; + +import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; +import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; +import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; + +public class ArrayInitialization { + @MutableReferenceAnnotation("") + private Object[] array; + + public Object[] getArray(int n) { + if (array == null || array.length < n) { + this.array = new Object[n]; + } + return array; + } + //@MutableReferenceAnnotation("") + private Object[] b; + public Object[] getB(boolean flag) throws Exception{ + if(b!=null) + return b; + else if(flag) + return null; //throw new Exception(""); + else { + this.b = new Object[5]; + return b; + } + + } +} +class SimpleLazyObjectsInstantiation{ + @LazyInitializedNotThreadSafeReferenceAnnotation("") + private SimpleLazyObjectsInstantiation instance; + public SimpleLazyObjectsInstantiation getInstance() { + if(instance==null) + instance = new SimpleLazyObjectsInstantiation(); + return instance; + } +} + + + + +class EscapingObjectDeep { + @DeepImmutableFieldAnnotation("") + private Object o; + public synchronized Object getO(){ + if(this.o==null) + this.o = new Object(); + return this.o; + } +} + +class EscapingObjectShallow { + @ShallowImmutableFieldAnnotation("") + private final Object o; + public EscapingObjectShallow() { + this.o = new EmptyClass(); + } + public EscapingObjectShallow(int n) { + this.o = new Object(); + } + public Object getO(){ + return this.o; + } +} + +class ClassUsingEmptyClass { + + @DeepImmutableFieldAnnotation("") + private EmptyClass emptyClass = new EmptyClass(); + + public EmptyClass getEmptyClass() { + return this.emptyClass; + } + +} +class ClassUsingEmptyClassExtensible { + + @ShallowImmutableFieldAnnotation("") + private EmptyClass emptyClass = new EmptyClass(); + + public ClassUsingEmptyClassExtensible(EmptyClass emptyClass) { + this.emptyClass = emptyClass; + } + +} + + + + +class EmptyClass {} \ No newline at end of file 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 index 8d72d075a9..2409f96fa3 100644 --- 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 @@ -334,7 +334,7 @@ public synchronized void initO2(){ o2 = new Object(); } - @ShallowImmutableFieldAnnotation("") + @DeepImmutableFieldAnnotation("The concrete type of the object that is assigned is known") @LazyInitializedThreadSafeReferenceAnnotation("") private Object o3; @@ -371,7 +371,7 @@ public synchronized void initLinkedList3(){ linkedList3.add(new Object()); } - @ShallowImmutableFieldAnnotation("") + @DeepImmutableFieldAnnotation("The concrete type of the object that is assigned is known") @LazyInitializedThreadSafeReferenceAnnotation("") private Object linkedList4; From acd0d7885764ce3b66237da3065a225ea69b86a3 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 09:21:31 +0200 Subject: [PATCH 251/327] undo formatting changes --- OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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 d1f6bdec5c..a0914aa47e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/Stmt.scala @@ -49,6 +49,7 @@ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { ): Stmt[DUVar[ValueInformation]] // TYPE CONVERSION METHODS + def asIf: If[V] = throw new ClassCastException(); def asGoto: Goto = throw new ClassCastException(); def asRet: Ret = throw new ClassCastException(); @@ -67,10 +68,8 @@ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { def asFieldWriteAccessStmt: FieldWriteAccessStmt[V] = throw new ClassCastException(); def asPutStatic: PutStatic[V] = throw new ClassCastException(); def asPutField: PutField[V] = throw new ClassCastException(); - /*inner type*/ - def asMethodCall: MethodCall[V] = throw new ClassCastException(); - /*inner type*/ - def asInstanceMethodCall: InstanceMethodCall[V] = throw new ClassCastException(); + /*inner type*/ def asMethodCall: MethodCall[V] = throw new ClassCastException(); + /*inner type*/ def asInstanceMethodCall: InstanceMethodCall[V] = throw new ClassCastException(); def asNonVirtualMethodCall: NonVirtualMethodCall[V] = throw new ClassCastException(); def asVirtualMethodCall: VirtualMethodCall[V] = throw new ClassCastException(); def asStaticMethodCall: StaticMethodCall[V] = throw new ClassCastException(); @@ -85,6 +84,7 @@ sealed abstract class Stmt[+V <: Var[V]] extends ASTNode[V] { def isNonVirtualMethodCall: Boolean = false def isVirtualMethodCall: Boolean = false def isStaticMethodCall: Boolean = false + def isIf: Boolean = false def isMonitorEnter: Boolean = false def isMonitorExit: Boolean = false @@ -117,7 +117,6 @@ 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 From 9e0491b8f41f675083b5e3eb020a9ee50bf67c50 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 09:29:10 +0200 Subject: [PATCH 252/327] Fix indentation --- OPAL/tac/src/main/resources/reference.conf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OPAL/tac/src/main/resources/reference.conf b/OPAL/tac/src/main/resources/reference.conf index ba7f4d0aa4..a24bbc0434 100644 --- a/OPAL/tac/src/main/resources/reference.conf +++ b/OPAL/tac/src/main/resources/reference.conf @@ -50,10 +50,10 @@ org.opalj { lazyFactory = "org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis" }, "L2PurityAnalysis_new" { - description = "Determines a method's purity.", - eagerFactory = "org.opalj.fpcf.analyses.EagerL2PurityAnalysis_new", - lazyFactory = "org.opalj.fpcf.analyses.LazyL2PurityAnalysis_new"t - } + description = "Determines a method's purity.", + eagerFactory = "org.opalj.fpcf.analyses.EagerL2PurityAnalysis_new", + lazyFactory = "org.opalj.fpcf.analyses.LazyL2PurityAnalysis_new" + } } }, analyses { From 3d7954be52e4ed8e2badd7094aaff3ebb12fd8e1 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 09:35:01 +0200 Subject: [PATCH 253/327] removed comment and fixed package declaration --- .../opalj/br/fpcf/properties/TypeImmutability_new.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala index 6f26603d01..84af958cb9 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -1,5 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.br.fpcf.properties +package org.opalj +package br +package fpcf +package properties import org.opalj.fpcf.OrderedProperty import org.opalj.fpcf.Entity @@ -44,9 +47,6 @@ sealed trait TypeImmutability_new def meet(other: TypeImmutability_new): TypeImmutability_new } -/** - * Common constants use by all [[TypeImmutability_new]] properties associated with methods. - */ object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new { /** From c3ec8f59e5f8e7a2fe0de224cc6ca86e130b3371 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 09:37:17 +0200 Subject: [PATCH 254/327] fixed grammar --- .../org/opalj/br/fpcf/properties/TypeImmutability_new.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala index 84af958cb9..1b59fbbbe5 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -15,7 +15,7 @@ sealed trait TypeImmutabilityPropertyMetaInformation_new extends PropertyMetaInf } /** - * 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 * 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., From 9176e15fe6e60ab02a10d471ae37865dfccdff66 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 10:11:23 +0200 Subject: [PATCH 255/327] undo changes --- .../src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala index e3b4d99ef9..9e72b84cf4 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala @@ -4,6 +4,7 @@ package br package fpcf import com.typesafe.config.Config + import org.opalj.log.LogContext import org.opalj.log.OPALLogger import org.opalj.concurrent.NumberOfThreadsForCPUBoundTasks @@ -11,7 +12,6 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.PropertyStoreContext import org.opalj.br.analyses.ProjectInformationKey import org.opalj.br.analyses.SomeProject -import org.opalj.fpcf.seq.PKESequentialPropertyStore /** * The ''key'' object to get the project's [[org.opalj.fpcf.PropertyStore]]. @@ -58,7 +58,7 @@ object PropertyStoreKey ) psFactory(context) case None ⇒ - val ps = PKESequentialPropertyStore(context: _*) + val ps = org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) //val ps = org.opalj.fpcf.par.PKECPropertyStore(context: _*) ps } From e02fe35b6b0766c98da121aef50fa2a17c3aa96a Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 11:03:59 +0200 Subject: [PATCH 256/327] fixed formatting and packages, optimized functions --- .../properties/ClassImmutability_new.scala | 50 +++---- .../fpcf/properties/FieldImmutability.scala | 42 +++--- .../FieldReferenceImmutability.scala | 128 ++++++++++++++++++ .../properties/TypeImmutability_new.scala | 12 +- 4 files changed, 179 insertions(+), 53 deletions(-) create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldReferenceImmutability.scala diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index ac9b92914a..df5ded8f8a 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -1,5 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.br.fpcf.properties +package org.opalj +package br +package fpcf +package properties import org.opalj.fpcf.Entity import org.opalj.fpcf.OrderedProperty @@ -11,17 +14,18 @@ sealed trait ClassImmutabilityPropertyMetaInformation_new extends PropertyMetaIn } /** - * Describes the class immutability of org.opalj.br.ClassFile + * Describes the class immutability of org.opalj.br.ClassFile. + * The immutability of the classes are represented via their instance fields * - * [[MutableClass]] A class with minimum 1 mutable field + * [[MutableClass]] A class with a mutable state. * - * [[DependentImmutableClass]] A class with no mutable field but with at least one generic field where the - * immutability depends on the generic type of the field + * [[ShallowImmutableClass]] A class which transitive state is not immutable but the values or objects representing + * this transitive state (are not / can not be) exchanged. * - * [[ShallowImmutableClass]] A class with no mutable field, no field with generic type but with at least one - * shallow immutable field + * [[DependentImmutableClass]] A class that is at least shallow immutable. + * Whether it is shallow or deep immutable depends on generic parameters. * - * [[DeepImmutableClass]] A class with only deep immutable fields + * [[DeepImmutableClass]] A class with a transitive immutable state. * * @author Tobias Peter Roth */ @@ -44,54 +48,53 @@ object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_ne } case object DeepImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: ClassImmutability_new): ClassImmutability_new = - if (this == that) - this - else - that + override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType + + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + + def meet(that: ClassImmutability_new): ClassImmutability_new = that } case object DependentImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = - DependentImmutableType //TODO check + override def correspondingTypeImmutability: TypeImmutability_new = DependentImmutableType def meet(that: ClassImmutability_new): ClassImmutability_new = if (that == MutableClass || that == ShallowImmutableClass) that else this - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { if (other == DeepImmutableClass) { throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } } - } case object ShallowImmutableClass extends ClassImmutability_new { + override def correspondingTypeImmutability: TypeImmutability_new = ShallowImmutableType - def meet(that: ClassImmutability_new): ClassImmutability_new = + + def meet(that: ClassImmutability_new): ClassImmutability_new = { if (that == MutableClass) that else this + } - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { if (other == DeepImmutableClass || other == DependentImmutableClass) { throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } } - } case object MutableClass extends ClassImmutability_new { + def correspondingTypeImmutability = MutableType_new + def meet(other: ClassImmutability_new): ClassImmutability_new = this override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { @@ -99,5 +102,4 @@ case object MutableClass extends ClassImmutability_new { throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } } - } 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 index 7ad675bdc0..c69e9633d0 100644 --- 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 @@ -1,5 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.br.fpcf.properties +package org.opalj +package br +package fpcf +package properties import org.opalj.fpcf.Entity import org.opalj.fpcf.OrderedProperty @@ -7,20 +10,21 @@ 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 an immutable reference + * Describes the field immutability of org.opalj.br.Field + * + * [[MutableField]] A field with a mutable field reference * - * [[ShallowImmutableField]] A field with an immutable reference and a shallow immutable or mutable data type + * [[ShallowImmutableField]] A field with an immutable field reference and a shallow immutable or mutable data type * - * [[DependentImmutableField]] A field which immutability depends on its 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 reference and a deep immutable field type + * [[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 Peter Roth */ @@ -44,39 +48,38 @@ object FieldImmutability extends FieldImmutabilityPropertyMetaInformation { } case object DeepImmutableField extends FieldImmutability { + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: FieldImmutability): FieldImmutability = - if (this == that) - this - else - that + + def meet(that: FieldImmutability): FieldImmutability = that } case object DependentImmutableField extends FieldImmutability { - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + 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 = + def meet(that: FieldImmutability): FieldImmutability = { if (that == MutableField || that == ShallowImmutableField) that else this - + } } case object ShallowImmutableField extends FieldImmutability { - def meet(that: FieldImmutability): FieldImmutability = + + def meet(that: FieldImmutability): FieldImmutability = { if (that == MutableField) that else this + } - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { if (other == DeepImmutableField || other == DependentImmutableField) { throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); } @@ -92,5 +95,4 @@ case object MutableField extends FieldImmutability { 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..fdf7c1d143 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/FieldReferenceImmutability.scala @@ -0,0 +1,128 @@ +/* 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): this.type = 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/TypeImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala index 1b59fbbbe5..5f510f69b9 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala @@ -24,9 +24,10 @@ sealed trait TypeImmutabilityPropertyMetaInformation_new extends PropertyMetaInf * * 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]]. + * [[ClassImmutability_new]]. * * @author Tobias Peter Roth + * @author Michael Eichberg */ sealed trait TypeImmutability_new extends OrderedProperty @@ -71,12 +72,7 @@ case object DeepImmutableType extends TypeImmutability_new { override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (this == that) - this - else - that - + def meet(that: TypeImmutability_new): TypeImmutability_new = that } case object DependentImmutableType extends TypeImmutability_new { @@ -92,12 +88,10 @@ case object DependentImmutableType extends TypeImmutability_new { this override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == DeepImmutableType) { throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } } - } case object ShallowImmutableType extends TypeImmutability_new { From 5fa974edb477d0d3833441857c1bcd30429eb36e Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 11:31:49 +0200 Subject: [PATCH 257/327] optimized functions, formatting and added comments --- .../classes}/ClassImmutabilityMatcher.scala | 41 ++++------- .../fields}/FieldImmutabilityMatcher.scala | 36 ++++------ .../FieldReferenceImmutabilityMatcher.scala | 59 ++++++++++++++++ .../MutableFieldReferenceMatcher.scala} | 19 ++--- .../types}/NewTypeImmutabilityMatcher.scala | 22 +++--- .../ReferenceImmutabilityMatcher.scala | 69 ------------------- 6 files changed, 106 insertions(+), 140 deletions(-) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/{class_immutability => immutability/classes}/ClassImmutabilityMatcher.scala (62%) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/{field_immutability => immutability/fields}/FieldImmutabilityMatcher.scala (62%) create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/references/FieldReferenceImmutabilityMatcher.scala rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/{reference_immutability/MutableReferenceMatcher.scala => immutability/references/MutableFieldReferenceMatcher.scala} (84%) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/{type_immutability => immutability/types}/NewTypeImmutabilityMatcher.scala (74%) delete mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/ClassImmutabilityMatcher.scala similarity index 62% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/ClassImmutabilityMatcher.scala index ac7646dfd2..707f7d8c96 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_immutability/ClassImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/ClassImmutabilityMatcher.scala @@ -1,23 +1,20 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.class_immutability +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_new -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.fpcf.Entity -import org.opalj.fpcf.Property -import org.opalj.fpcf.properties.AbstractPropertyMatcher /** + * This is the basis for the matchers that match a class immutability * @author Tobias Peter Roth */ -class ClassImmutabilityMatcher(val property: ClassImmutability_new) - extends AbstractPropertyMatcher { +class ClassImmutabilityMatcher(val property: ClassImmutability_new) extends AbstractPropertyMatcher { final private val PropertyReasonID = 0 @@ -31,10 +28,10 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) 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( @@ -44,31 +41,19 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) a: AnnotationLike, properties: Traversable[Property] ): Option[String] = { - if (!properties.exists(p ⇒ { - val tmpProperty = - p match { - case DeepImmutableClass ⇒ - DeepImmutableClass - case _ ⇒ - p - } - - property == tmpProperty - })) { //p == property)) { + 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(MutableClass) +class MutableClassMatcher extends ClassImmutabilityMatcher(br.fpcf.properties.MutableClass) -class DependentImmutableClassMatcher extends ClassImmutabilityMatcher(DependentImmutableClass) +class DependentImmutableClassMatcher extends ClassImmutabilityMatcher(br.fpcf.properties.DependentImmutableClass) -class ShallowImmutableClassMatcher extends ClassImmutabilityMatcher(ShallowImmutableClass) +class ShallowImmutableClassMatcher extends ClassImmutabilityMatcher(br.fpcf.properties.ShallowImmutableClass) -class DeepImmutableClassMatcher extends ClassImmutabilityMatcher(DeepImmutableClass) +class DeepImmutableClassMatcher extends ClassImmutabilityMatcher(br.fpcf.properties.DeepImmutableClass) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/fields/FieldImmutabilityMatcher.scala similarity index 62% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/fields/FieldImmutabilityMatcher.scala index 3afe3e8370..1e2af11528 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_immutability/FieldImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/fields/FieldImmutabilityMatcher.scala @@ -1,18 +1,17 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.field_immutability - -import org.opalj.br.{AnnotationLike, ObjectType} +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.DeepImmutableField -import org.opalj.br.fpcf.properties.DependentImmutableField import org.opalj.br.fpcf.properties.FieldImmutability -import org.opalj.br.fpcf.properties.MutableField -import org.opalj.br.fpcf.properties.ShallowImmutableField -import org.opalj.fpcf.Entity -import org.opalj.fpcf.Property -import org.opalj.fpcf.properties.AbstractPropertyMatcher /** + * 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 { @@ -42,26 +41,19 @@ class FieldImmutabilityMatcher(val property: FieldImmutability) extends Abstract a: AnnotationLike, properties: Traversable[Property] ): Option[String] = { - if (!properties.exists(p ⇒ { - p match { - case DependentImmutableField ⇒ property == DependentImmutableField //TODO optimize - case _ ⇒ p == property - - } - })) { //p == property)) { + 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(MutableField) +class MutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.MutableField) -class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(ShallowImmutableField) +class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.ShallowImmutableField) -class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(DependentImmutableField) +class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.DependentImmutableField) -class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(DeepImmutableField) +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..71d2acc9ce --- /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 Peter 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/reference_immutability/MutableReferenceMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/references/MutableFieldReferenceMatcher.scala similarity index 84% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/MutableReferenceMatcher.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/references/MutableFieldReferenceMatcher.scala index 18ccdeb426..e27852235b 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/MutableReferenceMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/references/MutableFieldReferenceMatcher.scala @@ -1,5 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.reference_immutability +package org.opalj +package fpcf +package properties +package immutability +package references import org.opalj.br.AnnotationLike import org.opalj.br.BooleanValue @@ -7,19 +11,15 @@ 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.MutableReference 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 mutable field references * @author Tobias Peter Roth */ -class MutableReferenceMatcher extends AbstractPropertyMatcher { +class MutableFieldReferenceMatcher extends AbstractPropertyMatcher { - val property = MutableReference + val property = br.fpcf.properties.MutableFieldReference final private val PropertyReasonID = 0 @@ -29,10 +29,12 @@ class MutableReferenceMatcher extends AbstractPropertyMatcher { 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; @@ -66,5 +68,4 @@ class MutableReferenceMatcher extends AbstractPropertyMatcher { None } } - } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/NewTypeImmutabilityMatcher.scala similarity index 74% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/NewTypeImmutabilityMatcher.scala index 2611624620..940b8f4716 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/type_immutability/NewTypeImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/NewTypeImmutabilityMatcher.scala @@ -1,19 +1,17 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.type_immutability +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.DeepImmutableType -import org.opalj.br.fpcf.properties.DependentImmutableType -import org.opalj.br.fpcf.properties.MutableType_new -import org.opalj.br.fpcf.properties.ShallowImmutableType import org.opalj.br.fpcf.properties.TypeImmutability_new -import org.opalj.fpcf.Entity -import org.opalj.fpcf.Property -import org.opalj.fpcf.properties.AbstractPropertyMatcher /** + * This is the basis for the matchers that match the immutability of a type * @author Tobias Peter Roth */ class NewTypeImmutabilityMatcher(val property: TypeImmutability_new) @@ -51,10 +49,10 @@ class NewTypeImmutabilityMatcher(val property: TypeImmutability_new) } } } -class NewMutableTypeMatcher extends NewTypeImmutabilityMatcher(MutableType_new) +class NewMutableTypeMatcher extends NewTypeImmutabilityMatcher(br.fpcf.properties.MutableType_new) -class ShallowImmutableTypeMatcher extends NewTypeImmutabilityMatcher(ShallowImmutableType) +class ShallowImmutableTypeMatcher extends NewTypeImmutabilityMatcher(br.fpcf.properties.ShallowImmutableType) -class DependentImmutableTypeMatcher extends NewTypeImmutabilityMatcher(DependentImmutableType) +class DependentImmutableTypeMatcher extends NewTypeImmutabilityMatcher(br.fpcf.properties.DependentImmutableType) -class DeepImmutableTypeMatcher extends NewTypeImmutabilityMatcher(DeepImmutableType) +class DeepImmutableTypeMatcher extends NewTypeImmutabilityMatcher(br.fpcf.properties.DeepImmutableType) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala deleted file mode 100644 index 6cda7addb6..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/reference_immutability/ReferenceImmutabilityMatcher.scala +++ /dev/null @@ -1,69 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.reference_immutability - -import org.opalj.br.AnnotationLike -import org.opalj.br.ObjectType -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties.ImmutableReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference -import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference -import org.opalj.br.fpcf.properties.ReferenceImmutability -import org.opalj.fpcf.Entity -import org.opalj.fpcf.Property -import org.opalj.fpcf.properties.AbstractPropertyMatcher - -/** - * @author Tobias Peter Roth - */ -class ReferenceImmutabilityMatcher(val property: ReferenceImmutability) - 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 ⇒ { - val tmpP = { - p match { - //case ImmutableReference(_) ⇒ ImmutableReference(true) - case _ ⇒ p - } - } - tmpP == property - })) { - // ... when we reach this point the expected property was not found. - Some(a.elementValuePairs(PropertyReasonID).value.asStringValue.value) - } else { - None - } - } -} - -class LazyInitializedThreadSafeReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedThreadSafeReference) - -class LazyInitializedNotThreadSafeButDeterministicReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedNotThreadSafeButDeterministicReference) - -class LazyInitializedNotThreadSafeReferenceMatcher extends ReferenceImmutabilityMatcher(LazyInitializedNotThreadSafeReference) - -class ImmutableReferenceMatcher extends ReferenceImmutabilityMatcher(ImmutableReference) From 4acc306874325d1229adaa08120e38165ebfb3f1 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 12:00:53 +0200 Subject: [PATCH 258/327] changed license --- .../field_mutability/DeclaredFinalFields.java | 29 +------------------ 1 file changed, 1 insertion(+), 28 deletions(-) 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..087081d919 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,31 +1,4 @@ -/* 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; From 49101b19a836d125f90a73382cab8fdc54b4afc1 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 13:51:12 +0200 Subject: [PATCH 259/327] formatting --- .../opalj/br/fpcf/properties/ClassImmutability_new.scala | 8 ++++---- .../org/opalj/br/fpcf/properties/FieldImmutability.scala | 2 +- .../br/fpcf/properties/FieldReferenceImmutability.scala | 6 ++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala index df5ded8f8a..8f1887d644 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala @@ -49,11 +49,11 @@ object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_ne case object DeepImmutableClass extends ClassImmutability_new { - override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType + override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: ClassImmutability_new): ClassImmutability_new = that + def meet(that: ClassImmutability_new): ClassImmutability_new = that } case object DependentImmutableClass extends ClassImmutability_new { @@ -84,7 +84,7 @@ case object ShallowImmutableClass extends ClassImmutability_new { this } - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { if (other == DeepImmutableClass || other == DependentImmutableClass) { throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } 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 index c69e9633d0..1455e2e50a 100644 --- 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 @@ -79,7 +79,7 @@ case object ShallowImmutableField extends FieldImmutability { this } - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { + override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { if (other == DeepImmutableField || other == DependentImmutableField) { 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 index fdf7c1d143..0cb03ea050 100644 --- 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 @@ -69,11 +69,9 @@ case object LazyInitializedThreadSafeFieldReference extends FieldReferenceImmuta } def meet(other: FieldReferenceImmutability): FieldReferenceImmutability = - if ( - other == MutableFieldReference || + if (other == MutableFieldReference || other == LazyInitializedNotThreadSafeFieldReference || - other == LazyInitializedNotThreadSafeButDeterministicFieldReference - ) { + other == LazyInitializedNotThreadSafeButDeterministicFieldReference) { other } else { this From 742dbbc0e50aa57016a3f1891d3d4a768dbe7f4d Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 13:54:06 +0200 Subject: [PATCH 260/327] revised, still wip --- .../ClassImmutabilityAnalysisDemo.scala | 140 ++++++++---------- .../FieldImmutabilityAnalysisDemo.scala | 139 ++++++++--------- ...eldReferenceImmutabilityAnalysisDemo.scala | 0 .../TypeImmutabilityAnalysisDemo.scala | 119 +++++++-------- 4 files changed, 179 insertions(+), 219 deletions(-) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/FieldReferenceImmutabilityAnalysisDemo.scala 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 index 33aba994bb..dac8016fa7 100644 --- 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 @@ -1,12 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.analyses +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_new import org.opalj.br.analyses.BasicReport @@ -22,15 +23,15 @@ import org.opalj.br.fpcf.properties.MutableClass import org.opalj.br.fpcf.properties.ShallowImmutableClass import org.opalj.fpcf.PropertyStore 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.EagerLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis +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_new /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -53,6 +54,7 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { + var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None val analysesManager = project.get(FPCFAnalysesManagerKey) @@ -64,15 +66,15 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .runAll( LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, LazyLxTypeImmutabilityAnalysis_new, EagerLxClassImmutabilityAnalysis_new, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis + LazyInterProceduralEscapeAnalysis //, + //LazyReturnValueFreshnessAnalysis, + //LazyFieldLocalityAnalysis ) ._1 propertyStore.waitOnPhaseCompletion(); @@ -81,84 +83,66 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet - val sb = new StringBuilder - sb.append("Mutable Class: \n") - val mutableClasses = propertyStore - .finalEntities(MutableClass) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - .toList - sb.append( - mutableClasses - .map(x ⇒ x.toString+" |Mutable Class\n") - ) - sb.append("\nShallow Immutable Class:\n") - val shallowImmutableClasses = propertyStore - .finalEntities(ShallowImmutableClass) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - .toList - sb.append( - shallowImmutableClasses - .map(x ⇒ x.toString+" |Shallow Immutable Class\n") - ) - sb.append("\nDependent Immutable Class: \n") - val dependentImmutableClasses = propertyStore - .finalEntities(DependentImmutableClass) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - .toList - sb.append( - dependentImmutableClasses - .map(x ⇒ x.toString+" |Dependent Immutable Class\n") - ) + val groupedResults = propertyStore.entities(ClassImmutability_new.key). + filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) - sb.append("\nDeep Immutable Class Classes:\n") + val order = (eps1: EPS[Entity, ClassImmutability_new], eps2: EPS[Entity, ClassImmutability_new]) ⇒ + 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) val allInterfaces = project.allProjectClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet - - val deepImmutables = propertyStore - .finalEntities(DeepImmutableClass) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - .toList val deepImmutableClassesInterfaces = deepImmutables - .filter(x ⇒ x.isInstanceOf[ObjectType] && allInterfaces.contains(x.asInstanceOf[ObjectType])) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) + .filter(eps ⇒ allInterfaces.contains(eps.asInstanceOf[ObjectType])).toSeq.sortWith(order) val deepImmutableClasses = deepImmutables - .filter(!deepImmutableClassesInterfaces.toSet.contains(_)) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - sb.append( - deepImmutableClasses.toList - .map(x ⇒ x.toString+" |Deep Immutable Class\n") - ) - sb.append("\nDeep Immutable Class Classes: Interface\n") + .filter(eps ⇒ !allInterfaces.contains(eps.asInstanceOf[ObjectType])).toSeq.sortWith(order) - sb.append( - deepImmutableClassesInterfaces - .map(x ⇒ x.toString+" |Deep Immutable Class Interface\n") - ) - sb.append(s""" - | 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} - | - | took : $analysisTime seconds - |"""".stripMargin) + val output = + s""" + | Mutable Classes: + | ${mutableClasses.mkString(" |Mutable Class")} + | + | 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} + | + | took : $analysisTime seconds + |"""".stripMargin - val calendar = Calendar.getInstance() val file = new File( - s"C:/MA/results/classImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" + s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt" ) - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() - - s""" - | took : $analysisTime seconds - |""".stripMargin + try { + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(output) + bw.close() + } catch { + case e: IOException ⇒ + println(s"Could not write the file: ${file.getName}"); throw e + case _: Throwable ⇒ + } + s" took : $analysisTime seconds" } } 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 index fed49703af..c89cc88300 100644 --- 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 @@ -1,5 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.analyses +package org.opalj +package fpcf +package analyses import java.net.URL @@ -15,7 +17,6 @@ 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.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis @@ -23,10 +24,18 @@ import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds +import org.opalj.br.fpcf.properties.FieldImmutability + +import java.io.BufferedWriter +import java.io.File +import java.io.FileWriter +import java.util.Calendar +import org.opalj.bytecode.JRELibraryFolder +import java.io.IOException /** * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. @@ -50,7 +59,7 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { - import org.opalj.bytecode.JRELibraryFolder + var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None val analysesManager = project.get(FPCFAnalysesManagerKey) @@ -58,7 +67,7 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { time { propertyStore = analysesManager .runAll( - LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, EagerL0FieldImmutabilityAnalysis, @@ -75,78 +84,60 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } { t ⇒ analysisTime = t.toSeconds } - val sb: StringBuilder = new StringBuilder - sb.append("Mutable Fields: \n") - val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields } - //.filter(f ⇒ (!f.isTransient && !f.isSyn)) // for ReImComparison - .toSet - val mutableFields = propertyStore - .finalEntities(MutableField) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - - sb.append( - mutableFields - .map(x ⇒ x.toString+" |Mutable Field\n") - .toString() - ) - sb.append("\nShallow Immutable Fields: \n") - val shallowImmutableFields = propertyStore - .finalEntities(ShallowImmutableField) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - sb.append( - shallowImmutableFields - .map(x ⇒ x.toString+" |Shallow Immutable Field\n") - .toString() - ) - sb.append("\nDependet Immutable Fields: \n") - val dependentImmutableFields = propertyStore - .finalEntities(DependentImmutableField) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - sb.append( - dependentImmutableFields - .map(x ⇒ x.toString+" |Dependent Immutable Field\n") - .toString() - ) - sb.append("Deep Immutable Fields: ") - val deepImmutableFields = propertyStore - .finalEntities(DeepImmutableField) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - sb.append( - deepImmutableFields - .map(x ⇒ x.toString+" |Deep Immutable Field\n") - .toString - ) - sb.append("\n\n") - sb.append( - s""" mutable fields: ${mutableFields.size} - | shallow immutable fields: ${shallowImmutableFields.size} - | dependent immutable fields: ${dependentImmutableFields.size} - | deep immutable fields: ${deepImmutableFields.size} - | - | count: ${mutableFields.size + shallowImmutableFields.size + dependentImmutableFields.size + deepImmutableFields.size} - | - | took : $analysisTime seconds - |""".stripMargin - ) - /*val calendar = Calendar.getInstance() + + val allFieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet + + val groupedResults = propertyStore.entities(FieldImmutability.key). + filter(field ⇒ allFieldsInProjectClassFiles.contains(field.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(MutableField).toSeq.sortWith(order) + val shallowImmutableFields = groupedResults(ShallowImmutableField).toSeq.sortWith(order) + val dependentImmutableFields = groupedResults(DependentImmutableField).toSeq.sortWith(order) + val deepImmutableFields = groupedResults(DeepImmutableField).toSeq.sortWith(order) + + val output = + s""" + | 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")} + | + | + | mutable fields: ${mutableFields.size} + | shallow immutable fields: ${shallowImmutableFields.size} + | dependent immutable fields: ${dependentImmutableFields.size} + | deep immutable fields: ${deepImmutableFields.size} + | + | count: ${mutableFields.size + shallowImmutableFields.size + dependentImmutableFields.size + deepImmutableFields.size} + | + | took : $analysisTime seconds + |""".stripMargin + val file = new File( - s"C:/MA/results/fieldImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" + s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt" ) - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() */ - println("xxx: "+JRELibraryFolder) - s""" - | took : $analysisTime seconds - |""".stripMargin + try { + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(output) + bw.close() + } catch { + case e: IOException ⇒ println(s"could not write file ${file.getName}"); throw e; + case _: Throwable ⇒ + } + println("JRELibraryFolder: "+JRELibraryFolder) + + s"took : $analysisTime seconds" } } 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..e69de29bb2 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 index 7e9cb1d5d5..94723eeb99 100644 --- 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 @@ -29,8 +29,9 @@ import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +import java.io.IOException /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -53,6 +54,9 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { + import org.opalj.br.fpcf.properties.TypeImmutability_new + import org.opalj.fpcf.EPS + import org.opalj.fpcf.Entity var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None val analysesManager = project.get(FPCFAnalysesManagerKey) @@ -63,7 +67,7 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .runAll( LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, LazyLxClassImmutabilityAnalysis_new, EagerLxTypeImmutabilityAnalysis_new, @@ -78,76 +82,57 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } { t ⇒ analysisTime = t.toSeconds } - val sb: StringBuilder = new StringBuilder - val allProjectClassFilesIterator = project.allProjectClassFiles - val types = - allProjectClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType).toSet - sb.append("\nMutableTypes: \n") - val mutableTypes = propertyStore - .finalEntities(MutableType_new) - .filter({ x ⇒ - types.contains(x.asInstanceOf[ObjectType]) - }) - .toList - sb.append( - mutableTypes - .map(x ⇒ x.toString+" |Mutable Type\n") - .toString - ) - sb.append("\nShallow Immutable Types:\n") - val shallowImmutableTypes = propertyStore - .finalEntities(ShallowImmutableType) - .filter({ x ⇒ - types.contains(x.asInstanceOf[ObjectType]) - }) - .toList - sb.append(shallowImmutableTypes.map(x ⇒ x.toString+" |Shallow Immutable Type\n")) - sb.append("\nDependent Immutable Types: \n") - val dependentImmutableTypes = propertyStore - .finalEntities(DependentImmutableType) - .filter({ x ⇒ - types.contains(x.asInstanceOf[ObjectType]) - }) - .toList - sb.append( - dependentImmutableTypes.map(x ⇒ x.toString+" |Dependent Immutable Type\n") - ) - sb.append("\nDeep Immutable Types:\n") - val deepImmutableTypes = propertyStore - .finalEntities(DeepImmutableType) - .filter({ x ⇒ - types.contains(x.asInstanceOf[ObjectType]) - }) - .toList - sb.append(deepImmutableTypes.map(x ⇒ x.toString+" |Deep Immutable Type\n")) - sb.append(s"\nType immutability analysis took: $analysisTime on average") - sb.append("\n\n") - sb.append( + val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet + + val groupedResults = propertyStore.entities(TypeImmutability_new.key). + filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) + + val order = (eps1: EPS[Entity, TypeImmutability_new], eps2: EPS[Entity, TypeImmutability_new]) ⇒ + eps1.e.toString < eps2.e.toString + val mutableTypes = groupedResults(MutableType_new).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.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 - ) + | 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 calendar = Calendar.getInstance() val file = new File( - s"C:/MA/results/typeImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" + s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt" ) - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() - s""" - | took : $analysisTime seconds - |""".stripMargin + try { + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(output) + bw.close() + } catch { + case e: IOException ⇒ + println(s"could not write file: ${file.getName}"); throw e + case _: Throwable ⇒ + } + + s" took : $analysisTime seconds" } } From ce575ff4d400e4ac3e2ac89ef1de4621177d5d19 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 13:55:41 +0200 Subject: [PATCH 261/327] revised, still wip --- .../org/opalj/support/info/Immutability.scala | 520 +++++++++--------- 1 file changed, 271 insertions(+), 249 deletions(-) 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 index 0ef9e8fb46..57f2bda4f7 100644 --- 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 @@ -1,5 +1,19 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.support.info +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 @@ -11,7 +25,7 @@ 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.reference.EagerL0ReferenceImmutabilityAnalysis +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 @@ -19,12 +33,12 @@ import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new 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.ImmutableReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference -import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference +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.MutableReference +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 @@ -44,24 +58,23 @@ import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis -import java.nio.file.Path +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis import org.opalj.br.analyses.Project import org.opalj.log.DevNullLogger import org.opalj.log.GlobalLogContext import org.opalj.log.OPALLogger -import java.io.File -import java.io.BufferedWriter -import java.io.FileWriter -import java.util.Calendar import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.PropertyStoreContext -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import org.opalj.support.info.RunningAnalyses.RunningAnalysis import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater -import java.nio.file.FileSystems +import org.opalj.ai.domain +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.ai.fpcf.properties.AIDomainFactoryKey + /** * Determines the immutability of references, fields, classes and types of a project * @@ -71,8 +84,14 @@ object RunningAnalyses extends Enumeration { type RunningAnalysis = Value val References, Fields, Classes, Types, All = Value } +import RunningAnalyses.References +import RunningAnalyses.Fields +import RunningAnalyses.Classes +import RunningAnalyses.Types +import RunningAnalyses.All object Immutability { + def evaluate( cp: File, analysis: RunningAnalysis, @@ -84,9 +103,16 @@ object Immutability { threadEvaluation: Boolean, withoutJDK: Boolean, isLibrary: Boolean, - closedWorldAssumption: Boolean + closedWorldAssumption: Boolean, + callGraphKey: AbstractCallGraphKey, + level: Int, + reImInferComparison: Boolean ): BasicReport = { - import org.opalj.ai.fpcf.properties.AIDomainFactoryKey + import org.opalj.br.fpcf.properties.ClassImmutability_new + import org.opalj.br.fpcf.properties.FieldImmutability + import org.opalj.br.fpcf.properties.FieldReferenceImmutability + import org.opalj.br.fpcf.properties.TypeImmutability_new + import org.opalj.fpcf.EPS OPALLogger.updateLogger(GlobalLogContext, DevNullLogger) @@ -111,7 +137,6 @@ object Immutability { // TODO: in case of application this value is already set if (closedWorldAssumption) { - import com.typesafe.config.ConfigValueFactory config = config.withValue( "org.opalj.br.analyses.cg.ClassExtensibilityKey.analysis", ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.ClassHierarchyIsNotExtensible") @@ -130,9 +155,8 @@ object Immutability { Traversable.empty ) } { t ⇒ projectTime = t.toSeconds } - val referenceDependencies: List[FPCFAnalysisScheduler] = List( - EagerL0ReferenceImmutabilityAnalysis, + EagerL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, LazyInterProceduralEscapeAnalysis, @@ -142,7 +166,7 @@ object Immutability { LazyFieldLocalityAnalysis ) val fieldDependencies: List[FPCFAnalysisScheduler] = List( - LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, EagerL0FieldImmutabilityAnalysis, @@ -157,7 +181,7 @@ object Immutability { val classDepencencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, LazyLxTypeImmutabilityAnalysis_new, EagerLxClassImmutabilityAnalysis_new, @@ -170,7 +194,7 @@ object Immutability { val typeDependencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, LazyLxClassImmutabilityAnalysis_new, EagerLxTypeImmutabilityAnalysis_new, @@ -184,7 +208,7 @@ object Immutability { List( LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, - EagerL0ReferenceImmutabilityAnalysis, + EagerL0FieldReferenceImmutabilityAnalysis, EagerL0FieldImmutabilityAnalysis, EagerLxClassImmutabilityAnalysis_new, EagerLxTypeImmutabilityAnalysis_new, @@ -194,41 +218,37 @@ object Immutability { LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis ) - var dependencies: List[FPCFAnalysisScheduler] = List.empty - if (analysis == RunningAnalyses.References) - dependencies = referenceDependencies - if (analysis == RunningAnalyses.Fields) - dependencies = fieldDependencies - if (analysis == RunningAnalyses.Classes) - dependencies = classDepencencies - if (analysis == RunningAnalyses.Types) - dependencies = typeDependencies - if (analysis == RunningAnalyses.All) - dependencies = allImmAnalysisDependencies + val dependencies = analysis match { + case References ⇒ referenceDependencies + case Fields ⇒ fieldDependencies + case Classes ⇒ classDepencencies + case Types ⇒ typeDependencies + case All ⇒ allImmAnalysisDependencies + } L2PurityAnalysis_new.setRater(Some(SystemOutLoggingAllExceptionRater)) var propertyStore: PropertyStore = null val analysesManager = project.get(FPCFAnalysesManagerKey) - + println(s"callgraph $callGraphKey") time { - analysesManager.project.get(RTACallGraphKey) + analysesManager.project.get(callGraphKey) } { t ⇒ callGraphTime = t.toSeconds } - + println(s"level: $level") analysesManager.project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ - import java.net.URL - - import org.opalj.ai.domain - //Set[Class[_ <: AnyRef]](classOf[domain.l0.BaseDomainWithDefUse[URL]]) - Set[Class[_ <: AnyRef]](classOf[domain.l1.DefaultDomainWithCFGAndDefUse[URL]]) + 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("wrong level") + //Set[Class[_ <: AnyRef]](classOf[domain.l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) } - //val propertyStore = time { project.get(PropertyStoreKey) } { t ⇒ propertyStoreTime = t.toSeconds } + //var propertyStore = time { project.get(PropertyStoreKey) } { t ⇒ propertyStoreTime = t.toSeconds } project.getOrCreateProjectInformationKeyInitializationData( PropertyStoreKey, (context: List[PropertyStoreContext[AnyRef]]) ⇒ { - import org.opalj.log.LogContext + implicit val lg: LogContext = project.logContext if (numThreads == 0) { org.opalj.fpcf.seq.PKESequentialPropertyStore(context: _*) @@ -254,196 +274,157 @@ object Immutability { val stringBuilderResults: StringBuilder = new StringBuilder() - val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet + val allFieldsInProjectClassFiles = { + if (reImInferComparison) + project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet + else + project.allProjectClassFiles.toIterator.flatMap { _.fields }. + filter(f ⇒ (!f.isTransient && !f.isSynthetic)).toSet + } - //References - val mutableReferences = propertyStore - .finalEntities(MutableReference) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) - - val lazyInitializedReferencesThreadSafe = propertyStore - .finalEntities(LazyInitializedThreadSafeReference) - .toList - .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) - - val lazyInitializedReferencesNotThreadSafeButDeterministic = propertyStore - .finalEntities(LazyInitializedNotThreadSafeButDeterministicReference) - .toList - .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) - - val notThreadSafeLazyInitialization = propertyStore - .finalEntities(LazyInitializedNotThreadSafeReference) - .toList - .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) - val immutableReferences = propertyStore - .entities( - eps ⇒ //allfieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field]) && - eps.isFinal && (eps.asFinal.p match { - case ImmutableReference ⇒ true - case _ ⇒ false - }) - ) - .toList - .sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) + val fieldReferenceGroupedResults = propertyStore.entities(FieldReferenceImmutability.key). + filter(field ⇒ allFieldsInProjectClassFiles.contains(field.asInstanceOf[Field])). + toTraversable.groupBy(_.toFinalELBP.p) + + val fieldReferenceOrder = (eps1: EPS[Entity, FieldReferenceImmutability], eps2: EPS[Entity, FieldReferenceImmutability]) ⇒ + eps1.e.toString < eps2.e.toString + val mutableFieldReferences = fieldReferenceGroupedResults(MutableFieldReference).toSeq.sortWith(fieldReferenceOrder) + val notThreadSafeLazyInitializedFieldReferences = + fieldReferenceGroupedResults(LazyInitializedNotThreadSafeFieldReference).toSeq.sortWith(fieldReferenceOrder) + val lazyInitializedReferencesNotThreadSafeButDeterministic = + fieldReferenceGroupedResults(LazyInitializedNotThreadSafeButDeterministicFieldReference).toSeq.sortWith(fieldReferenceOrder) + val threadSafeLazyInitializedFieldReferences = + fieldReferenceGroupedResults(LazyInitializedThreadSafeFieldReference).toSeq.sortWith(fieldReferenceOrder) + val immutableReferences = fieldReferenceGroupedResults(ImmutableFieldReference).toSeq.sortWith(fieldReferenceOrder) //Test.... //mutableReferences = mutableReferences ++ notThreadSafeLazyInitialization // for comparison reasons if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.References) { stringBuilderResults.append(s""" -| mutable References: -| ${mutableReferences.mkString("|| mutable Reference \n")} -| -| lazy initalized not thread safe and not deterministic references: -| ${notThreadSafeLazyInitialization.sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString).mkString("|| no ts & n dt ref\n")} -| -| lazy initialized not thread safe but deterministic references: -| ${lazyInitializedReferencesNotThreadSafeButDeterministic.mkString("|| li no ts b dt\n")} -| -| lazy initialized thread safe reference: -| ${lazyInitializedReferencesThreadSafe.mkString("|| li thread safe\n")} -| -| immutable Reference: -| ${immutableReferences.mkString("|| immutable References \n")} -| -|""".stripMargin) + | Mutable References: + | ${mutableFieldReferences.mkString(" || Mutable Reference \n")} + | + | Lazy Initalized Not Thread Safe And Not Deterministic References: + | ${ + notThreadSafeLazyInitializedFieldReferences. + mkString(" || Lazy Initalized Not Thread Safe And Not Deterministic Reference\n") + } + | + | Lazy Initialized Not Thread Safe But Deterministic References: + | ${ + lazyInitializedReferencesNotThreadSafeButDeterministic. + mkString(" || Lazy Initialized Not Thread Safe But Deterministic Reference\n") + } + | + | Lazy Initialized Thread Safe References: + | ${ + threadSafeLazyInitializedFieldReferences. + mkString(" || Lazy Initialized Thread Safe Reference\n") + } + | + | Immutable References: + | ${immutableReferences.mkString(" || immutable Reference\n")} + | + |""".stripMargin) } - //Fields - val mutableFields = propertyStore - .finalEntities(MutableField) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList.sortWith((e1: Entity, e2: Entity) ⇒ e1.toString < e2.toString) - val shallowImmutableFields = propertyStore - .finalEntities(ShallowImmutableField) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - - val dependentImmutableFields = propertyStore - .finalEntities(DependentImmutableField) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - - val deepImmutableFields = propertyStore - .finalEntities(DeepImmutableField) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList + val fieldGroupedResults = propertyStore.entities(FieldImmutability.key). + filter(field ⇒ allFieldsInProjectClassFiles.contains(field.asInstanceOf[Field])). + toTraversable.groupBy(_.toFinalELBP.p) + + val fieldOrder = (eps1: EPS[Entity, FieldImmutability], eps2: EPS[Entity, FieldImmutability]) ⇒ + eps1.e.toString < eps2.e.toString + val mutableFields = fieldGroupedResults(MutableField).toSeq.sortWith(fieldOrder) + val shallowImmutableFields = fieldGroupedResults(ShallowImmutableField).toSeq.sortWith(fieldOrder) + val dependentImmutableFields = fieldGroupedResults(DependentImmutableField).toSeq.sortWith(fieldOrder) + val deepImmutableFields = fieldGroupedResults(DeepImmutableField).toSeq.sortWith(fieldOrder) + if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.Fields) { stringBuilderResults.append( s""" - | mutable fields: - | ${mutableFields.mkString(" || mutable Field \n")} + | Mutable Fields: + | ${mutableFields.mkString(" || Mutable Field \n")} | - | shallow immutable fields: - | ${shallowImmutableFields.mkString(" || shallow immutable field \n")} + | Shallow Immutable Fields: + | ${shallowImmutableFields.mkString(" || Shallow Immutable Field \n")} | - | dependent immutable fields: - | ${dependentImmutableFields.mkString(" || dependent immutable field \n")} + | Dependent Immutable Fields: + | ${dependentImmutableFields.mkString(" || Dependent Immutable Field \n")} | - | deep immutable fields: - | ${deepImmutableFields.mkString(" || deep immutable field \n")} + | Deep Immutable Fields: + | ${deepImmutableFields.mkString(" || Deep Immutable Field \n")} | |""".stripMargin ) - } - //Classes - val mutableClasses = propertyStore - .finalEntities(MutableClass) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - .toList - val shallowImmutableClasses = propertyStore - .finalEntities(ShallowImmutableClass) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - .toList - - val dependentImmutableClasses = propertyStore - .finalEntities(DependentImmutableClass) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - .toList + val classGroupedResults = propertyStore.entities(ClassImmutability_new.key). + filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) + + val order = (eps1: EPS[Entity, ClassImmutability_new], eps2: EPS[Entity, ClassImmutability_new]) ⇒ + eps1.e.toString < eps2.e.toString + val mutableClasses = + classGroupedResults(MutableClass).toSeq.sortWith(order) + val shallowImmutableClasses = + classGroupedResults(ShallowImmutableClass).toSeq.sortWith(order) + val dependentImmutableClasses = + classGroupedResults(DependentImmutableClass).toSeq.sortWith(order) + val deepImmutables = classGroupedResults(DeepImmutableClass) val allInterfaces = project.allProjectClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet - - val deepImmutables = propertyStore - .finalEntities(DeepImmutableClass) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) - .toList val deepImmutableClassesInterfaces = deepImmutables - .filter(x ⇒ x.isInstanceOf[ObjectType] && allInterfaces.contains(x.asInstanceOf[ObjectType])) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) + .filter(eps ⇒ allInterfaces.contains(eps.asInstanceOf[ObjectType])).toSeq.sortWith(order) val deepImmutableClasses = deepImmutables - .filter(!deepImmutableClassesInterfaces.toSet.contains(_)) - .filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])) + .filter(eps ⇒ !allInterfaces.contains(eps.asInstanceOf[ObjectType])).toSeq.sortWith(order) if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.Classes) { stringBuilderResults.append( s""" - | mutable classes: - | ${mutableClasses.mkString(" || mutable class \n")} + | Mutable Classes: + | ${mutableClasses.mkString(" || Mutable Class \n")} | - | shallow immutable classes: - | ${shallowImmutableClasses.mkString(" || shallow immutable classes \n")} + | Shallow Immutable Classes: + | ${shallowImmutableClasses.mkString(" || Shallow Immutable Class \n")} | - | dependent immutable classes: - | ${dependentImmutableClasses.mkString(" || dependent immutable classes \n")} + | Dependent Immutable Classes: + | ${dependentImmutableClasses.mkString(" || Dependent Immutable Class \n")} | - | deep immutable classes: - | ${deepImmutableClasses.mkString(" || deep immutable classes \n")} + | Deep Immutable Classes: + | ${deepImmutableClasses.mkString(" || Deep Immutable Classes \n")} | - | deep immutable interfaces: - | ${deepImmutableClassesInterfaces.mkString(" || deep immutable interfaces \n")} + | Deep Immutable Interfaces: + | ${deepImmutableClassesInterfaces.mkString(" || Deep Immutable Interfaces \n")} |""".stripMargin ) } - //Types - val allProjectClassFilesIterator = project.allProjectClassFiles - val types = - allProjectClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType).toSet - val mutableTypes = propertyStore - .finalEntities(MutableType_new) - .filter({ x ⇒ - types.contains(x.asInstanceOf[ObjectType]) - }) - .toList - - val shallowImmutableTypes = propertyStore - .finalEntities(ShallowImmutableType) - .filter({ x ⇒ - types.contains(x.asInstanceOf[ObjectType]) - }) - .toList - val dependentImmutableTypes = propertyStore - .finalEntities(DependentImmutableType) - .filter({ x ⇒ - types.contains(x.asInstanceOf[ObjectType]) - }) - .toList - val deepImmutableTypes = propertyStore - .finalEntities(DeepImmutableType) - .filter({ x ⇒ - types.contains(x.asInstanceOf[ObjectType]) - }) - .toList + val typeGroupedResults = propertyStore.entities(TypeImmutability_new.key). + filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) + + val typeOrder = (eps1: EPS[Entity, TypeImmutability_new], eps2: EPS[Entity, TypeImmutability_new]) ⇒ + eps1.e.toString < eps2.e.toString + val mutableTypes = typeGroupedResults(MutableType_new).toSeq.sortWith(typeOrder) + val shallowImmutableTypes = typeGroupedResults(ShallowImmutableType).toSeq.sortWith(typeOrder) + val dependentImmutableTypes = typeGroupedResults(DependentImmutableType).toSeq.sortWith(typeOrder) + val deepImmutableTypes = typeGroupedResults(DeepImmutableType).toSeq.sortWith(typeOrder) + if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.Types) { stringBuilderResults.append( s""" - | mutable types: - | ${mutableTypes.mkString(" || mutable types \n")} + | Mutable Types: + | ${mutableTypes.mkString(" || Mutable Type \n")} | - | shallow immutable types: - | ${shallowImmutableTypes.mkString(" || shallow immutable types \n")} + | Shallow Immutable Types: + | ${shallowImmutableTypes.mkString(" || Shallow Immutable Types \n")} | - | dependent immutable types: - | ${dependentImmutableTypes.mkString(" || dependent immutable types \n")} + | Dependent Immutable Types: + | ${dependentImmutableTypes.mkString(" || Dependent Immutable Types \n")} | - | deep immutable types: - | ${deepImmutableTypes.mkString(" || deep immutable types\n")} + | Deep Immutable Types: + | ${deepImmutableTypes.mkString(" || Deep Immutable Types \n")} |""".stripMargin ) } @@ -453,35 +434,36 @@ object Immutability { if (analysis == RunningAnalyses.References || analysis == RunningAnalyses.All) { stringBuilderAmounts.append( s""" - | mutable References: ${mutableReferences.size} - | lazy initialized not thread safe ref.: ${notThreadSafeLazyInitialization.size} - | lazy initialization not thread safe but deterministic: ${lazyInitializedReferencesNotThreadSafeButDeterministic.size} - | lazy initialization thread safe: ${lazyInitializedReferencesThreadSafe.size} - | immutable references: ${immutableReferences.size} - | references: ${allfieldsInProjectClassFiles.size} + | 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 == RunningAnalyses.Fields || analysis == RunningAnalyses.All) { stringBuilderAmounts.append( s""" - | mutable fields: ${mutableFields.size} - | shallow immutable fields: ${shallowImmutableFields.size} - | dependent immutable fields: ${dependentImmutableFields.size} - | deep immutable fields: ${deepImmutableFields.size} - | fields: ${allfieldsInProjectClassFiles.size} + | 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 == RunningAnalyses.Classes || analysis == RunningAnalyses.All) { stringBuilderAmounts.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: ${allProjectClassFilesIterator.size} + | 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 ) @@ -489,11 +471,11 @@ object Immutability { if (analysis == RunningAnalyses.Types || analysis == RunningAnalyses.All) stringBuilderAmounts.append( s""" - | mutable types: ${mutableTypes.size} - | shallow immutable types: ${shallowImmutableTypes.size} - | dependent immutable types: ${dependentImmutableTypes.size} - | deep immutable types: ${deepImmutableTypes.size} - | types: ${allProjectClassTypes.size} + | Mutable Types: ${mutableTypes.size} + | Shallow Immutable Types: ${shallowImmutableTypes.size} + | Dependent Immutable Types: ${dependentImmutableTypes.size} + | Deep immutable Types: ${deepImmutableTypes.size} + | Types: ${allProjectClassTypes.size} |""".stripMargin ) @@ -507,51 +489,55 @@ object Immutability { | $callGraphTime seconds callGraphTime | $analysisTime seconds analysisTime | $analysisTime seconds - | with $numThreads threads |""".stripMargin ) println( s""" -| -| ${stringBuilderAmounts.toString()} -| -| Time Results: -| -| $numThreads Threads :: took $analysisTime -| -|""".stripMargin + | + | ${stringBuilderAmounts.toString()} + | + | Time Results: + | + | $numThreads Threads :: took $analysisTime + | + |""".stripMargin ) - println("resultsfolder: "+resultsFolder) + println("Results folder: "+resultsFolder) - val calendar = Calendar.getInstance() if (resultsFolder != null) { val file = new File( - s"${if (resultsFolder != null) resultsFolder.toAbsolutePath.toString else "."}"+ - s""+ - s"/${analysis.toString}_${calendar.get(Calendar.YEAR)}_"+ - s"${(calendar.get(Calendar.MONTH) + 1)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.SECOND)}_${numThreads}Threads.txt" + s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt" ) - file.createNewFile() - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(s""" ${stringBuilderResults.toString()} - | - | ${stringBuilderAmounts.toString()} - | - | jdk folder: $JRELibraryFolder - |"""".stripMargin) - bw.close() + try { + + file.createNewFile() + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(s""" ${stringBuilderResults.toString()} + | + | ${stringBuilderAmounts.toString()} + | + | jdk folder: $JRELibraryFolder + | + |"""".stripMargin) + bw.close() + } catch { + + case e: IOException ⇒ + println(s"could not write file: ${file.getName}"); throw e + case _: Throwable ⇒ + } + } println(s"propertyStore: ${propertyStore.getClass.toString()}") println(s"jdk folder: $JRELibraryFolder") - + println({ project.config.atKey("org.opalj.br.analyses.cg.ClosedPackagesKey") }) BasicReport( stringBuilderAmounts.toString() ) } def main(args: Array[String]): Unit = { + def usage: String = { s""" | Usage: java …ImmutabilityAnalysisEvaluation @@ -561,6 +547,9 @@ object Immutability { | [-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) |""".stripMargin } var i = 0 @@ -574,6 +563,9 @@ object Immutability { var withoutJDK: Boolean = false var closedWorldAssumption = false var isLibrary = false + var callGraphName: Option[String] = None + var level = 2 + var reImInferComparison = false def readNextArg(): String = { i = i + 1 @@ -607,13 +599,16 @@ object Immutability { 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 "-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 "-ReImInferComparison" ⇒ reImInferComparison = true case "-JDK" ⇒ cp = JRELibraryFolder; withoutJDK = true case unknown ⇒ @@ -622,7 +617,34 @@ object Immutability { } i += 1 } - evaluate(cp, analysis, numThreads, projectDir, libDir, resultFolder, timeEvaluation, threadEvaluation, withoutJDK, isLibrary, closedWorldAssumption) + 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 ; + } + + evaluate( + cp, + analysis, + numThreads, + projectDir, + libDir, + resultFolder, + timeEvaluation, + threadEvaluation, + withoutJDK, + isLibrary, + closedWorldAssumption, + callGraphKey, + level, + reImInferComparison + ) } } From 529e44163a57cc075c22faaa48dd228783995f70 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 14:08:37 +0200 Subject: [PATCH 262/327] revised, still wip --- .../classes/DeepImmutableClass.java} | 14 +++++++------- .../classes/DependentImmutableClass.java} | 4 ++-- .../classes/MutableClass.java} | 4 ++-- .../classes/ShallowImmutableClass.java} | 4 ++-- .../fields/DeepImmutableField.java} | 4 ++-- .../fields/DependentImmutableField.java} | 4 ++-- .../fields/MutableField.java} | 4 ++-- .../fields/ShallowImmutableField.java} | 4 ++-- .../references/ImmutableReference.java} | 10 +++++----- ...zedNotThreadSafeButDeterministicReference.java} | 10 +++++----- .../LazyInitializedNotThreadSafeReference.java} | 10 +++++----- .../LazyInitializedThreadSafeReference.java} | 10 +++++----- .../references/MutableReference.java} | 10 +++++----- .../types/DeepImmutableType.java} | 4 ++-- .../types/DependentImmutableType.java} | 4 ++-- .../types/MutableType.java} | 4 ++-- .../types/ShallowImmutableType.java} | 4 ++-- 17 files changed, 54 insertions(+), 54 deletions(-) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{class_immutability/DeepImmutableClassAnnotation.java => immutability/classes/DeepImmutableClass.java} (84%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{class_immutability/DependentImmutableClassAnnotation.java => immutability/classes/DependentImmutableClass.java} (88%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{class_immutability/MutableClassAnnotation.java => immutability/classes/MutableClass.java} (89%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{class_immutability/ShallowImmutableClassAnnotation.java => immutability/classes/ShallowImmutableClass.java} (88%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{field_immutability/DeepImmutableFieldAnnotation.java => immutability/fields/DeepImmutableField.java} (88%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{field_immutability/DependentImmutableFieldAnnotation.java => immutability/fields/DependentImmutableField.java} (88%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{field_immutability/MutableFieldAnnotation.java => immutability/fields/MutableField.java} (89%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{field_immutability/ShallowImmutableFieldAnnotation.java => immutability/fields/ShallowImmutableField.java} (88%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{reference_immutability/ImmutableReferenceAnnotation.java => immutability/references/ImmutableReference.java} (63%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{reference_immutability/LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation.java => immutability/references/LazyInitializedNotThreadSafeButDeterministicReference.java} (66%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{reference_immutability/LazyInitializedNotThreadSafeReferenceAnnotation.java => immutability/references/LazyInitializedNotThreadSafeReference.java} (61%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{reference_immutability/LazyInitializedThreadSafeReferenceAnnotation.java => immutability/references/LazyInitializedThreadSafeReference.java} (61%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{reference_immutability/MutableReferenceAnnotation.java => immutability/references/MutableReference.java} (72%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{type_immutability/DeepImmutableTypeAnnotation.java => immutability/types/DeepImmutableType.java} (88%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{type_immutability/DependentImmutableTypeAnnotation.java => immutability/types/DependentImmutableType.java} (88%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{type_immutability/MutableTypeAnnotation.java => immutability/types/MutableType.java} (89%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/{type_immutability/ShallowImmutableTypeAnnotation.java => immutability/types/ShallowImmutableType.java} (88%) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/DeepImmutableClass.java similarity index 84% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/DeepImmutableClass.java index 8ca5c1ed51..6d45d6d4f9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DeepImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/DeepImmutableClass.java @@ -1,14 +1,14 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.class_immutability; - -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.LxClassImmutabilityAnalysis_new; +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.LxClassImmutabilityAnalysis_new; + /** * Annotation to state that the annotated class is deep immutable * @@ -17,12 +17,12 @@ @PropertyValidator(key = "ClassImmutability_new",validator = DeepImmutableClassMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface DeepImmutableClassAnnotation { +public @interface DeepImmutableClass { /** * A short reasoning of this property. */ - String value();// default = "N/A"; + String value(); Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/DependentImmutableClass.java similarity index 88% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/DependentImmutableClass.java index 3c39d38b6b..27c76d75bf 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/DependentImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/DependentImmutableClass.java @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.class_immutability; +package org.opalj.fpcf.properties.immutability.classes; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; @@ -16,7 +16,7 @@ @PropertyValidator(key = "ClassImmutability_new",validator = DependentImmutableClassMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface DependentImmutableClassAnnotation { +public @interface DependentImmutableClass { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/MutableClass.java similarity index 89% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/MutableClass.java index fc5cd17938..710f5a5cad 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/MutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/MutableClass.java @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.class_immutability; +package org.opalj.fpcf.properties.immutability.classes; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; @@ -17,7 +17,7 @@ @PropertyValidator(key = "ClassImmutability_new",validator = MutableClassMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface MutableClassAnnotation { +public @interface MutableClass { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/ShallowImmutableClass.java similarity index 88% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/ShallowImmutableClass.java index 89972bb2b9..8c0cb6a7de 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/class_immutability/ShallowImmutableClassAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/classes/ShallowImmutableClass.java @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.class_immutability; +package org.opalj.fpcf.properties.immutability.classes; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; @@ -17,7 +17,7 @@ @PropertyValidator(key = "ClassImmutability_new",validator = ShallowImmutableClassMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface ShallowImmutableClassAnnotation { +public @interface ShallowImmutableClass { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/DeepImmutableField.java similarity index 88% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/DeepImmutableField.java index c9712da753..6ca2803496 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DeepImmutableFieldAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/DeepImmutableField.java @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.field_immutability; +package org.opalj.fpcf.properties.immutability.fields; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.immutability.L0FieldImmutabilityAnalysis; @@ -17,7 +17,7 @@ @PropertyValidator(key="FieldImmutability",validator=DeepImmutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface DeepImmutableFieldAnnotation { +public @interface DeepImmutableField { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/DependentImmutableField.java similarity index 88% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/DependentImmutableField.java index ab97301e87..da95178e9d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/DependentImmutableFieldAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/DependentImmutableField.java @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.field_immutability; +package org.opalj.fpcf.properties.immutability.fields; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.immutability.L0FieldImmutabilityAnalysis; @@ -17,7 +17,7 @@ @PropertyValidator(key="FieldImmutability",validator=DependentImmutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface DependentImmutableFieldAnnotation { +public @interface DependentImmutableField { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/MutableField.java similarity index 89% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/MutableField.java index 15aeac615e..38b0666c67 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/MutableFieldAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/MutableField.java @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.field_immutability; +package org.opalj.fpcf.properties.immutability.fields; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.immutability.L0FieldImmutabilityAnalysis; @@ -16,7 +16,7 @@ @PropertyValidator(key="FieldImmutability",validator=MutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface MutableFieldAnnotation { +public @interface MutableField { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/ShallowImmutableField.java similarity index 88% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/ShallowImmutableField.java index 5b39e6b03e..f54983e28b 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_immutability/ShallowImmutableFieldAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/fields/ShallowImmutableField.java @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.field_immutability; +package org.opalj.fpcf.properties.immutability.fields; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.immutability.L0FieldImmutabilityAnalysis; @@ -16,7 +16,7 @@ @PropertyValidator(key="FieldImmutability",validator=ShallowImmutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface ShallowImmutableFieldAnnotation { +public @interface ShallowImmutableField { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableReference.java similarity index 63% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableReference.java index 15f389d409..aa4973c286 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/ImmutableReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableReference.java @@ -1,9 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.reference_immutability; +package org.opalj.fpcf.properties.immutability.references; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.reference.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -13,16 +13,16 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = ImmutableReferenceMatcher.class) +@PropertyValidator(key = "ReferenceImmutability",validator = ImmutableFieldReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface ImmutableReferenceAnnotation { +public @interface ImmutableReference { /** * A short reasoning of this property. */ String value();// default = "N/A"; - Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeButDeterministicReference.java similarity index 66% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeButDeterministicReference.java index 6a7af07ba5..6df195da61 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeButDeterministicReference.java @@ -1,9 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.reference_immutability; +package org.opalj.fpcf.properties.immutability.references; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.reference.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -14,16 +14,16 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedNotThreadSafeButDeterministicReferenceMatcher.class) +@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedNotThreadSafeButDeterministicFieldReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation { +public @interface LazyInitializedNotThreadSafeButDeterministicReference { /** * A short reasoning of this property. */ String value();// default = "N/A"; - Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeReference.java similarity index 61% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeReferenceAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeReference.java index e77dc2b30f..5f733135a2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedNotThreadSafeReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeReference.java @@ -1,9 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.reference_immutability; +package org.opalj.fpcf.properties.immutability.references; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.reference.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -14,16 +14,16 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedNotThreadSafeReferenceMatcher.class) +@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedNotThreadSafeFieldReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface LazyInitializedNotThreadSafeReferenceAnnotation { +public @interface LazyInitializedNotThreadSafeReference { /** * A short reasoning of this property. */ String value();// default = "N/A"; - Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedThreadSafeReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeReference.java similarity index 61% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedThreadSafeReferenceAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeReference.java index e29dcda138..3a262e368d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/LazyInitializedThreadSafeReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeReference.java @@ -1,9 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.reference_immutability; +package org.opalj.fpcf.properties.immutability.references; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.reference.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -14,16 +14,16 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedThreadSafeReferenceMatcher.class) +@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedThreadSafeFieldReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface LazyInitializedThreadSafeReferenceAnnotation { +public @interface LazyInitializedThreadSafeReference { /** * A short reasoning of this property. */ String value();// default = "N/A"; - Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableReference.java similarity index 72% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableReference.java index 2723ec0c23..15bba20ee7 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/reference_immutability/MutableReferenceAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableReference.java @@ -1,9 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.reference_immutability; +package org.opalj.fpcf.properties.immutability.references; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.reference.L0ReferenceImmutabilityAnalysis; +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -14,10 +14,10 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = MutableReferenceMatcher.class) +@PropertyValidator(key = "ReferenceImmutability",validator = MutableFieldReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface MutableReferenceAnnotation { +public @interface MutableReference { /** @@ -31,6 +31,6 @@ */ String value() default "N/A"; - Class[] analyses() default {L0ReferenceImmutabilityAnalysis.class}; + Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/DeepImmutableType.java similarity index 88% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/DeepImmutableType.java index c5aae8139e..7f6605a072 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DeepImmutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/DeepImmutableType.java @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.type_immutability; +package org.opalj.fpcf.properties.immutability.types; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; @@ -17,7 +17,7 @@ @PropertyValidator(key = "TypeImmutability_new", validator = DeepImmutableTypeMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface DeepImmutableTypeAnnotation { +public @interface DeepImmutableType { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/DependentImmutableType.java similarity index 88% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/DependentImmutableType.java index c13b6b40b5..8b0a92a3a0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/DependentImmutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/DependentImmutableType.java @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.type_immutability; +package org.opalj.fpcf.properties.immutability.types; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; @@ -17,7 +17,7 @@ @PropertyValidator(key = "TypeImmutability_new", validator = DependentImmutableTypeMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface DependentImmutableTypeAnnotation { +public @interface DependentImmutableType { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/MutableType.java similarity index 89% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/MutableType.java index 692c4adaf7..39559bdb2e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/MutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/MutableType.java @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.type_immutability; +package org.opalj.fpcf.properties.immutability.types; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; @@ -17,7 +17,7 @@ @PropertyValidator(key = "TypeImmutability_new", validator = NewMutableTypeMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface MutableTypeAnnotation { +public @interface MutableType { /** * A short reasoning of this property. diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/ShallowImmutableType.java similarity index 88% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/ShallowImmutableType.java index 6b42c1ab61..c04536ed78 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_immutability/ShallowImmutableTypeAnnotation.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/types/ShallowImmutableType.java @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.type_immutability; +package org.opalj.fpcf.properties.immutability.types; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; @@ -17,7 +17,7 @@ @PropertyValidator(key = "TypeImmutability_new", validator = ShallowImmutableTypeMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface ShallowImmutableTypeAnnotation { +public @interface ShallowImmutableType { /** * A short reasoning of this property. From a5537ebd896c3cb0b2722f6946f0fa101869b606 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 14:09:53 +0200 Subject: [PATCH 263/327] formatting --- .../immutability/classes/ClassImmutabilityMatcher.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 707f7d8c96..469d507a30 100644 --- 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 @@ -41,7 +41,7 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) extends Abst a: AnnotationLike, properties: Traversable[Property] ): Option[String] = { - if (!properties.exists(p⇒ p == property)) { + 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 { From e03bdd6cf7b46ec77f5b2d3894d5ba6b931e8f01 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 16 Sep 2020 14:11:09 +0200 Subject: [PATCH 264/327] revised, still wip --- .../opalj/fpcf/ClassImmutabilityTests.scala | 86 +++++++++++-------- .../opalj/fpcf/FieldImmutabilityTests.scala | 76 ++++++++-------- ... => FieldReferenceImmutabilityTests.scala} | 46 ++++++++-- .../opalj/fpcf/TypeImmutabilityTests.scala | 33 +++---- 4 files changed, 148 insertions(+), 93 deletions(-) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/{ReferenceImmutabilityTests.scala => FieldReferenceImmutabilityTests.scala} (61%) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala index e4246af2b3..a065684da9 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala @@ -1,7 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf +package org.opalj +package fpcf import java.net.URL +import com.typesafe.config.Config +import com.typesafe.config.ConfigValueFactory +import com.typesafe.config.ConfigValueFactory.fromAnyRef import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey @@ -16,39 +20,72 @@ import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +import org.opalj.br.analyses.cg.InitialEntryPointsKey +import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey /** + * Tests the class immutability analysis + * * @author Tobias Peter Roth */ class ClassImmutabilityTests extends PropertiesTest { + override def withRT = true + override def fixtureProjectPackage: List[String] = { List("org/opalj/fpcf/fixtures/immutability") } + override def createConfig(): Config = { + + 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( + InitialInstantiatedTypesKey.ConfigKeyPrefix+ + "AllInstantiatedTypesFinder.projectClassesOnly", + ConfigValueFactory.fromAnyRef(true) + ) + .withValue( + "org.opalj.br.analyses.cg.ClosedPackagesKey", + fromAnyRef("org.opalj.br.analyses.cg.OpenCodeBase") + ) + .withValue("org.opalj.br.analyses.cg.ClassExtensibilityKey", ConfigValueFactory.fromAnyRef( + "org.opalj.br.analyses.cg.ConfiguredExtensibleClasses" + )) + } + 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("ClassImmutability_new")) - * }* - */ + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("ClassImmutability_new")) + } + describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { - println(1) val as = executeAnalyses( Set( LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, LazyLxTypeImmutabilityAnalysis_new, EagerLxClassImmutabilityAnalysis_new, @@ -66,31 +103,4 @@ class ClassImmutabilityTests extends PropertiesTest { Set("ClassImmutability_new") ) } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ } 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 index 483afe6a7f..ee07f34f05 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -1,10 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf +package org.opalj +package fpcf import java.net.URL +import com.typesafe.config.Config +import com.typesafe.config.ConfigValueFactory +import org.opalj.br.analyses.cg.InitialEntryPointsKey +import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey -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 @@ -16,10 +19,15 @@ import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis +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_new +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey /** + * Tests the field immutability analysis + * * @author Tobias Peter Roth */ class FieldImmutabilityTests extends PropertiesTest { @@ -30,10 +38,39 @@ class FieldImmutabilityTests extends PropertiesTest { List("org/opalj/fpcf/fixtures/immutability") } + override def createConfig(): Config = { + import com.typesafe.config.ConfigValueFactory.fromAnyRef + 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( + InitialInstantiatedTypesKey.ConfigKeyPrefix+ + "AllInstantiatedTypesFinder.projectClassesOnly", + ConfigValueFactory.fromAnyRef(true) + ) + .withValue( + "org.opalj.br.analyses.cg.ClosedPackagesKey", + fromAnyRef("org.opalj.br.analyses.cg.OpenCodeBase") + ) + .withValue("org.opalj.br.analyses.cg.ClassExtensibilityKey", ConfigValueFactory.fromAnyRef( + "org.opalj.br.analyses.cg.ConfiguredExtensibleClasses" + )) + } + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) } + p.get(RTACallGraphKey) } @@ -44,10 +81,9 @@ class FieldImmutabilityTests extends PropertiesTest { } describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { - import org.opalj.br.fpcf.analyses.LazyVirtualCallAggregatingEscapeAnalysis val as = executeAnalyses( Set( - LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, EagerL0FieldImmutabilityAnalysis, @@ -64,32 +100,4 @@ class FieldImmutabilityTests extends PropertiesTest { as.propertyStore.shutdown() validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) } - /** - * describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL1FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * }* - */ - /** - * describe("the org.opalj.fpcf.analyses.L2FieldMutabilityAnalysis is executed") { - * val as = executeAnalyses( - * Set( - * EagerL2FieldMutabilityAnalysis, - * LazyUnsoundPrematurelyReadFieldsAnalysis, - * LazyL2PurityAnalysis, - * LazyInterProceduralEscapeAnalysis - * ) - * ) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldMutability")) - * } * - */ - } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldReferenceImmutabilityTests.scala similarity index 61% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldReferenceImmutabilityTests.scala index 868a4c4f67..89bec7894e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldReferenceImmutabilityTests.scala @@ -1,7 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf +package org.opalj +package fpcf import java.net.URL +import com.typesafe.config.Config +import com.typesafe.config.ConfigValueFactory +import com.typesafe.config.ConfigValueFactory.fromAnyRef +import org.opalj.br.analyses.cg.InitialEntryPointsKey +import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey @@ -19,27 +25,55 @@ import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.EagerL0FieldReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater /** + * Tests the field reference immutability analysis + * * @author Tobias Peter Roth */ -class ReferenceImmutabilityTests extends PropertiesTest { +class FieldReferenceImmutabilityTests extends PropertiesTest { override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") + List("org/opalj/fpcf/fixtures/immutability/sandbox") + } + + override def createConfig(): Config = { + 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( + InitialInstantiatedTypesKey.ConfigKeyPrefix+ + "AllInstantiatedTypesFinder.projectClassesOnly", + ConfigValueFactory.fromAnyRef(true) + ) + .withValue( + "org.opalj.br.analyses.cg.ClosedPackagesKey", + fromAnyRef("org.opalj.br.analyses.cg.OpenCodeBase") + ) + .withValue("org.opalj.br.analyses.cg.ClassExtensibilityKey", ConfigValueFactory.fromAnyRef( + "org.opalj.br.analyses.cg.ConfiguredExtensibleClasses" + )) } override def init(p: Project[URL]): Unit = { - //println("Java version: "+System.getProperty("java.version")) + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) } + p.get(RTACallGraphKey) } @@ -54,7 +88,7 @@ class ReferenceImmutabilityTests extends PropertiesTest { describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { val as = executeAnalyses( Set( - EagerL0ReferenceImmutabilityAnalysis, + EagerL0FieldReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, LazyLxClassImmutabilityAnalysis_new, LazyLxTypeImmutabilityAnalysis_new, diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala index c2a072466f..f7643c105d 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala @@ -1,11 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf +package org.opalj +package fpcf import java.net.URL - import com.typesafe.config.Config import com.typesafe.config.ConfigValueFactory -import org.opalj.BaseConfig +import com.typesafe.config.ConfigValueFactory.fromAnyRef + import org.opalj.ai.domain.l2 import org.opalj.br.analyses.Project import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis @@ -18,25 +19,27 @@ import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.LazyL0ReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.cg.InitialEntryPointsKey import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey /** - * Tests the Type Immutability Analysis with the new lattice + * Tests the type immutability analysis * * @author Tobias Peter Roth */ class TypeImmutabilityTests extends PropertiesTest { + override def withRT = true + override def fixtureProjectPackage: List[String] = { List("org/opalj/fpcf/fixtures/immutability") } override def createConfig(): Config = { - import com.typesafe.config.ConfigValueFactory.fromAnyRef + val configForEntryPoints = BaseConfig.withValue( InitialEntryPointsKey.ConfigKeyPrefix+"analysis", ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllEntryPointsFinder") @@ -71,19 +74,18 @@ class TypeImmutabilityTests extends PropertiesTest { p.get(RTACallGraphKey) } - /** - * describe("no analysis is scheduled") { - * val as = executeAnalyses(Set.empty) - * as.propertyStore.shutdown() - * validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) - * } - */ + describe("no analysis is scheduled") { + val as = executeAnalyses(Set.empty) + as.propertyStore.shutdown() + validateProperties(as, fieldsWithAnnotations(as.project), Set("TypeImmutability_new")) + } + describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { val as = executeAnalyses( Set( LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis_new, - LazyL0ReferenceImmutabilityAnalysis, + LazyL0FieldReferenceImmutabilityAnalysis, LazyL0FieldImmutabilityAnalysis, LazyLxClassImmutabilityAnalysis_new, EagerLxTypeImmutabilityAnalysis_new, @@ -94,10 +96,11 @@ class TypeImmutabilityTests extends PropertiesTest { LazyFieldLocalityAnalysis ) ) + as.propertyStore.shutdown() + validateProperties( as, - // fieldsWithAnnotations(as.project), classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), Set("TypeImmutability_new") ) From 51d07ad300663a095c8b919bf467405ea0ae1973 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 17 Sep 2020 13:27:52 +0200 Subject: [PATCH 265/327] renaming --- .../ReferenceImmutabilityAnalysisDemo.scala | 180 ------------------ .../properties/ReferenceImmutability.scala | 117 ------------ 2 files changed, 297 deletions(-) delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala delete mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala deleted file mode 100644 index 634c83fab9..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ReferenceImmutabilityAnalysisDemo.scala +++ /dev/null @@ -1,180 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.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.br.fpcf.properties.MutableReference -import org.opalj.fpcf.PropertyStore -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.io._ -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.br.fpcf.properties.ImmutableReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference -import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference -import org.opalj.fpcf -import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis -import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.reference.EagerL0ReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new - -/** - * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. - * - * @author Tobias Peter Roth - */ -object ReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" - - override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" - - 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) - analysesManager.project.get(RTACallGraphKey) - time { - propertyStore = analysesManager - .runAll( - EagerL0ReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new, - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyInterProceduralEscapeAnalysis, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ._1 - - propertyStore.waitOnPhaseCompletion() - - } { t ⇒ - analysisTime = t.toSeconds - } - var sb: StringBuilder = new StringBuilder() - val allfieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet - //.filter(f ⇒ (!f.isTransient && !f.isSyn)) // for ReImComparison - .toSet - sb = sb.append("Mutable References: \n") - val mutableReferences = propertyStore - .finalEntities(MutableReference) - .filter(x ⇒ allfieldsInProjectClassFiles.contains(x.asInstanceOf[Field])) - .toList - .sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) - sb = sb.append( - mutableReferences.map(x ⇒ x.toString+"\n").toString() - ) - - val lazyInitializedReferencesThreadSafe = propertyStore - .finalEntities(LazyInitializedThreadSafeReference) - .toList - .sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) - - val lazyInitializedReferencesNotThreadSafeButDeterministic = propertyStore - .finalEntities(LazyInitializedNotThreadSafeButDeterministicReference) - .toList - .sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) - - val notThreadSafeOrNotDeterministicLazyInitialization = propertyStore - .finalEntities(LazyInitializedNotThreadSafeReference) - .toList - .sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) - - sb.append( - s""" - | lazy initialized thread safe references: ${ - lazyInitializedReferencesThreadSafe - .mkString(",\n") - } - | - | lazy initialized not thread safe but deterministic references: ${ - lazyInitializedReferencesNotThreadSafeButDeterministic - .mkString(", \n") - } - | - | lazy initialized not thread safe or not deterministic references: ${ - notThreadSafeOrNotDeterministicLazyInitialization - .mkString(", \n") - } - | - |""".stripMargin - ) - - val immutableReferences = propertyStore - .entities( - eps ⇒ //allfieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field]) && - eps.isFinal && (eps.asFinal.p match { - case ImmutableReference ⇒ true - case _ ⇒ false - }) - ) - .toList - .sortWith((e1: fpcf.Entity, e2: fpcf.Entity) ⇒ e1.toString < e2.toString) - sb = sb.append( - immutableReferences.map(x ⇒ x.toString+"\n").mkString(", ") - ) - - sb.append( - s""" - | mutable References: ${mutableReferences.size} - | lazy initialized references not thread safe or deterministic: ${notThreadSafeOrNotDeterministicLazyInitialization.size} - | lazy initialized references not thread safe but deterministic: ${lazyInitializedReferencesNotThreadSafeButDeterministic.size} - | lazy initialized thread safe references: ${lazyInitializedReferencesThreadSafe.size} - | immutable References: ${immutableReferences.size} - | - | took : $analysisTime seconds - | - |""".stripMargin - ) - - val calendar = Calendar.getInstance() - val file = new File( - s"/home/tobias/results/immutability/reference/refImm_withNewPurity_${calendar.get(Calendar.YEAR)}_"+ - s"${calendar.get(Calendar.MONTH)}_${calendar.get(Calendar.DAY_OF_MONTH)}_"+ - s"${calendar.get(Calendar.HOUR_OF_DAY)}_${calendar.get(Calendar.MINUTE)}_"+ - s"${calendar.get(Calendar.MILLISECOND)}.txt" - ) - if (!file.exists()) - file.createNewFile() - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(sb.toString()) - bw.close() - s""" - | ps: ${propertyStore.getClass} - | took : $analysisTime seconds - |""".stripMargin - - } -} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala deleted file mode 100644 index e7d8855d2c..0000000000 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ReferenceImmutability.scala +++ /dev/null @@ -1,117 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.br.fpcf.properties - -import org.opalj.br.fpcf.properties -import org.opalj.fpcf.Entity -import org.opalj.fpcf.OrderedProperty -import org.opalj.fpcf.PropertyKey -import org.opalj.fpcf.PropertyMetaInformation - -sealed trait ReferenceImmutabilityPropertyMetaInformation extends PropertyMetaInformation { - - type Self = ReferenceImmutability - -} - -/** - * Describes the reference immutability of org.opalj.br.Field. - * The analysis is used from the old implementation of the L2FieldMutabilityAnalysis. - * - * [[MutableReference]] A reference through which the object it points to could be changed. - * - * [[LazyInitializedReference]] A reference through which the object it points to is changed in a initial like phase - * and afterwards never changed through this reference - * - * [[ImmutableReference]] A reference through which the object it leads to is set in the constructor and afterwards - * never changed. And there is no possibility to change it afterwards through this reference.. - * - * @author Tobias Peter Roth - */ -sealed trait ReferenceImmutability - extends OrderedProperty - with ReferenceImmutabilityPropertyMetaInformation { - final def key: PropertyKey[ReferenceImmutability] = ReferenceImmutability.key - def isImmutableReference = false -} - -object ReferenceImmutability extends ReferenceImmutabilityPropertyMetaInformation { - - var notEscapes: Boolean = false - - final val PropertyKeyName = "opalj.ReferenceImmutability" - - final val key: PropertyKey[ReferenceImmutability] = { - PropertyKey.create( - PropertyKeyName, - MutableReference - ) - } -} - -case object ImmutableReference extends ReferenceImmutability { - override def isImmutableReference = true - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - def meet(that: ReferenceImmutability): ReferenceImmutability = - if (this == that) - this - else - that -} - -case object LazyInitializedThreadSafeReference extends ReferenceImmutability { - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = other match { - case ImmutableReference ⇒ - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - case _ ⇒ - } - def meet(other: ReferenceImmutability): ReferenceImmutability = - if (other == MutableReference || other == LazyInitializedNotThreadSafeReference || - other == LazyInitializedNotThreadSafeButDeterministicReference) { - other - } else { - this - } -} - -case object LazyInitializedNotThreadSafeButDeterministicReference extends ReferenceImmutability { - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = other match { - case ImmutableReference | LazyInitializedThreadSafeReference ⇒ - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - case _ ⇒ - } - - def meet(other: ReferenceImmutability): ReferenceImmutability = { - if (other == MutableReference || other == LazyInitializedNotThreadSafeReference) { - other - } else { - this - } - } -} -case object LazyInitializedNotThreadSafeReference extends ReferenceImmutability { - def meet(other: ReferenceImmutability): properties.ReferenceImmutability = { - if (other == MutableReference) { - other - } else { - this - } - } - override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { - other match { - case ImmutableReference | LazyInitializedNotThreadSafeButDeterministicReference | - LazyInitializedThreadSafeReference ⇒ - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this"); - case _ ⇒ - } - } -} - -case object MutableReference extends ReferenceImmutability { - def meet(other: ReferenceImmutability): this.type = this - - override def checkIsEqualOrBetterThan(e: Entity, other: ReferenceImmutability): Unit = { - if (other != MutableReference) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other => $this") - } - } -} From 0a636c81f6b11dde7a6ea04ffda2a66fd8c0adae Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 17 Sep 2020 13:29:10 +0200 Subject: [PATCH 266/327] revised, still wip --- ...eldReferenceImmutabilityAnalysisDemo.scala | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) 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 index e69de29bb2..b9819ed85c 100644 --- 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 @@ -0,0 +1,155 @@ +/* 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.LazyL0FieldImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +import org.opalj.tac.fpcf.analyses.immutability.fieldreference.EagerL0FieldReferenceImmutabilityAnalysis +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +import java.io.IOException +import java.io.BufferedWriter +import java.io.FileWriter +import java.io.File +import org.opalj.br.fpcf.properties.FieldReferenceImmutability + +/** + * Runs the EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. + * + * @author Tobias Peter Roth + */ +object FieldReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { + + override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def description: String = + "runs the EagerL0ReferenceImmutabilityAnalysis" + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () ⇒ Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + /*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.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 + + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) + time { + propertyStore = analysesManager + .runAll( + EagerL0FieldReferenceImmutabilityAnalysis, + LazyL0FieldImmutabilityAnalysis, + LazyLxClassImmutabilityAnalysis_new, + LazyLxTypeImmutabilityAnalysis_new, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL2PurityAnalysis_new, + 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 Fiel References: + | ${mutableReferences.mkString(" | Mutable Field Reference \n")} + | lazy initialized not thread deterministic references: + | ${notThreadSafeLazyInitializedFieldReferences.mkString(" | Not Thread \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 safe or deterministic: + | ${notThreadSafeLazyInitializedFieldReferences.size} + | lazy initialized references not thread safe but deterministic: + | ${lazyInitializedReferencesNotThreadSafeButDeterministic.size} + | lazy initialized thread safe references: ${threadSafeLazyInitializedFieldReferences.size} + | immutable References: ${immutableReferences.size} + | + | took : $analysisTime seconds + |""".stripMargin + + val file = new File( + s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt" + ) + + try { + + if (!file.exists()) + file.createNewFile() + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(output) + bw.close() + } catch { + case e: IOException ⇒ + println(s"Could not write file: ${file.getName}"); throw e + case _: Throwable ⇒ + } + + s"took : $analysisTime seconds" + + } +} From f5c4edb05c51aafa671849a8f99b960e23df1e1f Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 24 Sep 2020 16:06:47 +0200 Subject: [PATCH 267/327] integrate class immutability analyses and adapt all of them to the new lattice --- .../ClassImmutabilityAnalysisDemo.scala | 32 +- .../classes/DeepImmutableClass.java | 4 +- .../classes/DependentImmutableClass.java | 4 +- .../immutability/classes/MutableClass.java | 6 +- .../classes/ShallowImmutableClass.java | 4 +- .../fpcf/ClassAndTypeImmutabilityTests.scala | 116 +++ .../fpcf/ClassAndTypeMutabilityTests.scala | 37 - .../AbstractClassImmutabilityMatcher.scala | 24 +- ...cala => L0ClassImmutabilityAnalysis.scala} | 151 ++-- .../fpcf/properties/ClassImmutability.scala | 156 +--- .../properties/ClassImmutability_new.scala | 105 --- ...cala => L1ClassImmutabilityAnalysis.scala} | 145 ++-- .../purity/AbstractPurityAnalysis_new.scala | 734 ------------------ 13 files changed, 353 insertions(+), 1165 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala delete mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeMutabilityTests.scala rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/{class_mutability => immutability/classes}/AbstractClassImmutabilityMatcher.scala (64%) rename OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/{ClassImmutabilityAnalysis.scala => L0ClassImmutabilityAnalysis.scala} (80%) delete mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/{LxClassImmutabilityAnalysis_new.scala => L1ClassImmutabilityAnalysis.scala} (85%) delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala 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 index dac8016fa7..a7384c42d5 100644 --- 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 @@ -9,7 +9,7 @@ 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_new +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 @@ -21,17 +21,18 @@ 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.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis -import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +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_new +import org.opalj.br.fpcf.properties.ClassImmutability +import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis +import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis /** * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result @@ -62,19 +63,20 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { analysesManager.project.get(RTACallGraphKey) time { + propertyStore = analysesManager .runAll( LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, + LazyL2PurityAnalysis, LazyL0FieldReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new, + LazyL3FieldImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, + EagerL1ClassImmutabilityAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis //, - //LazyReturnValueFreshnessAnalysis, - //LazyFieldLocalityAnalysis + LazyInterProceduralEscapeAnalysis, + LazyReturnValueFreshnessAnalysis, + LazyFieldLocalityAnalysis ) ._1 propertyStore.waitOnPhaseCompletion(); @@ -83,10 +85,10 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet - val groupedResults = propertyStore.entities(ClassImmutability_new.key). + val groupedResults = propertyStore.entities(ClassImmutability.key). filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) - val order = (eps1: EPS[Entity, ClassImmutability_new], eps2: EPS[Entity, ClassImmutability_new]) ⇒ + val order = (eps1: EPS[Entity, ClassImmutability], eps2: EPS[Entity, ClassImmutability]) ⇒ eps1.e.toString < eps2.e.toString val mutableClasses = groupedResults(MutableClass).toSeq.sortWith(order) 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 index 6d45d6d4f9..d39e1a9ad1 100644 --- 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 @@ -7,7 +7,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.LxClassImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; /** * Annotation to state that the annotated class is deep immutable @@ -24,6 +24,6 @@ */ String value(); - Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; + 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 index 27c76d75bf..6453efc7e8 100644 --- 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 @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.LxClassImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -23,5 +23,5 @@ */ String value();// default = "N/A"; - Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; + 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 index 710f5a5cad..1844c0ec00 100644 --- 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 @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.LxClassImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -14,7 +14,7 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ClassImmutability_new",validator = MutableClassMatcher.class) +@PropertyValidator(key = "ClassImmutability",validator = MutableClassMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface MutableClass { @@ -24,5 +24,5 @@ */ String value();// default = "N/A"; - Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; + 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 index 8c0cb6a7de..9b33618d4e 100644 --- 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 @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.LxClassImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.L1ClassImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -24,5 +24,5 @@ */ String value();// default = "N/A"; - Class[] analyses() default {LxClassImmutabilityAnalysis_new.class}; + Class[] analyses() default {L1ClassImmutabilityAnalysis.class}; } 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..45af40a7d8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala @@ -0,0 +1,116 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.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 + +/** + * 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 fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/immutability") + } + /* + override def createConfig(): Config = { + + 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( + 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" + )) + } */ + + 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( + EagerL1ClassImmutabilityAnalysis, + EagerL1TypeImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis, + LazyL3FieldImmutabilityAnalysis, + 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/properties/class_mutability/AbstractClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/AbstractClassImmutabilityMatcher.scala similarity index 64% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/class_mutability/AbstractClassImmutabilityMatcher.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/AbstractClassImmutabilityMatcher.scala index a90a50e4b5..a6ccea4225 100644 --- 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/immutability/classes/AbstractClassImmutabilityMatcher.scala @@ -1,13 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package properties -package class_mutability +package org.opalj.fpcf.properties.immutability.classes import org.opalj.br.AnnotationLike import org.opalj.br.ObjectType import org.opalj.br.analyses.Project import org.opalj.br.fpcf.properties +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher class AbstractClassImmutabilityMatcher( val property: properties.ClassImmutability @@ -31,13 +30,16 @@ class AbstractClassImmutabilityMatcher( } } -class ImmutableObjectMatcher - extends AbstractClassImmutabilityMatcher(properties.ImmutableObject) +class DeepImmutableClassMatcher + extends AbstractClassImmutabilityMatcher(properties.DeepImmutableClass) -class ImmutableContainerObjectMatcher - extends AbstractClassImmutabilityMatcher(properties.ImmutableContainer) +class DependentImmutableClassMatcher + extends AbstractClassImmutabilityMatcher(properties.DependentImmutableClass) -class MutableObjectMatcher extends AbstractPropertyMatcher { +class ShallowImmutableClassMatcher + extends AbstractClassImmutabilityMatcher(properties.ShallowImmutableClass) + +class MutableClassMatcher extends AbstractPropertyMatcher { override def validateProperty( p: Project[_], as: Set[ObjectType], @@ -46,8 +48,8 @@ class MutableObjectMatcher extends AbstractPropertyMatcher { properties: Traversable[Property] ): Option[String] = { if (properties.exists { - case _: MutableObject ⇒ true - case _ ⇒ false + case _: MutableClass ⇒ true // MutableObject ⇒ true + case _ ⇒ false }) Some(a.elementValuePairs.head.value.asStringValue.value) else 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..69e2356203 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] @@ -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/properties/ClassImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability.scala index 2c9af888d4..c86412b372 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,166 +10,96 @@ 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 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 { /** * The key associated with every [[ClassImmutability]] property. */ final val key: PropertyKey[ClassImmutability] = PropertyKey.create( - "opalj.ClassImmutability", - MutableObjectDueToUnresolvableDependency + "opalj.ClassImmutability_new", + 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 { - def reason: String - final val correspondingTypeImmutability = MutableType + override def correspondingTypeImmutability: TypeImmutability = ShallowImmutableType + + 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 = 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_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala deleted file mode 100644 index 8f1887d644..0000000000 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_new.scala +++ /dev/null @@ -1,105 +0,0 @@ -/* 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_new extends PropertyMetaInformation { - final type Self = ClassImmutability_new -} - -/** - * Describes the class immutability of org.opalj.br.ClassFile. - * The immutability of the classes are represented via their instance fields - * - * [[MutableClass]] A class with a mutable state. - * - * [[ShallowImmutableClass]] A class which transitive state is not immutable but the values or objects representing - * this transitive state (are not / can not be) exchanged. - * - * [[DependentImmutableClass]] A class that is at least shallow immutable. - * Whether it is shallow or deep immutable depends on generic parameters. - * - * [[DeepImmutableClass]] A class with a transitive immutable state. - * - * @author Tobias Peter Roth - */ -sealed trait ClassImmutability_new - extends OrderedProperty - with ClassImmutabilityPropertyMetaInformation_new { - final def key: PropertyKey[ClassImmutability_new] = ClassImmutability_new.key - def correspondingTypeImmutability: TypeImmutability_new -} - -object ClassImmutability_new extends ClassImmutabilityPropertyMetaInformation_new { - - /** - * The key associated with every [[ClassImmutability_new]] property. - */ - final val key: PropertyKey[ClassImmutability_new] = PropertyKey.create( - "opalj.ClassImmutability_new", - MutableClass - ) -} - -case object DeepImmutableClass extends ClassImmutability_new { - - override def correspondingTypeImmutability: TypeImmutability_new = DeepImmutableType - - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = {} - - def meet(that: ClassImmutability_new): ClassImmutability_new = that -} - -case object DependentImmutableClass extends ClassImmutability_new { - - override def correspondingTypeImmutability: TypeImmutability_new = DependentImmutableType - - def meet(that: ClassImmutability_new): ClassImmutability_new = - if (that == MutableClass || that == ShallowImmutableClass) - that - else - this - - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == DeepImmutableClass) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") - } - } -} - -case object ShallowImmutableClass extends ClassImmutability_new { - - override def correspondingTypeImmutability: TypeImmutability_new = ShallowImmutableType - - def meet(that: ClassImmutability_new): ClassImmutability_new = { - if (that == MutableClass) - that - else - this - } - - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == DeepImmutableClass || other == DependentImmutableClass) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") - } - } -} - -case object MutableClass extends ClassImmutability_new { - - def correspondingTypeImmutability = MutableType_new - - def meet(other: ClassImmutability_new): ClassImmutability_new = this - - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other != MutableClass) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") - } - } -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L1ClassImmutabilityAnalysis.scala similarity index 85% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L1ClassImmutabilityAnalysis.scala index b5194c930f..6ded27252a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxClassImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L1ClassImmutabilityAnalysis.scala @@ -1,5 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.immutability +package org.opalj +package tac +package fpcf +package analyses +package immutability import org.opalj.br.ClassFile import org.opalj.br.ClassSignature @@ -12,7 +16,7 @@ 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_new +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 @@ -46,6 +50,8 @@ 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 /** * @@ -62,17 +68,17 @@ import org.opalj.log.OPALLogger * mutable. * * This analysis uses the [[org.opalj.br.fpcf.properties.FieldImmutability]] property to determine - * those fields which could be final, but which are not declared as final. + * 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 Peter Roth + * @author Tobias Roth * */ -class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnalysis { +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 @@ -88,7 +94,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal */ @inline private[this] def createResultForAllSubtypes( t: ObjectType, - immutability: ClassImmutability_new + immutability: ClassImmutability ): MultiResult = { val allSubtypes = classHierarchy.allSubclassTypes(t, reflexive = true) val r = allSubtypes.map { st ⇒ @@ -110,8 +116,8 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal case Some(scf) ⇒ nextComputations ::= ( ( - determineClassImmutability_new(t, cfMutability, cfMutabilityIsFinal, lazyComputation = false) _, - scf + determineL1ClassImmutability(t, cfMutability, cfMutabilityIsFinal, + lazyComputation = false) _, scf ) ) case None ⇒ @@ -119,7 +125,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal "project configuration - object immutability analysis", s"missing class file of ${t.toJava}; setting all subtypes to mutable" ) - results ::= createResultForAllSubtypes(t, MutableClass) //MutableObjectDueToUnknownSupertypes) + results ::= createResultForAllSubtypes(t, MutableClass) } } IncrementalResult(Results(results), nextComputations.iterator) @@ -153,32 +159,32 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal genericTypeBounds } - def doDetermineClassImmutability_new(e: Entity): ProperPropertyComputationResult = { + 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); //MutableObjectDueToUnknownSupertypes) + case None ⇒ Result(t, MutableClass); case Some(superClassType) ⇒ val cf = project.classFile(t) match { case None ⇒ - return Result(t, MutableClass); // MutableObjectByAnalysis) //TODO consider other lattice element + return Result(t, MutableClass); //TODO consider other lattice element case Some(cf) ⇒ cf } - propertyStore(superClassType, ClassImmutability_new.key) match { - case UBP(MutableClass) ⇒ //MutableObject) ⇒ + propertyStore(superClassType, ClassImmutability.key) match { + case UBP(MutableClass) ⇒ Result(t, MutableClass) - case eps: EPS[ObjectType, ClassImmutability_new] ⇒ - determineClassImmutability_new( + case eps: EPS[ObjectType, ClassImmutability] ⇒ + determineL1ClassImmutability( superClassType, eps, eps.isFinal, lazyComputation = true )(cf) case epk ⇒ - determineClassImmutability_new( + determineL1ClassImmutability( superClassType, epk, superClassMutabilityIsFinal = false, @@ -203,7 +209,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal * 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_new( + def determineL1ClassImmutability( superClassType: ObjectType, superClassInformation: EOptionP[Entity, Property], superClassMutabilityIsFinal: Boolean, @@ -231,37 +237,34 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) fieldsPropertyStoreInformation.foreach( - f ⇒ { - import org.opalj.fpcf.EPK - import org.opalj.fpcf.InterimE - f match { - case FinalP(MutableField) ⇒ { - if (lazyComputation) - return Result(t, MutableClass); - else - return createResultForAllSubtypes(t, MutableClass); - } - case FinalP(ShallowImmutableField) ⇒ - hasShallowImmutableFields = true - case FinalEP(fi, 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); + _ match { + case FinalP(MutableField) ⇒ { + if (lazyComputation) + return Result(t, MutableClass); + else + return createResultForAllSubtypes(t, MutableClass); + } + case FinalP(ShallowImmutableField) ⇒ + hasShallowImmutableFields = true + case FinalEP(fi, 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); } + ) /** @@ -273,17 +276,17 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal * ShallowImmutableClass //ImmutableContainer * } * */ - var minLocalImmutability: ClassImmutability_new = MutableClass + var minLocalImmutability: ClassImmutability = MutableClass // NOTE: maxLocalImmutability does not take the super classes' mutability into account! - var maxLocalImmutability: ClassImmutability_new = superClassInformation match { + var maxLocalImmutability: ClassImmutability = superClassInformation match { case UBP(MutableClass) ⇒ MutableClass - case UBP(ShallowImmutableClass) ⇒ ShallowImmutableClass //ImmutableContainer + case UBP(ShallowImmutableClass) ⇒ ShallowImmutableClass case UBP(DependentImmutableClass) ⇒ DependentImmutableClass - case _ ⇒ DeepImmutableClass // ImmutableObject + case _ ⇒ DeepImmutableClass } if (hasShallowImmutableFields) { - maxLocalImmutability = ShallowImmutableClass //ImmutableContainer + maxLocalImmutability = ShallowImmutableClass } //if (!dependentImmutableFields.isEmpty && maxLocalImmutability != ShallowImmutableClass) { @@ -295,7 +298,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal // 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 //ImmutableContainer + maxLocalImmutability = ShallowImmutableClass } if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { @@ -372,10 +375,10 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } case FinalP(MutableField) ⇒ return Result(t, MutableClass); - case UBP(MutableField) ⇒ //_: NonFinalField) ⇒ - return Result(t, MutableClass); //MutableObjectByAnalysis); + case UBP(MutableField) ⇒ + return Result(t, MutableClass); - case ELBP(e, ShallowImmutableField | DeepImmutableField) ⇒ //FinalField) => + case ELBP(e, ShallowImmutableField | DeepImmutableField) ⇒ dependees -= e //xx if (minLocalImmutability != ShallowImmutableClass) // && // ImmutableContainer && //!dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) @@ -391,7 +394,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } if (someEPS.isRefinable) { - val entity = if (someEPS.pk == ClassImmutability_new.key) SuperClassKey else someEPS.e + val entity = if (someEPS.pk == ClassImmutability.key) SuperClassKey else someEPS.e dependees += (entity -> someEPS) } @@ -425,9 +428,7 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal s"(old dependees: ${oldDependees.mkString(",")}" ) */ - Result(t, maxLocalImmutability) - } else { InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) } @@ -449,12 +450,12 @@ class LxClassImmutabilityAnalysis_new(val project: SomeProject) extends FPCFAnal } } -trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { +trait L1ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability_new) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability) final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, FieldImmutability) //TypeImmutability, FieldImmutability) + PropertyBounds.lubs(ClassImmutability, FieldImmutability) //TypeImmutability, //XXX override type InitializationData = TraversableOnce[ClassFile] @@ -469,7 +470,7 @@ trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { implicit val logContext: LogContext = project.logContext // 1.1 - // java.lang.Object is by definition immutable. + // java.lang.Object is by definition deep immutable. set(ObjectType.Object, DeepImmutableClass) //ImmutableObject) // 1.2 @@ -539,8 +540,8 @@ trait ClassImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { * @author Tobias Peter Roth * @author Michael Eichberg */ -object EagerLxClassImmutabilityAnalysis_new - extends ClassImmutabilityAnalysisScheduler_new +object EagerL1ClassImmutabilityAnalysis + extends L1ClassImmutabilityAnalysisScheduler with FPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -548,9 +549,9 @@ object EagerLxClassImmutabilityAnalysis_new override def derivesCollaboratively: Set[PropertyBounds] = Set.empty override def start(p: SomeProject, ps: PropertyStore, cfs: InitializationData): FPCFAnalysis = { - val analysis = new LxClassImmutabilityAnalysis_new(p) + val analysis = new L1ClassImmutabilityAnalysis(p) ps.scheduleEagerComputationsForEntities(cfs)( - analysis.determineClassImmutability_new( + analysis.determineL1ClassImmutability( superClassType = null, FinalEP(ObjectType.Object, DeepImmutableClass), superClassMutabilityIsFinal = true, @@ -565,8 +566,8 @@ object EagerLxClassImmutabilityAnalysis_new * Scheduler to run the immutability analysis lazily. * @author Michael Eichberg */ -object LazyLxClassImmutabilityAnalysis_new - extends ClassImmutabilityAnalysisScheduler_new +object LazyL1ClassImmutabilityAnalysis + extends L1ClassImmutabilityAnalysisScheduler with FPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) @@ -576,10 +577,10 @@ object LazyLxClassImmutabilityAnalysis_new ps: PropertyStore, unused: InitializationData ): FPCFAnalysis = { - val analysis = new LxClassImmutabilityAnalysis_new(p) + val analysis = new L1ClassImmutabilityAnalysis(p) ps.registerLazyPropertyComputation( - ClassImmutability_new.key, - analysis.doDetermineClassImmutability_new + ClassImmutability.key, + analysis.doDetermineL1ClassImmutability ) analysis } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala deleted file mode 100644 index c6f5f29425..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis_new.scala +++ /dev/null @@ -1,734 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.purity - -import org.opalj.ai -import org.opalj.ai.ValueOrigin -import org.opalj.ai.isImmediateVMException -import org.opalj.br.ComputationalTypeReference -import org.opalj.br.DeclaredMethod -import org.opalj.br.DefinedMethod -import org.opalj.br.Field -import org.opalj.br.Method -import org.opalj.br.ObjectType -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.analyses.ConfiguredPurity -import org.opalj.br.fpcf.analyses.ConfiguredPurityKey -import org.opalj.br.fpcf.properties.ClassImmutability_new -import org.opalj.br.fpcf.properties.CompileTimePure -import org.opalj.br.fpcf.properties.DeepImmutableClass -import org.opalj.br.fpcf.properties.DeepImmutableType -import org.opalj.br.fpcf.properties.ImmutableReference -import org.opalj.br.fpcf.properties.ImpureByAnalysis -import org.opalj.br.fpcf.properties.ImpureByLackOfInformation -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference -import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference -import org.opalj.br.fpcf.properties.MutableReference -import org.opalj.br.fpcf.properties.Pure -import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.properties.ReferenceImmutability -import org.opalj.br.fpcf.properties.SideEffectFree -import org.opalj.br.fpcf.properties.TypeImmutability_new -import org.opalj.br.fpcf.properties.cg.Callees -import org.opalj.collection.immutable.EmptyIntTrieSet -import org.opalj.collection.immutable.IntTrieSet -import org.opalj.fpcf.EOptionP -import org.opalj.fpcf.Entity -import org.opalj.fpcf.FinalEP -import org.opalj.fpcf.FinalP -import org.opalj.fpcf.InterimLUBP -import org.opalj.fpcf.InterimResult -import org.opalj.fpcf.InterimUBP -import org.opalj.fpcf.LBP -import org.opalj.fpcf.ProperPropertyComputationResult -import org.opalj.fpcf.Property -import org.opalj.fpcf.Result -import org.opalj.fpcf.SomeEOptionP -import org.opalj.fpcf.UBPS -import org.opalj.log.GlobalLogContext -import org.opalj.log.OPALLogger -import org.opalj.tac.ArrayLength -import org.opalj.tac.ArrayLoad -import org.opalj.tac.ArrayStore -import org.opalj.tac.Assignment -import org.opalj.tac.BinaryExpr -import org.opalj.tac.Call -import org.opalj.tac.CaughtException -import org.opalj.tac.Checkcast -import org.opalj.tac.ClassConst -import org.opalj.tac.Compare -import org.opalj.tac.DUVar -import org.opalj.tac.DoubleConst -import org.opalj.tac.Expr -import org.opalj.tac.ExprStmt -import org.opalj.tac.FieldRead -import org.opalj.tac.FloatConst -import org.opalj.tac.GetField -import org.opalj.tac.GetStatic -import org.opalj.tac.Goto -import org.opalj.tac.If -import org.opalj.tac.InstanceOf -import org.opalj.tac.IntConst -import org.opalj.tac.InvokedynamicFunctionCall -import org.opalj.tac.InvokedynamicMethodCall -import org.opalj.tac.JSR -import org.opalj.tac.LongConst -import org.opalj.tac.MethodHandleConst -import org.opalj.tac.MethodTypeConst -import org.opalj.tac.MonitorEnter -import org.opalj.tac.MonitorExit -import org.opalj.tac.New -import org.opalj.tac.NewArray -import org.opalj.tac.NonVirtualFunctionCall -import org.opalj.tac.NonVirtualMethodCall -import org.opalj.tac.Nop -import org.opalj.tac.NullExpr -import org.opalj.tac.Param -import org.opalj.tac.PrefixExpr -import org.opalj.tac.PrimitiveTypecastExpr -import org.opalj.tac.PutField -import org.opalj.tac.PutStatic -import org.opalj.tac.Ret -import org.opalj.tac.Return -import org.opalj.tac.ReturnValue -import org.opalj.tac.StaticFunctionCall -import org.opalj.tac.StaticMethodCall -import org.opalj.tac.Stmt -import org.opalj.tac.StringConst -import org.opalj.tac.Switch -import org.opalj.tac.TACMethodParameter -import org.opalj.tac.TACode -import org.opalj.tac.Throw -import org.opalj.tac.Var -import org.opalj.tac.VirtualFunctionCall -import org.opalj.tac.VirtualMethodCall -import org.opalj.tac.fpcf.analyses.cg.uVarForDefSites -import org.opalj.tac.fpcf.properties.TACAI -import org.opalj.value.ValueInformation - -import scala.annotation.switch - -/** - * Base trait for analyses that analyze the purity of methods. - * - * Provides types and methods needed for purity analyses. - */ -trait AbstractPurityAnalysis_new extends FPCFAnalysis { - - /** The type of the TAC domain. */ - type V = DUVar[ValueInformation] - - /** - * The state of the analysis. - * Analyses are expected to extend this trait with the information they need. - * - * lbPurity - The current minimum possible purity level for the method - * ubPurity - The current maximum purity level for the method - * method - The currently analyzed method - * declClass - The declaring class of the currently analyzed method - * code - The code of the currently analyzed method - */ - trait AnalysisState { - var lbPurity: Purity - var ubPurity: Purity - val method: Method - val definedMethod: DeclaredMethod - val declClass: ObjectType - var pcToIndex: Array[Int] - var code: Array[Stmt[V]] - } - - type StateType <: AnalysisState - - protected[this] def raterFqn: String - - val rater: DomainSpecificRater - - implicit protected[this] val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) - - val configuredPurity: ConfiguredPurity = project.get(ConfiguredPurityKey) - - /** - * Reduces the maxPurity of the current method to at most the given purity level. - */ - def reducePurityLB(newLevel: Purity)(implicit state: StateType): Unit = { - state.lbPurity = state.lbPurity meet newLevel - } - - /** - * Reduces the minPurity and maxPurity of the current method to at most the given purity level. - */ - def atMost(newLevel: Purity)(implicit state: StateType): Unit = { - state.lbPurity = state.lbPurity meet newLevel - state.ubPurity = state.ubPurity meet newLevel - } - - /** - * Examines whether the given expression denotes an object/array that is local to the current - * method, i.e. the method has control over the object/array and actions on it might not - * influence purity. - * - * @param otherwise The maxPurity will be reduced to at most this level if the expression is not - * local. - */ - def isLocal( - expr: Expr[V], - otherwise: Purity, - excludedDefSites: IntTrieSet = EmptyIntTrieSet - )(implicit state: StateType): Boolean - - /** - * Checks whether the statement, which is the origin of an exception, directly created the - * exception or if the VM instantiated the exception. Here, we are only concerned about the - * exceptions thrown by the instructions not about exceptions that are transitively thrown; - * e.g. if a method is called. - * TODO We need this method because currently, for exceptions that terminate the method, no - * definitions are recorded. Once this is done, use that information instead to determine - * whether it may be an immediate exception or not. - */ - def isSourceOfImmediateException(origin: ValueOrigin)(implicit state: StateType): Boolean = { - - def evaluationMayCauseVMLevelException(expr: Expr[V]): Boolean = { - (expr.astID: @switch) match { - - case NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ - val rcvr = expr.asInstanceFunctionCall.receiver - !rcvr.isVar || rcvr.asVar.value.asReferenceValue.isNull.isYesOrUnknown - - case StaticFunctionCall.ASTID ⇒ false - - case _ ⇒ true - } - } - - val stmt = state.code(origin) - (stmt.astID: @switch) match { - case StaticMethodCall.ASTID ⇒ false // We are looking for implicit exceptions only - - case Throw.ASTID ⇒ - stmt.asThrow.exception.asVar.value.asReferenceValue.isNull.isNotNo - - case NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ - val rcvr = stmt.asInstanceMethodCall.receiver - !rcvr.isVar || rcvr.asVar.value.asReferenceValue.isNull.isNotNo - - case Assignment.ASTID ⇒ evaluationMayCauseVMLevelException(stmt.asAssignment.expr) - - case ExprStmt.ASTID ⇒ evaluationMayCauseVMLevelException(stmt.asExprStmt.expr) - - case _ ⇒ true - } - } - - /** - * Examines whether a call constitutes a domain-specific action using the domain-specific rater. - * If it is, the maxPurity will be reduced to at most the domain-specific purity returned by the - * domain-specific rater. - */ - def isDomainSpecificCall( - call: Call[V], - receiver: Option[Expr[V]] - )(implicit state: StateType): Boolean = { - implicit val code: Array[Stmt[V]] = state.code - val ratedResult = rater.handleCall(call, receiver) - if (ratedResult.isDefined) - atMost(ratedResult.get) - ratedResult.isDefined - } - - /** - * Examines a statement for its influence on the method's purity. - * This method will return false for impure statements, so evaluation can be terminated early. - */ - def checkPurityOfStmt(stmt: Stmt[V])(implicit state: StateType): Boolean = { - val isStmtNotImpure = (stmt.astID: @switch) match { - // For method calls, purity will be checked later - case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ - true - - // We don't handle unresolved Invokedynamics - // - either OPAL removes it or we forget about it - case InvokedynamicMethodCall.ASTID ⇒ - atMost(ImpureByAnalysis) - false - - // Returning objects/arrays is pure, if the returned object/array is locally initialized - // and non-escaping or the object is immutable - case ReturnValue.ASTID ⇒ - checkPurityOfReturn(stmt.asReturnValue.expr) - true - case Throw.ASTID ⇒ - checkPurityOfReturn(stmt.asThrow.exception) - true - - // Synchronization on non-escaping locally initialized objects/arrays is pure (and - // useless...) - case MonitorEnter.ASTID | MonitorExit.ASTID ⇒ - isLocal(stmt.asSynchronizationStmt.objRef, ImpureByAnalysis) - - // Storing into non-escaping locally initialized objects/arrays is pure - case ArrayStore.ASTID ⇒ isLocal(stmt.asArrayStore.arrayRef, ImpureByAnalysis) - case PutField.ASTID ⇒ isLocal(stmt.asPutField.objRef, ImpureByAnalysis) - - case PutStatic.ASTID ⇒ - // Note that a putstatic is not necessarily pure/sideeffect free, even if it - // is executed within a static initializer to initialize a field of - // `the` class; it is possible that the initialization triggers the - // initialization of another class which reads the value of this static field. - // See - // https://stackoverflow.com/questions/6416408/static-circular-dependency-in-java - // for an in-depth discussion. - // (Howevever, if we would check for cycles, we could determine that it is pure, - // but this is not considered to be too useful...) - atMost(ImpureByAnalysis) - false - - // Creating implicit exceptions is side-effect free (because of fillInStackTrace) - // but it may be ignored as domain-specific - case CaughtException.ASTID ⇒ - for { - origin ← stmt.asCaughtException.origins - if isImmediateVMException(origin) - } { - val baseOrigin = state.code(ai.underlyingPC(origin)) - val ratedResult = rater.handleException(baseOrigin) - if (ratedResult.isDefined) atMost(ratedResult.get) - else atMost(SideEffectFree) - } - true - - // Reference comparisons may have different results for structurally equal values - case If.ASTID ⇒ - val If(_, left, _, right, _) = stmt - if (left.cTpe eq ComputationalTypeReference) - if (!(isLocal(left, CompileTimePure) || isLocal(right, CompileTimePure))) - atMost(SideEffectFree) - true - - // The following statements do not further influence purity - case Goto.ASTID | JSR.ASTID | Ret.ASTID | Switch.ASTID | Assignment.ASTID | Return.ASTID | - Nop.ASTID | ExprStmt.ASTID | Checkcast.ASTID ⇒ - true - } - - isStmtNotImpure && stmt.forallSubExpressions(checkPurityOfExpr) - } - - /** - * Examines an expression for its influence on the method's purity. - * This method will return false for impure expressions, so evaluation can be terminated early. - */ - def checkPurityOfExpr(expr: Expr[V])(implicit state: StateType): Boolean = { - val isExprNotImpure = (expr.astID: @switch) match { - // For function calls, purity will be checked later - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ - true - - // Field/array loads are pure if the field is (effectively) final or the object/array is - // local and non-escaping - case GetStatic.ASTID ⇒ - implicit val code: Array[Stmt[V]] = state.code - val ratedResult = rater.handleGetStatic(expr.asGetStatic) - if (ratedResult.isDefined) atMost(ratedResult.get) - else checkPurityOfFieldRef(expr.asGetStatic) - true - case GetField.ASTID ⇒ - checkPurityOfFieldRef(expr.asGetField) - true - case ArrayLoad.ASTID ⇒ - if (state.ubPurity.isDeterministic) - isLocal(expr.asArrayLoad.arrayRef, SideEffectFree) - true - - // We don't handle unresolved Invokedynamic - // - either OPAL removes it or we forget about it - case InvokedynamicFunctionCall.ASTID ⇒ - atMost(ImpureByAnalysis) - false - - // The following expressions do not further influence purity, potential exceptions are - // handled explicitly - case New.ASTID | NewArray.ASTID | InstanceOf.ASTID | Compare.ASTID | Param.ASTID | - MethodTypeConst.ASTID | MethodHandleConst.ASTID | IntConst.ASTID | LongConst.ASTID | - FloatConst.ASTID | DoubleConst.ASTID | StringConst.ASTID | ClassConst.ASTID | - NullExpr.ASTID | BinaryExpr.ASTID | PrefixExpr.ASTID | PrimitiveTypecastExpr.ASTID | - ArrayLength.ASTID | Var.ASTID ⇒ - true - - } - - isExprNotImpure && expr.forallSubExpressions(checkPurityOfExpr) - } - - def checkPurityOfMethod( - callee: DeclaredMethod, - params: Seq[Expr[V]] - )(implicit state: StateType): Boolean = { - if (callee.hasSingleDefinedMethod && (callee.definedMethod eq state.method)) { - true - } else { - val calleePurity = propertyStore(callee, Purity.key) - checkMethodPurity(calleePurity, params) - } - } - - def getCall(stmt: Stmt[V])(implicit state: StateType): Call[V] = stmt.astID match { - case StaticMethodCall.ASTID ⇒ stmt.asStaticMethodCall - case NonVirtualMethodCall.ASTID ⇒ stmt.asNonVirtualMethodCall - case VirtualMethodCall.ASTID ⇒ stmt.asVirtualMethodCall - case Assignment.ASTID ⇒ stmt.asAssignment.expr.asFunctionCall - case ExprStmt.ASTID ⇒ stmt.asExprStmt.expr.asFunctionCall - case CaughtException.ASTID ⇒ - /* - * There is no caught exception instruction in bytecode, so it might be the case, that - * in the three-address code, there is a CaughtException stmt right before the call - * with the same pc. Therefore, we have to get the call stmt after the current stmt. - * - * Example: - * void foo() { - * try { - * ... - * } catch (Exception e) { - * e.printStackTrace(); - * } - * } - * - * In TAC: - * 12: pc=52 caught java.lang.Exception ... - * 13: pc=52 java.lang.Exception.printStackTrace() - */ - getCall(state.code(state.pcToIndex(stmt.pc) + 1)) - case _ ⇒ - throw new IllegalStateException(s"unexpected stmt $stmt") - } - - /** - * Examines the influence of the purity property of a method on the examined method's purity. - * - * @note Adds dependendies when necessary. - */ - def checkMethodPurity( - ep: EOptionP[DeclaredMethod, Purity], - params: Seq[Expr[V]] = Seq.empty - )(implicit state: StateType): Boolean - - /** - * Examines whether a field read influences a method's purity. - * Reading values from fields that are not (effectively) final may cause nondeterministic - * behavior, so the method can only be side-effect free. - */ - def checkPurityOfFieldRef( - fieldRef: FieldRead[V] - )(implicit state: StateType): Unit = { - // Don't do dependee checks if already non-deterministic - if (state.ubPurity.isDeterministic) { - fieldRef.asFieldRead.resolveField match { - case Some(field) if field.isStatic ⇒ - checkFieldMutability(propertyStore(field, ReferenceImmutability.key), None) //FieldMutability.key), None) - case Some(field) ⇒ - checkFieldMutability( - propertyStore(field, ReferenceImmutability.key), - Some(fieldRef.asGetField.objRef) //FieldMutability.key), Some(fieldRef.asGetField.objRef) - ) - case _ ⇒ // Unknown field - if (fieldRef.isGetField) isLocal(fieldRef.asGetField.objRef, SideEffectFree) - else atMost(SideEffectFree) - } - } - } - - /** - * Examines the influence that a given field mutability has on the method's purity. - */ - def checkFieldMutability( - ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], - objRef: Option[Expr[V]] - )(implicit state: StateType): Unit = { - ep match { - - //case LBP(ImmutableReference(_)) ⇒ - //case LBP(LazyInitializedReference) ⇒ - case LBP(ImmutableReference | LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ - //_: FinalField) ⇒ // Final fields don't impede purity - case FinalP(MutableReference | LazyInitializedNotThreadSafeReference) ⇒ - //_: FinalEP[Field, ReferenceImmutability] ⇒ //FieldMutability] ⇒ // Mutable field - if (objRef.isDefined) { - if (state.ubPurity.isDeterministic) - isLocal(objRef.get, SideEffectFree) - } else atMost(SideEffectFree) - case _ ⇒ - reducePurityLB(SideEffectFree) - if (state.ubPurity.isDeterministic) - handleUnknownFieldMutability(ep, objRef) - - } - } - - /** - * Handles what to do when the mutability of a field is not yet known. - * Analyses must implement this method with the behavior they need, e.g. registering dependees. - */ - def handleUnknownFieldMutability( - ep: EOptionP[Field, ReferenceImmutability], //FieldMutability], - objRef: Option[Expr[V]] - )(implicit state: StateType): Unit - - /** - * Examines the effect of returning a value on the method's purity. - * Returning a reference to a mutable object or array may cause nondeterministic behavior - * as the object/array may be modified between invocations of the method, so the method can - * only be side-effect free. E.g., a given parameter which references a mutable object is - * returned (and not otherwise accessed). - */ - def checkPurityOfReturn(returnValue: Expr[V])(implicit state: StateType): Unit = { - if (returnValue.cTpe != ComputationalTypeReference) - return ; // Only non-primitive return values influence purity. - - if (!state.ubPurity.isDeterministic) - return ; // If the method can't be pure, the return value is not important. - - if (!returnValue.isVar) { - // The expression could refer to further expressions in a non-flat representation. To - // avoid special handling, we just fallback to SideEffectFreeWithoutAllocations here if - // the return value is not local as the analysis is intended to be used on flat - // representations anyway. - isLocal(returnValue, SideEffectFree) - return ; - } - - val value = returnValue.asVar.value.asReferenceValue - if (value.isNull.isYes) - return ; // Null is immutable - - if (value.upperTypeBound.exists(_.isArrayType)) { - // Arrays are always mutable - isLocal(returnValue, SideEffectFree) - return ; - } - - if (value.isPrecise) { // Precise class known, use ClassImmutability - val returnType = value.upperTypeBound.head - - val classImmutability = - propertyStore( - returnType, - ClassImmutability_new.key // ClassImmutability.key - ).asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]] // ClassImmutability]] - checkTypeMutability(classImmutability, returnValue) - - } else { // Precise class unknown, use TypeImmutability - // IMPROVE Use ObjectType once we attach the respective information to ObjectTypes - val returnTypes = value.upperTypeBound - - returnTypes.forall { returnType ⇒ - val typeImmutability = - propertyStore( - returnType, - TypeImmutability_new.key //.key - ).asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]] //TypeImmutability]] - checkTypeMutability(typeImmutability, returnValue) - } - } - } - - /** - * Examines the effect that the mutability of a returned value's type has on the method's - * purity. - */ - def checkTypeMutability( - ep: EOptionP[ObjectType, Property], - returnValue: Expr[V] - )(implicit state: StateType): Boolean = ep match { - // Returning immutable object is pure - case LBP(DeepImmutableType | DeepImmutableClass) ⇒ - true // ImmutableType | ImmutableObject) ⇒ true7 - case _: FinalEP[ObjectType, Property] ⇒ - atMost(Pure) // Can not be compile time pure if mutable object is returned - if (state.ubPurity.isDeterministic) - isLocal(returnValue, SideEffectFree) - false // Return early if we are already side-effect free - case _ ⇒ - reducePurityLB(SideEffectFree) - if (state.ubPurity.isDeterministic) - handleUnknownTypeMutability(ep, returnValue) - true - } - - /** - * Handles what to do when the mutability of a type is not yet known. - * Analyses must implement this method with the behavior they need, e.g. registering dependees. - */ - def handleUnknownTypeMutability( - ep: EOptionP[ObjectType, Property], - expr: Expr[V] - )(implicit state: StateType): Unit - - /** - * Examines the effect that the purity of all potential callees has on the purity of the method. - */ - def checkPurityOfCallees( - calleesEOptP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: StateType - ): Boolean = { - handleCalleesUpdate(calleesEOptP) - calleesEOptP match { - case UBPS(p: Callees, isFinal) ⇒ - if (!isFinal) reducePurityLB(ImpureByAnalysis) - - val hasIncompleteCallSites = - p.incompleteCallSites.exists { pc ⇒ - val index = state.pcToIndex(pc) - if (index < 0) - false // call will not be executed - else { - val call = getCall(state.code(state.pcToIndex(pc))) - !isDomainSpecificCall(call, call.receiverOption) - } - } - if (hasIncompleteCallSites) { - atMost(ImpureByAnalysis) - return false; - } - - val noDirectCalleeIsImpure = p.directCallSites().forall { - case (pc, callees) ⇒ - val index = state.pcToIndex(pc) - if (index < 0) - true // call will not be executed - else { - val call = getCall(state.code(index)) - isDomainSpecificCall(call, call.receiverOption) || - callees.forall { callee ⇒ - checkPurityOfMethod( - callee, - call.receiverOption.orNull +: call.params - ) - } - } - } - if (!noDirectCalleeIsImpure) { - return false - }; - - val noIndirectCalleeIsImpure = p.indirectCallSites().forall { - case (pc, callees) ⇒ - val index = state.pcToIndex(pc) - if (index < 0) - true // call will not be executed - else { - val call = getCall(state.code(index)) - isDomainSpecificCall(call, call.receiverOption) || - callees.forall { callee ⇒ - checkPurityOfMethod( - callee, - p.indirectCallReceiver(pc, callee) - .map(receiver ⇒ uVarForDefSites(receiver, state.pcToIndex)) - .orNull +: - p.indirectCallParameters(pc, callee).map { paramO ⇒ - paramO.map(uVarForDefSites(_, state.pcToIndex)).orNull - } - ) - } - } - } - noIndirectCalleeIsImpure - - case _ ⇒ - reducePurityLB(ImpureByAnalysis) - true - } - } - - /** - * Handles what to do when the set of potential callees changes. - * Analyses must implement this method with the behavior they need, e.g. registering dependees. - */ - def handleCalleesUpdate( - callees: EOptionP[DeclaredMethod, Callees] - )(implicit state: StateType): Unit - - /** - * Handles what to do if the TACAI is not yet final. - */ - def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: StateType): Unit - - /** - * Retrieves and commits the methods purity as calculated for its declaring class type for the - * current DefinedMethod that represents the non-overwritten method in a subtype. - */ - def baseMethodPurity(dm: DefinedMethod): ProperPropertyComputationResult = { - - 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) - case epk ⇒ - InterimResult(dm, ImpureByAnalysis, CompileTimePure, Seq(epk), c) - } - - c(propertyStore(declaredMethods(dm.definedMethod), Purity.key)) - } - - /** - * Determines the purity of the given method. - * - * @param definedMethod A defined method with a body. - */ - def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult - - /** Called when the analysis is scheduled lazily. */ - def doDeterminePurity(e: Entity): ProperPropertyComputationResult = { - e match { - case dm: DefinedMethod if dm.definedMethod.body.isDefined ⇒ - determinePurity(dm) - case dm: DeclaredMethod ⇒ Result(dm, ImpureByLackOfInformation) - case _ ⇒ - throw new IllegalArgumentException(s"$e is not a declared method") - } - } - - /** - * Returns the TACode for a method if available, registering dependencies as necessary. - */ - def getTACAI( - method: Method - )(implicit state: StateType): Option[TACode[TACMethodParameter, V]] = { - propertyStore(method, TACAI.key) match { - case finalEP: FinalEP[Method, TACAI] ⇒ - handleTACAI(finalEP) - finalEP.ub.tac - case eps @ InterimUBP(ub: TACAI) ⇒ - reducePurityLB(ImpureByAnalysis) - handleTACAI(eps) - ub.tac - case epk ⇒ - reducePurityLB(ImpureByAnalysis) - handleTACAI(epk) - None - } - } - - def resolveDomainSpecificRater(fqn: String): DomainSpecificRater = { - import scala.reflect.runtime.universe.runtimeMirror - val mirror = runtimeMirror(getClass.getClassLoader) - try { - val module = mirror.staticModule(fqn) - mirror.reflectModule(module).instance.asInstanceOf[DomainSpecificRater] - } catch { - case ex @ (_: ScalaReflectionException | _: ClassCastException) ⇒ - OPALLogger.error( - "analysis configuration", - "resolve of domain specific rater failed, change "+ - s"org.opalj.fpcf.${this.getClass.getName}.domainSpecificRater in "+ - "ai/reference.conf to an existing DomainSpecificRater implementation", - ex - )(GlobalLogContext) - new BaseDomainSpecificRater // Provide a safe default if resolution failed - } - } - -} From 440d9d769a45afa2a44e05bff2bd5f247aad2ade Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 24 Sep 2020 16:08:36 +0200 Subject: [PATCH 268/327] integrate type Immutability analyses and adapt all of them to the new lattice --- .../TypeImmutabilityAnalysisDemo.scala | 26 ++-- .../AbstractTypeImmutabilityMatcher.scala | 18 +-- OPAL/br/src/main/resources/reference.conf | 6 +- ...scala => L0TypeImmutabilityAnalysis.scala} | 65 +++++---- .../br/fpcf/properties/TypeImmutability.scala | 76 ++++++---- .../properties/TypeImmutability_new.scala | 133 ------------------ .../tac/fpcf/analyses/L1PuritySmokeTest.scala | 10 +- ...scala => L1TypeImmutabilityAnalysis.scala} | 86 ++++++----- 8 files changed, 158 insertions(+), 262 deletions(-) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/{type_mutability => immutability/types}/AbstractTypeImmutabilityMatcher.scala (72%) rename OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/{TypeImmutabilityAnalysis.scala => L0TypeImmutabilityAnalysis.scala} (83%) delete mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/{LxTypeImmutabilityAnalysis_new.scala => L1TypeImmutabilityAnalysis.scala} (81%) 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 index 94723eeb99..8894f4e542 100644 --- 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 @@ -17,7 +17,7 @@ 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_new +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 @@ -26,11 +26,11 @@ 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.EagerLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new +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_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import java.io.IOException /** @@ -54,7 +54,7 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { - import org.opalj.br.fpcf.properties.TypeImmutability_new + import org.opalj.br.fpcf.properties.TypeImmutability import org.opalj.fpcf.EPS import org.opalj.fpcf.Entity var propertyStore: PropertyStore = null @@ -66,11 +66,11 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { propertyStore = analysesManager .runAll( LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, + LazyL2PurityAnalysis, LazyL0FieldReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new, + LazyL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + EagerL1TypeImmutabilityAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyInterProceduralEscapeAnalysis, @@ -85,12 +85,12 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet - val groupedResults = propertyStore.entities(TypeImmutability_new.key). + val groupedResults = propertyStore.entities(TypeImmutability.key). filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) - val order = (eps1: EPS[Entity, TypeImmutability_new], eps2: EPS[Entity, TypeImmutability_new]) ⇒ + val order = (eps1: EPS[Entity, TypeImmutability], eps2: EPS[Entity, TypeImmutability]) ⇒ eps1.e.toString < eps2.e.toString - val mutableTypes = groupedResults(MutableType_new).toSeq.sortWith(order) + 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) 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 72% 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..8003f8508c 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,13 +1,12 @@ /* 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 @@ -31,9 +30,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/OPAL/br/src/main/resources/reference.conf b/OPAL/br/src/main/resources/reference.conf index 24d814f92c..826d32fbe0 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 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 83% 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..42a148f299 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,15 @@ 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.DeepImmutableType +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.ImmutableContainerType +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 +41,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 @@ -92,22 +94,22 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal val thisLB = lb.correspondingTypeImmutability InterimResult(t, thisLB, thisUB, Seq(eps), c) case epk ⇒ - InterimResult(t, MutableType, ImmutableType, Seq(epk), c) + InterimResult(t, MutableType, DeepImmutableType, Seq(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 +123,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) @@ -192,15 +194,16 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal } (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() @@ -223,7 +226,7 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal } } -trait TypeImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +trait L0TypeImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability) @@ -237,8 +240,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 +250,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 +263,8 @@ object EagerTypeImmutabilityAnalysis } -object LazyTypeImmutabilityAnalysis - extends TypeImmutabilityAnalysisScheduler +object LazyL0TypeImmutabilityAnalysis + extends L0TypeImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) @@ -271,9 +274,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/properties/TypeImmutability.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability.scala index 7f1c71fe6d..2f67be5f48 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,11 @@ 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 * 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,31 +26,34 @@ 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 { /** * The key associated with every [[TypeImmutability]] property. */ final val key: PropertyKey[TypeImmutability] = PropertyKey.create( - "org.opalj.TypeImmutability", + "org.opalj.TypeImmutability_new", MutableType ) } @@ -60,45 +62,71 @@ 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 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_new.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala deleted file mode 100644 index 5f510f69b9..0000000000 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_new.scala +++ /dev/null @@ -1,133 +0,0 @@ -/* 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_new extends PropertyMetaInformation { - - final type Self = TypeImmutability_new -} - -/** - * 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 - * 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_new]]. - * - * @author Tobias Peter Roth - * @author Michael Eichberg - */ -sealed trait TypeImmutability_new - extends OrderedProperty - with TypeImmutabilityPropertyMetaInformation_new { - - /** - * Returns the key used by all `TypeImmutability_new` properties. - */ - final def key = TypeImmutability_new.key - - 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_new): TypeImmutability_new -} - -object TypeImmutability_new extends TypeImmutabilityPropertyMetaInformation_new { - - /** - * The key associated with every [[TypeImmutability_new]] property. - */ - final val key: PropertyKey[TypeImmutability_new] = PropertyKey.create( - "org.opalj.TypeImmutability_new", - MutableType_new - ) -} - -/** - * 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 DeepImmutableType extends TypeImmutability_new { - - 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_new): TypeImmutability_new = that -} - -case object DependentImmutableType extends TypeImmutability_new { - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = false - override def isDependentImmutable: Boolean = true - - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (that == MutableType_new || 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 ShallowImmutableType extends TypeImmutability_new { - - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = true - override def isMutable: Boolean = false - override def isDependentImmutable: Boolean = false - - def meet(that: TypeImmutability_new): TypeImmutability_new = - if (that == MutableType_new) - that - else - this - - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - - if (other == DeepImmutableType || other == DependentImmutableType) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") - } - } -} - -case object MutableType_new extends TypeImmutability_new { - - override def isDeepImmutable: Boolean = false - override def isShallowImmutable: Boolean = false - override def isMutable: Boolean = true - override def isDependentImmutable: Boolean = false - - def meet(other: TypeImmutability_new): this.type = this - - override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - - if (other != MutableType_new) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") - } - } -} 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/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L1TypeImmutabilityAnalysis.scala similarity index 81% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L1TypeImmutabilityAnalysis.scala index 38a198c6cf..6451e4d574 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/LxTypeImmutabilityAnalysis_new.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L1TypeImmutabilityAnalysis.scala @@ -1,10 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.immutability +package org.opalj +package tac +package fpcf +package analyses +package immutability -import org.opalj.Answer -import org.opalj.No -import org.opalj.Unknown -import org.opalj.Yes import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.TypeExtensibilityKey @@ -12,16 +12,16 @@ 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_new +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_new +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_new +import org.opalj.br.fpcf.properties.TypeImmutability import org.opalj.fpcf.ELUBP import org.opalj.fpcf.EOptionP import org.opalj.fpcf.EPS @@ -42,13 +42,13 @@ import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP /** - * Determines the mutability of a specific type by checking if all subtypes of a specific + * 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 Peter Roth + * @author Tobias Roth */ -class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FPCFAnalysis { +class L1TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnalysis { def doDetermineTypeImmutability_new( typeExtensibility: ObjectType ⇒ Answer @@ -71,7 +71,7 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP val te = typeExtensibility(t) te match { case Yes | Unknown ⇒ - Result(t, MutableType_new) // MutableType) + Result(t, MutableType) case No ⇒ step2(t) } } @@ -85,7 +85,7 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP val c = new ProperOnUpdateContinuation { c ⇒ def apply(eps: SomeEPS): ProperPropertyComputationResult = { eps match { - case ELUBP(_, lb: ClassImmutability_new, ub: ClassImmutability_new) ⇒ + case ELUBP(_, lb: ClassImmutability, ub: ClassImmutability) ⇒ val thisLB = lb.correspondingTypeImmutability val thisUB = ub.correspondingTypeImmutability if (eps.isFinal) @@ -96,7 +96,7 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP } } - val resultToMatch = ps(t, ClassImmutability_new.key) + val resultToMatch = ps(t, ClassImmutability.key) resultToMatch match { case x @ FinalP(p) ⇒ { Result(t, p.correspondingTypeImmutability); @@ -107,19 +107,19 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP val thisLB = lb.correspondingTypeImmutability InterimResult(t, thisLB, thisUB, Seq(eps), c) case epk ⇒ { - InterimResult(t, MutableType_new, DeepImmutableType, Seq(epk), c) + InterimResult(t, MutableType, DeepImmutableType, Seq(epk), c) } } } else { var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] - var joinedImmutability: TypeImmutability_new = DeepImmutableType //ImmutableType // this may become "Mutable..." - var maxImmutability: TypeImmutability_new = DeepImmutableType // ImmutableType + var joinedImmutability: TypeImmutability = DeepImmutableType //this may become "Mutable..." + var maxImmutability: TypeImmutability = DeepImmutableType - val resultToMatch2 = ps(t, ClassImmutability_new.key) + val resultToMatch2 = ps(t, ClassImmutability.key) resultToMatch2 match { case FinalP(DeepImmutableClass) ⇒ case FinalP(MutableClass) ⇒ - return Result(t, MutableType_new); + return Result(t, MutableType); case FinalP(ShallowImmutableClass) ⇒ joinedImmutability = ShallowImmutableType maxImmutability = ShallowImmutableType @@ -134,15 +134,15 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP dependencies += (t -> eps) case eOptP ⇒ - joinedImmutability = MutableType_new //MutableType + joinedImmutability = MutableType dependencies += (t -> eOptP) } directSubtypes foreach { subtype ⇒ - ps(subtype, TypeImmutability_new.key) match { + ps(subtype, TypeImmutability.key) match { case FinalP(DeepImmutableType) ⇒ - case UBP(MutableType_new) ⇒ - return Result(t, MutableType_new); + case UBP(MutableType) ⇒ + return Result(t, MutableType); case FinalP(ShallowImmutableType) ⇒ joinedImmutability = joinedImmutability.meet(ShallowImmutableType) @@ -159,7 +159,7 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP dependencies += ((subtype, eps)) case epk ⇒ - joinedImmutability = MutableType_new + joinedImmutability = MutableType dependencies += ((subtype, epk)) } } @@ -190,13 +190,14 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP val n = depIt.next() if (n.hasLBP) n.lb match { - case lb: TypeImmutability_new ⇒ + case lb: TypeImmutability ⇒ joinedImmutability = joinedImmutability.meet(lb) - case lb: ClassImmutability_new ⇒ - joinedImmutability = joinedImmutability.meet(lb.correspondingTypeImmutability) + case lb: ClassImmutability ⇒ + joinedImmutability = + joinedImmutability.meet(lb.correspondingTypeImmutability) } else { - joinedImmutability = MutableType_new + joinedImmutability = MutableType continue = false } } @@ -220,8 +221,8 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP dependencies = dependencies - e nextResult() - case UBP(x) if (x == MutableType_new || x == MutableClass) ⇒ - Result(t, MutableType_new) //MutableType) + case UBP(x) if (x == MutableType || x == MutableClass) ⇒ + Result(t, MutableType) //MutableType) case FinalEP(e, x) if (x == ShallowImmutableType || x == ShallowImmutableClass) ⇒ maxImmutability = ShallowImmutableType @@ -236,9 +237,9 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP case eps @ InterimEUBP(e, subtypeP) ⇒ dependencies = dependencies.updated(e, eps) subtypeP match { - case subtypeP: TypeImmutability_new ⇒ + case subtypeP: TypeImmutability ⇒ maxImmutability = maxImmutability.meet(subtypeP) - case subtypeP: ClassImmutability_new ⇒ + case subtypeP: ClassImmutability ⇒ maxImmutability = maxImmutability.meet(subtypeP.correspondingTypeImmutability) } nextResult() @@ -250,12 +251,12 @@ class LxTypeImmutabilityAnalysis_new( final val project: SomeProject) extends FP } } -trait TypeImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { +trait L1TypeImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability_new) + final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability) final override def uses: Set[PropertyBounds] = - PropertyBounds.lubs(ClassImmutability_new, TypeImmutability_new) + PropertyBounds.lubs(ClassImmutability, TypeImmutability) } @@ -264,8 +265,7 @@ trait TypeImmutabilityAnalysisScheduler_new extends FPCFAnalysisScheduler { * * @author Michael Eichberg */ -object EagerLxTypeImmutabilityAnalysis_new - extends TypeImmutabilityAnalysisScheduler_new +object EagerL1TypeImmutabilityAnalysis extends L1TypeImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -274,10 +274,7 @@ object EagerLxTypeImmutabilityAnalysis_new override def start(project: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val typeExtensibility = project.get(TypeExtensibilityKey) - val analysis = new LxTypeImmutabilityAnalysis_new(project) - //val allProjectClassFilesIterator = project.allProjectClassFiles - //val types = allProjectClassFilesIterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) - // + val analysis = new L1TypeImmutabilityAnalysis(project) val types = project.allClassFiles.iterator.filter(_.thisType ne ObjectType.Object).map(_.thisType) ps.scheduleEagerComputationsForEntities(types) { analysis.step1(typeExtensibility) @@ -286,8 +283,7 @@ object EagerLxTypeImmutabilityAnalysis_new } } -object LazyLxTypeImmutabilityAnalysis_new - extends TypeImmutabilityAnalysisScheduler_new +object LazyL1TypeImmutabilityAnalysis extends L1TypeImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) @@ -298,10 +294,10 @@ object LazyLxTypeImmutabilityAnalysis_new */ override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val typeExtensibility = p.get(TypeExtensibilityKey) - val analysis = new LxTypeImmutabilityAnalysis_new(p) + val analysis = new L1TypeImmutabilityAnalysis(p) val analysisRunner: ProperPropertyComputation[Entity] = analysis.doDetermineTypeImmutability_new(typeExtensibility) - ps.registerLazyPropertyComputation(TypeImmutability_new.key, analysisRunner) + ps.registerLazyPropertyComputation(TypeImmutability.key, analysisRunner) analysis } } From 9097265f274839b9c5e776288b966240647ab87c Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 24 Sep 2020 16:10:56 +0200 Subject: [PATCH 269/327] integrate field immutability analyses and adapt all of them to the new lattice --- .../FieldImmutabilityAnalysisDemo.scala | 16 +- ...eldReferenceImmutabilityAnalysisDemo.scala | 16 +- .../opalj/fpcf/FieldImmutabilityTests.scala | 91 ++- .../fields/FieldImmutabilityMatcher.scala | 34 +- .../fields/MutableFieldMatcher.scala} | 21 +- ...cala => L0FieldImmutabilityAnalysis.scala} | 56 +- .../src/main/scala/org/opalj/tac/Expr.scala | 3 +- ...cala => L1FieldImmutabilityAnalysis.scala} | 57 +- ...cala => L2FieldImmutabilityAnalysis.scala} | 129 ++-- ...cala => L3FieldImmutabilityAnalysis.scala} | 574 ++++++++++-------- 10 files changed, 549 insertions(+), 448 deletions(-) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/{field_mutability/NonFinalMatcher.scala => immutability/fields/MutableFieldMatcher.scala} (77%) rename OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/{L0FieldMutabilityAnalysis.scala => L0FieldImmutabilityAnalysis.scala} (72%) rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/{L1FieldMutabilityAnalysis.scala => L1FieldImmutabilityAnalysis.scala} (88%) rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/{L2FieldMutabilityAnalysis.scala => L2FieldImmutabilityAnalysis.scala} (90%) rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/{L0FieldImmutabilityAnalysis.scala => L3FieldImmutabilityAnalysis.scala} (69%) 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 index c89cc88300..33546a54f8 100644 --- 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 @@ -21,11 +21,11 @@ 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.EagerL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +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_new +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 @@ -69,10 +69,10 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { .runAll( LazyL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - EagerL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new, + LazyL2PurityAnalysis, + EagerL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyInterProceduralEscapeAnalysis, 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 index b9819ed85c..df92b6420e 100644 --- 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 @@ -19,11 +19,11 @@ 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.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +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_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import java.io.IOException import java.io.BufferedWriter import java.io.FileWriter @@ -71,11 +71,11 @@ object FieldReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication propertyStore = analysesManager .runAll( EagerL0FieldReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new, + LazyL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, + LazyL2PurityAnalysis, LazyInterProceduralEscapeAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, 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 index ee07f34f05..ddfaccbeed 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -3,10 +3,6 @@ package org.opalj package fpcf import java.net.URL -import com.typesafe.config.Config -import com.typesafe.config.ConfigValueFactory -import org.opalj.br.analyses.cg.InitialEntryPointsKey -import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey import org.opalj.br.analyses.Project import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis @@ -16,28 +12,29 @@ 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.EagerL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +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_new +import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.tac.fpcf.analyses.EagerL2FieldImmutabilityAnalysis /** - * Tests the field immutability analysis + * Tests the field immutability analyses * - * @author Tobias Peter Roth + * @author Tobias Roth */ class FieldImmutabilityTests extends PropertiesTest { - + /* override def withRT = true override def fixtureProjectPackage: List[String] = { List("org/opalj/fpcf/fixtures/immutability") - } - + }*/ + /* override def createConfig(): Config = { import com.typesafe.config.ConfigValueFactory.fromAnyRef val configForEntryPoints = BaseConfig.withValue( @@ -64,19 +61,75 @@ class FieldImmutabilityTests extends PropertiesTest { "org.opalj.br.analyses.cg.ConfiguredExtensibleClasses" )) } +*/ + + override def withRT = true + + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/immutability") + } override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + 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")) + } + + describe("the org.opalj.fpcf.analyses.L0FieldMutabilityAnalysis is executed") { + import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis + + 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") { + import org.opalj.tac.fpcf.analyses.EagerL1FieldImmutabilityAnalysis + + 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")) } @@ -85,10 +138,10 @@ class FieldImmutabilityTests extends PropertiesTest { Set( LazyL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - EagerL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new, + LazyL2PurityAnalysis, + EagerL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyInterProceduralEscapeAnalysis, 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 index 1e2af11528..80f89aec6a 100644 --- 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 @@ -1,22 +1,27 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf -package properties -package immutability -package fields +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.analyses.SomeProject import org.opalj.br.fpcf.properties.FieldImmutability +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher +/*import org.opalj.br.fpcf.properties.FieldMutability +import org.opalj.br.fpcf.properties.LazyInitializedField*/ /** - * This is the basis for the matchers that match the immutability of a field - * @author Tobias Peter Roth + * 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 Michael Eichberg + * @author Dominik Helm */ class FieldImmutabilityMatcher(val property: FieldImmutability) extends AbstractPropertyMatcher { - final private val PropertyReasonID = 0 + private final val PropertyReasonID = 0 override def isRelevant( p: SomeProject, @@ -31,7 +36,6 @@ class FieldImmutabilityMatcher(val property: FieldImmutability) extends Abstract val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) analyses.exists(as.contains) - } def validateProperty( @@ -48,12 +52,16 @@ class FieldImmutabilityMatcher(val property: FieldImmutability) extends Abstract None } } + } +/* +class DeclaredFinalMatcher extends FieldImmutabilityMatcher(ShallowImmutableField) -class MutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.MutableField) +class EffectivelyFinalMatcher extends FieldImmutabilityMatcher(ShallowImmutableField) */ -class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.ShallowImmutableField) +//class MutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.MutableField) +class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.ShallowImmutableField) //ShallowImmutableField) -class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.DependentImmutableField) +class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.ShallowImmutableField) +class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.ShallowImmutableField) -class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.DeepImmutableField) 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/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/tac/src/main/scala/org/opalj/tac/Expr.scala b/OPAL/tac/src/main/scala/org/opalj/tac/Expr.scala index d658edcce5..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(); @@ -178,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 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 90% 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..214e3fa950 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,26 +8,13 @@ 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 @@ -35,8 +22,6 @@ 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 +52,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 +65,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 +77,33 @@ 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 ) { 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) + fieldImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) } } @@ -127,8 +115,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 +129,25 @@ 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); + 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) + 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 +155,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 +165,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). @@ -190,7 +179,7 @@ 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); + 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 @@ -217,7 +206,7 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext taCode ← getTACAI(method, pcs) } { if (methodUpdatesField(method, taCode, pcs)) - return Result(field, NonFinalFieldByAnalysis); + return Result(field, MutableField); } if (state.lazyInitInvocation.isDefined) { @@ -250,12 +239,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 +325,16 @@ 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) + Result(state.field, state.fieldImmutability) } /** @@ -374,15 +363,15 @@ 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) + Result(state.field, MutableField) else createResult() } @@ -554,7 +543,7 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext 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) + if (state.fieldImmutability == ShallowImmutableField) //LazyInitializedField) return true; // A lazily initialized instance field must be initialized only @@ -579,7 +568,7 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext )) 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 +750,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 +767,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 } @@ -1055,18 +1044,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 +1069,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 analysis = new L2FieldImmutabilityAnalysis(p) val fields = p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldMutability) + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) analysis } @@ -1110,8 +1099,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 +1108,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/immutability/L0FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L3FieldImmutabilityAnalysis.scala similarity index 69% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L3FieldImmutabilityAnalysis.scala index 2324a9035f..194dfc753c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L0FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L3FieldImmutabilityAnalysis.scala @@ -1,5 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.immutability +package org.opalj +package tac +package fpcf +package analyses +package immutability import org.opalj.br.Attribute import org.opalj.br.ClassSignature @@ -25,22 +29,21 @@ 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.ImmutableReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference -import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference +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.MutableReference -import org.opalj.br.fpcf.properties.MutableType_new -import org.opalj.br.fpcf.properties.ReferenceImmutability +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_new +import org.opalj.br.fpcf.properties.TypeImmutability 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.ProperPropertyComputationResult import org.opalj.fpcf.Property @@ -49,80 +52,70 @@ import org.opalj.fpcf.PropertyComputationResult import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS -import org.opalj.tac.fpcf.analyses.immutability.DependentImmutabilityKind.DependentImmutabilityKind import org.opalj.br.Method import org.opalj.br.fpcf.properties.EscapeProperty -import org.opalj.tac.NonVirtualMethodCall -import org.opalj.tac.TACMethodParameter -import org.opalj.tac.TACode import org.opalj.tac.fpcf.analyses.AbstractIFDSAnalysis.V import org.opalj.tac.common.DefinitionSitesKey import org.opalj.collection.immutable.IntTrieSet -import org.opalj.br.fpcf.properties.AtMost -import org.opalj.br.fpcf.properties.EscapeInCallee -import org.opalj.br.fpcf.properties.EscapeViaReturn import org.opalj.br.fpcf.properties.NoEscape -import org.opalj.fpcf.InterimUBP import org.opalj.tac.common.DefinitionSite import org.opalj.tac.fpcf.properties.TACAI -import org.opalj.tac.Stmt import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.tac.StaticFunctionCall -import org.opalj.Yes -import org.opalj.tac.Expr import org.opalj.br.PCs -import org.opalj.br.fpcf.properties.ClassImmutability_new +import org.opalj.br.fpcf.properties.ClassImmutability import org.opalj.value.ASArrayValue 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 -case class State( - field: Field, - var typeIsImmutable: Option[Boolean] = Some(true), - var referenceIsImmutable: Option[Boolean] = None, - var noEscapePossibilityViaReference: Boolean = true, - var dependentImmutability: Option[DependentImmutabilityKind] = Some(DependentImmutabilityKind.onlyDeepImmutable), - var genericTypeSetNotDeepImmutable: Boolean = false, - 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 escapesStillDetermined: Boolean = false, - var concreteClassTypeIsKnown: Boolean = false, - var totalAmountOfFieldWrites: Int = -1 -) { - def hasDependees: Boolean = { - !dependees.isEmpty || tacDependees.nonEmpty - } - - def getDependees: Traversable[EOptionP[Entity, Property]] = { - dependees ++ tacDependees.valuesIterator.map(_._1) - } -} - -/** - * Describes the different kinds of dependent immutable fields: - * [[DependentImmutabilityKind.dependent]] Shallow or mutable types could still exist - * [[DependentImmutabilityKind.notShallowOrMutable]] There are no shallow or mutable types - * [[DependentImmutabilityKind.onlyDeepImmutable]] There are no generic parameters left. - * All have been replaced with deep immutable types. - */ -object DependentImmutabilityKind extends Enumeration { - type DependentImmutabilityKind = Value - val notShallowOrMutable, dependent, onlyDeepImmutable = Value -} - /** * Analyses that determines the immutability of org.opalj.br.Field - * The information of the reference of the field from the [[L0ReferenceImmutabilityAnalysis]] - * and the information of the type immutability determined by the [[LxTypeImmutabilityAnalysis_new]] + * It uses the results of the [[L3FieldReferenceImmutabilityAnalysis]] + * and of the [[L1TypeImmutabilityAnalysis]] * * @author Tobias Peter Roth */ -class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) +class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { + /** + * Describes the different kinds of dependent immutable fields: + * [[DependentImmutabilityKind.Dependent]] Shallow or mutable types could still exist + * [[DependentImmutabilityKind.NotShallowOrMutable]] There are no shallow or mutable types + * [[DependentImmutabilityKind.OnlyDeepImmutable]] There are no generic parameters left. + * All have been replaced with deep immutable types. + */ + object DependentImmutabilityKind extends Enumeration { + type DependentImmutabilityKind = Value + val NotShallowOrMutable, Dependent, OnlyDeepImmutable = Value + } + + import DependentImmutabilityKind._ + + case class State( + field: Field, + var typeIsImmutable: Boolean = true, + var referenceIsImmutable: Option[Boolean] = None, + var noEscapePossibilityViaReference: Boolean = true, + var dependentImmutability: DependentImmutabilityKind = OnlyDeepImmutable, + var genericTypeSetNotDeepImmutable: Boolean = false, + 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 escapesStillDetermined: Boolean = false, + var concreteClassTypeIsKnown: Boolean = false, + var totalNumberOfFieldWrites: Int = 0 + ) { + + def hasDependees: Boolean = { + !dependees.isEmpty || tacDependees.nonEmpty + } + + def getDependees: Traversable[EOptionP[Entity, Property]] = { + dependees ++ tacDependees.valuesIterator.map(_._1) + } + } final val typeExtensibility = project.get(TypeExtensibilityKey) final val closedPackages = project.get(ClosedPackagesKey) @@ -135,7 +128,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case field: Field ⇒ determineFieldImmutability(field) case _ ⇒ - val m = s"""${entity.getClass.getName} is not an org.opalj.br.Field""" + val m = s"${entity.getClass.getName} is not an org.opalj.br.Field" throw new IllegalArgumentException(m) } } @@ -143,11 +136,11 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) private[analyses] def determineFieldImmutability( field: Field ): ProperPropertyComputationResult = { - //stores the formal type parameters of the fields class or outer class + var classFormalTypeParameters: Option[Set[String]] = None /** - * Loads the formal typeparameters from the classes and outer class signature + * Loads the formal type parameters from the classes and outer class signature */ def loadFormalTypeParameter(): Unit = { var result: Set[String] = Set.empty @@ -156,25 +149,23 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) * * Extract the formal type parameter if it exists of a class attribute */ - def collectFormalTypeParameterFromClassAttribute: Attribute ⇒ Unit = { - attribute ⇒ - attribute match { - case ClassSignature(typeParameters, _, _) ⇒ { - typeParameters.foreach( - _ match { - case FormalTypeParameter(identifier, _, _) ⇒ result += identifier - case _ ⇒ - } - ) + def collectFormalTypeParameterFromClassAttribute(attribute: Attribute): Unit = attribute match { + case ClassSignature(typeParameters, _, _) ⇒ { + typeParameters.foreach( + _ match { + case FormalTypeParameter(identifier, _, _) ⇒ result += identifier + case _ ⇒ } - case _ ⇒ - } + ) + } + case _ ⇒ } /** * If the genericity is nested in an inner class - * collect the generic type parameters from the fields outer class + * collect the generic type parameters from the field's outer class */ + //TODO recursive function for nested if (field.classFile.outerType.isDefined) { val outerClassFile = project.classFile(field.classFile.outerType.get._1) if (outerClassFile.isDefined) { @@ -207,24 +198,24 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def handleTypeImmutability()(implicit state: State): Unit = { val objectType = field.fieldType.asFieldType if (objectType == ObjectType.Object) { - state.typeIsImmutable = Some(false) //handling generic fields + state.typeIsImmutable = false //handling generic fields } else if (objectType.isBaseType || objectType == ObjectType.String) { // we state here the strings deep immutability // base types are by design deep immutable //state.typeImmutability = Some(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.typeIsImmutable = Some(false) + state.typeIsImmutable = false } else { - val result = propertyStore(objectType, TypeImmutability_new.key) + val result = propertyStore(objectType, TypeImmutability.key) result match { case FinalP(DeepImmutableType) ⇒ // deep immutable type is set as default case FinalP(DependentImmutableType) ⇒ - state.typeIsImmutable = Some(false) - case FinalEP(t, ShallowImmutableType | MutableType_new) ⇒ - state.typeIsImmutable = Some(false) + state.typeIsImmutable = false + case FinalEP(t, ShallowImmutableType | MutableType) ⇒ + state.typeIsImmutable = false if (field.fieldType != ObjectType.Object) - state.dependentImmutability = Some(DependentImmutabilityKind.dependent) + state.dependentImmutability = DependentImmutabilityKind.Dependent case epk ⇒ state.dependees += epk } } @@ -279,17 +270,17 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } ) genericParameters.foreach(objectType ⇒ { - val result = propertyStore(objectType, TypeImmutability_new.key) + val result = propertyStore(objectType, TypeImmutability.key) result match { case FinalP(DeepImmutableType) ⇒ //nothing to do here: default value is deep immutable case FinalP(DependentImmutableType) ⇒ onlyDeepImmutableTypesInGenericTypeFound = false - state.typeIsImmutable = Some(false) - case FinalP(ShallowImmutableType | MutableType_new) ⇒ { + state.typeIsImmutable = false + case FinalP(ShallowImmutableType | MutableType) ⇒ { noShallowOrMutableTypesInGenericTypeFound = false onlyDeepImmutableTypesInGenericTypeFound = false - state.typeIsImmutable = Some(false) + state.typeIsImmutable = false } case ep ⇒ state.dependees += ep @@ -299,31 +290,26 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) //Prevents the case of keeping the default values of these // flags only because of no relevant attribute has been found if (!noRelevantAttributesFound) { - /** - * The above defined functions are called - */ - if (state.dependentImmutability.isDefined) { - if (onlyDeepImmutableTypesInGenericTypeFound) { - //nothing to do... - //state.dependentImmutability = Some(DependentImmutabilityKind.onlyDeepImmutable) - } else if (noShallowOrMutableTypesInGenericTypeFound && - state.dependentImmutability != Some(DependentImmutabilityKind.dependent)) - state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) - else state.dependentImmutability = Some(DependentImmutabilityKind.dependent) - } - } else state.dependentImmutability = Some(DependentImmutabilityKind.dependent) + if (onlyDeepImmutableTypesInGenericTypeFound) { + //nothing to do... + } else if (noShallowOrMutableTypesInGenericTypeFound && + state.dependentImmutability != DependentImmutabilityKind.Dependent) { + state.dependentImmutability = DependentImmutabilityKind.NotShallowOrMutable + } else + state.dependentImmutability = DependentImmutabilityKind.Dependent + + } else + state.dependentImmutability = DependentImmutabilityKind.Dependent } /** * Returns the TACode for a method if available, registering dependencies as necessary. */ def getTACAI( - method: Method, - pcs: PCs, - readOrWrite: String + method: Method, + pcs: PCs, + isRead: Boolean )(implicit state: State): Option[TACode[TACMethodParameter, V]] = { - if (readOrWrite != "read" && readOrWrite != "write") - throw new Exception propertyStore(method, TACAI.key) match { case finalEP: FinalEP[Method, TACAI] ⇒ finalEP.ub.tac @@ -334,11 +320,10 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) reads = state.tacDependees(method)._2._1 writes = state.tacDependees(method)._2._2 } - if (readOrWrite == "read") { + if (isRead) { state.tacDependees += method -> ((epk, (reads ++ pcs, writes))) } - if (readOrWrite == "writes") { - state.tacDependees += method -> ((epk, (reads, writes ++ pcs))) + if (!isRead) { state.tacDependees += method -> ((epk, (reads, writes ++ pcs))) } None @@ -350,9 +335,14 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) * @return true if the object - on which a field write occurred - escapes, false otherwise. * @note (Re-)Adds dependees as necessary. */ - def handleEscapeProperty( + def doesItEscapeViaMethod( ep: EOptionP[DefinitionSite, EscapeProperty] )(implicit state: State): Boolean = { + 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.InterimEP + import org.opalj.fpcf.InterimUBP ep match { case FinalP(NoEscape) ⇒ false case InterimUBP(NoEscape) ⇒ @@ -373,18 +363,23 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) /** * Determine if the referenced object can escape either via field reads or writes. */ - def determineEscapePossibilityOfReferencedObjectOrValue()(implicit state: State): Unit = { + def determineEscapeOfReferencedObjectOrValue()(implicit state: State): Unit = { state.escapesStillDetermined = true state.noEscapePossibilityViaReference = state.field.isPrivate if (state.noEscapePossibilityViaReference) { val writes = fieldAccessInformation.writeAccesses(state.field) - state.totalAmountOfFieldWrites = writes.map(x ⇒ x._2.size).fold(0)(_ + _) + + //has to be determined before the following foreach loop because the information is needed + state.totalNumberOfFieldWrites = writes.foldLeft(0)(_ + _._2.size) + //println("total amaount ouf writes: "+state.totalNumberOfFieldWrites) writes.foreach(writeAccess ⇒ { + val method = writeAccess._1 val pcs = writeAccess._2 checkFieldWritesForEffImmutability(method, pcs) }) + //println("state no escape 1: "+state.noEscapePossibilityViaReference) if (state.noEscapePossibilityViaReference) { val reads = fieldAccessInformation.readAccesses(state.field) reads.foreach(read ⇒ { @@ -392,6 +387,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val pcs = read._2 determineEscapePossibilityViaFieldReads(method, pcs) }) + //println("state no escape 2: "+state.noEscapePossibilityViaReference) } } } @@ -401,86 +397,121 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) */ def determineEscapePossibilityViaFieldReads(method: Method, pcs: PCs)(implicit state: State): Unit = { - val taCodeOption = getTACAI(method, pcs, "read") + val taCodeOption = getTACAI(method, pcs, isRead = true) if (taCodeOption.isDefined) { val taCode = taCodeOption.get - pcs.foreach( - 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 - - if (handleEscapeProperty( - propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) - )) { - state.noEscapePossibilityViaReference = false - return ; - } else for { - useSite ← assignment.targetVar.usedBy - } { - val fieldsUseSiteStmt = taCode.stmts(useSite) - if (fieldsUseSiteStmt.isAssignment) { - val assignment = fieldsUseSiteStmt.asAssignment - 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) { - // nothing to do, because it can not be mutated - } else if (innerArrayType.isArrayType) { - state.noEscapePossibilityViaReference = false // to be sound - return ; - } else if (innerArrayType.isObjectType) { - //If a deep immutable object escapes, it can not be mutated - propertyStore(innerArrayType, TypeImmutability_new.key) match { - case FinalP(DeepImmutableType) ⇒ //nothing to to - case FinalP(_) ⇒ - state.noEscapePossibilityViaReference = false - return ; - case ep ⇒ { - state.innerArrayTypes += innerArrayType.asObjectType - state.dependees += ep - } + pcs.foreach(pc ⇒ { + val readIndex = taCode.pcToIndex(pc) + // This if-statement is necessary, because there are -1 elements in the array + //println("readIndex: "+readIndex) + if (readIndex != -1) { + val stmt = taCode.stmts(readIndex) + //println("read stmt: "+stmt) + //println("index: "+taCode.pcToIndex(stmt.pc)) + if (stmt.isAssignment) { + val assignment = stmt.asAssignment + + /*if (doesItEscapeViaMethod( + propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) + )) { + state.noEscapePossibilityViaReference = false + println("false1") + return ; + } else */ for { + useSite ← assignment.targetVar.usedBy + } { + val fieldsUseSiteStmt = taCode.stmts(useSite) + //println("fieldUseSiteStmt: "+fieldsUseSiteStmt) + if (fieldsUseSiteStmt.isAssignment) { + val assignment = fieldsUseSiteStmt.asAssignment + if (assignment.expr.isVirtualFunctionCall) { + // import org.opalj.br.fpcf.properties.Purity + val virtualFunctionCall = assignment.expr.asVirtualFunctionCall + // println("virtualfunction call: "+virtualFunctionCall) + // println("isconst: "+virtualFunctionCall.isIntConst) + //println("field Type: "+field.fieldType) + if (field.fieldType.isObjectType) { + //val m = virtualFunctionCall.resolveCallTargets(field.fieldType.asObjectType) + if (virtualFunctionCall.params.exists(!_.isConst)) { + state.noEscapePossibilityViaReference = false + + //println("for all is const: "+) + //println("...................................."+field.classFile.thisType.simpleName) + //if (field.classFile.thisType.simpleName.contains("EffectivelyImmutableFields")) { + //println("is const: "+virtualFunctionCall.isCompare) + //m.foreach(m ⇒ println()) + //pri// + // ntln("m: "+m) + //throw new Exception("") + } + } + + // val propertyResult = propertyStore(declaredMethods(method), Purity.key) + // println("propertyResult: "+propertyResult) + } 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) { + // nothing to do, because it can not be mutated + } else if (innerArrayType.isArrayType) { + state.noEscapePossibilityViaReference = false // to be sound + // println("false2") + return ; + } else if (innerArrayType.isObjectType) { + //If a deep immutable object escapes, it can not be mutated + propertyStore(innerArrayType, TypeImmutability.key) match { + case FinalP(DeepImmutableType) ⇒ //nothing to to + case FinalP(_) ⇒ + state.noEscapePossibilityViaReference = false + // println("false3") + return ; + case ep ⇒ { + state.innerArrayTypes += innerArrayType.asObjectType + state.dependees += ep } - } else { - state.noEscapePossibilityViaReference = false - return ; } - case _ ⇒ { + } else { state.noEscapePossibilityViaReference = false + // println("false4") return ; } + case _ ⇒ { + state.noEscapePossibilityViaReference = false + // println("false5") + return ; } - } else { - state.noEscapePossibilityViaReference = false - return ; } - } else if (fieldsUseSiteStmt.isMonitorEnter || - fieldsUseSiteStmt.isMonitorExit || - fieldsUseSiteStmt.isIf) { - //nothing to do } else { + // println("assignemtn6"+assignment) state.noEscapePossibilityViaReference = false + // println("false6") return ; } + } else if (fieldsUseSiteStmt.isMonitorEnter || + fieldsUseSiteStmt.isMonitorExit || + fieldsUseSiteStmt.isIf) { + //nothing to do + } else { + state.noEscapePossibilityViaReference = false + // println("false7") + return ; } - } else if (stmt.isExprStmt) { - //nothing to do here, because the value is only read but not assigned to another one - } else { - state.noEscapePossibilityViaReference = false - return ; } + } else if (stmt.isExprStmt) { + //nothing to do here, because the value is only read but not assigned to another one } else { - //nothing to do - // -1 means NOTHING as a placeholder + state.noEscapePossibilityViaReference = false + // println("false8") + return ; } + } else { + //nothing to do + // -1 means NOTHING as a placeholder } - ) + }) } } @@ -499,22 +530,29 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) staticFunctionCall: StaticFunctionCall[V], tacCode: TACode[TACMethodParameter, V] ): Unit = { + /*println( + s""" + | static function call + | params: ${staticFunctionCall.params} + | ${staticFunctionCall.params.map(x ⇒ tacCode.stmts(x.asVar.definedBy.head).asAssignment.expr.isConst)} + |""".stripMargin + )*/ if (staticFunctionCall.params.exists(p ⇒ p.asVar.definedBy.size != 1 || p.asVar.definedBy.head < 0 || !tacCode.stmts(p.asVar.definedBy.head).asAssignment.expr.isConst)) { state.noEscapePossibilityViaReference = false return ; - } + } //else println("ELSE") } def handleKnownClassType(objectType: ObjectType)(implicit state: State): Unit = { state.concreteClassTypeIsKnown = true - val propertyStoreResult = propertyStore(objectType, ClassImmutability_new.key) + val propertyStoreResult = propertyStore(objectType, ClassImmutability.key) propertyStoreResult match { - case FinalP(DeepImmutableClass) ⇒ state.typeIsImmutable = Some(true) - case FinalP(_) ⇒ state.typeIsImmutable = Some(false) + case FinalP(DeepImmutableClass) ⇒ state.typeIsImmutable = true + case FinalP(_) ⇒ state.typeIsImmutable = false case eps ⇒ state.dependees += eps } } @@ -568,7 +606,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (field.fieldType.isObjectType && newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && - state.totalAmountOfFieldWrites == 1) { + state.totalNumberOfFieldWrites == 1) { handleKnownClassType(newStmt.tpe.mostPreciseObjectType) } for (usedSiteIndex ← paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy) { @@ -601,7 +639,6 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (!seen.contains(stmt)) { seen += stmt handlePut(stmt, method, tacCode) - return ; } return ; } else if (stmt.isArrayStore) { @@ -609,7 +646,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) return ; } //else if // other cases that the purity analysis can not handle else { - if (handleEscapeProperty( + if (doesItEscapeViaMethod( propertyStore(definitionSitesOfParam, EscapeProperty.key) )) { state.noEscapePossibilityViaReference = false @@ -627,27 +664,25 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) * Checks if a reference object can escape via a given putfield or putstatic */ def handlePut(putStmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { - var putDefinitionSites: IntTrieSet = IntTrieSet.empty - var putValue: Expr[V] = null - if (putStmt.isPutField) { - val putField = putStmt.asPutField - putDefinitionSites = putField.value.asVar.definedBy - putValue = putField.value - } else if (putStmt.isPutStatic) { - val putStatic = putStmt.asPutStatic - putDefinitionSites = putStatic.value.asVar.definedBy - putValue = putStatic.value - } else { - state.noEscapePossibilityViaReference = false - return ; - } + + val (putDefinitionSites, putValue) = + if (putStmt.isPutField) { + val putField = putStmt.asPutField + (putField.value.asVar.definedBy, putField.value) + } else if (putStmt.isPutStatic) { + val putStatic = putStmt.asPutStatic + (putStatic.value.asVar.definedBy, putStatic.value) + } else { + state.noEscapePossibilityViaReference = false + return ; + } val putValueDefinedByIndex = putValue.asVar.definedBy.head if (putValue.asVar.value.isArrayValue == Yes) { if (putValueDefinedByIndex >= 0) { //necessary - tacCode.stmts(putValueDefinedByIndex).asAssignment.targetVar.usedBy.foreach(x ⇒ { - val arrayStmt = tacCode.stmts(x) + tacCode.stmts(putValueDefinedByIndex).asAssignment.targetVar.usedBy.foreach(usedByIndex ⇒ { + val arrayStmt = tacCode.stmts(usedByIndex) if (arrayStmt != putStmt) if (arrayStmt.isArrayStore) { val arrayStore = arrayStmt.asArrayStore @@ -690,7 +725,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val newStmt = assignedExpr.asNew if (field.fieldType.isObjectType && newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && - state.totalAmountOfFieldWrites == 1) { + state.totalNumberOfFieldWrites == 1) { handleKnownClassType(newStmt.tpe.mostPreciseObjectType) } valueAssignment.targetVar.asVar.usedBy.foreach(index ⇒ { @@ -723,17 +758,20 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) }) } else { state.noEscapePossibilityViaReference = false - return ; } } else for { i ← putDefinitionSites } { - if (i > 0) { //necessary + if (i >= 0) { //necessary val definitionSiteStatement = tacCode.stmts(i) + // println("def site stmt: "+definitionSiteStatement) val definitionSiteAssignment = definitionSiteStatement.asAssignment + // println("def site assignement: "+definitionSiteAssignment) if (definitionSiteAssignment.expr.isStaticFunctionCall) { + // println("handle static function call") handleStaticFunctionCall(definitionSiteAssignment.expr.asStaticFunctionCall, tacCode) + return ; } else if (definitionSiteAssignment.expr.isVar) { val definitionSiteVar = definitionSiteAssignment.expr.asVar for (definitionSiteVarUseSite ← definitionSiteVar.usedBy) { @@ -752,7 +790,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (field.fieldType.isObjectType && newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && - state.totalAmountOfFieldWrites == 1) { + state.totalNumberOfFieldWrites == 1) { handleKnownClassType(newStmt.tpe.mostPreciseObjectType) } if (!method.isConstructor) { @@ -809,7 +847,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) /** * Begin of method check field writes */ - val tacCodeOption = getTACAI(method, pcs, "write") + val tacCodeOption = getTACAI(method, pcs, isRead = false) if (tacCodeOption.isDefined) { val taCode = tacCodeOption.get pcs.foreach( @@ -817,6 +855,7 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val index = taCode.pcToIndex(pc) if (index >= 0) { val stmt = taCode.stmts(index) + // println("stmt: "+stmt) if (!seen.contains(stmt)) { seen += stmt handlePut(stmt, method, taCode) @@ -833,14 +872,32 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) /** * If there are no dependencies left, this method can be called to create the result. */ - def createResult(state: State): ProperPropertyComputationResult = { + def createResult(implicit state: State): ProperPropertyComputationResult = { + /* println( + s""" + | ${state.referenceIsImmutable} + | ${state.typeIsImmutable} + | ${state.concreteClassTypeIsKnown} + | ${state.noEscapePossibilityViaReference} + | ${state.dependentImmutability} + | + |""".stripMargin + ) */ if (state.hasDependees) { + val lowerBound = + if (state.referenceIsImmutable.isDefined && state.referenceIsImmutable.get) ShallowImmutableField + else MutableField + + val upperBound = /*if (state.typeIsImmutable && state.noEscapePossibilityViaReference)*/ + DeepImmutableField + /*else + DependentImmutableField*/ InterimResult( field, - MutableField, - DeepImmutableField, + lowerBound, + upperBound, state.getDependees, - c(state) + c ) } else { if (!state.escapesStillDetermined) @@ -849,22 +906,19 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(false) | None ⇒ Result(field, MutableField) case Some(true) ⇒ { - state.typeIsImmutable match { - case Some(true) ⇒ - Result(field, DeepImmutableField) + if (state.typeIsImmutable) { + Result(field, DeepImmutableField) + } else { + if (state.noEscapePossibilityViaReference) { Result(field, DeepImmutableField) - case Some(false) | None ⇒ { - if (state.noEscapePossibilityViaReference) { - Result(field, DeepImmutableField) - } else { - state.dependentImmutability match { - case Some(DependentImmutabilityKind.notShallowOrMutable) ⇒ - Result(field, DependentImmutableField) - case Some(DependentImmutabilityKind.onlyDeepImmutable) ⇒ - Result(field, DeepImmutableField) - case _ ⇒ { - Result(field, ShallowImmutableField) - } + } else { + state.dependentImmutability match { + case NotShallowOrMutable ⇒ + Result(field, DependentImmutableField) + case OnlyDeepImmutable ⇒ + Result(field, DeepImmutableField) + case _ ⇒ { + Result(field, ShallowImmutableField) } } } @@ -875,41 +929,38 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } - def c(state: State)(eps: SomeEPS): ProperPropertyComputationResult = { + def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { if (eps.asEPS.pk != TACAI.key) - state.dependees = state.dependees.iterator.filter(_.e ne eps.e).toSet + state.dependees = state.dependees.filter(_.e ne eps.e) eps match { - /*case _: InterimEP[_, _] ⇒ { - state.dependees += eps - InterimResult(field, MutableField, DeepImmutableField, state.dependees, c(state)) - }*/ + /**/ case FinalP(DeepImmutableType) ⇒ //nothing to do -> is default - case FinalEP(t, MutableType_new | ShallowImmutableType) ⇒ - state.typeIsImmutable = Some(false) + case FinalEP(t, MutableType | ShallowImmutableType) ⇒ + state.typeIsImmutable = false if (t != ObjectType.Object) { // in case of generic fields - state.dependentImmutability = Some(DependentImmutabilityKind.dependent) + state.dependentImmutability = DependentImmutabilityKind.Dependent } if (t.isInstanceOf[ObjectType] && state.innerArrayTypes.contains(t.asInstanceOf[ObjectType])) state.noEscapePossibilityViaReference = false case FinalEP(t, DependentImmutableType) ⇒ { - state.typeIsImmutable = Some(false) - if (t != field.fieldType && state.dependentImmutability == Some(DependentImmutabilityKind.onlyDeepImmutable)) - state.dependentImmutability = Some(DependentImmutabilityKind.notShallowOrMutable) + state.typeIsImmutable = false + if (t != field.fieldType && state.dependentImmutability == OnlyDeepImmutable) + state.dependentImmutability = DependentImmutabilityKind.NotShallowOrMutable if (t.isInstanceOf[ObjectType] && state.innerArrayTypes.contains(t.asInstanceOf[ObjectType])) state.noEscapePossibilityViaReference = false } case FinalP( - MutableReference | LazyInitializedNotThreadSafeReference + MutableFieldReference | LazyInitializedNotThreadSafeFieldReference ) ⇒ { - state.typeIsImmutable = Some(false) + state.typeIsImmutable = false state.referenceIsImmutable = Some(false) return Result(field, MutableField); } case FinalP( - ImmutableReference | - LazyInitializedThreadSafeReference | - LazyInitializedNotThreadSafeButDeterministicReference + ImmutableFieldReference | + LazyInitializedThreadSafeFieldReference | + LazyInitializedNotThreadSafeButDeterministicFieldReference ) ⇒ { state.referenceIsImmutable = Some(true) } @@ -929,26 +980,26 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } case eps if eps.isFinal && eps.asEPS.pk == EscapeProperty.key ⇒ - if (handleEscapeProperty(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) + if (doesItEscapeViaMethod(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) state.noEscapePossibilityViaReference = false - case FinalP(DeepImmutableClass) ⇒ state.typeIsImmutable = Some(true) - case FinalP(ShallowImmutableClass | DependentImmutableClass | MutableClass) ⇒ state.typeIsImmutable = Some(false) + case FinalP(DeepImmutableClass) ⇒ state.typeIsImmutable = true + case FinalP(ShallowImmutableClass | DependentImmutableClass | MutableClass) ⇒ state.typeIsImmutable = false case eps ⇒ state.dependees += eps } - createResult(state) + createResult } /** * Begin of determine field immutability function */ implicit val state: State = State(field) - val referenceImmutabilityPropertyStoreResult = propertyStore(state.field, ReferenceImmutability.key) + val referenceImmutabilityPropertyStoreResult = propertyStore(field, FieldReferenceImmutability.key) referenceImmutabilityPropertyStoreResult match { - case FinalP(ImmutableReference) ⇒ state.referenceIsImmutable = Some(true) - case FinalP(LazyInitializedThreadSafeReference | LazyInitializedNotThreadSafeButDeterministicReference) ⇒ + case FinalP(ImmutableFieldReference) ⇒ state.referenceIsImmutable = Some(true) + case FinalP(LazyInitializedThreadSafeFieldReference | LazyInitializedNotThreadSafeButDeterministicFieldReference) ⇒ state.referenceIsImmutable = Some(true) - case FinalP(MutableReference | LazyInitializedNotThreadSafeReference) ⇒ + case FinalP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ return Result(field, MutableField); case ep @ _ ⇒ { state.dependees += ep @@ -958,21 +1009,21 @@ class L0FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) hasGenericType() if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) { - determineEscapePossibilityOfReferencedObjectOrValue() + determineEscapeOfReferencedObjectOrValue() } if (!state.concreteClassTypeIsKnown) handleTypeImmutability() - createResult(state) + createResult } } -trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +trait L3FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { final override def uses: Set[PropertyBounds] = Set( - PropertyBounds.finalP(TACAI), + PropertyBounds.ub(TACAI), PropertyBounds.ub(EscapeProperty), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.lub(TypeImmutability_new), + PropertyBounds.lub(FieldReferenceImmutability), + PropertyBounds.lub(TypeImmutability), PropertyBounds.lub(FieldImmutability) ) @@ -983,27 +1034,26 @@ trait L0FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { /** * Executor for the field immutability analysis. */ -object EagerL0FieldImmutabilityAnalysis - extends L0FieldImmutabilityAnalysisScheduler +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 L0FieldImmutabilityAnalysis(p) + val analysis = new L3FieldImmutabilityAnalysis(p) val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) // p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) analysis } - - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** * Executor for the lazy field immutability analysis. */ -object LazyL0FieldImmutabilityAnalysis - extends L0FieldImmutabilityAnalysisScheduler +object LazyL3FieldImmutabilityAnalysis + extends L3FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { final override def register( @@ -1011,7 +1061,7 @@ object LazyL0FieldImmutabilityAnalysis ps: PropertyStore, unused: Null ): FPCFAnalysis = { - val analysis = new L0FieldImmutabilityAnalysis(p) + val analysis = new L3FieldImmutabilityAnalysis(p) ps.registerLazyPropertyComputation( FieldImmutability.key, analysis.determineFieldImmutability From c04c3509a706c7bc95f27ca31d2477246a17a906 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 24 Sep 2020 16:13:20 +0200 Subject: [PATCH 270/327] adapt purity analyses to the new immutability analysis and lattice --- .../fpcf/analyses/PurityAnalysisDemo.scala | 4 +- .../scala/org/opalj/support/info/Purity.scala | 46 +- .../scala/org/opalj/fpcf/PurityTests.scala | 72 +- .../properties/purity/PurityMatcher.scala | 6 +- .../br/fpcf/analyses/L0PurityAnalysis.scala | 36 +- .../purity/AbstractPurityAnalysis.scala | 30 +- .../analyses/purity/L1PurityAnalysis.scala | 32 +- .../analyses/purity/L2PurityAnalysis.scala | 25 +- .../purity/L2PurityAnalysis_new.scala | 1033 ----------------- 9 files changed, 150 insertions(+), 1134 deletions(-) delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala 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/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..85b842d2fc 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 @@ -69,7 +69,7 @@ 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 +79,9 @@ 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 /** * Executes a purity analysis (L2 by default) along with necessary supporting analysis. @@ -533,11 +533,11 @@ object Purity { } if (eager) { - support ::= EagerClassImmutabilityAnalysis - support ::= EagerTypeImmutabilityAnalysis + support ::= EagerL0ClassImmutabilityAnalysis + support ::= EagerL0TypeImmutabilityAnalysis } else { - support ::= LazyClassImmutabilityAnalysis - support ::= LazyTypeImmutabilityAnalysis + support ::= LazyL0ClassImmutabilityAnalysis + support ::= LazyL0TypeImmutabilityAnalysis } escapeAnalysisName match { @@ -556,28 +556,28 @@ 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 None ⇒ analysis match { - case LazyL0PurityAnalysis ⇒ LazyL0FieldMutabilityAnalysis - case LazyL1PurityAnalysis ⇒ LazyL1FieldMutabilityAnalysis - case LazyL2PurityAnalysis ⇒ LazyL1FieldMutabilityAnalysis + case LazyL0PurityAnalysis ⇒ LazyL0FieldImmutabilityAnalysis + case LazyL1PurityAnalysis ⇒ LazyL1FieldImmutabilityAnalysis + case LazyL2PurityAnalysis ⇒ LazyL1FieldImmutabilityAnalysis } case Some(a) ⇒ 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/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/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..5d67aea8e8 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 } @@ -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/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..b9e4c5f07c 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], //FieldMutability], 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) 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..a6743fee5b 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,9 +6,7 @@ package analyses package purity import scala.annotation.switch - import scala.collection.immutable.IntMap - import net.ceedubs.ficus.Ficus._ import org.opalj.collection.immutable.EmptyIntTrieSet @@ -45,7 +43,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,17 +115,20 @@ 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 @@ -160,7 +161,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)) { @@ -583,7 +584,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 +645,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 +659,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 +667,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 +742,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] @@ -944,7 +948,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 +985,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)( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala deleted file mode 100644 index b7fe324d2d..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis_new.scala +++ /dev/null @@ -1,1033 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package tac -package fpcf -package analyses -package purity - -import org.opalj.fpcf.Entity -import org.opalj.fpcf.EOptionP -import org.opalj.fpcf.FinalP -import org.opalj.fpcf.InterimResult -import org.opalj.fpcf.LBP -import org.opalj.fpcf.LUBP -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.SomeEOptionP -import org.opalj.fpcf.SomeEPS -import org.opalj.fpcf.UBP -import org.opalj.br.ComputationalTypeReference -import org.opalj.br.DeclaredMethod -import org.opalj.br.DefinedMethod -import org.opalj.br.Field -import org.opalj.br.Method -import org.opalj.br.ObjectType -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject -import org.opalj.br.cfg.CFG -import org.opalj.br.fpcf.properties.ClassifiedImpure -import org.opalj.br.fpcf.properties.CompileTimePure -import org.opalj.br.fpcf.properties.ContextuallyPure -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.FreshReturnValue -import org.opalj.br.fpcf.properties.Getter -import org.opalj.br.fpcf.properties.ImpureByAnalysis -import org.opalj.br.fpcf.properties.LocalField -import org.opalj.br.fpcf.properties.LocalFieldWithGetter -import org.opalj.br.fpcf.properties.NoFreshReturnValue -import org.opalj.br.fpcf.properties.NoLocalField -import org.opalj.br.fpcf.properties.PrimitiveReturnValue -import org.opalj.br.fpcf.properties.Pure -import org.opalj.br.fpcf.properties.ReturnValueFreshness -import org.opalj.br.fpcf.properties.SideEffectFree -import org.opalj.br.fpcf.properties.StaticDataUsage -import org.opalj.br.fpcf.properties.UsesConstantDataOnly -import org.opalj.br.fpcf.properties.UsesNoStaticData -import org.opalj.br.fpcf.properties.UsesVaryingData -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.FPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler -import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.FPCFAnalysisScheduler -import org.opalj.br.fpcf.analyses.ConfiguredPurityKey -import org.opalj.br.fpcf.properties.cg.Callees -import org.opalj.br.fpcf.properties.cg.Callers -import org.opalj.ai.isImmediateVMException -import org.opalj.br.fpcf.properties.ClassImmutability_new -import org.opalj.br.fpcf.properties.FieldImmutability -import org.opalj.br.fpcf.properties.ReferenceImmutability -import org.opalj.br.fpcf.properties.TypeImmutability_new -import org.opalj.collection.immutable.EmptyIntTrieSet -import org.opalj.collection.immutable.IntTrieSet -import org.opalj.tac.fpcf.properties.TACAI - -import scala.annotation.switch -import scala.collection.immutable.IntMap -import net.ceedubs.ficus.Ficus._ -import org.opalj.br.fpcf.properties.cg.NoCallers - -/** - * An inter-procedural analysis to determine a method's purity. - * - * @note This analysis is sound only up to the usual standards, i.e. it does not cope with - * VirtualMachineErrors, LinkageErrors and ReflectiveOperationExceptions and may be unsound in - * the presence of native code, reflection or `sun.misc.Unsafe`. Calls to native methods are - * handled soundly in general as they are considered [[org.opalj.br.fpcf.properties.ImpureByAnalysis]], - * but native methods may break soundness of this analysis by invalidating assumptions such as - * which fields are effectively final. - * @note This analysis is sound even if the three address code hierarchy is not flat, it will - * produce better results for a flat hierarchy, though. This is because it will not assess the - * types of expressions other than [[org.opalj.tac.Var]]s. - * @note This analysis derives all purity level. - * A configurable [[org.opalj.tac.fpcf.analyses.purity.DomainSpecificRater]] is used to - * identify calls, expressions and exceptions that are `LBDPure` instead of `LBImpure` or any - * `SideEffectFree` purity level. Compared to `L1PurityAnaylsis` it identifies objects/arrays - * returned from pure callees that can be considered local. Synchronized methods are treated - * as `ExternallyPure`. - * @author Dominik Helm - */ -class L2PurityAnalysis_new private[analyses] (val project: SomeProject) - extends AbstractPurityAnalysis_new { - - /** - * Holds the state of this analysis. - * @param lbPurity The current minimum purity level for the method - * @param ubPurity The current maximum purity level for the method that will be assigned by - * checkPurityOfX methods to aggregrate the purity - * @param method The currently analyzed method - * @param definedMethod The corresponding DefinedMethod we report results for - * @param declClass The declaring class of the currently analyzed method - * @param code The code of the currently analyzed method - */ - class State( - val method: Method, - val definedMethod: DeclaredMethod, - val declClass: ObjectType, - var pcToIndex: Array[Int] = Array.empty, - var code: Array[Stmt[V]] = Array.empty, - var lbPurity: Purity = CompileTimePure, - var ubPurity: Purity = CompileTimePure - ) extends AnalysisState { - var fieldLocalityDependees: Map[Field, (EOptionP[Field, FieldLocality], Set[(Expr[V], Purity)])] = Map.empty - - var fieldMutabilityDependees: Map[Field, (EOptionP[Field, ReferenceImmutability], Set[Option[Expr[V]]])] = Map.empty - //FieldMutability - - var classImmutabilityDependees: Map[ObjectType, (EOptionP[ObjectType, ClassImmutability_new], Set[Expr[V]])] = Map.empty - //ClassImmutability - var typeImmutabilityDependees: Map[ObjectType, (EOptionP[ObjectType, TypeImmutability_new], Set[Expr[V]])] = Map.empty - //TypeImmutability - - 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]] = - (fieldLocalityDependees.valuesIterator.map(_._1) ++ - fieldMutabilityDependees.valuesIterator.map(_._1) ++ - classImmutabilityDependees.valuesIterator.map(_._1) ++ - typeImmutabilityDependees.valuesIterator.map(_._1) ++ - purityDependees.valuesIterator.map(_._1) ++ - calleesDependee ++ - rvfDependees.valuesIterator.map(_._1) ++ - staticDataUsage ++ - tacai).toTraversable - - def addFieldLocalityDependee( - f: Field, - eop: EOptionP[Field, FieldLocality], - data: (Expr[V], Purity) - ): Unit = { - if (fieldLocalityDependees.contains(f)) { - val (_, oldValues) = fieldLocalityDependees(f) - fieldLocalityDependees += ((f, (eop, oldValues + data))) - } else { - fieldLocalityDependees += ((f, (eop, Set(data)))) - } - } - - def addFieldMutabilityDependee( - f: Field, - eop: EOptionP[Field, ReferenceImmutability], // FieldMutability], - owner: Option[Expr[V]] - ): Unit = { - if (fieldMutabilityDependees.contains(f)) { - val (_, oldOwners) = fieldMutabilityDependees(f) - fieldMutabilityDependees += ((f, (eop, oldOwners + owner))) - } else { - fieldMutabilityDependees += ((f, (eop, Set(owner)))) - } - } - - def addClassImmutabilityDependee( - t: ObjectType, - eop: EOptionP[ObjectType, ClassImmutability_new], //ClassImmutability], - value: Expr[V] - ): Unit = { - if (classImmutabilityDependees.contains(t)) { - val (_, oldValues) = classImmutabilityDependees(t) - classImmutabilityDependees += ((t, (eop, oldValues + value))) - } else { - classImmutabilityDependees += ((t, (eop, Set(value)))) - } - } - - def addTypeImmutabilityDependee( - t: ObjectType, - eop: EOptionP[ObjectType, TypeImmutability_new], // TypeImmutability], - value: Expr[V] - ): Unit = { - if (typeImmutabilityDependees.contains(t)) { - val (_, oldValues) = typeImmutabilityDependees(t) - typeImmutabilityDependees += ((t, (eop, oldValues + value))) - } else { - typeImmutabilityDependees += ((t, (eop, Set(value)))) - } - } - - def addPurityDependee( - dm: DeclaredMethod, - eop: EOptionP[DeclaredMethod, Purity], - params: Seq[Expr[V]] - ): Unit = { - if (purityDependees.contains(dm)) { - val (_, oldParams) = purityDependees(dm) - purityDependees += ((dm, (eop, oldParams + params))) - } else { - purityDependees += ((dm, (eop, Set(params)))) - } - } - - def updateCalleesDependee(eps: EOptionP[DeclaredMethod, Callees]): Unit = { - if (eps.isFinal) calleesDependee = None - else calleesDependee = Some(eps) - if (eps.hasUBP) - callees = Some(eps.ub) - } - - def addRVFDependee( - dm: DeclaredMethod, - eop: EOptionP[DeclaredMethod, ReturnValueFreshness], - data: (Option[Expr[V]], Purity) - ): Unit = { - if (rvfDependees.contains(dm)) { - val (_, oldValues) = rvfDependees(dm) - rvfDependees += ((dm, (eop, oldValues + data))) - } else { - rvfDependees += ((dm, (eop, Set(data)))) - } - } - - def removeFieldLocalityDependee(f: Field): Unit = fieldLocalityDependees -= f - def removeFieldMutabilityDependee(f: Field): Unit = fieldMutabilityDependees -= f - def removeClassImmutabilityDependee(t: ObjectType): Unit = classImmutabilityDependees -= t - def removeTypeImmutabilityDependee(t: ObjectType): Unit = typeImmutabilityDependees -= t - def removePurityDependee(dm: DeclaredMethod): Unit = purityDependees -= dm - def removeRVFDependee(dm: DeclaredMethod): Unit = rvfDependees -= dm - - def updateStaticDataUsage(eps: Option[EOptionP[DeclaredMethod, StaticDataUsage]]): Unit = { - staticDataUsage = eps - } - - def updateTacai(eps: EOptionP[Method, TACAI]): Unit = { - if (eps.isFinal) tacai = None - else tacai = Some(eps) - if (eps.hasUBP && eps.ub.tac.isDefined) { - val tac = eps.ub.tac.get - pcToIndex = tac.pcToIndex - code = tac.stmts - } - } - } - - type StateType = State - - val raterFqn: String = project.config.as[String]( - "org.opalj.fpcf.analyses.L2PurityAnalysis_new.domainSpecificRater" - ) - - val rater: DomainSpecificRater = - L2PurityAnalysis_new.rater.getOrElse(resolveDomainSpecificRater(raterFqn)) - - /** - * Examines whether the given expression denotes an object/array that is local to the current - * method, i.e. the method has control over the object/array and actions on it might not - * influence purity. - * - * @param otherwise The maxPurity will be reduced to at most this level if the expression is not - * local. - */ - override def isLocal( - expr: Expr[V], - otherwise: Purity, - excludedDefSites: IntTrieSet = EmptyIntTrieSet - )(implicit state: State): Boolean = { - isLocalInternal( - expr, - otherwise, - _ ⇒ CompileTimePure, - treatParamsAsFresh = false - ) - } - - /** - * Examines whether the given expression denotes an object/array that is local to the current - * method, i.e. the method has control over the object/array and actions on it might not - * influence purity. - * - * @note Fresh references can be treated as non-escaping as the analysis result will be impure - * if anything escapes the method via parameters, static field assignments or calls. - * - * @param otherwise The maxPurity will be reduced to at most this level if the expression is not - * local - * @param onParameter The maxPurity will be reduced to at most this level if the expression can - * be a parameter - * @param treatParamsAsFresh The value to be returned if the expression can be a parameter - */ - def isLocalInternal( - expr: Expr[V], - otherwise: Purity, - onParameter: Int ⇒ Purity, - 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.isConst) - return true; - - if (!expr.isVar) { - // The expression could refer to further expressions in a non-flat representation. - // In that case it could be, e.g., a GetStatic. In that case the reference is not - // locally created and/or initialized. To avoid special handling, we just fallback to - // false here as the analysis is intended to be used on flat representations anyway. - atMost(otherwise) - return false; - } - - // Primitive values are always local (required for parameters of contextually pure calls) - // TODO (value is null for the self reference of a throwable constructor...) - if (expr.asVar.value != null && - (expr.asVar.value.computationalType ne ComputationalTypeReference)) - return true; - - val defSites = expr.asVar.definedBy -- excludedDefSites - val isLocal = - defSites.forall( - isLocalDefsite( - _, - otherwise, - onParameter, - treatParamsAsFresh, - defSites, - excludedDefSites - ) - ) - if (!isLocal) atMost(otherwise) - isLocal - } - - /** - * Examines whether the given defsite denotes an object/array that is local to the current - * method, i.e. the method has control over the object/array and actions on it might not - * influence purity. - * - * @param otherwise The maxPurity will be reduced to at most this level if the defsite is not - * local - * @param onParameter The maxPurity will be reduced to at most this level if the defsite is a - * parameter - * @param treatParamsAsFresh The value to be returned if the defsite is a parameter - */ - def isLocalDefsite( - defSite: Int, - otherwise: Purity, - onParameter: Int ⇒ Purity, - treatParamsAsFresh: Boolean, - defSites: IntTrieSet, - excludedDefSites: IntTrieSet - )(implicit state: State): Boolean = { - if (isImmediateVMException(defSite)) - return true; // VMLevelValues are freshly created - - if (ai.isMethodExternalExceptionOrigin(defSite)) - return false; // Method external exceptions are not freshly created - - if (defSite == OriginOfThis) { - if (!state.method.isConstructor) { - atMost(onParameter(0)) - } - return treatParamsAsFresh | state.method.isConstructor; - } - - if (defSite < 0) { - atMost(onParameter(-defSite - 1)) - return treatParamsAsFresh; - } - - val stmt = state.code(defSite) - assert(stmt.astID == Assignment.ASTID, "defSite should be assignment") - - val rhs = stmt.asAssignment.expr - if (rhs.isConst) - return true; - - (rhs.astID: @switch) match { - case New.ASTID | NewArray.ASTID ⇒ true - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ - val oldPurityLevel = - state.rvfCallSites.get(stmt.pc).map(_._2).getOrElse(CompileTimePure) - val data = (rhs.asFunctionCall.receiverOption, otherwise meet oldPurityLevel) - if (state.callees.isDefined) { - checkFreshnessOfReturn(stmt.pc, data, state.callees.get) - } else { - state.rvfCallSites += stmt.pc -> data - reducePurityLB(otherwise) - } - true - case GetField.ASTID ⇒ - val GetField(_, declClass, name, fieldType, objRef) = rhs - project.resolveFieldReference(declClass, name, fieldType) match { - case Some(field) ⇒ - val locality = propertyStore(field, FieldLocality.key) - checkLocalityOfField(locality, (objRef, otherwise)) && - isLocalInternal( - objRef, - otherwise, - onParameter, - treatParamsAsFresh, - excludedDefSites ++ defSites - ) - case None ⇒ false - } - case _ ⇒ false - } - } - - def checkLocalityOfField( - ep: EOptionP[Field, FieldLocality], - data: (Expr[V], Purity) - )(implicit state: State): Boolean = { - val isLocal = ep match { - case FinalP(LocalField | LocalFieldWithGetter) ⇒ - true - case FinalP(ExtensibleLocalField | ExtensibleLocalFieldWithGetter) ⇒ - if (data._1.isVar) { - val value = data._1.asVar.value.asReferenceValue - value.isPrecise && - !classHierarchy.isSubtypeOf(value.asReferenceType, ObjectType.Cloneable) - } else - false - case UBP(NoLocalField) ⇒ - false - case _ ⇒ - reducePurityLB(data._2) - if (data._2 meet state.ubPurity ne state.ubPurity) - state.addFieldLocalityDependee(ep.e, ep, data) - true - } - if (!isLocal) - atMost(data._2) - isLocal - } - - def checkLocalityOfReturn( - ep: EOptionP[DeclaredMethod, Property], - data: (Option[Expr[V]], Purity) - )(implicit state: State): Unit = { - import project.classHierarchy.isSubtypeOf - ep match { - case LBP(PrimitiveReturnValue | FreshReturnValue) ⇒ - case FinalP(Getter) ⇒ - if (data._2 meet state.ubPurity ne state.ubPurity) - isLocal(data._1.get, data._2) - case FinalP(ExtensibleGetter) ⇒ - if (data._1.get.isVar) { - val value = data._1.get.asVar.value.asReferenceValue - if (value.isPrecise && !isSubtypeOf(value.asReferenceType, ObjectType.Cloneable)) { - if (data._2 meet state.ubPurity ne state.ubPurity) - isLocal(data._1.get, data._2) - } else { - atMost(data._2) - } - } else { - atMost(data._2) - } - case UBP(NoFreshReturnValue) ⇒ - atMost(data._2) - case _: SomeEOptionP ⇒ - reducePurityLB(data._2) - if (data._2 meet state.ubPurity ne state.ubPurity) { - state.addRVFDependee( - ep.e, - ep.asInstanceOf[EOptionP[DeclaredMethod, ReturnValueFreshness]], - data - ) - } - } - } - - def checkFreshnessOfReturn( - pc: Int, - data: (Option[Expr[V]], Purity), - callees: Callees - )(implicit state: State): Unit = { - callees.callees(pc).foreach { callee ⇒ - if (callee.descriptor.returnType.isReferenceType) - checkLocalityOfReturn(propertyStore(callee, ReturnValueFreshness.key), data) - } - } - - /** - * Examines a statement for its influence on the method's purity. - * This method will return false for impure statements, so evaluation can be terminated early. - */ - override def checkPurityOfStmt(stmt: Stmt[V])(implicit state: State): Boolean = { - (stmt.astID: @switch) match { - // Synchronization on non-escaping local objects/arrays is pure (and useless...) - case MonitorEnter.ASTID | MonitorExit.ASTID ⇒ - val objRef = stmt.asSynchronizationStmt.objRef - isLocalInternal( - objRef, - ImpureByAnalysis, - param ⇒ ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true - ) && stmt.forallSubExpressions(checkPurityOfExpr) - - // Storing into non-escaping locally initialized arrays/objects is pure - case ArrayStore.ASTID ⇒ - val arrayRef = stmt.asArrayStore.arrayRef - isLocalInternal( - arrayRef, - ImpureByAnalysis, - param ⇒ ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true - ) && stmt.forallSubExpressions(checkPurityOfExpr) - case PutField.ASTID ⇒ - val objRef = stmt.asPutField.objRef - isLocalInternal( - objRef, - ImpureByAnalysis, - param ⇒ ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true - ) && stmt.forallSubExpressions(checkPurityOfExpr) - - case _ ⇒ super.checkPurityOfStmt(stmt) - } - } - - /** - * Examines the influence of the purity property of a method on the examined method's purity. - * - * @note Adds dependendees when necessary. - */ - def checkMethodPurity( - ep: EOptionP[DeclaredMethod, Purity], - params: Seq[Expr[V]] - )(implicit state: State): Boolean = - { - ep match { - case UBP(_: ClassifiedImpure) ⇒ - atMost(ImpureByAnalysis) - false - case eps @ LUBP(lb, ub) ⇒ - if (eps.isRefinable && ((lb meet state.ubPurity) ne state.ubPurity)) { - // On conditional, keep dependence - state.addPurityDependee(ep.e, ep, params) - reducePurityLB(lb) - } - // Contextual/external purity is handled below - atMost(ub.withoutContextual) - ub.modifiedParams.forall( - param ⇒ - isLocalInternal( - params(param), - ImpureByAnalysis, - param ⇒ ContextuallyPure(IntTrieSet(param)), - treatParamsAsFresh = true - ) - ) - case _: SomeEOptionP ⇒ - reducePurityLB(ImpureByAnalysis) - state.addPurityDependee(ep.e, ep, params) - true - } - } - - /** - * Handles the effect of static data usage on the purity level. - * - * @note Modifies dependees as necessary. - */ - def checkStaticDataUsage( - ep: EOptionP[DeclaredMethod, StaticDataUsage] - )(implicit state: State): Unit = { - ep match { - case LBP(UsesNoStaticData | UsesConstantDataOnly) ⇒ - state.updateStaticDataUsage(None) - case UBP(UsesVaryingData) ⇒ - state.updateStaticDataUsage(None) - atMost(Pure) - case _ ⇒ - reducePurityLB(Pure) - state.updateStaticDataUsage(Some(ep)) - } - } - - /** - * Adds the dependee necessary if a field mutability is not known yet. - */ - override def handleUnknownFieldMutability( - ep: EOptionP[Field, ReferenceImmutability], // FieldMutability], - objRef: Option[Expr[V]] - )(implicit state: State): Unit = { - state.addFieldMutabilityDependee(ep.e, ep, objRef) - } - - /** - * Adds the dependee necessary if a type mutability is not known yet. - */ - override def handleUnknownTypeMutability( - ep: EOptionP[ObjectType, Property], - expr: Expr[V] - )(implicit state: State): Unit = { - if (ep.pk == ClassImmutability_new.key) //ClassImmutability.key) - state.addClassImmutabilityDependee( - ep.e, - ep.asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]], //ClassImmutability]], - expr - ) - else - state.addTypeImmutabilityDependee( - ep.e, - ep.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]], //TypeImmutability]], - expr - ) - } - - /** - * Add or remove the dependee when the callees property changes. - */ - override def handleCalleesUpdate( - callees: EOptionP[DeclaredMethod, Callees] - )(implicit state: State): Unit = { - state.updateCalleesDependee(callees) - if (callees.isRefinable) { - reducePurityLB(ImpureByAnalysis) - } - } - - /* - * Adds the dependee necessary if the TACAI is not yet final. - */ - override def handleTACAI(ep: EOptionP[Method, TACAI])(implicit state: State): Unit = { - state.updateTacai(ep) - } - - /** - * Removes dependees that are known to not be needed anymore as they can not reduce the max - * purity level further. - */ - def cleanupDependees()(implicit state: State): Unit = { - if (state.ubPurity ne CompileTimePure) - state.updateStaticDataUsage(None) - - if (!state.ubPurity.isDeterministic) { - state.fieldMutabilityDependees = Map.empty - state.classImmutabilityDependees = Map.empty - state.typeImmutabilityDependees = Map.empty - state.updateStaticDataUsage(None) - } - - 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))) - } - state.fieldLocalityDependees = newFieldLocalityDependees - - var newRVFCallsites: IntMap[(Option[Expr[V]], Purity)] = IntMap.empty - for ((callsite, data) ← state.rvfCallSites) { - if (data._2 meet state.ubPurity ne state.ubPurity) newRVFCallsites += ((callsite, data)) - } - 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))) - } - 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)) - } - state.purityDependees = newPurityDependees - } - - /** - * Raises the lower bound on the purity whenever possible. - */ - def adjustLowerBound()(implicit state: State): Unit = { - if (state.calleesDependee.isDefined) - return ; // Nothing to be done, lower bound is still LBImpure - - var newLowerBound = state.ubPurity - - if (state.tacai.isDefined) return ; // Nothing to be done, lower bound is still LBImpure - - for ((eop, _) ← state.purityDependees.valuesIterator) { - eop match { - case LBP(lb) ⇒ newLowerBound = newLowerBound meet lb - case _ ⇒ - return ; // Nothing to be done, lower bound is still LBImpure - } - } - - if (state.staticDataUsage.isDefined) newLowerBound = newLowerBound meet Pure - - if (state.fieldMutabilityDependees.nonEmpty || - state.classImmutabilityDependees.nonEmpty || - state.typeImmutabilityDependees.nonEmpty) { - newLowerBound = newLowerBound meet SideEffectFree - } - - for { - (_, data) ← state.fieldLocalityDependees.valuesIterator - (_, purity) ← data - } { - newLowerBound = newLowerBound meet purity - } - - for { - (_, purity) ← state.rvfCallSites.valuesIterator - } { - newLowerBound = newLowerBound meet purity - } - - for { - (_, data) ← state.rvfDependees.valuesIterator - (_, purity) ← data - } { - newLowerBound = newLowerBound meet purity - } - - state.lbPurity = newLowerBound - } - - /** - * Continuation to handle updates to properties of dependees. - * Dependees may be - * - methods / virtual methods called (for their purity) - * - fields read (for their mutability) - * - classes files for class types returned (for their mutability) - */ - def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { - val oldPurity = state.ubPurity - eps.ub.key match { - case Purity.key ⇒ - val e = eps.e.asInstanceOf[DeclaredMethod] - val dependees = state.purityDependees(e) - state.removePurityDependee(e) - dependees._2.foreach { e ⇒ - checkMethodPurity(eps.asInstanceOf[EOptionP[DeclaredMethod, Purity]], e) - } - //case FieldMutability.key ⇒ - case ReferenceImmutability.key ⇒ - val e = eps.e.asInstanceOf[Field] - val dependees = state.fieldMutabilityDependees(e) - state.removeFieldMutabilityDependee(e) - dependees._2.foreach { e ⇒ - checkFieldMutability(eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]], e) //FieldMutability]], e) - } - //case ClassImmutability.key ⇒ - case ClassImmutability_new.key ⇒ - val e = eps.e.asInstanceOf[ObjectType] - val dependees = state.classImmutabilityDependees(e) - state.removeClassImmutabilityDependee(e) - dependees._2.foreach { e ⇒ - checkTypeMutability( - eps.asInstanceOf[EOptionP[ObjectType, ClassImmutability_new]], // ClassImmutability]], - e - ) - } - //case TypeImmutability.key ⇒ - case TypeImmutability_new.key ⇒ - val e = eps.e.asInstanceOf[ObjectType] - val dependees = state.typeImmutabilityDependees(e) - state.removeTypeImmutabilityDependee(e) - dependees._2.foreach { e ⇒ - checkTypeMutability(eps.asInstanceOf[EOptionP[ObjectType, TypeImmutability_new]], e) //TypeImmutability]], e) - } - case ReturnValueFreshness.key ⇒ - val e = eps.e.asInstanceOf[DeclaredMethod] - val dependees = state.rvfDependees(e) - state.removeRVFDependee(e) - dependees._2.foreach { e ⇒ - checkLocalityOfReturn( - eps.asInstanceOf[EOptionP[DeclaredMethod, ReturnValueFreshness]], - e - ) - } - case FieldLocality.key ⇒ - val e = eps.e.asInstanceOf[Field] - val dependees = state.fieldLocalityDependees(e) - state.removeFieldLocalityDependee(e) - dependees._2.foreach { e ⇒ - checkLocalityOfField(eps.asInstanceOf[EOptionP[Field, FieldLocality]], e) - } - case Callees.key ⇒ - checkPurityOfCallees(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) - state.rvfCallSites.foreach { - case (pc, data) ⇒ checkFreshnessOfReturn(pc, data, eps.ub.asInstanceOf[Callees]) - } - case StaticDataUsage.key ⇒ - checkStaticDataUsage(eps.asInstanceOf[EOptionP[DeclaredMethod, StaticDataUsage]]) - case TACAI.key ⇒ - state.updateTacai(eps.asInstanceOf[EOptionP[Method, TACAI]]) - return determineMethodPurity(eps.ub.asInstanceOf[TACAI].tac.get.cfg); - } - - if (state.ubPurity eq ImpureByAnalysis) { - return Result(state.definedMethod, ImpureByAnalysis) - }; - - if (state.ubPurity ne oldPurity) - cleanupDependees() // Remove dependees that we don't need anymore. - adjustLowerBound() - - val dependees = state.dependees - if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { - Result(state.definedMethod, state.ubPurity) - } else { - InterimResult( - state.definedMethod, - state.lbPurity, - state.ubPurity, - dependees, - c - ) - } - } - - /** - * Determines the purity of a method once TACAI is available. - */ - def determineMethodPurity( - cfg: CFG[Stmt[V], TACStmts[V]] - )(implicit state: State): ProperPropertyComputationResult = { - // Special case: The Throwable constructor is `LBSideEffectFree`, but subtype constructors - // may not be because of overridable fillInStackTrace method - if (state.method.isConstructor && state.declClass.isSubtypeOf(ObjectType.Throwable)) - project.instanceMethods(state.declClass).foreach { mdc ⇒ - if (mdc.name == "fillInStackTrace" && - mdc.method.classFile.thisType != ObjectType.Throwable) { - // "The value" is actually not used at all - hence, we can use "null" - // over here. - val selfReference = UVar(null, SelfReferenceParameter) - val fISTPurity = propertyStore(declaredMethods(mdc.method), Purity.key) - if (!checkMethodPurity(fISTPurity, Seq(selfReference))) { - // Early return for impure fillInStackTrace - return Result(state.definedMethod, state.ubPurity); - } - } - } - // Synchronized methods have a visible side effect on the receiver - // Static synchronized methods lock the class which is potentially globally visible - if (state.method.isSynchronized) { - if (state.method.isStatic) { - //("pa2 method is additionally static and thus impure") - return Result(state.definedMethod, ImpureByAnalysis); - } else atMost(ContextuallyPure(IntTrieSet(0))) - } - val stmtCount = state.code.length - var s = 0 - while (s < stmtCount) { - if (!checkPurityOfStmt(state.code(s))) { // Early return for impure statements - assert(state.ubPurity.isInstanceOf[ClassifiedImpure]) - return Result(state.definedMethod, state.ubPurity); - } - s += 1 - } - val callees = propertyStore(state.definedMethod, Callees.key) - if (!checkPurityOfCallees(callees)) - return Result(state.definedMethod, state.ubPurity) - if (callees.hasUBP) - state.rvfCallSites.foreach { - case (pc, data) ⇒ - checkFreshnessOfReturn(pc, data, callees.ub) - } - // Creating implicit exceptions is side-effect free (because of fillInStackTrace) - // but it may be ignored as domain-specific - val bbsCausingExceptions = cfg.abnormalReturnNode.predecessors - for { - bb ← bbsCausingExceptions - } { - val pc = bb.asBasicBlock.endPC - if (isSourceOfImmediateException(pc)) { - val throwingStmt = state.code(pc) - val ratedResult = rater.handleException(throwingStmt) - if (ratedResult.isDefined) atMost(ratedResult.get) - else atMost(SideEffectFree) - } - } - if (state.ubPurity eq CompileTimePure) // Check static data usage only if necessary - checkStaticDataUsage(propertyStore(state.definedMethod, StaticDataUsage.key)) - else - cleanupDependees() // Remove dependees we already know we won't need - val dependees = state.dependees - if (dependees.isEmpty || (state.lbPurity == state.ubPurity)) { - Result(state.definedMethod, state.ubPurity) - } else { - org.opalj.fpcf.InterimResult( - state.definedMethod, - state.lbPurity, - state.ubPurity, - dependees, - c - ) - } - } - - /** - * Determines the purity of the given method. - * - * @param definedMethod A defined method with a body. - */ - def determinePurity(definedMethod: DefinedMethod): ProperPropertyComputationResult = { - val method = definedMethod.definedMethod - val declClass = method.classFile.thisType - - // If this is not the method's declaration, but a non-overwritten method in a subtype, - // don't re-analyze the code - if (declClass ne definedMethod.declaringClassType) - return baseMethodPurity(definedMethod.asDefinedMethod); - - implicit val state: State = - new State(method, definedMethod, declClass) - - val tacaiO = getTACAI(method) - - if (tacaiO.isEmpty) { - return InterimResult( - definedMethod, - ImpureByAnalysis, - CompileTimePure, - state.dependees, - c - ); - } - - determineMethodPurity(tacaiO.get.cfg) - } -} - -object L2PurityAnalysis_new { - - /** - * Domain-specific rater used to examine whether certain statements and expressions are - * domain-specific. - * If the Option is None, a rater is created from a config file option. - */ - var rater: Option[DomainSpecificRater] = None - - def setRater(newRater: Option[DomainSpecificRater]): Unit = { - rater = newRater - } -} - -trait L2PurityAnalysisScheduler_new extends FPCFAnalysisScheduler { - - final def derivedProperty: PropertyBounds = PropertyBounds.lub(Purity) - - override def requiredProjectInformation: ProjectInformationKeys = - Seq(DeclaredMethodsKey, ConfiguredPurityKey) - - override def uses: Set[PropertyBounds] = { - Set( - //PropertyBounds.lub(FieldMutability), - PropertyBounds.lub(ReferenceImmutability), - PropertyBounds.lub(FieldImmutability), - //PropertyBounds.lub(ClassImmutability), - PropertyBounds.lub(ClassImmutability_new), - //PropertyBounds.lub(TypeImmutability), - PropertyBounds.lub(TypeImmutability_new), - PropertyBounds.lub(StaticDataUsage), - PropertyBounds.lub(ReturnValueFreshness), - PropertyBounds.ub(FieldLocality), - PropertyBounds.ub(TACAI), - PropertyBounds.ub(Callees), - PropertyBounds.lub(Purity) - ) - } - - final override type InitializationData = L2PurityAnalysis_new - final override def init(p: SomeProject, ps: PropertyStore): InitializationData = { - new L2PurityAnalysis_new(p) - } - - 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 = {} - -} - -object EagerL2PurityAnalysis_new extends L2PurityAnalysisScheduler_new with FPCFEagerAnalysisScheduler { - - override def start( - p: SomeProject, ps: PropertyStore, analysis: InitializationData - ): FPCFAnalysis = { - 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 ⇒ - dm.asDefinedMethod - } - ps.scheduleEagerComputationsForEntities(methods)( - analysis.determinePurity - ) - analysis - } - - override def uses: Set[PropertyBounds] = super.uses + PropertyBounds.finalP(Callers) - - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty -} - -object LazyL2PurityAnalysis_new extends L2PurityAnalysisScheduler_new with FPCFLazyAnalysisScheduler { - - override def register( - p: SomeProject, ps: PropertyStore, analysis: InitializationData - ): FPCFAnalysis = { - val analysis = new L2PurityAnalysis_new(p) - ps.registerLazyPropertyComputation(Purity.key, analysis.doDeterminePurity) - analysis - } - - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) -} From 88783c072daec3070cad3a00836cc6ba62c16657 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 24 Sep 2020 16:14:51 +0200 Subject: [PATCH 271/327] renew the property annotations for the test fixtures --- .../fields/DeepImmutableField.java | 42 ++++++++++++++---- .../fields/DependentImmutableField.java | 44 +++++++++++++++---- .../immutability/fields/MutableField.java | 28 ++++++++---- .../fields/ShallowImmutableField.java | 44 +++++++++++++++---- .../references/ImmutableReference.java | 2 +- ...otThreadSafeButDeterministicReference.java | 2 +- ...LazyInitializedNotThreadSafeReference.java | 2 +- .../LazyInitializedThreadSafeReference.java | 2 +- .../references/MutableReference.java | 2 +- .../immutability/types/DeepImmutableType.java | 4 +- .../types/DependentImmutableType.java | 4 +- .../immutability/types/MutableType.java | 6 +-- .../types/ShallowImmutableType.java | 4 +- 13 files changed, 139 insertions(+), 47 deletions(-) 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 index 6ca2803496..2963236aef 100644 --- 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 @@ -1,20 +1,48 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +/* 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.immutability.fields; import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.tac.fpcf.analyses.immutability.L0FieldImmutabilityAnalysis; 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 deep immutable + * Annotation to state that the annotated field is lazily initialized. * - * @author Tobias Peter Roth + * @author Michael Eichberg + * @author Dominik Helm */ -@PropertyValidator(key="FieldImmutability",validator=DeepImmutableFieldMatcher.class) +@PropertyValidator(key="FieldMutability",validator= DeepImmutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface DeepImmutableField { @@ -24,7 +52,5 @@ */ String value() ; // default = "N/A"; - Class[] analyses() default { - L0FieldImmutabilityAnalysis.class - }; + 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 index da95178e9d..0e479402a9 100644 --- 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 @@ -1,20 +1,49 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +/* 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.immutability.fields; import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.tac.fpcf.analyses.immutability.L0FieldImmutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableFieldMatcher; +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 shallow immutable + * Annotation to state that the annotated field is lazily initialized. * - * @author Tobias Peter Roth + * @author Michael Eichberg + * @author Dominik Helm */ -@PropertyValidator(key="FieldImmutability",validator=DependentImmutableFieldMatcher.class) +@PropertyValidator(key="FieldMutability",validator= DependentImmutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface DependentImmutableField { @@ -26,8 +55,5 @@ String genericString(); - Class[] analyses() default { - L0FieldImmutabilityAnalysis.class - }; - + 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 index 38b0666c67..018b440086 100644 --- 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 @@ -2,18 +2,23 @@ package org.opalj.fpcf.properties.immutability.fields; import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.tac.fpcf.analyses.immutability.L0FieldImmutabilityAnalysis; +import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.immutability.fields.MutableFieldMatcher; +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 + * Annotation to state that the annotated field is not mutable. * - * @author Tobias Peter Roth + * @author Michael Eichberg */ -@PropertyValidator(key="FieldImmutability",validator=MutableFieldMatcher.class) +@PropertyValidator(key = "FieldImmutability",validator = MutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface MutableField { @@ -21,9 +26,16 @@ /** * A short reasoning of this property. */ - String value() ; // default = "N/A"; + String value();// default = "N/A"; + + /** + * True if the field is non-final because it is read prematurely. + * Tests may ignore @NonFinal 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}; - Class[] analyses() default { - L0FieldImmutabilityAnalysis.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 index f54983e28b..3d571f4fdf 100644 --- 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 @@ -1,19 +1,49 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +/* 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.immutability.fields; import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.tac.fpcf.analyses.immutability.L0FieldImmutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableFieldMatcher; +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 shallow immutable + * Annotation to state that the annotated field is lazily initialized. * - * @author Tobias Peter Roth + * @author Michael Eichberg + * @author Dominik Helm */ -@PropertyValidator(key="FieldImmutability",validator=ShallowImmutableFieldMatcher.class) +@PropertyValidator(key="FieldMutability",validator= ShallowImmutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface ShallowImmutableField { @@ -23,7 +53,5 @@ */ String value() ; // default = "N/A"; - Class[] analyses() default { - L0FieldImmutabilityAnalysis.class - }; + Class[] analyses() default { L3FieldImmutabilityAnalysis.class }; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableReference.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableReference.java index aa4973c286..f06adc6240 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableReference.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableReference.java @@ -13,7 +13,7 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = ImmutableFieldReferenceMatcher.class) +@PropertyValidator(key = "FieldReferenceImmutability",validator = ImmutableFieldReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface ImmutableReference { 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 index 6df195da61..8c5171a60a 100644 --- 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 @@ -14,7 +14,7 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedNotThreadSafeButDeterministicFieldReferenceMatcher.class) +@PropertyValidator(key = "FieldReferenceImmutability",validator = LazyInitializedNotThreadSafeButDeterministicFieldReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface LazyInitializedNotThreadSafeButDeterministicReference { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeReference.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeReference.java index 5f733135a2..541398a8e3 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeReference.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeReference.java @@ -14,7 +14,7 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedNotThreadSafeFieldReferenceMatcher.class) +@PropertyValidator(key = "FieldReferenceImmutability",validator = LazyInitializedNotThreadSafeFieldReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface LazyInitializedNotThreadSafeReference { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeReference.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeReference.java index 3a262e368d..29db7f584d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeReference.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeReference.java @@ -14,7 +14,7 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = LazyInitializedThreadSafeFieldReferenceMatcher.class) +@PropertyValidator(key = "FieldReferenceImmutability",validator = LazyInitializedThreadSafeFieldReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface LazyInitializedThreadSafeReference { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableReference.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableReference.java index 15bba20ee7..9f10c45e07 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableReference.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableReference.java @@ -14,7 +14,7 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "ReferenceImmutability",validator = MutableFieldReferenceMatcher.class) +@PropertyValidator(key = "FieldReferenceImmutability",validator = MutableFieldReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface MutableReference { 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 index 7f6605a072..1c199a3421 100644 --- 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 @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.LxTypeImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -24,5 +24,5 @@ */ String value();// default = "N/A"; - Class[] analyses() default {LxTypeImmutabilityAnalysis_new.class}; + 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 index 8b0a92a3a0..357a51eff0 100644 --- 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 @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.LxTypeImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -24,5 +24,5 @@ */ String value();// default = "N/A"; - Class[] analyses() default {LxTypeImmutabilityAnalysis_new.class}; + 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 index 39559bdb2e..faa9c19f66 100644 --- 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 @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.LxTypeImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -14,7 +14,7 @@ * * @author Tobias Peter Roth */ -@PropertyValidator(key = "TypeImmutability_new", validator = NewMutableTypeMatcher.class) +@PropertyValidator(key = "TypeImmutability_new", validator = MutableTypeMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface MutableType { @@ -24,5 +24,5 @@ */ String value();// default = "N/A"; - Class[] analyses() default {LxTypeImmutabilityAnalysis_new.class}; + 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 index c04536ed78..2ceb3256be 100644 --- 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 @@ -3,7 +3,7 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.LxTypeImmutabilityAnalysis_new; +import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -24,5 +24,5 @@ */ String value();// default = "N/A"; - Class[] analyses() default {LxTypeImmutabilityAnalysis_new.class}; + Class[] analyses() default {L1TypeImmutabilityAnalysis.class}; } From 1d3312e83e71c82beef372aa775f8c5986d97c35 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 24 Sep 2020 16:17:58 +0200 Subject: [PATCH 272/327] general immutability adjustments --- .../analyses/ImmutabilityAnalysisDemo.scala | 19 +++--- .../opalj/support/info/FieldMutability.scala | 4 +- .../org/opalj/support/info/Immutability.scala | 68 +++++++++---------- .../support/info/ImmutabilityAnalysis.scala | 22 +++--- .../opalj/support/info/PureVoidMethods.scala | 12 ++-- .../opalj/support/info/UnusedResults.scala | 12 ++-- OPAL/tac/src/main/resources/reference.conf | 46 ++++++++++--- 7 files changed, 107 insertions(+), 76 deletions(-) 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/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 index 57f2bda4f7..702ad2845b 100644 --- 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 @@ -29,7 +29,7 @@ import org.opalj.tac.fpcf.analyses.immutability.fieldreference.EagerL0FieldRefer import org.opalj.tac.cg.RTACallGraphKey import org.opalj.br.ObjectType import org.opalj.fpcf.PropertyStore -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +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 @@ -47,17 +47,17 @@ 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_new +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.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +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.EagerL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.EagerLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.EagerLxTypeImmutabilityAnalysis_new +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.log.DevNullLogger @@ -66,7 +66,7 @@ import org.opalj.log.OPALLogger import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.PropertyStoreContext import org.opalj.support.info.RunningAnalyses.RunningAnalysis -import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis_new +import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater import org.opalj.ai.domain import org.opalj.log.LogContext @@ -108,10 +108,10 @@ object Immutability { level: Int, reImInferComparison: Boolean ): BasicReport = { - import org.opalj.br.fpcf.properties.ClassImmutability_new + 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_new + import org.opalj.br.fpcf.properties.TypeImmutability import org.opalj.fpcf.EPS OPALLogger.updateLogger(GlobalLogContext, DevNullLogger) @@ -158,7 +158,7 @@ object Immutability { val referenceDependencies: List[FPCFAnalysisScheduler] = List( EagerL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, + LazyL2PurityAnalysis, LazyInterProceduralEscapeAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, @@ -168,10 +168,10 @@ object Immutability { val fieldDependencies: List[FPCFAnalysisScheduler] = List( LazyL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - EagerL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new, + LazyL2PurityAnalysis, + EagerL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyInterProceduralEscapeAnalysis, @@ -180,11 +180,11 @@ object Immutability { ) val classDepencencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, + LazyL2PurityAnalysis, LazyL0FieldReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new, + LazyL3FieldImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, + EagerL1ClassImmutabilityAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyInterProceduralEscapeAnalysis, @@ -193,11 +193,11 @@ object Immutability { ) val typeDependencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, + LazyL2PurityAnalysis, LazyL0FieldReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new, + LazyL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + EagerL1TypeImmutabilityAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyInterProceduralEscapeAnalysis, @@ -207,11 +207,11 @@ object Immutability { val allImmAnalysisDependencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, + LazyL2PurityAnalysis, EagerL0FieldReferenceImmutabilityAnalysis, - EagerL0FieldImmutabilityAnalysis, - EagerLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new, + EagerL3FieldImmutabilityAnalysis, + EagerL1ClassImmutabilityAnalysis, + EagerL1TypeImmutabilityAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyInterProceduralEscapeAnalysis, @@ -226,7 +226,7 @@ object Immutability { case All ⇒ allImmAnalysisDependencies } - L2PurityAnalysis_new.setRater(Some(SystemOutLoggingAllExceptionRater)) + L2PurityAnalysis.setRater(Some(SystemOutLoggingAllExceptionRater)) var propertyStore: PropertyStore = null @@ -360,10 +360,10 @@ object Immutability { ) } - val classGroupedResults = propertyStore.entities(ClassImmutability_new.key). + val classGroupedResults = propertyStore.entities(ClassImmutability.key). filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) - val order = (eps1: EPS[Entity, ClassImmutability_new], eps2: EPS[Entity, ClassImmutability_new]) ⇒ + val order = (eps1: EPS[Entity, ClassImmutability], eps2: EPS[Entity, ClassImmutability]) ⇒ eps1.e.toString < eps2.e.toString val mutableClasses = classGroupedResults(MutableClass).toSeq.sortWith(order) @@ -401,12 +401,12 @@ object Immutability { ) } - val typeGroupedResults = propertyStore.entities(TypeImmutability_new.key). + val typeGroupedResults = propertyStore.entities(TypeImmutability.key). filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) - val typeOrder = (eps1: EPS[Entity, TypeImmutability_new], eps2: EPS[Entity, TypeImmutability_new]) ⇒ + val typeOrder = (eps1: EPS[Entity, TypeImmutability], eps2: EPS[Entity, TypeImmutability]) ⇒ eps1.e.toString < eps2.e.toString - val mutableTypes = typeGroupedResults(MutableType_new).toSeq.sortWith(typeOrder) + val mutableTypes = typeGroupedResults(MutableType).toSeq.sortWith(typeOrder) val shallowImmutableTypes = typeGroupedResults(ShallowImmutableType).toSeq.sortWith(typeOrder) val dependentImmutableTypes = typeGroupedResults(DependentImmutableType).toSeq.sortWith(typeOrder) val deepImmutableTypes = typeGroupedResults(DeepImmutableType).toSeq.sortWith(typeOrder) 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/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/OPAL/tac/src/main/resources/reference.conf b/OPAL/tac/src/main/resources/reference.conf index a24bbc0434..8c707ef617 100644 --- a/OPAL/tac/src/main/resources/reference.conf +++ b/OPAL/tac/src/main/resources/reference.conf @@ -14,10 +14,45 @@ 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" + }, + "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.", @@ -48,11 +83,6 @@ org.opalj { description = "Determines a method's purity.", eagerFactory = "org.opalj.tac.fpcf.analyses.purity.EagerL2PurityAnalysis", lazyFactory = "org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis" - }, - "L2PurityAnalysis_new" { - description = "Determines a method's purity.", - eagerFactory = "org.opalj.fpcf.analyses.EagerL2PurityAnalysis_new", - lazyFactory = "org.opalj.fpcf.analyses.LazyL2PurityAnalysis_new" } } }, From 6fe0dd03de893e0e762c297f6e9af3ea890408b9 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 24 Sep 2020 16:54:15 +0200 Subject: [PATCH 273/327] adapting the tests to the uniform immutability analyses --- .../field_mutability/LazyInitialization.java | 79 +++++++++---------- 1 file changed, 38 insertions(+), 41 deletions(-) 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..54a9c96cb0 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,10 @@ */ 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; /** * Test classes for simple lazy initialization patterns and anti-patterns. @@ -44,9 +41,9 @@ class Simple { - @LazyInitialized("Simple lazy initialization") - @NonFinal(value = "Analysis doesn't recognize lazy initialization", - analyses = { L0FieldMutabilityAnalysis.class, L1FieldMutabilityAnalysis.class }) + @ShallowImmutableField("Simple lazy initialization") + @MutableField(value = "Analysis doesn't recognize lazy initialization", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) private int x; public int init() { @@ -59,9 +56,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 +72,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 +86,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 +102,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 +119,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 +134,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 +157,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 +197,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 +214,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 +249,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 +282,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 +297,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 +314,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 +328,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 +373,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 +388,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) { From 06fc7d0a0d3b9e576dbf233758195f8f2c42602d Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 24 Sep 2020 16:57:07 +0200 Subject: [PATCH 274/327] remove comments --- .../PossibleExceptionInInitialization.java | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/PossibleExceptionInInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/PossibleExceptionInInitialization.java index ed5e38a5a2..13367c6d95 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/PossibleExceptionInInitialization.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/PossibleExceptionInInitialization.java @@ -1,12 +1,13 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.classes; -import org.opalj.fpcf.properties.field_mutability.NonFinal; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeButDeterministicReference; +import org.opalj.fpcf.properties.immutability.references.MutableReference; class PossibleExceptionInInitialization { - @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Incorrect because lazy initialization is may not happen due to exception") + @LazyInitializedNotThreadSafeButDeterministicReference("Incorrect because lazy initialization is may " + + "not happen due to exception") private int x; public int init(int i) { @@ -21,7 +22,7 @@ public int init(int i) { class CaughtExceptionInInitialization { - @MutableReferenceAnnotation("Incorrect because lazy initialization is may not happen due to exception") + @MutableReference("Incorrect because lazy initialization is may not happen due to exception") private int x; public int init(int i) { @@ -36,15 +37,4 @@ public int init(int i) { return 0; } } - //Test - //com.sun.corba.se.impl.io.FVDCodeBaseImp - //com.sun.xml.internal.bind.v2.model.impl.Messages - //com.sun.xml.internal.bind.v2.model.impl.Messages - //com.sun.jmx.defaults.JmxProperties - //com.sun.jmx.defaults - //com.sun.xml.internal.bind.v2.model.impl.ModelBuilder - //javax.management.loading.MLet - - //com.sun.corba.se.impl.io.FVDCodeBaseImpl - // } \ No newline at end of file From 41e0b8ef9928614e9622c952565da6ff54653972 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 28 Sep 2020 10:27:20 +0200 Subject: [PATCH 275/327] revise --- .../ClassImmutabilityAnalysisDemo.scala | 72 ++-- .../FieldImmutabilityAnalysisDemo.scala | 44 +-- ...eldReferenceImmutabilityAnalysisDemo.scala | 102 ++--- .../TypeImmutabilityAnalysisDemo.scala | 47 ++- .../org/opalj/support/info/Immutability.scala | 371 ++++++++++-------- 5 files changed, 354 insertions(+), 282 deletions(-) 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 index a7384c42d5..30d257f9a7 100644 --- 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 @@ -8,6 +8,7 @@ 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 @@ -35,15 +36,15 @@ import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis /** - * Runs the EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result + * Runs the EagerL1ClassImmutabilityAnalysis as well as analyses needed for improving the result * - * @author Tobias Peter Roth + * @author Tobias Roth */ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "run EagerLxClassImmutabilityAnalysis_new" + override def title: String = "determines the immutability of classes" - override def description: String = "run EagerLxClassImmutabilityAnalysis_new" + override def description: String = "identifies classes that are immutable in a shallow or deep way" override def doAnalyze( project: Project[URL], @@ -57,13 +58,14 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { def analyze(project: Project[URL]): String = { var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + val analysesManager = project.get(FPCFAnalysesManagerKey) analysesManager.project.get(RTACallGraphKey) time { - propertyStore = analysesManager .runAll( LazyUnsoundPrematurelyReadFieldsAnalysis, @@ -79,10 +81,13 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { LazyFieldLocalityAnalysis ) ._1 - propertyStore.waitOnPhaseCompletion(); + + propertyStore.waitOnPhaseCompletion() + } { t ⇒ analysisTime = t.toSeconds } + val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet val groupedResults = propertyStore.entities(ClassImmutability.key). @@ -90,31 +95,38 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { 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) + + 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])).toSeq.sortWith(order) + .filter(eps ⇒ allInterfaces.contains(eps.asInstanceOf[ObjectType])).sortWith(order) + val deepImmutableClasses = deepImmutables - .filter(eps ⇒ !allInterfaces.contains(eps.asInstanceOf[ObjectType])).toSeq.sortWith(order) + .filter(eps ⇒ !allInterfaces.contains(eps.asInstanceOf[ObjectType])).sortWith(order) val output = s""" | Mutable Classes: - | ${mutableClasses.mkString(" |Mutable Class")} + | ${mutableClasses.mkString(" | Mutable Class\n")} | | Shallow Immutable Classes: - | ${shallowImmutableClasses.mkString(" |Shallow Immutable Class\n")} + | ${shallowImmutableClasses.mkString(" | Shallow Immutable Class\n")} | | Dependent Immutable Classes: - | ${dependentImmutableClasses.mkString(" |Dependent Immutable Class\n")} + | ${dependentImmutableClasses.mkString(" | Dependent Immutable Class\n")} | | Deep Immutable Classes: | ${deepImmutableClasses.mkString(" | Deep Immutable Classes\n")} @@ -123,28 +135,34 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { | ${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} + | 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} | - | took : $analysisTime seconds + | 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 file = new File(s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt") + + val bw = new BufferedWriter(new FileWriter(file)) + try { - val bw = new BufferedWriter(new FileWriter(file)) bw.write(output) bw.close() } catch { - case e: IOException ⇒ - println(s"Could not write the file: ${file.getName}"); throw e - case _: Throwable ⇒ + case _: IOException ⇒ + println(s"Could not write file: ${file.getName}") + } finally { + bw.close() } - s" took : $analysisTime seconds" + + 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 index 33546a54f8..2ca3a9dab8 100644 --- 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 @@ -4,6 +4,11 @@ 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 @@ -30,24 +35,17 @@ import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds import org.opalj.br.fpcf.properties.FieldImmutability -import java.io.BufferedWriter -import java.io.File -import java.io.FileWriter -import java.util.Calendar -import org.opalj.bytecode.JRELibraryFolder -import java.io.IOException - /** - * Runs the EagerL0FieldImmutabilityAnalysis including analysis needed for improving the result. + * Runs the EagerL0FieldImmutabilityAnalysis including all analyses needed for improving the result. * * @author Tobias Peter Roth */ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0FieldImmutabilityAnalysis" + override def title: String = "determines the immutability of (instance/static) fields" override def description: String = - "runs the EagerL0FieldImmutabilityAnalysis" + "identifies fields that are immutable in a deep or shallow way" override def doAnalyze( project: Project[URL], @@ -113,31 +111,29 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { | ${deepImmutableFields.mkString(" | Deep Immutable Field\n")} | | - | mutable fields: ${mutableFields.size} - | shallow immutable fields: ${shallowImmutableFields.size} - | dependent immutable fields: ${dependentImmutableFields.size} - | deep immutable fields: ${deepImmutableFields.size} + | Mutable Fields: ${mutableFields.size} + | Shallow Immutable Fields: ${shallowImmutableFields.size} + | Dependent Immutable Fields: ${dependentImmutableFields.size} + | Deep Immutable Fields: ${deepImmutableFields.size} | - | count: ${mutableFields.size + shallowImmutableFields.size + dependentImmutableFields.size + deepImmutableFields.size} + | sum: ${mutableFields.size + shallowImmutableFields.size + dependentImmutableFields.size + deepImmutableFields.size} | | took : $analysisTime seconds |""".stripMargin - val file = new File( - s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt" - ) + val file = new File(s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt") + + val bw = new BufferedWriter(new FileWriter(file)) try { - val bw = new BufferedWriter(new FileWriter(file)) bw.write(output) bw.close() } catch { - case e: IOException ⇒ println(s"could not write file ${file.getName}"); throw e; - case _: Throwable ⇒ + case e: IOException ⇒ println(s"could not write file ${file.getName}") + } finally { + bw.close() } - println("JRELibraryFolder: "+JRELibraryFolder) - - s"took : $analysisTime seconds" + 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 index df92b6420e..6fe7c00a23 100644 --- 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 @@ -29,18 +29,23 @@ 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 EagerL0ReferenceImmutabilityAnalysis including all analysis needed for improving the result. + * Runs the EagerL0FieldReferenceImmutabilityAnalysis including all analysis needed for improving the result. * * @author Tobias Peter Roth */ object FieldReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "runs the EagerL0ReferenceImmutabilityAnalysis" + override def title: String = "determines the immutability of (static/instance) field references" override def description: String = - "runs the EagerL0ReferenceImmutabilityAnalysis" + "identifies immutable field references" override def doAnalyze( project: Project[URL], @@ -52,15 +57,6 @@ object FieldReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication } def analyze(project: Project[URL]): String = { - /*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.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 var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None @@ -106,50 +102,56 @@ object FieldReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication val threadSafeLazyInitializedFieldReferences = groupedResults(LazyInitializedThreadSafeFieldReference).toSeq. sortWith(order) val immutableReferences = groupedResults(ImmutableFieldReference).toSeq.sortWith(order) - val output = s""" - | Mutable Fiel References: - | ${mutableReferences.mkString(" | Mutable Field Reference \n")} - | lazy initialized not thread deterministic references: - | ${notThreadSafeLazyInitializedFieldReferences.mkString(" | Not Thread \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 safe or deterministic: - | ${notThreadSafeLazyInitializedFieldReferences.size} - | lazy initialized references not thread safe but deterministic: - | ${lazyInitializedReferencesNotThreadSafeButDeterministic.size} - | lazy initialized thread safe references: ${threadSafeLazyInitializedFieldReferences.size} - | immutable References: ${immutableReferences.size} - | - | took : $analysisTime seconds - |""".stripMargin - - val file = new File( - s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt" - ) + 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 { - - if (!file.exists()) - file.createNewFile() - val bw = new BufferedWriter(new FileWriter(file)) bw.write(output) bw.close() } catch { - case e: IOException ⇒ - println(s"Could not write file: ${file.getName}"); throw e - case _: Throwable ⇒ + case e: IOException ⇒ println(s"Could not write file: ${file.getName}") + } finally { + bw.close() } - s"took : $analysisTime seconds" - + output } } 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 index 8894f4e542..9a1fea829e 100644 --- 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 @@ -32,17 +32,21 @@ 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 EagerLxClassImmutabilityAnalysis_new as well as analysis needed for improving the result + * Runs the EagerL1TypeImmutabilityAnalysis as well as all analyses needed for improving the result * - * @author Tobias Peter Roth + * @author Tobias Roth */ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { - override def title: String = "run EagerLxTypeImmutabilityAnalysis_new" + override def title: String = "determines the immutability of types respecting all possible subtypes" - override def description: String = "run EagerLxTypeImmutabilityAnalysis_new" + 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], @@ -54,12 +58,13 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } def analyze(project: Project[URL]): String = { - import org.opalj.br.fpcf.properties.TypeImmutability - import org.opalj.fpcf.EPS - import org.opalj.fpcf.Entity + var propertyStore: PropertyStore = null + var analysisTime: Seconds = Seconds.None + val analysesManager = project.get(FPCFAnalysesManagerKey) + analysesManager.project.get(RTACallGraphKey) time { @@ -78,7 +83,9 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { LazyFieldLocalityAnalysis ) ._1 + propertyStore.waitOnPhaseCompletion(); + } { t ⇒ analysisTime = t.toSeconds } @@ -90,9 +97,13 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { 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 = @@ -110,29 +121,29 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { | ${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} + | 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 file = new File(s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt") + + val bw = new BufferedWriter(new FileWriter(file)) try { - val bw = new BufferedWriter(new FileWriter(file)) bw.write(output) bw.close() } catch { case e: IOException ⇒ - println(s"could not write file: ${file.getName}"); throw e - case _: Throwable ⇒ + println(s"could not write file: ${file.getName}") + } finally { + bw.close() } - s" took : $analysisTime seconds" + output } } 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 index 702ad2845b..9fa651120c 100644 --- 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 @@ -74,9 +74,14 @@ import org.opalj.tac.cg.AllocationSiteBasedPointsToCallGraphKey import org.opalj.tac.cg.CHACallGraphKey import org.opalj.tac.cg.AbstractCallGraphKey import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +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 /** - * Determines the immutability of references, fields, classes and types of a project + * Determines the immutability of field references, fields, classes or/and types * * @author Tobias Peter Roth */ @@ -108,11 +113,6 @@ object Immutability { level: Int, reImInferComparison: Boolean ): BasicReport = { - 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 OPALLogger.updateLogger(GlobalLogContext, DevNullLogger) @@ -154,7 +154,10 @@ object Immutability { libraryClassFilesAreInterfacesOnly = false, Traversable.empty ) - } { t ⇒ projectTime = t.toSeconds } + } { + t ⇒ projectTime = t.toSeconds + } + val referenceDependencies: List[FPCFAnalysisScheduler] = List( EagerL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, @@ -165,6 +168,7 @@ object Immutability { LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis ) + val fieldDependencies: List[FPCFAnalysisScheduler] = List( LazyL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, @@ -178,6 +182,7 @@ object Immutability { LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis ) + val classDepencencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, @@ -191,6 +196,7 @@ object Immutability { LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis ) + val typeDependencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, @@ -204,6 +210,7 @@ object Immutability { LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis ) + val allImmAnalysisDependencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, @@ -218,6 +225,7 @@ object Immutability { LazyReturnValueFreshnessAnalysis, LazyFieldLocalityAnalysis ) + val dependencies = analysis match { case References ⇒ referenceDependencies case Fields ⇒ fieldDependencies @@ -231,19 +239,25 @@ object Immutability { var propertyStore: PropertyStore = null val analysesManager = project.get(FPCFAnalysesManagerKey) + println(s"callgraph $callGraphKey") + time { analysesManager.project.get(callGraphKey) } { t ⇒ callGraphTime = t.toSeconds } + println(s"level: $level") + analysesManager.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("wrong level") - //Set[Class[_ <: AnyRef]](classOf[domain.l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + 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("wrong level") } - //var propertyStore = time { project.get(PropertyStoreKey) } { t ⇒ propertyStoreTime = t.toSeconds } project.getOrCreateProjectInformationKeyInitializationData( PropertyStoreKey, @@ -267,20 +281,21 @@ object Immutability { ) ._1 propertyStore.waitOnPhaseCompletion() - - } { t ⇒ - analysisTime = t.toSeconds + } { + t ⇒ analysisTime = t.toSeconds } val stringBuilderResults: StringBuilder = new StringBuilder() val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet + val allFieldsInProjectClassFiles = { - if (reImInferComparison) + if (reImInferComparison) { project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet - else + } else { project.allProjectClassFiles.toIterator.flatMap { _.fields }. filter(f ⇒ (!f.isTransient && !f.isSynthetic)).toSet + } } val fieldReferenceGroupedResults = propertyStore.entities(FieldReferenceImmutability.key). @@ -289,45 +304,49 @@ object Immutability { val fieldReferenceOrder = (eps1: EPS[Entity, FieldReferenceImmutability], eps2: EPS[Entity, FieldReferenceImmutability]) ⇒ eps1.e.toString < eps2.e.toString + val mutableFieldReferences = fieldReferenceGroupedResults(MutableFieldReference).toSeq.sortWith(fieldReferenceOrder) + val notThreadSafeLazyInitializedFieldReferences = fieldReferenceGroupedResults(LazyInitializedNotThreadSafeFieldReference).toSeq.sortWith(fieldReferenceOrder) + val lazyInitializedReferencesNotThreadSafeButDeterministic = fieldReferenceGroupedResults(LazyInitializedNotThreadSafeButDeterministicFieldReference).toSeq.sortWith(fieldReferenceOrder) + val threadSafeLazyInitializedFieldReferences = fieldReferenceGroupedResults(LazyInitializedThreadSafeFieldReference).toSeq.sortWith(fieldReferenceOrder) + val immutableReferences = fieldReferenceGroupedResults(ImmutableFieldReference).toSeq.sortWith(fieldReferenceOrder) - //Test.... - //mutableReferences = mutableReferences ++ notThreadSafeLazyInitialization - // for comparison reasons if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.References) { - stringBuilderResults.append(s""" - | Mutable References: - | ${mutableFieldReferences.mkString(" || Mutable Reference \n")} - | - | Lazy Initalized Not Thread Safe And Not Deterministic References: - | ${ - notThreadSafeLazyInitializedFieldReferences. - mkString(" || Lazy Initalized Not Thread Safe And Not Deterministic Reference\n") - } - | - | Lazy Initialized Not Thread Safe But Deterministic References: - | ${ - lazyInitializedReferencesNotThreadSafeButDeterministic. - mkString(" || Lazy Initialized Not Thread Safe But Deterministic Reference\n") - } - | - | Lazy Initialized Thread Safe References: - | ${ - threadSafeLazyInitializedFieldReferences. - mkString(" || Lazy Initialized Thread Safe Reference\n") - } - | - | Immutable References: - | ${immutableReferences.mkString(" || immutable Reference\n")} - | - |""".stripMargin) + stringBuilderResults.append( + s""" + | Mutable References: + | ${mutableFieldReferences.mkString(" || Mutable Reference \n")} + | + | Lazy Initalized Not Thread Safe And Not Deterministic References: + | ${ + notThreadSafeLazyInitializedFieldReferences. + mkString(" | Lazy Initialized Not Thread Safe And Not Deterministic Reference\n") + } + | + | Lazy Initialized Not Thread Safe But Deterministic References: + | ${ + lazyInitializedReferencesNotThreadSafeButDeterministic. + mkString(" || Lazy Initialized Not Thread Safe But Deterministic Reference\n") + } + | + | Lazy Initialized Thread Safe References: + | ${ + threadSafeLazyInitializedFieldReferences. + mkString(" || Lazy Initialized Thread Safe Reference\n") + } + | + | Immutable References: + | ${immutableReferences.mkString(" || immutable Reference\n")} + | + |""".stripMargin + ) } val fieldGroupedResults = propertyStore.entities(FieldImmutability.key). @@ -336,27 +355,31 @@ object Immutability { val fieldOrder = (eps1: EPS[Entity, FieldImmutability], eps2: EPS[Entity, FieldImmutability]) ⇒ eps1.e.toString < eps2.e.toString + val mutableFields = fieldGroupedResults(MutableField).toSeq.sortWith(fieldOrder) + val shallowImmutableFields = fieldGroupedResults(ShallowImmutableField).toSeq.sortWith(fieldOrder) + val dependentImmutableFields = fieldGroupedResults(DependentImmutableField).toSeq.sortWith(fieldOrder) + val deepImmutableFields = fieldGroupedResults(DeepImmutableField).toSeq.sortWith(fieldOrder) if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.Fields) { stringBuilderResults.append( s""" - | 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")} - | - |""".stripMargin + | 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")} + | + |""".stripMargin ) } @@ -365,39 +388,45 @@ object Immutability { val order = (eps1: EPS[Entity, ClassImmutability], eps2: EPS[Entity, ClassImmutability]) ⇒ eps1.e.toString < eps2.e.toString + val mutableClasses = classGroupedResults(MutableClass).toSeq.sortWith(order) + val shallowImmutableClasses = classGroupedResults(ShallowImmutableClass).toSeq.sortWith(order) + val dependentImmutableClasses = classGroupedResults(DependentImmutableClass).toSeq.sortWith(order) + val deepImmutables = classGroupedResults(DeepImmutableClass) + val allInterfaces = project.allProjectClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet + val deepImmutableClassesInterfaces = deepImmutables .filter(eps ⇒ allInterfaces.contains(eps.asInstanceOf[ObjectType])).toSeq.sortWith(order) - val deepImmutableClasses = - deepImmutables - .filter(eps ⇒ !allInterfaces.contains(eps.asInstanceOf[ObjectType])).toSeq.sortWith(order) + + val deepImmutableClasses = deepImmutables + .filter(eps ⇒ !allInterfaces.contains(eps.asInstanceOf[ObjectType])).toSeq.sortWith(order) if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.Classes) { stringBuilderResults.append( 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 Interfaces: - | ${deepImmutableClassesInterfaces.mkString(" || Deep Immutable Interfaces \n")} - |""".stripMargin + | 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 Interfaces: + | ${deepImmutableClassesInterfaces.mkString(" || Deep Immutable Interfaces \n")} + |""".stripMargin ) } @@ -406,26 +435,30 @@ object Immutability { val typeOrder = (eps1: EPS[Entity, TypeImmutability], eps2: EPS[Entity, TypeImmutability]) ⇒ eps1.e.toString < eps2.e.toString + val mutableTypes = typeGroupedResults(MutableType).toSeq.sortWith(typeOrder) + val shallowImmutableTypes = typeGroupedResults(ShallowImmutableType).toSeq.sortWith(typeOrder) + val dependentImmutableTypes = typeGroupedResults(DependentImmutableType).toSeq.sortWith(typeOrder) + val deepImmutableTypes = typeGroupedResults(DeepImmutableType).toSeq.sortWith(typeOrder) if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.Types) { stringBuilderResults.append( s""" - | Mutable Types: - | ${mutableTypes.mkString(" || Mutable Type \n")} - | - | Shallow Immutable Types: - | ${shallowImmutableTypes.mkString(" || Shallow Immutable Types \n")} - | - | Dependent Immutable Types: - | ${dependentImmutableTypes.mkString(" || Dependent Immutable Types \n")} - | - | Deep Immutable Types: - | ${deepImmutableTypes.mkString(" || Deep Immutable Types \n")} - |""".stripMargin + | Mutable Types: + | ${mutableTypes.mkString(" | Mutable Type \n")} + | + | Shallow Immutable Types: + | ${shallowImmutableTypes.mkString(" | Shallow Immutable Types \n")} + | + | Dependent Immutable Types: + | ${dependentImmutableTypes.mkString(" | Dependent Immutable Types \n")} + | + | Deep Immutable Types: + | ${deepImmutableTypes.mkString(" | Deep Immutable Types \n")} + |""".stripMargin ) } @@ -434,103 +467,109 @@ object Immutability { if (analysis == RunningAnalyses.References || analysis == RunningAnalyses.All) { stringBuilderAmounts.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 + | 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 == RunningAnalyses.Fields || analysis == RunningAnalyses.All) { stringBuilderAmounts.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 + | 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 == RunningAnalyses.Classes || analysis == RunningAnalyses.All) { stringBuilderAmounts.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 + | 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 == RunningAnalyses.Types || analysis == RunningAnalyses.All) stringBuilderAmounts.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 + | 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 // + propertyStoreTime - // $propertyStoreTime seconds propertyStoreTime + val totalTime = projectTime + callGraphTime + analysisTime + stringBuilderAmounts.append( s""" | running ${analysis.toString} analysis - | took $totalTime total time - | $projectTime seconds projectTime - | $callGraphTime seconds callGraphTime - | $analysisTime seconds analysisTime - | $analysisTime seconds + | took: + | $totalTime seconds total time + | $projectTime seconds project time + | $callGraphTime seconds callgraph time + | $analysisTime seconds analysis time |""".stripMargin ) + println( s""" - | - | ${stringBuilderAmounts.toString()} - | - | Time Results: - | - | $numThreads Threads :: took $analysisTime - | - |""".stripMargin + | + | ${stringBuilderAmounts.toString()} + | + | time results: + | + | $numThreads Threads :: took $analysisTime seconds analysis time + | + | results folder: $resultsFolder + |""".stripMargin ) - println("Results folder: "+resultsFolder) if (resultsFolder != null) { - val file = new File( - s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt" - ) + + 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( + s""" ${stringBuilderResults.toString()} + | + | ${stringBuilderAmounts.toString()} + | + | jdk folder: $JRELibraryFolder + | + |"""".stripMargin + ) - file.createNewFile() - val bw = new BufferedWriter(new FileWriter(file)) - bw.write(s""" ${stringBuilderResults.toString()} - | - | ${stringBuilderAmounts.toString()} - | - | jdk folder: $JRELibraryFolder - | - |"""".stripMargin) bw.close() } catch { - - case e: IOException ⇒ - println(s"could not write file: ${file.getName}"); throw e - case _: Throwable ⇒ + case e: IOException ⇒ println(s"could not write file: ${file.getName}") + } finally { + bw.close } } + println(s"propertyStore: ${propertyStore.getClass.toString()}") + println(s"jdk folder: $JRELibraryFolder") - println({ project.config.atKey("org.opalj.br.analyses.cg.ClosedPackagesKey") }) + BasicReport( stringBuilderAmounts.toString() ) @@ -540,18 +579,23 @@ object Immutability { def usage: String = { s""" - | Usage: java …ImmutabilityAnalysisEvaluation - | -cp OR -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) - |""".stripMargin + | 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) + |""".stripMargin } + var i = 0 var cp: File = null var resultFolder: Path = null @@ -617,7 +661,8 @@ object Immutability { } i += 1 } - if (!(0 <= level && level <= 2)) throw new Exception(s"not a domain level: $level") + if (!(0 <= level && level <= 2)) + throw new Exception(s"not a domain level: $level") val callGraphKey = callGraphName match { case Some("CHA") ⇒ CHACallGraphKey From c159db544695515d3bef1c17af6307fb280c318f Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 28 Sep 2020 12:02:03 +0200 Subject: [PATCH 276/327] revise --- .../fpcf/ClassAndTypeImmutabilityTests.scala | 30 +---- .../opalj/fpcf/ClassImmutabilityTests.scala | 106 ----------------- .../opalj/fpcf/TypeImmutabilityTests.scala | 108 ------------------ 3 files changed, 2 insertions(+), 242 deletions(-) delete mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala delete mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala 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 index 45af40a7d8..ab0ff21e48 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala @@ -1,5 +1,6 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf +package org.opalj +package fpcf import org.opalj.ai.fpcf.analyses.LazyL0BaseAIAnalysis import java.net.URL @@ -32,33 +33,6 @@ class ClassAndTypeImmutabilityTests extends PropertiesTest { override def fixtureProjectPackage: List[String] = { List("org/opalj/fpcf/fixtures/immutability") } - /* - override def createConfig(): Config = { - - 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( - 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" - )) - } */ override def init(p: Project[URL]): Unit = { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala deleted file mode 100644 index a065684da9..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassImmutabilityTests.scala +++ /dev/null @@ -1,106 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf - -import java.net.URL -import com.typesafe.config.Config -import com.typesafe.config.ConfigValueFactory -import com.typesafe.config.ConfigValueFactory.fromAnyRef - -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.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.EagerLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new -import org.opalj.br.analyses.cg.InitialEntryPointsKey -import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey - -/** - * Tests the class immutability analysis - * - * @author Tobias Peter Roth - */ -class ClassImmutabilityTests extends PropertiesTest { - - override def withRT = true - - override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") - } - - override def createConfig(): Config = { - - 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( - InitialInstantiatedTypesKey.ConfigKeyPrefix+ - "AllInstantiatedTypesFinder.projectClassesOnly", - ConfigValueFactory.fromAnyRef(true) - ) - .withValue( - "org.opalj.br.analyses.cg.ClosedPackagesKey", - fromAnyRef("org.opalj.br.analyses.cg.OpenCodeBase") - ) - .withValue("org.opalj.br.analyses.cg.ClassExtensibilityKey", ConfigValueFactory.fromAnyRef( - "org.opalj.br.analyses.cg.ConfiguredExtensibleClasses" - )) - } - - 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("ClassImmutability_new")) - } - - describe("the org.opalj.fpcf.analyses.LxClassImmutabilityAnalysis is executed") { - val as = executeAnalyses( - Set( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0FieldReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxTypeImmutabilityAnalysis_new, - EagerLxClassImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ) - as.propertyStore.shutdown() - validateProperties( - as, - classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), - Set("ClassImmutability_new") - ) - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala deleted file mode 100644 index f7643c105d..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/TypeImmutabilityTests.scala +++ /dev/null @@ -1,108 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package fpcf - -import java.net.URL -import com.typesafe.config.Config -import com.typesafe.config.ConfigValueFactory -import com.typesafe.config.ConfigValueFactory.fromAnyRef - -import org.opalj.ai.domain.l2 -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.EagerLxTypeImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldReferenceImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new -import org.opalj.ai.fpcf.properties.AIDomainFactoryKey -import org.opalj.br.analyses.cg.InitialEntryPointsKey -import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey - -/** - * Tests the type immutability analysis - * - * @author Tobias Peter Roth - */ -class TypeImmutabilityTests extends PropertiesTest { - - override def withRT = true - - override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") - } - - override def createConfig(): Config = { - - 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( - InitialInstantiatedTypesKey.ConfigKeyPrefix+ - "AllInstantiatedTypesFinder.projectClassesOnly", - ConfigValueFactory.fromAnyRef(true) - ) - .withValue( - "org.opalj.br.analyses.cg.ClosedPackagesKey", - fromAnyRef("org.opalj.br.analyses.cg.OpenCodeBase") - ) - .withValue("org.opalj.br.analyses.cg.ClassExtensibilityKey", ConfigValueFactory.fromAnyRef( - "org.opalj.br.analyses.cg.ConfiguredExtensibleClasses" - )) - } - - 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("TypeImmutability_new")) - } - - describe("the org.opalj.fpcf.analyses.LxTypeImmutabilityAnalysis_new is executed") { - val as = executeAnalyses( - Set( - LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis_new, - LazyL0FieldReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - EagerLxTypeImmutabilityAnalysis_new, - LazyStaticDataUsageAnalysis, - LazyL0CompileTimeConstancyAnalysis, - LazyInterProceduralEscapeAnalysis, - LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis - ) - ) - - as.propertyStore.shutdown() - - validateProperties( - as, - classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), - Set("TypeImmutability_new") - ) - } -} From af4c2347739c7569b09ecd5f74016145d398805c Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 28 Sep 2020 12:29:57 +0200 Subject: [PATCH 277/327] remove commented out code --- .../opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b9e4c5f07c..b2a3961249 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 @@ -389,7 +389,7 @@ trait AbstractPurityAnalysis extends FPCFAnalysis { * Examines the influence that a given field mutability has on the method's purity. */ def checkFieldMutability( - ep: EOptionP[Field, FieldImmutability], //FieldMutability], + ep: EOptionP[Field, FieldImmutability], objRef: Option[Expr[V]] )(implicit state: StateType): Unit = ep match { case LBP(ShallowImmutableField | From 932acaa78da6037f09c13ea7cbdae5bbd289cb0d Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 7 Oct 2020 14:05:33 +0200 Subject: [PATCH 278/327] revised, still wip --- ...tFieldReferenceImmutabilityAnalysis.scala} | 116 ++++++++++++------ 1 file changed, 79 insertions(+), 37 deletions(-) rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/{reference/AbstractReferenceImmutabilityAnalysis.scala => fieldreference/AbstractFieldReferenceImmutabilityAnalysis.scala} (55%) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/AbstractFieldReferenceImmutabilityAnalysis.scala similarity index 55% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/AbstractFieldReferenceImmutabilityAnalysis.scala index c40627e66e..5690e4221b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/AbstractFieldReferenceImmutabilityAnalysis.scala @@ -1,6 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.immutability.reference +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 @@ -15,34 +22,36 @@ 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.ImmutableReference -import org.opalj.br.fpcf.properties.MutableReference +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.ReferenceImmutability -import org.opalj.br.fpcf.properties.TypeImmutability_new +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.EUBP 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.DUVar -import org.opalj.tac.Stmt -import org.opalj.tac.TACMethodParameter -import org.opalj.tac.TACode import org.opalj.tac.common.DefinitionSite import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.properties.TACAI import org.opalj.value.ValueInformation /** - * Encompasses the base functions used in the [[L0ReferenceImmutabilityAnalysis]] + * + * Encompasses the base functions for determining field reference immutability. + * + * @author Tobias Roth + * @author Dominik Helm + * @author Florian Kübler + * @author Michael Eichberg + * */ -trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { +trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { type V = DUVar[ValueInformation] final val typeExtensibility = project.get(TypeExtensibilityKey) @@ -53,30 +62,31 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { case class State( field: Field, - var referenceImmutability: ReferenceImmutability = ImmutableReference, - 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 referenceImmutabilityDependees: Set[EOptionP[Field, ReferenceImmutability]] = 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_new]] = Set.empty + 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 ) { def hasDependees: Boolean = { prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.isDefined || referenceImmutabilityDependees.nonEmpty || + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || + calleesDependee.nonEmpty || referenceImmutabilityDependees.nonEmpty || escapeDependees.nonEmpty || tacDependees.nonEmpty || typeDependees.nonEmpty } def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - referenceImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) ++ typeDependees + prematurelyReadDependee ++ purityDependees ++ referenceImmutabilityDependees ++ escapeDependees ++ + calleesDependee.valuesIterator.map(_._1) ++ tacDependees.valuesIterator.map(_._1) ++ typeDependees } } /** - * Returns the TACode for a method if available, registering dependencies as necessary. + * Returns the TACode for a method if available and registers dependencies if necessary. */ def getTACAI( method: Method, @@ -101,7 +111,7 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { )(implicit state: State): Boolean = eop match { case LBP(p: Purity) if p.isDeterministic ⇒ false - case EUBP(e, p: Purity) if !p.isDeterministic ⇒ + case UBP(p: Purity) if !p.isDeterministic ⇒ true case _ ⇒ state.purityDependees += eop @@ -112,11 +122,11 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { method: Method, code: Array[Stmt[V]] )(implicit state: State): Boolean = { - val propertyStoreResult = propertyStore(declaredMethods(method), Purity.key) - val resultIsNonDeterministic = !isNonDeterministic( - propertyStoreResult - ) - method.descriptor.parametersCount == 0 && resultIsNonDeterministic + //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)) } /** @@ -124,12 +134,10 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { * ensuring that the same value is written even for concurrent executions. */ def isImmutableReference( - eop: EOptionP[Field, ReferenceImmutability] + eop: EOptionP[Field, FieldReferenceImmutability] )(implicit state: State): Boolean = eop match { - case FinalEP(e, ImmutableReference) ⇒ true - case FinalEP(e, MutableReference) ⇒ false - case LBP(ImmutableReference) ⇒ true - case UBP(MutableReference) ⇒ false + case LBP(ImmutableFieldReference) ⇒ true + case UBP(MutableFieldReference) ⇒ false case _ ⇒ state.referenceImmutabilityDependees += eop true @@ -146,10 +154,44 @@ trait AbstractReferenceImmutabilityAnalysis extends FPCFAnalysis { case LBP(NotPrematurelyReadField) ⇒ state.prematurelyReadDependee = None false - case UBP(PrematurelyReadField) ⇒ true + case UBP(PrematurelyReadField) ⇒ + state.prematurelyReadDependee = None + true case eps ⇒ state.prematurelyReadDependee = Some(eps) false } } + + def handleCalls( + calleesEOP: EOptionP[DeclaredMethod, Callees], + pc: PC + )( + implicit + state: State + ): Boolean = { + calleesEOP match { + case FinalP(callees) ⇒ handleCallees(callees, pc) + case InterimUBP(callees) ⇒ handleCallees(callees.asInstanceOf[Callees], pc) + case _ ⇒ + state.calleesDependee += + calleesEOP.e → ((calleesEOP, pc :: state.calleesDependee(calleesEOP.e)._2)) + false + } + } + + def handleCallees(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 + } + } + } } From 58e4b8b709aeea8ed03fe8450e47bf1a14ddf547 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 7 Oct 2020 14:24:36 +0200 Subject: [PATCH 279/327] renamed --- ...mutabilityAnalysisLazyInitialization.scala | 745 ------------------ 1 file changed, 745 deletions(-) delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala deleted file mode 100644 index 2178118ac8..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/AbstractReferenceImmutabilityAnalysisLazyInitialization.scala +++ /dev/null @@ -1,745 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.immutability.reference - -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.PC -import org.opalj.br.cfg.BasicBlock -import org.opalj.br.cfg.CFG -import org.opalj.br.cfg.CFGNode -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeReference -import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference -import org.opalj.br.fpcf.properties.MutableReference -import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.properties.ReferenceImmutability -import org.opalj.collection.immutable.IntTrieSet -import org.opalj.tac.SelfReferenceParameter -import org.opalj.tac.Assignment -import org.opalj.tac.CaughtException -import org.opalj.tac.ClassConst -import org.opalj.tac.DVar -import org.opalj.tac.Expr -import org.opalj.tac.FieldWriteAccessStmt -import org.opalj.tac.GetField -import org.opalj.tac.GetStatic -import org.opalj.tac.If -import org.opalj.tac.MonitorEnter -import org.opalj.tac.MonitorExit -import org.opalj.tac.NonVirtualFunctionCall -import org.opalj.tac.ReturnValue -import org.opalj.tac.StaticFunctionCall -import org.opalj.tac.Stmt -import org.opalj.tac.TACMethodParameter -import org.opalj.tac.TACStmts -import org.opalj.tac.TACode -import org.opalj.tac.UVar -import org.opalj.tac.VirtualFunctionCall -import org.opalj.tac.Throw -import org.opalj.br.ObjectType -import scala.annotation.switch -import org.opalj.tac.NewArray -import org.opalj.tac.Compare - -trait AbstractReferenceImmutabilityAnalysisLazyInitialization - extends AbstractReferenceImmutabilityAnalysis - with FPCFAnalysis { - - /** - * handles the lazy initialization determinations for the method - * methodUpdatesField - * Returns Some(true) if we have no thread safe or deterministic lazy initialization - * - */ - def handleLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int], - tacCai: TACode[TACMethodParameter, V] - )(implicit state: State): Option[Boolean] = { - val doubleCheckedLockingResult: ReferenceImmutability = - isThreadSafeLazyInitialization(writeIndex, defaultValue, method, code, cfg, pcToIndex, tacCai) - state.referenceImmutability = doubleCheckedLockingResult - if (doubleCheckedLockingResult == MutableReference) - Some(true) - else None - } - - /** - * Determines if a given field is thread safe lazy initialized. - * E.g. in a synchronized method with a nonnull-check. - */ - - def isThreadSafeLazyInitialization( - writeIndex: Int, - defaultValue: Any, - method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int], - tacCode: TACode[TACMethodParameter, V] - )(implicit state: State): ReferenceImmutability = { - val write = code(writeIndex).asFieldWriteAccessStmt - val writeBB = cfg.bb(writeIndex).asBasicBlock - val domTree = cfg.dominatorTree - val resultCaughtsAndThrows = findCaughtsThrowsAndResults(tacCode, cfg) - - def noInterferingExceptions: Boolean = { - resultCaughtsAndThrows._1.forall(bbCatch ⇒ - resultCaughtsAndThrows._2.exists(bbThrow ⇒ - ((domTree.strictlyDominates(bbCatch._4.nodeId, bbThrow._3.nodeId) || //domination - (bbCatch._4 == bbThrow._3 && bbCatch._1 < bbThrow._1)) && // or equal and successor - bbThrow._2 == bbCatch._3))) - } - - val (guardIndex, guardedIndex, readIndex, afterGuardRecognizedTheDefaultValueIndex) = { - val findGuardResult: (Option[(Int, Int, Int, CFGNode, Int)]) = - findGuard(method, writeIndex, defaultValue, code, cfg, tacCode) - if (findGuardResult.isDefined) - ( - findGuardResult.get._1, - findGuardResult.get._2, - findGuardResult.get._3, - findGuardResult.get._5 - ) - else { - return MutableReference; - } - } - val guardedBB = cfg.bb(afterGuardRecognizedTheDefaultValueIndex) - val elseBB = cfg.bb(guardedIndex) - - if (isTransitivePredecessorsOf(elseBB, writeBB, Set.empty)) //succsrs.toSet.contains(writeBB)) - return MutableReference; - - val reads = fieldAccessInformation.readAccesses(state.field) - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { - return MutableReference; - } - val writes = fieldAccessInformation.writeAccesses(state.field) - if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { - return MutableReference; - } - if (method.returnType == state.field.fieldType && - !checkThatTheValueOfTheFieldIsReturned(write, writeIndex, readIndex, code, tacCode)) { - return MutableReference; - } - //when the method is synchronized the monitor has not to be searched - if (method.isSynchronized) { - if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || - (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { - if (noInterferingExceptions) { - LazyInitializedThreadSafeReference // result //DCL - } else { - MutableReference - } - } else { - MutableReference - } - } else { - val monitorResult: ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = - findMonitors(writeIndex, defaultValue, code, cfg, tacCode) - if ((monitorResult._1._1.isDefined && monitorResult._1._2.isDefined && monitorResult._2._1.isDefined) - && - ( - (domTree.strictlyDominates(monitorResult._2._1.get.nodeId, guardedBB.nodeId) || - (monitorResult._2._1.get == guardedBB && monitorResult._1._1.get < afterGuardRecognizedTheDefaultValueIndex)) && //writeIndex)) && //monitor enter dominates guard1 - ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) - || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex) //&& //true case dominates Write - )) { - if (noInterferingExceptions) - LazyInitializedThreadSafeReference // result //DCL - else { - MutableReference - } - } else { - if ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || - (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) && - write.value.asVar.definedBy.size >= 0 && - (( //IsDeepImmutable //state.field.isFinal ||write.value.asVar.definedBy.head > -1 - write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.nonEmpty && - checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code) - ))) { - if (noInterferingExceptions) { - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - LazyInitializedNotThreadSafeReference - } else - LazyInitializedNotThreadSafeButDeterministicReference - } else { - MutableReference - } - } else { - MutableReference - } - } - } - } - - def findCaughtsThrowsAndResults( - tacCode: TACode[TACMethodParameter, V], - cfg: CFG[Stmt[V], TACStmts[V]] - ): (List[(Int, ObjectType, IntTrieSet, CFGNode)], List[(Int, IntTrieSet, CFGNode)], Option[CFGNode]) = { - var exceptions: List[(Int, ObjectType, IntTrieSet, CFGNode)] = List.empty - var throwStatements: List[(Int, IntTrieSet, CFGNode)] = List.empty - var returnNode: Option[CFGNode] = None - for (stmt ← tacCode.stmts) { - if (!stmt.isNop) { - val currentBB = cfg.bb(tacCode.pcToIndex(stmt.pc)) - (stmt.astID: @switch) match { - case CaughtException.ASTID ⇒ - val caughtException = stmt.asCaughtException - val exceptionType = - if (caughtException.exceptionType.isDefined) { - val intermediateExceptionType = caughtException.exceptionType.get - if (intermediateExceptionType.isObjectType) - intermediateExceptionType.asObjectType - else - ObjectType.Exception - } else - ObjectType.Exception - exceptions = (caughtException.pc, exceptionType, caughtException.origins, currentBB) :: exceptions - case Throw.ASTID ⇒ - val throwStatement = stmt.asThrow - val throwStatementDefinedBys = - if (throwStatement.exception.isVar) { - throwStatement.exception.asVar.definedBy - } else - IntTrieSet.empty - throwStatements = (throwStatement.pc, throwStatementDefinedBys, currentBB) :: throwStatements - case ReturnValue.ASTID ⇒ - returnNode = Some(currentBB) - case _ ⇒ - } - } - } - (exceptions, throwStatements, returnNode) - } - - def findMonitors( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - tacCode: TACode[TACMethodParameter, V] - )(implicit state: State): ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = { - - var result: (Option[Int], Option[Int]) = (None, None) - var dclEnterBBs: List[CFGNode] = List.empty - var dclExitBBs: List[CFGNode] = List.empty - val startBB = cfg.bb(fieldWrite) - var MonitorExitqueuedBBs: Set[CFGNode] = startBB.successors - var worklistMonitorExit = getSuccessors(startBB, Set.empty) - - def checkMonitor(pc: PC, v: V, curBB: CFGNode)( - implicit - state: State - ): Boolean = { - v.definedBy.iterator - .filter(i ⇒ { - if (i >= 0) { - val stmt = tacCode.stmts(i) - stmt match { - case Assignment(pc1, DVar(useSites, value), cc @ ClassConst(_, constant)) ⇒ { - state.field.classFile.thisType == cc.value || state.field.fieldType == cc.value - } - case Assignment( - pc1, - DVar(value1, defSites1), - GetField(pc2, t, name, classType, UVar(value2, defSites2)) - ) ⇒ - classType == - state.field.classFile.thisType - case _ ⇒ false - } - } else // (i <= -1) - true - }) - .size == v.definedBy.size - } - - 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 flag = true - for (i ← startPC to endPC) { - (code(i).astID: @switch) match { - case MonitorEnter.ASTID ⇒ - val me = code(i).asMonitorEnter - if (checkMonitor(me.pc, me.objRef.asVar, curBB)) { - result = (Some(tacCode.pcToIndex(me.pc)), (result._2)) - dclEnterBBs = curBB :: dclEnterBBs - flag = false - } - case _ ⇒ - } - } - if (flag) { - 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 = code(endPC) - (cfStmt.astID: @switch) match { - case MonitorExit.ASTID ⇒ - val mex = cfStmt.asMonitorExit - if (checkMonitor(mex.pc, mex.objRef.asVar, curBB)) { - result = ((result._1), Some(tacCode.pcToIndex(mex.pc))) - dclExitBBs = curBB :: dclExitBBs - } - case _ ⇒ - val successors = getSuccessors(curBB, MonitorExitqueuedBBs) - worklistMonitorExit ++= successors - MonitorExitqueuedBBs ++= successors - } - } - - val bbsEnter = { - if (dclEnterBBs.nonEmpty) - Some(dclEnterBBs.head) - else None - } - val bbsExit = { - if (dclExitBBs.nonEmpty) - Some(dclExitBBs.head) - else None - } - (result, (bbsEnter, bbsExit)) - } - - /** - * 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. - */ - // var savedGuards: mutable.HashMap[(Int, Method), ((Option[(Int, Int, Int, CFGNode, Int)]))] = - // new mutable.HashMap[(Int, Method), ((Option[(Int, Int, Int, CFGNode, Int)]))]() - - def findGuard( - method: Method, - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - tacCode: TACode[TACMethodParameter, V] - )(implicit state: State): (Option[(Int, Int, Int, CFGNode, Int)]) = { - val startBB = cfg.bb(fieldWrite).asBasicBlock - - var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) - - var result: Option[(Int, Int, CFGNode, Int)] = None - - while (worklist.nonEmpty) { - val curBB = worklist.head - worklist = worklist.tail - - val startPC = curBB.startPC - val endPC = curBB.endPC - - val cfStmt = code(endPC) - (cfStmt.astID: @switch) match { - case If.ASTID ⇒ - val ifStmt = cfStmt.asIf - //ifStmt.condition match { - if (ifStmt.condition.equals(EQ) && curBB != startBB && isGuard( - ifStmt, - defaultValue, - code, - tacCode - )) { - //case EQ - //if => - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != endPC + 1) - return None; - } else { - result = Some((endPC, endPC + 1, curBB, ifStmt.targetStmt)) - } - } else if (ifStmt.condition.equals(NE) && curBB != startBB && isGuard( - ifStmt, - defaultValue, - code, - tacCode - )) { - //case NE - //if => - if (result.isDefined) { - if (result.get._1 != endPC || result.get._2 != ifStmt.targetStmt) - return None - } else { - result = Some((endPC, ifStmt.targetStmt, curBB, endPC + 1)) - } - } else { - // Otherwise, we have to ensure that a guard is present for all predecessors - //case _ => - if (startPC == 0) return None; - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - //} - - // Otherwise, we have to ensure that a guard is present for all predecessors - case _ ⇒ - if (startPC == 0) return None; - - val predecessors = getPredecessors(curBB, enqueuedBBs) - worklist ++= predecessors - enqueuedBBs ++= predecessors - } - } - - val finalResult: Option[(Int, Int, Int, CFGNode, Int)] = - if (result.isDefined) { - // 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.get._1).asIf - val expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr else ifStmt.leftExpr - val definitions = expr.asVar.definedBy - if (definitions.head < 0) - return None; - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy //TODO ... - val fieldReadUsedCorrectly = fieldReadUses forall { use ⇒ - use == result.get._1 || use == result.get._2 - } - if (definitions.size == 1 && definitions.head >= 0 && fieldReadUsedCorrectly) - Some((result.get._1, result.get._2, definitions.head, result.get._3, result.get._4)); // Found proper guard - else None - } else None - finalResult - } - - /** - * Gets all predecessor BasicBlocks of a CFGNode. - */ - def getPredecessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.predecessors.iterator flatMap { curNode ⇒ - if (curNode.isBasicBlock) - if (visited.contains(curNode)) None - else Some(curNode.asBasicBlock) - else getPredecessors(curNode, visited) - } - result.toList - } - - def isTransitivePredecessorsOf(possiblePredecessor: CFGNode, node: CFGNode, visited: Set[CFGNode]): Boolean = { - var tmpVisited = visited + node - if (possiblePredecessor == node) - true - else { - node.predecessors.foreach( - currentNode ⇒ { - if (!tmpVisited.contains(currentNode)) - if (isTransitivePredecessorsOf(possiblePredecessor, currentNode, tmpVisited)) - return true; - tmpVisited += currentNode - } - ) - false - } - } - - /** - * Gets all successors BasicBlocks of a CFGNode - */ - def getSuccessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { - val result = node.successors.iterator flatMap ({ currentNode ⇒ - if (currentNode.isBasicBlock) - if (visited.contains(currentNode)) None - else Some(currentNode.asBasicBlock) - else getSuccessors(currentNode, visited) - }) - result.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]] - )(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(defSites.head).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, ReferenceImmutability.key)) - case _ ⇒ // Unknown field - false - }) - } - } - - val result = defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) - result - } - - val value = origin.expr - - def isNonConstDeterministic(value: Expr[V]): Boolean = { //val isNonConstDeterministic = - - value.astID match { - //case ⇒ - case GetStatic.ASTID | GetField.ASTID ⇒ - value.asFieldRead.resolveField(p) match { - case Some(field) ⇒ - state.field == field || isImmutableReference(propertyStore(field, ReferenceImmutability.key)) - case _ ⇒ // Unknown field - false - } - 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.exists(!isConstant(_))) { - false - } else { - state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) - true - } - case NewArray.ASTID ⇒ - true //TODO look at it - case _ if value.isVar ⇒ { - val varValue = value.asVar - varValue.definedBy.forall(i ⇒ - i >= 0 && code(i).isAssignment && isNonConstDeterministic(code(i).asAssignment.expr)) - } - case _ if value.isNew ⇒ { - val nonVirtualFunctionCallIndex = - origin.asAssignment.targetVar.usedBy.iterator.filter(i ⇒ code(i).isNonVirtualMethodCall).toList.head - origin.asAssignment.targetVar.usedBy.size == 2 && - code(nonVirtualFunctionCallIndex).asNonVirtualMethodCall.params.forall(isConstant) - } - 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, code) - } - } - val result = value.isConst || isNonConstDeterministic(value) - - result - } - - /** - * 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])(implicit state: State): Boolean = { - var seenExpressions: Set[Expr[V]] = Set.empty - def _isReadOfCurrentField(expr: Expr[V], tacCode: TACode[TACMethodParameter, V])(implicit state: State): Boolean = { - - if (seenExpressions.contains(expr)) { - return false - }; - seenExpressions += expr - - (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 Compare.ASTID ⇒ { - val leftExpr = expr.asCompare.left - val rightExpr = expr.asCompare.right - val leftDefinitionIndex = leftExpr.asVar.definedBy.filter(i ⇒ i != expr.asCompare.pc).head - val rightDefinitionIndex = rightExpr.asVar.definedBy.filter(i ⇒ i != expr.asCompare.pc).head - if (leftDefinitionIndex < 0 || rightDefinitionIndex < 0) - return false; - - val definitionStmtLeft = tacCode.stmts(leftDefinitionIndex) - val definitionStmtRight = tacCode.stmts(rightDefinitionIndex) - - if (definitionStmtLeft.asAssignment.expr.isGetField || - definitionStmtLeft.asAssignment.expr.isGetStatic || - definitionStmtLeft.asAssignment.expr.isVirtualFunctionCall) { - if (definitionStmtRight.asAssignment.expr.isConst) { //TODO ggf konservativer - _isReadOfCurrentField(definitionStmtLeft.asAssignment.expr, tacCode) - } else - false - } else { - if (definitionStmtLeft.asAssignment.expr.isConst) //TODO siehe oben - _isReadOfCurrentField(definitionStmtRight.asAssignment.expr, tacCode) - else - false - } - - } - case VirtualFunctionCall.ASTID ⇒ { - val virtualFunctionCall = expr.asVirtualFunctionCall - val receiverDefSites = virtualFunctionCall.receiver.asVar.definedBy - for { - defSite ← receiverDefSites - } { - if (defSite >= 0) { - if (_isReadOfCurrentField(tacCode.stmts(defSite).asAssignment.expr, tacCode)) { //nothing to do - } else { - return false; - } - } else { - return false; - } - } - true - } - case _ ⇒ false - } - } - _isReadOfCurrentField(expr, tacCode) - } - - /** - * 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] - )(implicit state: State): Boolean = { - import scala.annotation.tailrec - - /** - * Checks if an expression is an IntConst or FloatConst with the corresponding default value. - */ - @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 || - expr.isFloatConst && defaultValue == expr.asFloatConst.value || - expr.isDoubleConst && defaultValue == expr.asDoubleConst || 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]): Boolean = { - - expr.definedBy forall { index ⇒ - if (index < 0) false // If the value is from a parameter, this can not be the guard - else { - if (code(index).asAssignment.expr.isVirtualFunctionCall) { - //in case of Integer etc.... .initValue() - val virtualFunctionCall = code(index).asAssignment.expr.asVirtualFunctionCall - val callTargets = virtualFunctionCall.resolveCallTargets(state.field.classFile.thisType) - callTargets.foreach( - method ⇒ { - val propertyStorePurityResult = propertyStore(declaredMethods(method), Purity.key) - if (virtualFunctionCall.params.exists(!_.isConst) && - isNonDeterministic(propertyStorePurityResult)) - return false - } - ) - if (callTargets.isEmpty || virtualFunctionCall.params.exists(!_.isConst)) - return false; - - val receiverDefSite = virtualFunctionCall.receiver.asVar.definedBy.head - - receiverDefSite >= 0 && isReadOfCurrentField( - code(receiverDefSite).asAssignment.expr, tacCode - ) - } else { - isReadOfCurrentField(code(index).asAssignment.expr, tacCode) - } - } - } - } - - if ((ifStmt.rightExpr.isVar) && isDefaultConst(ifStmt.leftExpr)) { - isGuardInternal(ifStmt.rightExpr.asVar, tacCode) - } else if (ifStmt.leftExpr.isVar && isDefaultConst(ifStmt.rightExpr)) { - isGuardInternal(ifStmt.leftExpr.asVar, tacCode) - } else { - false - } - } - - /** - * - * Checks that the value of the field is returned. - */ - def checkThatTheValueOfTheFieldIsReturned( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - code: Array[Stmt[V]], - tacCode: TACode[TACMethodParameter, V] - )(implicit state: State): Boolean = { - var index = writeIndex + 1 - - var load = -1 - - while (index < code.length) { - val stmt = code(index) - (stmt.astID: @switch) match { - case a @ Assignment.ASTID ⇒ - if (isReadOfCurrentField(stmt.asAssignment.expr, tacCode)) { - load = index - } - // No field read or a field read of a different field - case ReturnValue.ASTID ⇒ - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) { - return true; - } // direct return of the written value - else if (load >= 0 && (returnValueDefs == IntTrieSet(load) || - returnValueDefs == IntTrieSet(readIndex, load))) { - return true; - } // return of field value loaded by field read - else - return false; // return of different value - case _ ⇒ ; - } - index += 1 - } - false - } -} From 03b30034af26c878b84359b3ce173e3f744b3920 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Wed, 7 Oct 2020 14:33:25 +0200 Subject: [PATCH 280/327] correction --- .../AbstractFieldReferenceImmutabilityAnalysis.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 5690e4221b..e10b86f465 100644 --- 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 @@ -123,7 +123,7 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { code: Array[Stmt[V]] )(implicit state: State): Boolean = { //over-approximates that the lazy initialization can not be influenced via a parameter - if (method.descriptor.parametersCount >= 0) + if (method.descriptor.parametersCount > 0) false else !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) From 281349b56673026d5b99822aa6bc7c86c6b644ec Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 9 Oct 2020 14:06:02 +0200 Subject: [PATCH 281/327] revised, still wip --- .../org/opalj/support/info/Immutability.scala | 14 +- .../L2FieldImmutabilityAnalysis.scala | 56 +- .../L1ClassImmutabilityAnalysis.scala | 16 +- .../L3FieldImmutabilityAnalysis.scala | 358 ++++--- ...mutabilityAnalysisLazyInitialization.scala | 981 ++++++++++++++++++ ...0FieldReferenceImmutabilityAnalysis.scala} | 246 ++--- 6 files changed, 1350 insertions(+), 321 deletions(-) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/AbstractFieldReferenceImmutabilityAnalysisLazyInitialization.scala rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/{reference/L0ReferenceImmutabilityAnalysis.scala => fieldreference/L0FieldReferenceImmutabilityAnalysis.scala} (72%) 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 index 9fa651120c..4d296df37f 100644 --- 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 @@ -302,21 +302,25 @@ object Immutability { filter(field ⇒ allFieldsInProjectClassFiles.contains(field.asInstanceOf[Field])). toTraversable.groupBy(_.toFinalELBP.p) - val fieldReferenceOrder = (eps1: EPS[Entity, FieldReferenceImmutability], eps2: EPS[Entity, FieldReferenceImmutability]) ⇒ - eps1.e.toString < eps2.e.toString + val fieldReferenceOrder = + (eps1: EPS[Entity, FieldReferenceImmutability], eps2: EPS[Entity, FieldReferenceImmutability]) ⇒ + eps1.e.toString < eps2.e.toString - val mutableFieldReferences = fieldReferenceGroupedResults(MutableFieldReference).toSeq.sortWith(fieldReferenceOrder) + val mutableFieldReferences = + fieldReferenceGroupedResults(MutableFieldReference).toSeq.sortWith(fieldReferenceOrder) val notThreadSafeLazyInitializedFieldReferences = fieldReferenceGroupedResults(LazyInitializedNotThreadSafeFieldReference).toSeq.sortWith(fieldReferenceOrder) val lazyInitializedReferencesNotThreadSafeButDeterministic = - fieldReferenceGroupedResults(LazyInitializedNotThreadSafeButDeterministicFieldReference).toSeq.sortWith(fieldReferenceOrder) + fieldReferenceGroupedResults(LazyInitializedNotThreadSafeButDeterministicFieldReference). + toSeq.sortWith(fieldReferenceOrder) val threadSafeLazyInitializedFieldReferences = fieldReferenceGroupedResults(LazyInitializedThreadSafeFieldReference).toSeq.sortWith(fieldReferenceOrder) - val immutableReferences = fieldReferenceGroupedResults(ImmutableFieldReference).toSeq.sortWith(fieldReferenceOrder) + val immutableReferences = fieldReferenceGroupedResults(ImmutableFieldReference). + toSeq.sortWith(fieldReferenceOrder) if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.References) { stringBuilderResults.append( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala index 214e3fa950..0aea0f705f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala @@ -136,8 +136,9 @@ class L2FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e implicit val state: State = State(field) // Fields are not final if they are read prematurely! - if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) - return Result(field, MutableField); + if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { + return Result(field, MutableField) + }; if (field.isFinal) return createResult(); @@ -146,8 +147,9 @@ class L2FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e val thisType = field.classFile.thisType - if (field.isPublic) + 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 @@ -178,8 +180,9 @@ class L2FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } // If there are native methods, we give up - if (classesHavingAccess.exists(_.methods.exists(_.isNative))) - return Result(field, MutableField); + 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 @@ -205,8 +208,9 @@ class L2FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method, pcs) } { - if (methodUpdatesField(method, taCode, pcs)) - return Result(field, MutableField); + if (methodUpdatesField(method, taCode, pcs)) { + return Result(field, MutableField) + }; } if (state.lazyInitInvocation.isDefined) { @@ -333,8 +337,9 @@ class L2FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e state.dependees, c ) - else + else { Result(state.field, state.fieldImmutability) + } } /** @@ -370,9 +375,9 @@ class L2FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e !isFinalField(newEP) } - if (isNotFinal) + if (isNotFinal) { Result(state.field, MutableField) - else + } else createResult() } @@ -531,30 +536,30 @@ class L2FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e 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.fieldImmutability == ShallowImmutableField) //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 @@ -565,8 +570,9 @@ class L2FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e taCode.stmts, taCode.cfg, taCode.pcToIndex - )) - return true; + )) { + return true + }; state.fieldImmutability = ShallowImmutableField } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { @@ -1086,7 +1092,7 @@ object EagerL2FieldImmutabilityAnalysis final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L2FieldImmutabilityAnalysis(p) - val fields = p.allFields + val fields = p.allProjectClassFiles.flatMap(_.fields) // p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) analysis } 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 index 6ded27252a..95349653a3 100644 --- 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 @@ -235,6 +235,8 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis var hasDependentImmutableFields = false val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) + //println("class Inner: ") + //fieldsPropertyStoreInformation.foreach(println(_)) fieldsPropertyStoreInformation.foreach( _ match { @@ -278,6 +280,7 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis */ var minLocalImmutability: ClassImmutability = MutableClass + //println(s"suerclassinformation $superClassInformation") // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability = superClassInformation match { case UBP(MutableClass) ⇒ MutableClass @@ -286,6 +289,7 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis case _ ⇒ DeepImmutableClass } if (hasShallowImmutableFields) { + //println("has shallow immutable fields") maxLocalImmutability = ShallowImmutableClass } @@ -536,12 +540,11 @@ trait L1ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { } /** - * Scheduler to run the immutability analysis eagerly. - * @author Tobias Peter Roth + * Scheduler to run the class immutability analysis eagerly. + * @author Tobias Roth * @author Michael Eichberg */ -object EagerL1ClassImmutabilityAnalysis - extends L1ClassImmutabilityAnalysisScheduler +object EagerL1ClassImmutabilityAnalysis extends L1ClassImmutabilityAnalysisScheduler with FPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -563,11 +566,10 @@ object EagerL1ClassImmutabilityAnalysis } /** - * Scheduler to run the immutability analysis lazily. + * Scheduler to run the class immutability analysis lazily. * @author Michael Eichberg */ -object LazyL1ClassImmutabilityAnalysis - extends L1ClassImmutabilityAnalysisScheduler +object LazyL1ClassImmutabilityAnalysis extends L1ClassImmutabilityAnalysisScheduler with FPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) 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 index 194dfc753c..27cbb2858d 100644 --- 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 @@ -16,14 +16,6 @@ import org.opalj.br.RuntimeInvisibleAnnotationTable import org.opalj.br.SimpleClassTypeSignature import org.opalj.br.SourceFile import org.opalj.br.TypeVariableSignature -import org.opalj.br.analyses.FieldAccessInformationKey -import org.opalj.br.analyses.SomeProject -import org.opalj.br.analyses.cg.ClosedPackagesKey -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.DeepImmutableField import org.opalj.br.fpcf.properties.DeepImmutableType import org.opalj.br.fpcf.properties.DependentImmutableField @@ -41,44 +33,60 @@ 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.Entity import org.opalj.fpcf.FinalEP import org.opalj.fpcf.FinalP import org.opalj.fpcf.InterimResult import org.opalj.fpcf.ProperPropertyComputationResult -import org.opalj.fpcf.Property -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.br.Method import org.opalj.br.fpcf.properties.EscapeProperty import org.opalj.tac.fpcf.analyses.AbstractIFDSAnalysis.V -import org.opalj.tac.common.DefinitionSitesKey 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.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey 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.ClassImmutability import org.opalj.value.ASArrayValue 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.InterimEP +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyBounds +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 /** * Analyses that determines the immutability of org.opalj.br.Field * It uses the results of the [[L3FieldReferenceImmutabilityAnalysis]] * and of the [[L1TypeImmutabilityAnalysis]] * - * @author Tobias Peter Roth + * @author Tobias Roth + * */ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { + /** * Describes the different kinds of dependent immutable fields: * [[DependentImmutabilityKind.Dependent]] Shallow or mutable types could still exist @@ -94,7 +102,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) import DependentImmutabilityKind._ case class State( - field: Field, + val field: Field, var typeIsImmutable: Boolean = true, var referenceIsImmutable: Option[Boolean] = None, var noEscapePossibilityViaReference: Boolean = true, @@ -143,6 +151,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) * Loads the formal type parameters from the classes and outer class signature */ def loadFormalTypeParameter(): Unit = { + import org.opalj.br.ClassFile var result: Set[String] = Set.empty /** @@ -153,8 +162,9 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case ClassSignature(typeParameters, _, _) ⇒ { typeParameters.foreach( _ match { - case FormalTypeParameter(identifier, _, _) ⇒ result += identifier - case _ ⇒ + case FormalTypeParameter(identifier, _, _) ⇒ + result += identifier + case parameter ⇒ } ) } @@ -163,56 +173,60 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) /** * If the genericity is nested in an inner class - * collect the generic type parameters from the field's outer class + * collect the generic type parameters from the field' outer classes */ - //TODO recursive function for nested - if (field.classFile.outerType.isDefined) { - val outerClassFile = project.classFile(field.classFile.outerType.get._1) - if (outerClassFile.isDefined) { - outerClassFile.get.attributes.foreach( - collectFormalTypeParameterFromClassAttribute - ) + def collectAllFormalParametersFromOuterClasses(classFile: ClassFile): Unit = { + classFile.attributes.foreach(collectFormalTypeParameterFromClassAttribute) + if (classFile.outerType.isDefined) { + val outerClassFile = project.classFile(classFile.outerType.get._1) + if (outerClassFile.isDefined && outerClassFile.get != classFile) { + collectAllFormalParametersFromOuterClasses(outerClassFile.get) + } } } + collectAllFormalParametersFromOuterClasses(field.classFile) - /** - * Collect the generic type parameters from the fields class - */ - field.classFile.attributes.foreach(collectFormalTypeParameterFromClassAttribute) - - if (result.nonEmpty) + if (result.nonEmpty) { classFormalTypeParameters = Some(result) - + /*println( + s""" + |field: ${field} + | parameters: ${classFormalTypeParameters.mkString("\n ")} + |""".stripMargin + ) */ + } } + /** - * Returns, if a generic parameter like e.g. 'T' is in the classes or the first outer classes Signature + * Returns, if a generic parameter like e.g. 'T' is in the class' or the first outer class' Signature * @param string The generic type parameter that should be looked for */ def isInClassesGenericTypeParameters(string: String): Boolean = classFormalTypeParameters.isDefined && classFormalTypeParameters.get.contains(string) /** - * Checks the immutability of a fields type. Returns the Result and registers the dependencies if necessary. - * @param state + * Determines the immutability of a fields type. Adjusts the state and registers the dependencies if necessary. + * */ - def handleTypeImmutability()(implicit state: State): Unit = { + def handleTypeImmutability(state: State): Unit = { val objectType = field.fieldType.asFieldType if (objectType == ObjectType.Object) { + // println("i1") state.typeIsImmutable = false //handling generic fields } else if (objectType.isBaseType || objectType == ObjectType.String) { - // we state here the strings deep immutability // base types are by design deep immutable //state.typeImmutability = Some(true) // true is default } else if (objectType.isArrayType) { + // println("i3") // Because the entries of an array can be reassigned we state it as not being deep immutable state.typeIsImmutable = false } else { - val result = propertyStore(objectType, TypeImmutability.key) - result match { + // println("i4") + propertyStore(objectType, TypeImmutability.key) match { case FinalP(DeepImmutableType) ⇒ // deep immutable type is set as default case FinalP(DependentImmutableType) ⇒ state.typeIsImmutable = false - case FinalEP(t, ShallowImmutableType | MutableType) ⇒ + case UBP(ShallowImmutableType | MutableType) ⇒ state.typeIsImmutable = false if (field.fieldType != ObjectType.Object) state.dependentImmutability = DependentImmutabilityKind.Dependent @@ -226,7 +240,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var onlyDeepImmutableTypesInGenericTypeFound = true var genericParameters: List[ObjectType] = List() var noRelevantAttributesFound = true - state.field.asField.attributes.foreach( + state.field.attributes.foreach( _ match { case RuntimeInvisibleAnnotationTable(_) ⇒ case SourceFile(_) ⇒ @@ -235,49 +249,41 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) onlyDeepImmutableTypesInGenericTypeFound = false if (!isInClassesGenericTypeParameters(t)) noShallowOrMutableTypesInGenericTypeFound = false - case ClassTypeSignature( - packageIdentifier, - SimpleClassTypeSignature(simpleName, typeArguments), - _ - ) ⇒ + case ClassTypeSignature(_, SimpleClassTypeSignature(_, typeArguments), _) ⇒ noRelevantAttributesFound = false - typeArguments - .foreach({ - _ match { - case ProperTypeArgument(variance, TypeVariableSignature(identifier: String)) ⇒ - onlyDeepImmutableTypesInGenericTypeFound = false - if (!isInClassesGenericTypeParameters(identifier)) - noShallowOrMutableTypesInGenericTypeFound = false - case ProperTypeArgument(varianceIndicator, - ClassTypeSignature(outerPackageIdentifier, - SimpleClassTypeSignature(innerPackageIdentifier, typeArguments2), _)) ⇒ { - val objectPath = - outerPackageIdentifier match { - case Some(prepackageIdentifier) ⇒ - prepackageIdentifier + innerPackageIdentifier - case _ ⇒ innerPackageIdentifier - } - genericParameters ::= ObjectType(objectPath) - } - case _ ⇒ + typeArguments.foreach({ + _ match { + case ProperTypeArgument(_, TypeVariableSignature(identifier)) ⇒ + onlyDeepImmutableTypesInGenericTypeFound = false + if (!isInClassesGenericTypeParameters(identifier)) noShallowOrMutableTypesInGenericTypeFound = false - onlyDeepImmutableTypesInGenericTypeFound = 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 ⇒ { - val result = propertyStore(objectType, TypeImmutability.key) - - result match { + propertyStore(objectType, TypeImmutability.key) match { case FinalP(DeepImmutableType) ⇒ //nothing to do here: default value is deep immutable case FinalP(DependentImmutableType) ⇒ onlyDeepImmutableTypesInGenericTypeFound = false state.typeIsImmutable = false - case FinalP(ShallowImmutableType | MutableType) ⇒ { + case UBP(ShallowImmutableType | MutableType) ⇒ { noShallowOrMutableTypesInGenericTypeFound = false onlyDeepImmutableTypesInGenericTypeFound = false state.typeIsImmutable = false @@ -338,24 +344,20 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def doesItEscapeViaMethod( ep: EOptionP[DefinitionSite, EscapeProperty] )(implicit state: State): Boolean = { - 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.InterimEP - import org.opalj.fpcf.InterimUBP ep match { case FinalP(NoEscape) ⇒ false case InterimUBP(NoEscape) ⇒ + state.dependees += ep false - case FinalP(EscapeInCallee | EscapeViaReturn) ⇒ true + 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 _: InterimEP[DefinitionSite, EscapeProperty] ⇒ true // Escape state is worse than via return - case _ ⇒ - state.dependees += ep + case epk ⇒ + state.dependees += epk false } } @@ -463,10 +465,11 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } else if (innerArrayType.isObjectType) { //If a deep immutable object escapes, it can not be mutated propertyStore(innerArrayType, TypeImmutability.key) match { - case FinalP(DeepImmutableType) ⇒ //nothing to to - case FinalP(_) ⇒ + case FinalP(DeepImmutableType) ⇒ //nothing to do + case UBP(DependentImmutableType | + ShallowImmutableType | + MutableType) ⇒ state.noEscapePossibilityViaReference = false - // println("false3") return ; case ep ⇒ { state.innerArrayTypes += innerArrayType.asObjectType @@ -475,19 +478,15 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } else { state.noEscapePossibilityViaReference = false - // println("false4") return ; } case _ ⇒ { state.noEscapePossibilityViaReference = false - // println("false5") return ; } } } else { - // println("assignemtn6"+assignment) state.noEscapePossibilityViaReference = false - // println("false6") return ; } } else if (fieldsUseSiteStmt.isMonitorEnter || @@ -546,13 +545,18 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } //else println("ELSE") } + /** + * 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)(implicit state: State): Unit = { - state.concreteClassTypeIsKnown = true - - val propertyStoreResult = propertyStore(objectType, ClassImmutability.key) - propertyStoreResult match { + val result = propertyStore(objectType, ClassImmutability.key) + //println(s"handle type imm result: $result") + result match { + case UBP(MutableClass | + ShallowImmutableClass | + DependentImmutableClass) ⇒ state.typeIsImmutable = false case FinalP(DeepImmutableClass) ⇒ state.typeIsImmutable = true - case FinalP(_) ⇒ state.typeIsImmutable = false case eps ⇒ state.dependees += eps } } @@ -607,6 +611,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && state.totalNumberOfFieldWrites == 1) { + state.concreteClassTypeIsKnown = true handleKnownClassType(newStmt.tpe.mostPreciseObjectType) } for (usedSiteIndex ← paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy) { @@ -644,8 +649,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } else if (stmt.isArrayStore) { state.noEscapePossibilityViaReference = false //TODO handling that case more precise return ; - } //else if // other cases that the purity analysis can not handle - else { + } else { // other cases that the purity analysis can not handle if (doesItEscapeViaMethod( propertyStore(definitionSitesOfParam, EscapeProperty.key) )) { @@ -665,7 +669,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) */ def handlePut(putStmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { - val (putDefinitionSites, putValue) = + val (putDefinitionSites, putValue) = { if (putStmt.isPutField) { val putField = putStmt.asPutField (putField.value.asVar.definedBy, putField.value) @@ -676,6 +680,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.noEscapePossibilityViaReference = false return ; } + } val putValueDefinedByIndex = putValue.asVar.definedBy.head if (putValue.asVar.value.isArrayValue == Yes) { @@ -726,6 +731,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (field.fieldType.isObjectType && newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && state.totalNumberOfFieldWrites == 1) { + state.concreteClassTypeIsKnown = true handleKnownClassType(newStmt.tpe.mostPreciseObjectType) } valueAssignment.targetVar.asVar.usedBy.foreach(index ⇒ { @@ -765,14 +771,15 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } { if (i >= 0) { //necessary val definitionSiteStatement = tacCode.stmts(i) - // println("def site stmt: "+definitionSiteStatement) + //println("def site stmt: "+definitionSiteStatement) val definitionSiteAssignment = definitionSiteStatement.asAssignment - // println("def site assignement: "+definitionSiteAssignment) + //println("def site assignement: "+definitionSiteAssignment) if (definitionSiteAssignment.expr.isStaticFunctionCall) { - // println("handle static function call") + // println("handle static function call") handleStaticFunctionCall(definitionSiteAssignment.expr.asStaticFunctionCall, tacCode) return ; } else if (definitionSiteAssignment.expr.isVar) { + // println("def site is var") val definitionSiteVar = definitionSiteAssignment.expr.asVar for (definitionSiteVarUseSite ← definitionSiteVar.usedBy) { val definitionSiteVarUseSiteStmt = tacCode.stmts(definitionSiteVarUseSite) @@ -786,11 +793,21 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) //TODO andere Fälle bedenken } } else if (definitionSiteAssignment.expr.isNew) { + //println("is new") + val newStmt = definitionSiteAssignment.expr.asNew + /*println( + s""" + | newStmt: $newStmt + | newStmt.tpe.mostPreciseObjectType: ${newStmt.tpe.mostPreciseObjectType} + | fhandleKnownClassTypeieldType: ${field.fieldType.asObjectType} + | state.totalNumberOfFieldWrites + |""".stripMargin + ) */ if (field.fieldType.isObjectType && - newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && state.totalNumberOfFieldWrites == 1) { + state.concreteClassTypeIsKnown = true handleKnownClassType(newStmt.tpe.mostPreciseObjectType) } if (!method.isConstructor) { @@ -850,22 +867,20 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val tacCodeOption = getTACAI(method, pcs, isRead = false) if (tacCodeOption.isDefined) { val taCode = tacCodeOption.get - pcs.foreach( - pc ⇒ { - val index = taCode.pcToIndex(pc) - if (index >= 0) { - val stmt = taCode.stmts(index) - // println("stmt: "+stmt) - if (!seen.contains(stmt)) { - seen += stmt - handlePut(stmt, method, taCode) - } - } else { - state.noEscapePossibilityViaReference = false - return ; + pcs.foreach(pc ⇒ { + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = taCode.stmts(index) + //println("stmt: "+stmt) + if (!seen.contains(stmt)) { + seen += stmt + handlePut(stmt, method, taCode) } + } else { + state.noEscapePossibilityViaReference = false + return ; } - ) + }) } } @@ -873,25 +888,54 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) * If there are no dependencies left, this method can be called to create the result. */ def createResult(implicit state: State): ProperPropertyComputationResult = { - /* println( + /* println( s""" - | ${state.referenceIsImmutable} - | ${state.typeIsImmutable} - | ${state.concreteClassTypeIsKnown} - | ${state.noEscapePossibilityViaReference} - | ${state.dependentImmutability} - | - |""".stripMargin + | create result + | field: ${state.field} + | type is immutable: ${state.typeIsImmutable} + | dependent immutability: ${state.dependentImmutability} + | does not escape: ${state.noEscapePossibilityViaReference} + | concrete classtype is known: ${state.concreteClassTypeIsKnown} + |""".stripMargin ) */ if (state.hasDependees) { val lowerBound = - if (state.referenceIsImmutable.isDefined && state.referenceIsImmutable.get) ShallowImmutableField - else MutableField + if (state.referenceIsImmutable.isDefined && state.referenceIsImmutable.get) + ShallowImmutableField + else + MutableField + val upperBound = { + if (!state.referenceIsImmutable.isDefined) + DeepImmutableField + else + state.referenceIsImmutable match { + case Some(false) | None ⇒ + MutableField + case Some(true) ⇒ { + if (state.tacDependees.isEmpty && !state.concreteClassTypeIsKnown) { + if (state.typeIsImmutable) { + DeepImmutableField + } else { + if (state.noEscapePossibilityViaReference) { + DeepImmutableField + } else { + state.dependentImmutability match { + case NotShallowOrMutable ⇒ + DependentImmutableField + case OnlyDeepImmutable ⇒ + DeepImmutableField + case _ ⇒ { + ShallowImmutableField + } + } + } + } + } else DeepImmutableField + } + } - val upperBound = /*if (state.typeIsImmutable && state.noEscapePossibilityViaReference)*/ - DeepImmutableField - /*else - DependentImmutableField*/ + } //TODO check + DeepImmutableField InterimResult( field, lowerBound, @@ -926,17 +970,15 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } } - } def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { - + import org.opalj.fpcf.EUBP if (eps.asEPS.pk != TACAI.key) state.dependees = state.dependees.filter(_.e ne eps.e) eps match { - /**/ case FinalP(DeepImmutableType) ⇒ //nothing to do -> is default - case FinalEP(t, MutableType | ShallowImmutableType) ⇒ + case EUBP(t, MutableType | ShallowImmutableType) ⇒ state.typeIsImmutable = false if (t != ObjectType.Object) { // in case of generic fields state.dependentImmutability = DependentImmutabilityKind.Dependent @@ -950,7 +992,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (t.isInstanceOf[ObjectType] && state.innerArrayTypes.contains(t.asInstanceOf[ObjectType])) state.noEscapePossibilityViaReference = false } - case FinalP( + case UBP( MutableFieldReference | LazyInitializedNotThreadSafeFieldReference ) ⇒ { state.typeIsImmutable = false @@ -965,7 +1007,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.referenceIsImmutable = Some(true) } case FinalP(DeepImmutableField) ⇒ // nothing to do - case FinalP(DependentImmutableField | ShallowImmutableField | MutableField) ⇒ + case UBP(DependentImmutableField | ShallowImmutableField | MutableField) ⇒ state.noEscapePossibilityViaReference = false case eps if eps.asEPS.pk == TACAI.key ⇒ val newEP = eps.asInstanceOf[EOptionP[Method, TACAI]] @@ -982,8 +1024,8 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case eps if eps.isFinal && eps.asEPS.pk == EscapeProperty.key ⇒ if (doesItEscapeViaMethod(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) state.noEscapePossibilityViaReference = false - case FinalP(DeepImmutableClass) ⇒ state.typeIsImmutable = true - case FinalP(ShallowImmutableClass | DependentImmutableClass | MutableClass) ⇒ state.typeIsImmutable = false + case FinalP(DeepImmutableClass) ⇒ state.typeIsImmutable = true + case UBP(ShallowImmutableClass | DependentImmutableClass | MutableClass) ⇒ state.typeIsImmutable = false case eps ⇒ state.dependees += eps } @@ -991,11 +1033,10 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } /** - * Begin of determine field immutability function + * Requests the immutability of the field reference and registers the dependees if necessary */ implicit val state: State = State(field) - val referenceImmutabilityPropertyStoreResult = propertyStore(field, FieldReferenceImmutability.key) - referenceImmutabilityPropertyStoreResult match { + propertyStore(field, FieldReferenceImmutability.key) match { case FinalP(ImmutableFieldReference) ⇒ state.referenceIsImmutable = Some(true) case FinalP(LazyInitializedThreadSafeFieldReference | LazyInitializedNotThreadSafeButDeterministicFieldReference) ⇒ state.referenceIsImmutable = Some(true) @@ -1005,20 +1046,36 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.dependees += ep } } + // println("A") + /** + * Determines whether the field is dependent immutable + */ loadFormalTypeParameter() - hasGenericType() + // println("B") + /** + * Determines whether the reference object escapes or can be mutated. + */ if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) { determineEscapeOfReferencedObjectOrValue() } - if (!state.concreteClassTypeIsKnown) - handleTypeImmutability() + // println("c") + /** + * In cases where we know the concrete class type assigned to the field we could use the immutabiltiy of this. + */ + if (!state.concreteClassTypeIsKnown) { + // println("C1") + handleTypeImmutability(state) + } + // println("D") createResult } } trait L3FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { + import org.opalj.fpcf.PropertyBounds + final override def uses: Set[PropertyBounds] = Set( PropertyBounds.ub(TACAI), PropertyBounds.ub(EscapeProperty), @@ -1028,11 +1085,12 @@ trait L3FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { ) final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldImmutability) - } /** - * Executor for the field immutability analysis. + * + * Executor for the eager field immutability analysis. + * */ object EagerL3FieldImmutabilityAnalysis extends L3FieldImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { @@ -1043,19 +1101,22 @@ object EagerL3FieldImmutabilityAnalysis extends L3FieldImmutabilityAnalysisSched final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L3FieldImmutabilityAnalysis(p) - val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) // p.allFields + 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 +object LazyL3FieldImmutabilityAnalysis extends L3FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { + override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) + final override def register( p: SomeProject, ps: PropertyStore, @@ -1068,5 +1129,4 @@ object LazyL3FieldImmutabilityAnalysis ) analysis } - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } 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..b967a76080 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/AbstractFieldReferenceImmutabilityAnalysisLazyInitialization.scala @@ -0,0 +1,981 @@ +/* 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.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.CFG +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 + +/** + * + * 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 the write in a given method + * @author Tobias Roth + * @return true if we have no thread safe or deterministic lazy initialization + */ + def handleLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + pcToIndex: Array[Int], + tacCai: TACode[TACMethodParameter, V] + )(implicit state: State): Boolean = { + println("handle lazy initialization") + println( + s""" + | field: ${state.field} + |""".stripMargin + ) + val lazyInitializationResult: FieldReferenceImmutability = + determineLazyInitialization(writeIndex, defaultValue, method, code, cfg, tacCai) + state.referenceImmutability = lazyInitializationResult + lazyInitializationResult == MutableFieldReference + } + + /** + * Determines if a given field is lazy initialized in the given method. + * @author Tobias Roth + */ + def determineLazyInitialization( + writeIndex: Int, + defaultValue: Any, + method: Method, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + tacCode: TACode[TACMethodParameter, V] + )(implicit state: State): FieldReferenceImmutability = { + //println("0") + val write = code(writeIndex).asFieldWriteAccessStmt + val writeBB = cfg.bb(writeIndex).asBasicBlock + val domTree = cfg.dominatorTree + //println("1") + val resultCatchesAndThrows = findCaughtsThrowsAndResults(tacCode, cfg) + //println("2") + def noInterferingExceptions: Boolean = { + resultCatchesAndThrows._1.forall(bbCatch ⇒ + resultCatchesAndThrows._2.exists(bbThrow ⇒ + ((domTree.strictlyDominates(bbCatch._4.nodeId, bbThrow._3.nodeId) || //domination + (bbCatch._4 == bbThrow._3 && bbCatch._1 < bbThrow._1)) && // or equal and successor + bbThrow._2 == bbCatch._3))) + } + // println("3") + val findGuardResult: (List[(Int, Int, Int, CFGNode, Int, Int)]) = { + findGuard(method, writeIndex, defaultValue, code, cfg, tacCode) + } + // println("4") + val (guardedIndex, readIndex, afterGuardRecognizedTheDefaultValueIndex, fieldReadIndex) = { + + if (findGuardResult.nonEmpty) + ( + //findGuardResult.head._1, + findGuardResult.head._2, + findGuardResult.head._3, + findGuardResult.head._5, + findGuardResult.head._6 + ) + else { + return MutableFieldReference; + } + } + //println("5") + val guardedBB = cfg.bb(afterGuardRecognizedTheDefaultValueIndex) + val elseBB = cfg.bb(guardedIndex) + //println("6") + if (isTransitivePredecessor(elseBB, writeBB)) { + return MutableFieldReference; + } + //println("7") + //prevents that the field is seen with another value + if (method.returnType == state.field.fieldType && { + !tacCode.stmts.forall(stmt ⇒ { //TODO + if (stmt.isReturnValue) { + (findGuardResult.exists(x ⇒ { + isTransitivePredecessor( + cfg.bb(x._2), + cfg.bb(tacCode.pcToIndex(stmt.pc)) + ) + }) + || isTransitivePredecessor(writeBB, cfg.bb(tacCode.pcToIndex(stmt.pc)))) + } else + true + + }) + }) { + return MutableFieldReference; + } + //println("8") + val reads = fieldAccessInformation.readAccesses(state.field) + // println("9") + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + return MutableFieldReference; + } + // println("B") + val writes = fieldAccessInformation.writeAccesses(state.field) + if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { + return MutableFieldReference; + } + // println("C") + // println (s"field: ${state.field}") + if (method.returnType == state.field.fieldType && + !isTheFieldsValueReturned(write, writeIndex, readIndex, cfg, tacCode)) { + return MutableFieldReference; + } + // println("D") + //when the method is synchronized the monitor has not to be searched + if (method.isSynchronized) { + println("E") + if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || + (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { + // println("F") + if (noInterferingExceptions) { + // println("G") + LazyInitializedThreadSafeFieldReference // result //DCL + } else { + // println("H") + MutableFieldReference + } + } else { + // println("I") + MutableFieldReference + } + } else { + //println("else") + val monitorResult: ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = { + // println("find monitors start") + findMonitors(writeIndex, defaultValue, code, cfg, tacCode) //... + + } + // println("find monitors end") + if ((monitorResult._1._1.isDefined && monitorResult._1._2.isDefined && monitorResult._2._1.isDefined) + && + ( + (domTree.strictlyDominates(monitorResult._2._1.get.nodeId, guardedBB.nodeId) || + (monitorResult._2._1.get == guardedBB && monitorResult._1._1.get < afterGuardRecognizedTheDefaultValueIndex)) && //writeIndex)) && //monitor enter dominates guard1 + ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) + || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex) && //&& //true case dominates Write + // The field read must be within the synchronized block + (domTree.strictlyDominates(monitorResult._2._1.get.nodeId, cfg.bb(fieldReadIndex).nodeId) || + monitorResult._2._1.get == cfg.bb(fieldReadIndex) && monitorResult._1._1.get < fieldReadIndex) + )) { + if (noInterferingExceptions) { + // println("J") + LazyInitializedThreadSafeFieldReference // result //DCL + } else { + // println("K") + MutableFieldReference + } + } else { + //println("check write is deterministic: "+checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code)) + if ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || + (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) && + write.value.asVar.definedBy.size >= 0 && + (( //IsDeepImmutable //state.field.isFinal ||write.value.asVar.definedBy.head > -1 + write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.nonEmpty && + checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code) + ))) { + // println("L") + if (noInterferingExceptions) { + if (state.field.fieldType.computationalType != ComputationalTypeInt && + state.field.fieldType.computationalType != ComputationalTypeFloat) { + + LazyInitializedNotThreadSafeFieldReference + } else + LazyInitializedNotThreadSafeButDeterministicFieldReference + } else { + // println("M") + MutableFieldReference + } + } else { + // println("N") + 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) + * The third element of the triple returns: + * @author Tobias Roth + */ + def findCaughtsThrowsAndResults( + tacCode: TACode[TACMethodParameter, V], + cfg: CFG[Stmt[V], TACStmts[V]] + ): (List[(Int, ObjectType, IntTrieSet, CFGNode)], List[(Int, IntTrieSet, CFGNode)]) = { //}, List[CFGNode]) = { + var caughtExceptions: List[(Int, ObjectType, IntTrieSet, CFGNode)] = List.empty + var throwStatements: List[(Int, IntTrieSet, CFGNode)] = List.empty + //var returnNodes: List[CFGNode] = List.empty + for (stmt ← tacCode.stmts) { + if (!stmt.isNop) { //pc < ... + val currentBB = cfg.bb(tacCode.pcToIndex(stmt.pc)) + (stmt.astID: @switch) match { + case CaughtException.ASTID ⇒ + val caughtException = stmt.asCaughtException + val exceptionType = + if (caughtException.exceptionType.isDefined) { + val intermediateExceptionType = caughtException.exceptionType.get + //if (intermediateExceptionType.isObjectType) + intermediateExceptionType.asObjectType + //else + //ObjectType.Throwable // ObjectType.Exception + } else + ObjectType.Throwable // ObjectType.Exception + caughtExceptions = + ((caughtException.pc, exceptionType, caughtException.origins, currentBB)) :: caughtExceptions + case Throw.ASTID ⇒ + val throwStatement = stmt.asThrow + val throwStatementDefinedBys = + // if (throwStatement.exception.isVar) { + throwStatement.exception.asVar.definedBy + // } else + // IntTrieSet.empty + throwStatements = ((throwStatement.pc, throwStatementDefinedBys, currentBB)) :: throwStatements + //case ReturnValue.ASTID ⇒ + // returnNodes = (currentBB) :: returnNodes + case _ ⇒ + } + //} + } + } + (caughtExceptions, throwStatements) //, returnNodes) + } + + def findMonitors( + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + tacCode: TACode[TACMethodParameter, V] + )(implicit state: State): ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = { + + var result: (Option[Int], Option[Int]) = (None, None) + var dclEnterBBs: List[CFGNode] = List.empty + var dclExitBBs: List[CFGNode] = List.empty + val startBB = cfg.bb(fieldWrite) + var monitorExitQueuedBBs: Set[CFGNode] = startBB.successors + var worklistMonitorExit = getSuccessors(startBB, Set.empty) + + def checkMonitor(v: V)(implicit state: State): Boolean = { + v.definedBy + .forall(definedByIndex ⇒ { + if (definedByIndex >= 0) { + val stmt = tacCode.stmts(definedByIndex) + stmt match { + case Assignment(_, DVar(_, _), classConst: ClassConst) ⇒ { + state.field.classFile.thisType == classConst.value || + state.field.fieldType == classConst.value + } + case Assignment( + _, + DVar(_, _), + GetField(_, _, _, + classType, + UVar(_, _)) + ) ⇒ + classType == + state.field.classFile.thisType + case _ ⇒ false + } + } else // (definedByIndex <= -1) + true + }) + } + + var monitorEnterQueuedBBs: Set[CFGNode] = startBB.predecessors + var worklistMonitorEnter = getPredecessors(startBB, Set.empty) + // println("find monitorenter") + //find monitorenter + while (worklistMonitorEnter.nonEmpty) { + val curBB = worklistMonitorEnter.head + // println("curBB: "+curBB) + worklistMonitorEnter = worklistMonitorEnter.tail + val startPC = curBB.startPC + val endPC = curBB.endPC + var flag = true + for (i ← startPC to endPC) { + (code(i).astID: @switch) match { + case MonitorEnter.ASTID ⇒ + val me = code(i).asMonitorEnter + if (checkMonitor(me.objRef.asVar)) { //me.pc, , curBB + result = (Some(tacCode.pcToIndex(me.pc)), (result._2)) + dclEnterBBs = curBB :: dclEnterBBs + flag = false + } + case _ ⇒ + } + } + if (flag) { + val predecessor = getPredecessors(curBB, monitorEnterQueuedBBs) + worklistMonitorEnter ++= predecessor + monitorEnterQueuedBBs ++= predecessor + } + } + //println("find monitorexit") + //find monitorexit + while (worklistMonitorExit.nonEmpty) { + val curBB = worklistMonitorExit.head + + // println("curBB: "+curBB) + worklistMonitorExit = worklistMonitorExit.tail + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case MonitorExit.ASTID ⇒ + val mex = cfStmt.asMonitorExit + if (checkMonitor(mex.objRef.asVar)) { + result = ((result._1), Some(tacCode.pcToIndex(mex.pc))) + dclExitBBs = curBB :: dclExitBBs + } + case _ ⇒ + val successors = getSuccessors(curBB, monitorExitQueuedBBs) + worklistMonitorExit ++= successors + monitorExitQueuedBBs ++= successors + } + } + + val bbsEnter = { + if (dclEnterBBs.nonEmpty) + Some(dclEnterBBs.head) + else None + } + val bbsExit = { + if (dclExitBBs.nonEmpty) + Some(dclExitBBs.head) + else None + } + (result, (bbsEnter, bbsExit)) + } + + /** + * 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 fieldread. + */ + // var savedGuards: mutable.HashMap[(Int, Method), ((Option[(Int, Int, Int, CFGNode, Int)]))] = + // new mutable.HashMap[(Int, Method), ((Option[(Int, Int, Int, CFGNode, Int)]))]() + + def findGuard( + method: Method, + fieldWrite: Int, + defaultValue: Any, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + tacCode: TACode[TACMethodParameter, V] + )(implicit state: State): (List[(Int, Int, Int, CFGNode, Int, Int)]) = { + + // println(s"start findguard defaultValue: $defaultValue") + val startBB = cfg.bb(fieldWrite).asBasicBlock + + var enqueuedBBs: Set[CFGNode] = startBB.predecessors + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty).toList + var seen: Set[BasicBlock] = Set.empty + var result: List[(Int, Int, CFGNode, Int)] = List.empty //None + + // println("startBB: "+startBB) + + while (worklist.nonEmpty) { + val curBB = worklist.head + worklist = worklist.tail + if (!seen.contains(curBB)) { + seen += curBB + + // println("curBB: "+curBB) + + //val startPC = curBB.startPC + val endPC = curBB.endPC + + val cfStmt = code(endPC) + (cfStmt.astID: @switch) match { + case If.ASTID ⇒ + + // println("ifstmt: ") + // println(cfStmt) + val ifStmt = cfStmt.asIf + //ifStmt.condition match { + // println("AA") + /* println(s"isguard: ${ + isGuard( + ifStmt, + defaultValue, + code, + tacCode + ) + }") */ + if (ifStmt.condition.equals(EQ) && curBB != startBB && isGuard( + ifStmt, + defaultValue, + code, + tacCode, + method + )) { + // println("BB") + + //case EQ + //if => + ///if (result.size > 0) { //.isDefined) { + /// if (result.head._1 != endPC || result.head._2 != endPC + 1) {} //return result //None; + ///} else { + result = (endPC, endPC + 1, curBB, ifStmt.targetStmt) :: result + ///} + } else if (ifStmt.condition.equals(NE) && curBB != startBB && isGuard( + ifStmt, + defaultValue, + code, + tacCode, + method + )) { + // println("CC") + //case NE + //if => + ///if (result.size > 0) { //.isDefined) { + /// if (result.head._1 != endPC || result.head._2 != ifStmt.targetStmt) + /// result //None + ///} else { + result = (endPC, ifStmt.targetStmt, curBB, endPC + 1) :: result + ///} + } else { + // println("DD") + // Otherwise, we have to ensure that a guard is present for all predecessors + //case _ => + //if (startPC == 0) result //None; + + //isTransitivePredecessor(cfg.bb(fieldWrite), cfg.bb(ifStmt.target), Set.empty) + + //fieldWrite != ifStmt.target + // + // println("befor transitive call...") + if ((cfg.bb(fieldWrite) != cfg.bb(ifStmt.target) || fieldWrite < ifStmt.target) && + isTransitivePredecessor(cfg.bb(fieldWrite), cfg.bb(ifStmt.target))) //(ifStmt.target > fieldWrite) + 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 _ ⇒ + ///if (startPC == 0) result //None; + + val predecessors = getPredecessors(curBB, enqueuedBBs) + worklist ++= predecessors + enqueuedBBs ++= predecessors + } + } + + } + + var finalResult: List[(Int, Int, Int, CFGNode, 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.exists(_ < 0)) //.head < 0) + return finalResult; + println( + s"""definitions: + |field read + | ${code(definitions.head).asAssignment.expr} + | + | + |""".stripMargin + ) + fieldReadIndex = definitions.head + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy //TODO ... + val fieldReadUsedCorrectly = + fieldReadUses.forall(use ⇒ use == result._1 || use == result._2) + + if (definitions.size == 1 && definitions.head >= 0 && fieldReadUsedCorrectly) + finalResult = (result._1, result._2, definitions.head, result._3, result._4, fieldReadIndex) :: finalResult // Found proper guard + else finalResult + }) // else None) + 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 = { + var visited: Set[CFGNode] = Set.empty + def isTransitivePredecessorInternal(possiblePredecessor: CFGNode, node: CFGNode): Boolean = { + /*println( + s""" + |is transitive predecessor: + |possiblePredecessor: $possiblePredecessor + | node: $node + | visited: ${visited.mkString("\n")} + |""".stripMargin + ) */ + + if (possiblePredecessor == node) { + true + } else if (visited.contains(node)) + false + else { + visited += node + //val predecessors = node.predecessors //getPredecessors(node, Set.empty) + //val tmpVisited2 = tmpVisited ++ predecessors + //println("predecessors: "+predecessors.mkString(", \n")) + //println("tmpVisited2: "+tmpVisited2) + node.predecessors.exists( + currentNode ⇒ { + isTransitivePredecessorInternal(possiblePredecessor, currentNode) + } + ) + } + } + println("--------------------------------------------------------------------------------------------------------") + 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]] + )(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 _ ⇒ // Unknown field + false + }) + } + } + val result = defSites == SelfReferenceParameter || + defSites.size == 1 && isConstantDef(defSites.head) + result + } + + val value = origin.expr + + def isNonConstDeterministic(value: Expr[V]): 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 _ ⇒ // Unknown field + false + } + 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 && //stronger requirement -> only one definition side + varValue.definedBy.forall(i ⇒ i >= 0 && isNonConstDeterministic(code(i).asAssignment.expr)) + } + case New.ASTID ⇒ { + //TODO constructor must be deterministic + //TODO check that the nonvirtualmethod call calls the constructor + //TODO check + val nonVirtualMethodCallIndex = + origin.asAssignment.targetVar.usedBy.iterator.filter(i ⇒ code(i).isNonVirtualMethodCall).toList.head + val resultCallees = propertyStore(declaredMethods(method), Callees.key) + !handleCalls(resultCallees, nonVirtualMethodCallIndex) && + code(nonVirtualMethodCallIndex).asNonVirtualMethodCall.params.forall(isConstant) + } + 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, code) + } + } + val result = value.isConst || isNonConstDeterministic(value) + + result + } + + /** + * 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])(implicit state: State): Boolean = { + var seenExpressions: Set[Expr[V]] = Set.empty + def isReadOfCurrentFieldInternal(expr: Expr[V], tacCode: TACode[TACMethodParameter, V])(implicit state: State): Boolean = { + + if (seenExpressions.contains(expr)) { + return false + }; + seenExpressions += expr + def isExprReadOfCurrentField(pc: Int): Int ⇒ Boolean = index ⇒ + index == tacCode.pcToIndex(pc) || + index >= 0 && + isReadOfCurrentFieldInternal(tacCode.stmts(index).asAssignment.expr, tacCode) + (expr.astID: @switch) match { + case GetField.ASTID ⇒ + + //f (state.field.classFile.thisType.simpleName.contains("DoubleCheckedLocking")) { + /*println( + s""" + | field: ${state.field} + | expr: $expr + | objectDefinition: ${expr.asGetField.objRef.asVar.definedBy} + | SelfReferenceParameter: ${SelfReferenceParameter} + | resolve: ${if (expr.asGetField.objRef.asVar.definedBy == SelfReferenceParameter) expr.asGetField.resolveField(project)} + | field: ${state.field} + |""".stripMargin + ) */ + //} + 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 Compare.ASTID ⇒ { + val leftExpr = expr.asCompare.left + val rightExpr = expr.asCompare.right + + leftExpr.asVar.definedBy.forall(isExprReadOfCurrentField(expr.asCompare.pc)) || + rightExpr.asVar.definedBy.forall(isExprReadOfCurrentField(expr.asCompare.pc)) + //TODO + //TODO check if sufficient + //TODO check if handling of methods needed + //TODO check if semantic is correct + //TODO check if handling of method arguments needed + } + case VirtualFunctionCall.ASTID + | NonVirtualFunctionCall.ASTID + | StaticFunctionCall.ASTID ⇒ { + // val (receiverDefSites, + val (params, pc) = + if (expr.isVirtualFunctionCall) { + val virtualFunctionCall = expr.asVirtualFunctionCall + //(virtualFunctionCall.receiver.asVar.definedBy, + (virtualFunctionCall.params, virtualFunctionCall.pc) + } else if (expr.isStaticFunctionCall) { + val staticFunctionCall = expr.asStaticFunctionCall + //val receiverDefSites = staticFunctionCall.receiverOption + //if (receiverDefSites.isEmpty) + // return false; + //(receiverDefSites.get.asVar.definedBy, + (staticFunctionCall.params, staticFunctionCall.pc) + } else { + val nonVirtualFunctionCall = expr.asNonVirtualFunctionCall + //(nonVirtualFunctionCall.receiver.asVar.definedBy, + (nonVirtualFunctionCall.params, nonVirtualFunctionCall.pc) + } + params.forall(expr ⇒ + expr.asVar.definedBy.forall(isExprReadOfCurrentField(pc))) + //isReadOfCurrentFieldInternal(expr, tacCode)) + } + case _ ⇒ false + } + } + isReadOfCurrentFieldInternal(expr, tacCode) + } + + /** + * 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 = { + import org.opalj.br.FieldType + import scala.annotation.tailrec + + /** + * 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 { + /* + println( + s""" + | isIntConst: ${expr.isIntConst} + | defaultValue: $defaultValue + | expr: ${expr.asIntConst.value} + | isFloatConst: ${expr.isFloatConst} + | isDoublConst: ${expr.isDoubleConst} + | isLongConst: ${expr.isLongConst} + | isNullExpr: ${expr.isNullExpr} + | expr.isIntConst && defaultValue == expr.asIntConst.value: ${expr.isIntConst && defaultValue == expr.asIntConst.value} + |""".stripMargin + )*/ + + 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 = { + // println("i1") + expr.definedBy forall { index ⇒ + // println("index: "+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 (handleCalls(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)) + } else { //if(isStaticFunctionCall){ + //val staticFunctionCall = code(index).asAssignment.expr.asStaticFunctionCall + //staticFunctionCall.receiverOption. + isReadOfCurrentField(code(index).asAssignment.expr, tacCode) + } + } else { + method.asMethod.isVirtualCallTarget + isReadOfCurrentField(code(index).asAssignment.expr, tacCode) + } + } + } + } + + def isFloatDoubleOrLong(fieldType: FieldType): Boolean = + fieldType.isDoubleType || fieldType.isFloatType || fieldType.isLongType + + if (ifStmt.rightExpr.isVar && isFloatDoubleOrLong(state.field.fieldType) && ifStmt.rightExpr.asVar.definedBy.head > 0 && tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.isCompare) { + //ifStmt.leftExpr.isIntConst + //ifStmt.leftExpr.asIntConst.value==0 + // println("1") + val left = tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.asCompare.left.asVar + // println("2") + val right = tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.asCompare.right.asVar + // println("3") + val leftExpr = tacCode.stmts(left.definedBy.head).asAssignment.expr + // println("4") + val rightExpr = tacCode.stmts(right.definedBy.head).asAssignment.expr + // println("5") + if (leftExpr.isGetField) { + // println("6") + val result = isDefaultConst(rightExpr) + // println("result: "+result) + result + } else if (rightExpr.isGetField) { + // println("7") + val result = isDefaultConst(leftExpr) + // println("result: "+result) + result + } else { false } //TODO reasoning + } else if (ifStmt.leftExpr.isVar && ifStmt.rightExpr.isVar && ifStmt.leftExpr.asVar.definedBy.head >= 0 && + ifStmt.rightExpr.asVar.definedBy.head >= 0 && + isFloatDoubleOrLong(state.field.fieldType) && tacCode.stmts(ifStmt.leftExpr.asVar.definedBy.head). + asAssignment.expr.isCompare && + ifStmt.leftExpr.isVar && ifStmt.rightExpr.isVar) { + // println("9") + 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) { + val result = isDefaultConst(rightExpr) + result + } else if (rightExpr.isGetField) { + isDefaultConst(leftExpr) + } else false //TODO reasoning + } else if ((ifStmt.rightExpr.isVar) && isDefaultConst(ifStmt.leftExpr)) { + // println("12") + isGuardInternal(ifStmt.rightExpr.asVar, tacCode, method) + } else if (ifStmt.leftExpr.isVar && isDefaultConst(ifStmt.rightExpr)) { + // println("13") + val result = isGuardInternal(ifStmt.leftExpr.asVar, tacCode, method) + // println(s"result: $result") + result + } else { + // println("14") + false + } + } + + /** + * + * Checks that the value of the field is returned. + * + */ + def isTheFieldsValueReturned( + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + cfg: CFG[Stmt[V], TACStmts[V]], + tacCode: TACode[TACMethodParameter, V] + )(implicit state: State): Boolean = { + val startBB = cfg.bb(0).asBasicBlock + //var curBB = startBB + var queuedNodes: Set[CFGNode] = Set.empty + var workList = getSuccessors(startBB, queuedNodes) + workList ++= Set(startBB) + var tmp = -1 + while (workList.nonEmpty) { + val currentBB = workList.head + workList = workList.tail + val startPC = { + if (currentBB == startBB) + writeIndex + 1 + else + currentBB.startPC + } + val endPC = currentBB.endPC + var pc = startPC + while (pc <= endPC) { + val index = pc + val stmt = tacCode.stmts(index) + if (stmt.isAssignment) { + val assignment = stmt.asAssignment + if (isReadOfCurrentField(assignment.expr, tacCode)) { + tmp = pc + } + } else if (stmt.isReturnValue) { + val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefs.size == 2 && + returnValueDefs.contains(write.value.asVar.definedBy.head) && + returnValueDefs.contains(readIndex)) { + return true; + } // direct return of the written value + else if (tmp >= 0 && (returnValueDefs == IntTrieSet(tmp) || + returnValueDefs == IntTrieSet(readIndex, tmp))) { + return true; + } // return of field value loaded by field read + else { + return false; + } // return of different value + } else { + + } + pc = pc + 1 + } + val successors = getSuccessors(currentBB, queuedNodes) + workList ++= successors + queuedNodes ++= successors + } + false + } + +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/L0FieldReferenceImmutabilityAnalysis.scala similarity index 72% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/L0FieldReferenceImmutabilityAnalysis.scala index 7e75969c18..abd36e3444 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/reference/L0ReferenceImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/fieldreference/L0FieldReferenceImmutabilityAnalysis.scala @@ -1,16 +1,17 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.immutability.reference +package org.opalj +package tac +package fpcf +package analyses +package immutability +package fieldreference import org.opalj.br.BooleanType -import org.opalj.br.ByteType import org.opalj.br.ClassFile import org.opalj.br.DeclaredMethod import org.opalj.br.Field -import org.opalj.br.FloatType -import org.opalj.br.IntegerType import org.opalj.br.Method import org.opalj.br.PCs -import org.opalj.br.ShortType import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler @@ -21,13 +22,13 @@ 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.ImmutableReference -import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicReference -import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeReference -import org.opalj.br.fpcf.properties.MutableReference +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.ReferenceImmutability +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 @@ -42,26 +43,23 @@ import org.opalj.fpcf.PropertyComputationResult import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS -import org.opalj.tac.PutField -import org.opalj.tac.PutStatic -import org.opalj.tac.Stmt -import org.opalj.tac.TACMethodParameter -import org.opalj.tac.TACode import org.opalj.tac.common.DefinitionSite import org.opalj.tac.fpcf.properties.TACAI -import org.opalj.tac.SelfReferenceParameter import scala.annotation.switch 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 classes field. - * In cases of fields with primitive types, we consider them also as a combination of fields and types - * The field immutability will be determined in the FieldImmutabilityAnalysis. + * A field reference can either refer to an reference object or store a value. * * Examples: * class ... { @@ -71,27 +69,26 @@ import org.opalj.br.ObjectType * ... * } * - * In both cases we consider o and an as references with their respective types. - * The combination of both is the field immutability. - * + * 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 integer-value. * * @note Requires that the 3-address code's expressions are not deeply nested. * - * @author Tobias Peter Roth + * @author Tobias Roth * @author Dominik Helm * @author Florian Kübler * @author Michael Eichberg * */ -class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProject) - extends AbstractReferenceImmutabilityAnalysisLazyInitialization - with AbstractReferenceImmutabilityAnalysis +class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeProject) + extends AbstractFieldReferenceImmutabilityAnalysisLazyInitialization + with AbstractFieldReferenceImmutabilityAnalysis with FPCFAnalysis { - def doDetermineReferenceImmutability(entity: Entity): PropertyComputationResult = { + def doDetermineFieldReferenceImmutability(entity: Entity): PropertyComputationResult = { entity match { case field: Field ⇒ { - determineReferenceImmutability(field) + determineFieldReferenceImmutability(field) } case _ ⇒ val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" @@ -106,29 +103,37 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * If the analysis is schedulued using its companion object all class files with * native methods are filtered. */ - private[analyses] def determineReferenceImmutability( + private[analyses] def determineFieldReferenceImmutability( field: Field ): ProperPropertyComputationResult = { + println( + s""" + | determine field reference immutability: + | field ${field} + | + | + |""".stripMargin + ) + if (field.isFinal) - return Result(field, ImmutableReference); + return Result(field, ImmutableFieldReference); + if (field.isPublic) - return Result(field, MutableReference); + return Result(field, MutableFieldReference); + implicit val state: State = State(field) - state.referenceImmutability = ImmutableReference + val thisType = field.classFile.thisType // Fields are not final if they are read prematurely! - if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) { - return Result(field, MutableReference) - }; + 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) { - if (!closedPackages.isClosed(thisType.packageName)) { - return Result(field, MutableReference); - } project.classesPerPackage(thisType.packageName) } else { Set(field.classFile) @@ -136,7 +141,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val classesHavingAccess: Iterator[ClassFile] = if (field.isProtected) { if (typeExtensibility(thisType).isYesOrUnknown) { - return Result(field, MutableReference); + return Result(field, MutableFieldReference); } val subclassesIterator: Iterator[ClassFile] = classHierarchy.allSubclassTypes(thisType, reflexive = false).flatMap { ot ⇒ @@ -149,80 +154,39 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec // If there are native methods, we give up if (classesHavingAccess.exists(_.methods.exists(_.isNative))) { if (!field.isFinal) - return Result(field, MutableReference) + return Result(field, MutableFieldReference) } for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) taCode ← getTACAI(method, pcs) } { if (methodUpdatesField(method, taCode, pcs)) { - return Result(field, MutableReference); + return Result(field, MutableFieldReference); } } if (state.lazyInitInvocation.isDefined) { val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - handleCalls(calleesEOP) + handleCalls(calleesEOP, state.lazyInitInvocation.get._2) } createResult() } - def handleCalls( - calleesEOP: EOptionP[DeclaredMethod, Callees] - )( - implicit - state: State - ): Boolean = { - calleesEOP match { - case FinalP(callees) ⇒ - state.calleesDependee = None - handleCallees(callees) - case InterimUBP(callees) ⇒ - state.calleesDependee = Some(calleesEOP) - handleCallees(callees) - case _ ⇒ - state.calleesDependee = Some(calleesEOP) - false - } - } - - def handleCallees(callees: Callees)(implicit state: State): Boolean = { - val pc = state.lazyInitInvocation.get._2 - if (callees.isIncompleteCallSite(pc)) { - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else { - val targets = callees.callees(pc) - if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { - state.referenceImmutability = MutableReference //NonFinalFieldByAnalysis - true - } else false - } - } - /** * Returns the value the field will have after initialization or None if there may be multiple * values. */ - def getDefaultValue()(implicit state: State): Option[Any] = { + def getDefaultValue()(implicit state: State): Any = { state.field.fieldType match { - case ObjectType.Integer - | ObjectType.Float - | ObjectType.Long - | ObjectType.Short - | ObjectType.Byte - | ObjectType.Double ⇒ Some(0) - case FloatType ⇒ Some(0.0f) - case DoubleType ⇒ Some(0.0d) - case LongType ⇒ Some(0.0) - case CharType ⇒ Some(0) - case IntegerType ⇒ Some(0) - case _: ReferenceType ⇒ Some(null) - case BooleanType ⇒ Some(false) - case ByteType ⇒ Some(0) - case ShortType ⇒ Some(0) - - case _ ⇒ None + 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 } } @@ -231,10 +195,11 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * dependees or as Result otherwise. */ def createResult()(implicit state: State): ProperPropertyComputationResult = { - if (state.hasDependees && (state.referenceImmutability ne MutableReference)) { //NonFinalFieldByAnalysis)) + + if (state.hasDependees && (state.referenceImmutability ne MutableFieldReference)) { InterimResult( state.field, - MutableReference, //NonFinalFieldByAnalysis, + MutableFieldReference, state.referenceImmutability, state.dependees, c @@ -249,8 +214,14 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec * property of the method that initializes a (potentially) lazy initialized field. */ def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { - var isNotFinal = false + var isNotFinal = false + println( + s""" + | enter continuation + | eps: $eps + |""".stripMargin + ) eps.pk match { case EscapeProperty.key ⇒ val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] @@ -263,12 +234,19 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec state.tacDependees -= method if (eps.isRefinable) state.tacDependees += method -> ((newEP, pcs)) - //TODO tacai funktionen alle ausführen - val tmp = method - if (tmp != method) - isNotFinal = methodUpdatesField(method, newEP.ub.tac.get, pcs) + isNotFinal = methodUpdatesField(method, newEP.ub.tac.get, pcs) case Callees.key ⇒ - isNotFinal = handleCalls(eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]]) + //if (eps.e.isInstanceOf[DeclaredMethod]) + + //state.lazyInitInvocation + val newEPS = eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]] + val pcs = state.calleesDependee(newEPS.e)._2 + isNotFinal = pcs.forall(pc ⇒ handleCalls(newEPS, pc)) + //state.calleesDependee+= (calleesEOP.e → (calleesEOP,pc :: state.calleesDependee(calleesEOP.e)._2)) + // isNotFinal = handleCalls() + //else { + //-T ODO callees handling + //} case FieldPrematurelyRead.key ⇒ isNotFinal = isPrematurelyRead(eps.asInstanceOf[EOptionP[Field, FieldPrematurelyRead]]) case Purity.key ⇒ @@ -281,14 +259,16 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec isNotFinal = nonDeterministicResult //} - case ReferenceImmutability.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[Field, ReferenceImmutability]] + case FieldReferenceImmutability.key ⇒ + val newEP = eps.asInstanceOf[EOptionP[Field, FieldReferenceImmutability]] state.referenceImmutabilityDependees = state.referenceImmutabilityDependees.iterator.filter(_.e ne newEP.e).toSet isNotFinal = !isImmutableReference(newEP) } + + println("result is not final: "+isNotFinal) if (isNotFinal) - state.referenceImmutability = MutableReference + state.referenceImmutability = MutableFieldReference createResult() } @@ -305,7 +285,7 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec val stmts = taCode.stmts for (pc ← pcs.iterator) { val index = taCode.pcToIndex(pc) - if (index > -1) { //TODO actually, unnecessary but required because there are '-1' + 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 { @@ -324,34 +304,33 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec 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.referenceImmutability == LazyInitializedThreadSafeReference || - state.referenceImmutability == LazyInitializedNotThreadSafeButDeterministicReference) //LazyInitializedField) + if (state.referenceImmutability == LazyInitializedThreadSafeFieldReference || + state.referenceImmutability == LazyInitializedNotThreadSafeButDeterministicFieldReference) //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; - val defaultValue = getDefaultValue() - if (defaultValue.isEmpty) - return true; // A field written outside an initializer must be lazily // initialized or it is non-final val result = handleLazyInitialization( index, - defaultValue.get, + getDefaultValue(), method, taCode.stmts, taCode.cfg, taCode.pcToIndex, taCode ) - if (result.isDefined) - return result.get; + if (result) { + println("result7: "+result) + return result + }; } 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 @@ -436,61 +415,58 @@ class L0ReferenceImmutabilityAnalysis private[analyses] (val project: SomeProjec } -trait L0ReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +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.finalP(TACAI), + PropertyBounds.ub(TACAI), PropertyBounds.ub(EscapeProperty), - PropertyBounds.ub(ReferenceImmutability), + PropertyBounds.ub(FieldReferenceImmutability), PropertyBounds.ub(FieldImmutability) ) - final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReferenceImmutability) - + final def derivedProperty: PropertyBounds = PropertyBounds.lub(FieldReferenceImmutability) } /** - * Executor for the field mutability analysis. + * Executor for the eager field reference immutability analysis. */ -object EagerL0ReferenceImmutabilityAnalysis - extends L0ReferenceImmutabilityAnalysisScheduler +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 L0ReferenceImmutabilityAnalysis(p) + val analysis = new L0FieldReferenceImmutabilityAnalysis(p) val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) //p.allFields - ps.scheduleEagerComputationsForEntities(fields)(analysis.determineReferenceImmutability) + ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldReferenceImmutability) analysis } - - override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - - override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } /** - * Executor for the lazy field mutability analysis. + * Executor for the lazy field reference immutability analysis. */ -object LazyL0ReferenceImmutabilityAnalysis - extends L0ReferenceImmutabilityAnalysisScheduler +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 L0ReferenceImmutabilityAnalysis(p) + val analysis = new L0FieldReferenceImmutabilityAnalysis(p) ps.registerLazyPropertyComputation( - ReferenceImmutability.key, - analysis.determineReferenceImmutability + FieldReferenceImmutability.key, + analysis.determineFieldReferenceImmutability ) analysis } - - override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) } From aedaa5cf5b44da6af52e96c10faa245397dedc05 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 13 Oct 2020 16:24:04 +0200 Subject: [PATCH 282/327] revised, still wip --- .../ClassImmutabilityAnalysisDemo.scala | 10 +- .../FieldImmutabilityAnalysisDemo.scala | 11 +- ...eldReferenceImmutabilityAnalysisDemo.scala | 6 +- .../TypeImmutabilityAnalysisDemo.scala | 7 +- .../org/opalj/support/info/Immutability.scala | 52 ++-- .../fpcf/properties/ClassImmutability.scala | 8 +- .../br/fpcf/properties/TypeImmutability.scala | 2 +- .../L1ClassImmutabilityAnalysis.scala | 70 +---- .../L3FieldImmutabilityAnalysis.scala | 248 +++++++----------- ...ctFieldReferenceImmutabilityAnalysis.scala | 8 +- ...mutabilityAnalysisLazyInitialization.scala | 149 ++++------- ...L0FieldReferenceImmutabilityAnalysis.scala | 19 +- 12 files changed, 219 insertions(+), 371 deletions(-) mode change 100644 => 100755 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/immutability/L3FieldImmutabilityAnalysis.scala 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 index 30d257f9a7..19f38f566a 100644 --- 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 @@ -8,6 +8,7 @@ import java.io.File import java.io.FileWriter import java.net.URL import java.util.Calendar +import java.io.IOException import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis @@ -36,7 +37,7 @@ 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 + * Runs the EagerL1ClassImmutabilityAnalysis as well as analyses needed for improving the result. * * @author Tobias Roth */ @@ -157,8 +158,11 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { bw.write(output) bw.close() } catch { - case _: IOException ⇒ - println(s"Could not write file: ${file.getName}") + case e: IOException ⇒ println( + s""" Could not write file: ${file.getName} + | ${e.getMessage} + |""".stripMargin + ) } finally { bw.close() } 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 index 2ca3a9dab8..daffbef98c 100644 --- 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 @@ -116,7 +116,10 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { | Dependent Immutable Fields: ${dependentImmutableFields.size} | Deep Immutable Fields: ${deepImmutableFields.size} | - | sum: ${mutableFields.size + shallowImmutableFields.size + dependentImmutableFields.size + deepImmutableFields.size} + | sum: ${ + mutableFields.size + shallowImmutableFields.size + + dependentImmutableFields.size + deepImmutableFields.size + } | | took : $analysisTime seconds |""".stripMargin @@ -129,7 +132,11 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { bw.write(output) bw.close() } catch { - case e: IOException ⇒ println(s"could not write file ${file.getName}") + case e: IOException ⇒ println( + s""" Could not write file: ${file.getName} + | ${e.getMessage} + |""".stripMargin + ) } finally { bw.close() } 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 index 6fe7c00a23..88db90581b 100644 --- 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 @@ -147,7 +147,11 @@ object FieldReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication bw.write(output) bw.close() } catch { - case e: IOException ⇒ println(s"Could not write file: ${file.getName}") + case e: IOException ⇒ println( + s""" Could not write file: ${file.getName} + | ${e.getMessage} + |""".stripMargin + ) } finally { bw.close() } 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 index 9a1fea829e..190cd4e325 100644 --- 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 @@ -138,8 +138,11 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { bw.write(output) bw.close() } catch { - case e: IOException ⇒ - println(s"could not write file: ${file.getName}") + case e: IOException ⇒ println( + s""" Could not write file: ${file.getName} + | ${e.getMessage} + |""".stripMargin + ) } finally { bw.close() } 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 index 4d296df37f..6b8e750790 100644 --- 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 @@ -65,7 +65,6 @@ import org.opalj.log.GlobalLogContext import org.opalj.log.OPALLogger import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.PropertyStoreContext -import org.opalj.support.info.RunningAnalyses.RunningAnalysis import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater import org.opalj.ai.domain @@ -85,18 +84,15 @@ import org.opalj.fpcf.EPS * * @author Tobias Peter Roth */ -object RunningAnalyses extends Enumeration { - type RunningAnalysis = Value - val References, Fields, Classes, Types, All = Value -} -import RunningAnalyses.References -import RunningAnalyses.Fields -import RunningAnalyses.Classes -import RunningAnalyses.Types -import RunningAnalyses.All - object Immutability { + sealed trait RunningAnalysis + case object References extends RunningAnalysis + case object Fields extends RunningAnalysis + case object Classes extends RunningAnalysis + case object Types extends RunningAnalysis + case object All extends RunningAnalysis + def evaluate( cp: File, analysis: RunningAnalysis, @@ -291,10 +287,10 @@ object Immutability { val allFieldsInProjectClassFiles = { if (reImInferComparison) { - project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet - } else { project.allProjectClassFiles.toIterator.flatMap { _.fields }. - filter(f ⇒ (!f.isTransient && !f.isSynthetic)).toSet + filter(f ⇒ !f.isTransient && !f.isSynthetic).toSet + } else { + project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet } } @@ -322,7 +318,7 @@ object Immutability { val immutableReferences = fieldReferenceGroupedResults(ImmutableFieldReference). toSeq.sortWith(fieldReferenceOrder) - if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.References) { + if (analysis == All || analysis == References) { stringBuilderResults.append( s""" | Mutable References: @@ -368,7 +364,7 @@ object Immutability { val deepImmutableFields = fieldGroupedResults(DeepImmutableField).toSeq.sortWith(fieldOrder) - if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.Fields) { + if (analysis == All || analysis == Fields) { stringBuilderResults.append( s""" | Mutable Fields: @@ -413,7 +409,7 @@ object Immutability { val deepImmutableClasses = deepImmutables .filter(eps ⇒ !allInterfaces.contains(eps.asInstanceOf[ObjectType])).toSeq.sortWith(order) - if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.Classes) { + if (analysis == All || analysis == Classes) { stringBuilderResults.append( s""" | Mutable Classes: @@ -448,7 +444,7 @@ object Immutability { val deepImmutableTypes = typeGroupedResults(DeepImmutableType).toSeq.sortWith(typeOrder) - if (analysis == RunningAnalyses.All || analysis == RunningAnalyses.Types) { + if (analysis == All || analysis == Types) { stringBuilderResults.append( s""" | Mutable Types: @@ -468,7 +464,7 @@ object Immutability { val stringBuilderAmounts: StringBuilder = new StringBuilder - if (analysis == RunningAnalyses.References || analysis == RunningAnalyses.All) { + if (analysis == References || analysis == All) { stringBuilderAmounts.append( s""" | Mutable References: ${mutableFieldReferences.size} @@ -481,7 +477,7 @@ object Immutability { |""".stripMargin ) } - if (analysis == RunningAnalyses.Fields || analysis == RunningAnalyses.All) { + if (analysis == Fields || analysis == All) { stringBuilderAmounts.append( s""" | Mutable Fields: ${mutableFields.size} @@ -493,7 +489,7 @@ object Immutability { ) } - if (analysis == RunningAnalyses.Classes || analysis == RunningAnalyses.All) { + if (analysis == Classes || analysis == All) { stringBuilderAmounts.append( s""" | Mutable Classes: ${mutableClasses.size} @@ -507,7 +503,7 @@ object Immutability { ) } - if (analysis == RunningAnalyses.Types || analysis == RunningAnalyses.All) + if (analysis == Types || analysis == All) stringBuilderAmounts.append( s""" | Mutable Types: ${mutableTypes.size} @@ -625,22 +621,22 @@ object Immutability { } } - var analysis: RunningAnalysis = RunningAnalyses.All + var analysis: RunningAnalysis = All while (i < args.length) { args(i) match { case "-analysis" ⇒ { val result = readNextArg() if (result == "All") - analysis = RunningAnalyses.All + analysis = All else if (result == "References") - analysis = RunningAnalyses.References + analysis = References else if (result == "Fields") - analysis = RunningAnalyses.Fields + analysis = Fields else if (result == "Classes") - analysis = RunningAnalyses.Classes + analysis = Classes else if (result == "Types") - analysis = RunningAnalyses.Types + analysis = Types else throw new IllegalArgumentException(s"unknown parameter: $result") } case "-threads" ⇒ numThreads = readNextArg().toInt 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 c86412b372..c9f2c25925 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 @@ -15,7 +15,8 @@ sealed trait ClassImmutabilityPropertyMetaInformation extends PropertyMetaInform /** * Describes the class immutability of org.opalj.br.ClassFile. - * The immutability of the classes are represented via their instance fields and the immutability of its supertype. + * The immutability of the classes are represented via the lower bound of the immutability of + * their instance fields and the immutability of its supertype. * * [[MutableClass]] A class with a mutable state. * @@ -42,7 +43,7 @@ object ClassImmutability extends ClassImmutabilityPropertyMetaInformation { * The key associated with every [[ClassImmutability]] property. */ final val key: PropertyKey[ClassImmutability] = PropertyKey.create( - "opalj.ClassImmutability_new", + "opalj.ClassImmutability", MutableClass ) } @@ -57,7 +58,6 @@ case object DeepImmutableClass extends ClassImmutability { } case object DependentImmutableClass extends ClassImmutability { - override def correspondingTypeImmutability: TypeImmutability = DependentImmutableType def meet(that: ClassImmutability): ClassImmutability = @@ -74,7 +74,6 @@ case object DependentImmutableClass extends ClassImmutability { } case object ShallowImmutableClass extends ClassImmutability { - override def correspondingTypeImmutability: TypeImmutability = ShallowImmutableType def meet(that: ClassImmutability): ClassImmutability = { @@ -92,7 +91,6 @@ case object ShallowImmutableClass extends ClassImmutability { } case object MutableClass extends ClassImmutability { - def correspondingTypeImmutability = MutableType def meet(other: ClassImmutability): ClassImmutability = this 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 2f67be5f48..cb565e0d98 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 @@ -53,7 +53,7 @@ object TypeImmutability extends TypeImmutabilityPropertyMetaInformation { * The key associated with every [[TypeImmutability]] property. */ final val key: PropertyKey[TypeImmutability] = PropertyKey.create( - "org.opalj.TypeImmutability_new", + "org.opalj.TypeImmutability", MutableType ) } 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 index 95349653a3..d3ebecd488 100644 --- 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 @@ -246,13 +246,9 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis else return createResultForAllSubtypes(t, MutableClass); } - case FinalP(ShallowImmutableField) ⇒ - hasShallowImmutableFields = true - case FinalEP(fi, DependentImmutableField) ⇒ { - hasDependentImmutableFields = true - } - - case FinalP(DeepImmutableField) ⇒ + case FinalP(ShallowImmutableField) ⇒ hasShallowImmutableFields = true + case FinalP(DependentImmutableField) ⇒ hasDependentImmutableFields = true + case FinalP(DeepImmutableField) ⇒ case ep @ InterimE(e) ⇒ hasFieldsWithUnknownMutability = true dependees += (e -> ep) @@ -269,18 +265,8 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis ) - /** - * var minLocalImmutability: ClassImmutability_new = - * if (!superClassMutabilityIsFinal) { - * MutableClass //MutableObjectByAnalysis - * - * } else { - * ShallowImmutableClass //ImmutableContainer - * } * - */ var minLocalImmutability: ClassImmutability = MutableClass - //println(s"suerclassinformation $superClassInformation") // NOTE: maxLocalImmutability does not take the super classes' mutability into account! var maxLocalImmutability: ClassImmutability = superClassInformation match { case UBP(MutableClass) ⇒ MutableClass @@ -289,12 +275,11 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis case _ ⇒ DeepImmutableClass } if (hasShallowImmutableFields) { - //println("has shallow immutable fields") maxLocalImmutability = ShallowImmutableClass } - //if (!dependentImmutableFields.isEmpty && maxLocalImmutability != ShallowImmutableClass) { - if (hasDependentImmutableFields && maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { + if (hasDependentImmutableFields && + maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { maxLocalImmutability = DependentImmutableClass } @@ -357,44 +342,19 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis } else if (maxLocalImmutability != MutableClass && maxLocalImmutability != ShallowImmutableClass) { maxLocalImmutability = DependentImmutableClass } - /** - * if (hasShallowImmutableFields && maxLocalImmutability != MutableClass) { - * maxLocalImmutability = ShallowImmutableClass - * } else if (maxLocalImmutability != ShallowImmutableClass && maxLocalImmutability != MutableClass) { - * maxLocalImmutability = DependentImmutableClass - * } else - * maxLocalImmutability = ShallowImmutableClass - * }* - */ - - //} else - // maxLocalImmutability = DependentImmutableClass // DependentImmutableClass - } - - // Field Mutability 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 - //xx if (minLocalImmutability != ShallowImmutableClass) // && // ImmutableContainer && - //!dependees.valuesIterator.exists(_.pk != TypeImmutability_new.key)) //TypeImmutability.key)) - //xx minLocalImmutability = ShallowImmutableClass //ImmutableContainer // Lift lower bound when possible - - case UBP(DeepImmutableField) ⇒ //_: FinalField) ⇒ // no information about field mutability - case UBP(ShallowImmutableField) ⇒ maxLocalImmutability = ShallowImmutableClass + // 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) { @@ -409,10 +369,6 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis ) */ - // Lift lower bound once no dependencies other than field type mutabilities are left - ////if (minLocalImmutability != ShallowImmutableClass) // && //ImmutableContainer && - //dependees.valuesIterator.forall(_.pk == TypeImmutability_new.key)) //TypeImmutability.key)) - //// minLocalImmutability = ShallowImmutableClass //ImmutableContainer if (dependees.isEmpty || minLocalImmutability == maxLocalImmutability) { /*[DEBUG] assert( 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 old mode 100644 new mode 100755 index 27cbb2858d..03b78632bb --- 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 @@ -9,7 +9,6 @@ import org.opalj.br.Attribute import org.opalj.br.ClassSignature import org.opalj.br.ClassTypeSignature import org.opalj.br.Field -import org.opalj.br.FormalTypeParameter import org.opalj.br.ObjectType import org.opalj.br.ProperTypeArgument import org.opalj.br.RuntimeInvisibleAnnotationTable @@ -75,34 +74,37 @@ 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.ClassFile +import org.opalj.br.FormalTypeParameter /** - * Analyses that determines the immutability of org.opalj.br.Field - * It uses the results of the [[L3FieldReferenceImmutabilityAnalysis]] - * and of the [[L1TypeImmutabilityAnalysis]] + * Analysis that determines the immutability of org.opalj.br.Field * * @author Tobias Roth * */ -class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) - extends FPCFAnalysis { +class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { /** - * Describes the different kinds of dependent immutable fields: - * [[DependentImmutabilityKind.Dependent]] Shallow or mutable types could still exist - * [[DependentImmutabilityKind.NotShallowOrMutable]] There are no shallow or mutable types - * [[DependentImmutabilityKind.OnlyDeepImmutable]] There are no generic parameters left. - * All have been replaced with deep immutable types. + * Describes the different kinds of dependent immutable fields, that depend on the concrete types that replace + * the generic parameters. + * + * [[Dependent]] 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 */ - object DependentImmutabilityKind extends Enumeration { - type DependentImmutabilityKind = Value - val NotShallowOrMutable, Dependent, OnlyDeepImmutable = Value - } - - import DependentImmutabilityKind._ + sealed trait DependentImmutabilityKind + case object Dependent extends DependentImmutabilityKind + case object NotShallowOrMutable extends DependentImmutabilityKind + case object OnlyDeepImmutable extends DependentImmutabilityKind case class State( - val field: Field, + field: Field, var typeIsImmutable: Boolean = true, var referenceIsImmutable: Option[Boolean] = None, var noEscapePossibilityViaReference: Boolean = true, @@ -115,14 +117,9 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var concreteClassTypeIsKnown: Boolean = false, var totalNumberOfFieldWrites: Int = 0 ) { + def hasDependees: Boolean = dependees.nonEmpty || tacDependees.nonEmpty - def hasDependees: Boolean = { - !dependees.isEmpty || tacDependees.nonEmpty - } - - def getDependees: Traversable[EOptionP[Entity, Property]] = { - dependees ++ tacDependees.valuesIterator.map(_._1) - } + def getDependees: Traversable[EOptionP[Entity, Property]] = dependees ++ tacDependees.valuesIterator.map(_._1) } final val typeExtensibility = project.get(TypeExtensibilityKey) @@ -136,7 +133,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case field: Field ⇒ determineFieldImmutability(field) case _ ⇒ - val m = s"${entity.getClass.getName} is not an org.opalj.br.Field" + val m = s"${entity.getClass.getName} is not an org.opalj.br.Field" throw new IllegalArgumentException(m) } } @@ -145,64 +142,52 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) field: Field ): ProperPropertyComputationResult = { - var classFormalTypeParameters: Option[Set[String]] = None - /** - * Loads the formal type parameters from the classes and outer class signature + * Returns the formal type parameters from the class' and outer class' signature */ - def loadFormalTypeParameter(): Unit = { - import org.opalj.br.ClassFile - var result: Set[String] = Set.empty + def getFormalTypeParameters(): Set[String] = { /** * - * Extract the formal type parameter if it exists of a class attribute + * Extract the formal type parameters if it exist of a class attribute */ - def collectFormalTypeParameterFromClassAttribute(attribute: Attribute): Unit = attribute match { + def collectFormalTypeParameterFromClassAttribute(attribute: Attribute): Iterator[String] = attribute match { case ClassSignature(typeParameters, _, _) ⇒ { - typeParameters.foreach( - _ match { - case FormalTypeParameter(identifier, _, _) ⇒ - result += identifier - case parameter ⇒ - } - ) + typeParameters.iterator.map { + case FormalTypeParameter(identifier, _, _) ⇒ identifier + } } - case _ ⇒ + case _ ⇒ Iterator.empty } /** * If the genericity is nested in an inner class - * collect the generic type parameters from the field' outer classes + * collect the generic type parameters from the field's outer classes */ - def collectAllFormalParametersFromOuterClasses(classFile: ClassFile): Unit = { - classFile.attributes.foreach(collectFormalTypeParameterFromClassAttribute) - if (classFile.outerType.isDefined) { - val outerClassFile = project.classFile(classFile.outerType.get._1) - if (outerClassFile.isDefined && outerClassFile.get != classFile) { - collectAllFormalParametersFromOuterClasses(outerClassFile.get) + 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 } - } - } - collectAllFormalParametersFromOuterClasses(field.classFile) - - if (result.nonEmpty) { - classFormalTypeParameters = Some(result) - /*println( - s""" - |field: ${field} - | parameters: ${classFormalTypeParameters.mkString("\n ")} - |""".stripMargin - ) */ } + getAllFormalParameters(field.classFile).toSet } + val classFormalTypeParameters: Set[String] = getFormalTypeParameters() + /** - * Returns, if a generic parameter like e.g. 'T' is in the class' or the first outer class' Signature + * 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.isDefined && classFormalTypeParameters.get.contains(string) + classFormalTypeParameters.contains(string) /** * Determines the immutability of a fields type. Adjusts the state and registers the dependencies if necessary. @@ -211,17 +196,14 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def handleTypeImmutability(state: State): Unit = { val objectType = field.fieldType.asFieldType if (objectType == ObjectType.Object) { - // println("i1") state.typeIsImmutable = false //handling generic fields } else if (objectType.isBaseType || objectType == ObjectType.String) { // base types are by design deep immutable - //state.typeImmutability = Some(true) // true is default + //state.typeImmutability = true // true is default } else if (objectType.isArrayType) { - // println("i3") // Because the entries of an array can be reassigned we state it as not being deep immutable state.typeIsImmutable = false } else { - // println("i4") propertyStore(objectType, TypeImmutability.key) match { case FinalP(DeepImmutableType) ⇒ // deep immutable type is set as default case FinalP(DependentImmutableType) ⇒ @@ -229,7 +211,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case UBP(ShallowImmutableType | MutableType) ⇒ state.typeIsImmutable = false if (field.fieldType != ObjectType.Object) - state.dependentImmutability = DependentImmutabilityKind.Dependent + state.dependentImmutability = Dependent case epk ⇒ state.dependees += epk } } @@ -242,8 +224,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) var noRelevantAttributesFound = true state.field.attributes.foreach( _ match { - case RuntimeInvisibleAnnotationTable(_) ⇒ - case SourceFile(_) ⇒ + case RuntimeInvisibleAnnotationTable(_) | SourceFile(_) ⇒ case TypeVariableSignature(t) ⇒ noRelevantAttributesFound = false onlyDeepImmutableTypesInGenericTypeFound = false @@ -259,12 +240,11 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) noShallowOrMutableTypesInGenericTypeFound = false case ProperTypeArgument(_, ClassTypeSignature(outerPackageIdentifier, SimpleClassTypeSignature(innerPackageIdentifier, _), _)) ⇒ { - val objectPath = - outerPackageIdentifier match { - case Some(prepackageIdentifier) ⇒ - prepackageIdentifier + innerPackageIdentifier - case _ ⇒ innerPackageIdentifier - } + val objectPath = outerPackageIdentifier match { + case Some(prepackageIdentifier) ⇒ + prepackageIdentifier + innerPackageIdentifier + case _ ⇒ innerPackageIdentifier + } genericParameters ::= ObjectType(objectPath) } case _ ⇒ @@ -288,8 +268,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) onlyDeepImmutableTypesInGenericTypeFound = false state.typeIsImmutable = false } - case ep ⇒ - state.dependees += ep + case ep ⇒ state.dependees += ep } }) @@ -299,13 +278,13 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (onlyDeepImmutableTypesInGenericTypeFound) { //nothing to do... } else if (noShallowOrMutableTypesInGenericTypeFound && - state.dependentImmutability != DependentImmutabilityKind.Dependent) { - state.dependentImmutability = DependentImmutabilityKind.NotShallowOrMutable + state.dependentImmutability != Dependent) { + state.dependentImmutability = NotShallowOrMutable } else - state.dependentImmutability = DependentImmutabilityKind.Dependent + state.dependentImmutability = Dependent } else - state.dependentImmutability = DependentImmutabilityKind.Dependent + state.dependentImmutability = Dependent } /** @@ -317,8 +296,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) isRead: Boolean )(implicit state: State): Option[TACode[TACMethodParameter, V]] = { propertyStore(method, TACAI.key) match { - case finalEP: FinalEP[Method, TACAI] ⇒ - finalEP.ub.tac + case finalEP: FinalEP[Method, TACAI] ⇒ finalEP.ub.tac case epk ⇒ var reads = IntTrieSet.empty var writes = IntTrieSet.empty @@ -352,8 +330,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) 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 InterimUBP(AtMost(_)) ⇒ true case _: InterimEP[DefinitionSite, EscapeProperty] ⇒ true // Escape state is worse than via return case epk ⇒ @@ -376,7 +353,6 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.totalNumberOfFieldWrites = writes.foldLeft(0)(_ + _._2.size) //println("total amaount ouf writes: "+state.totalNumberOfFieldWrites) writes.foreach(writeAccess ⇒ { - val method = writeAccess._1 val pcs = writeAccess._2 checkFieldWritesForEffImmutability(method, pcs) @@ -387,7 +363,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) reads.foreach(read ⇒ { val method = read._1 val pcs = read._2 - determineEscapePossibilityViaFieldReads(method, pcs) + determineEscapeViaFieldReads(method, pcs) }) //println("state no escape 2: "+state.noEscapePossibilityViaReference) } @@ -397,7 +373,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) /** * Determine if the referenced object can escape via field reads. */ - def determineEscapePossibilityViaFieldReads(method: Method, pcs: PCs)(implicit state: State): Unit = { + def determineEscapeViaFieldReads(method: Method, pcs: PCs)(implicit state: State): Unit = { val taCodeOption = getTACAI(method, pcs, isRead = true) if (taCodeOption.isDefined) { @@ -405,14 +381,10 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) pcs.foreach(pc ⇒ { val readIndex = taCode.pcToIndex(pc) // This if-statement is necessary, because there are -1 elements in the array - //println("readIndex: "+readIndex) if (readIndex != -1) { val stmt = taCode.stmts(readIndex) - //println("read stmt: "+stmt) - //println("index: "+taCode.pcToIndex(stmt.pc)) if (stmt.isAssignment) { val assignment = stmt.asAssignment - /*if (doesItEscapeViaMethod( propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) )) { @@ -427,27 +399,13 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (fieldsUseSiteStmt.isAssignment) { val assignment = fieldsUseSiteStmt.asAssignment if (assignment.expr.isVirtualFunctionCall) { - // import org.opalj.br.fpcf.properties.Purity val virtualFunctionCall = assignment.expr.asVirtualFunctionCall - // println("virtualfunction call: "+virtualFunctionCall) - // println("isconst: "+virtualFunctionCall.isIntConst) - //println("field Type: "+field.fieldType) if (field.fieldType.isObjectType) { //val m = virtualFunctionCall.resolveCallTargets(field.fieldType.asObjectType) if (virtualFunctionCall.params.exists(!_.isConst)) { state.noEscapePossibilityViaReference = false - - //println("for all is const: "+) - //println("...................................."+field.classFile.thisType.simpleName) - //if (field.classFile.thisType.simpleName.contains("EffectivelyImmutableFields")) { - //println("is const: "+virtualFunctionCall.isCompare) - //m.foreach(m ⇒ println()) - //pri// - // ntln("m: "+m) - //throw new Exception("") } } - // val propertyResult = propertyStore(declaredMethods(method), Purity.key) // println("propertyResult: "+propertyResult) } else if (assignment.expr.isArrayLoad) { @@ -529,20 +487,13 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) staticFunctionCall: StaticFunctionCall[V], tacCode: TACode[TACMethodParameter, V] ): Unit = { - /*println( - s""" - | static function call - | params: ${staticFunctionCall.params} - | ${staticFunctionCall.params.map(x ⇒ tacCode.stmts(x.asVar.definedBy.head).asAssignment.expr.isConst)} - |""".stripMargin - )*/ if (staticFunctionCall.params.exists(p ⇒ p.asVar.definedBy.size != 1 || p.asVar.definedBy.head < 0 || !tacCode.stmts(p.asVar.definedBy.head).asAssignment.expr.isConst)) { state.noEscapePossibilityViaReference = false return ; - } //else println("ELSE") + } } /** @@ -569,7 +520,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) nonVirtualMethodCall: NonVirtualMethodCall[V], tacCode: TACode[TACMethodParameter, V] ): Unit = { - nonVirtualMethodCall.params.foreach( + nonVirtualMethodCall.params.foreach( //TODO forall param ⇒ { param.asVar.definedBy.foreach( paramDefinedByIndex ⇒ @@ -686,7 +637,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (putValue.asVar.value.isArrayValue == Yes) { if (putValueDefinedByIndex >= 0) { //necessary - tacCode.stmts(putValueDefinedByIndex).asAssignment.targetVar.usedBy.foreach(usedByIndex ⇒ { + tacCode.stmts(putValueDefinedByIndex).asAssignment.targetVar.usedBy.foreach(usedByIndex ⇒ { //TODO forall val arrayStmt = tacCode.stmts(usedByIndex) if (arrayStmt != putStmt) if (arrayStmt.isArrayStore) { @@ -867,7 +818,8 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val tacCodeOption = getTACAI(method, pcs, isRead = false) if (tacCodeOption.isDefined) { val taCode = tacCodeOption.get - pcs.foreach(pc ⇒ { + + pcs.foreach(pc ⇒ { //TODO forall val index = taCode.pcToIndex(pc) if (index >= 0) { val stmt = taCode.stmts(index) @@ -888,16 +840,6 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) * If there are no dependencies left, this method can be called to create the result. */ def createResult(implicit state: State): ProperPropertyComputationResult = { - /* println( - s""" - | create result - | field: ${state.field} - | type is immutable: ${state.typeIsImmutable} - | dependent immutability: ${state.dependentImmutability} - | does not escape: ${state.noEscapePossibilityViaReference} - | concrete classtype is known: ${state.concreteClassTypeIsKnown} - |""".stripMargin - ) */ if (state.hasDependees) { val lowerBound = if (state.referenceIsImmutable.isDefined && state.referenceIsImmutable.get) @@ -909,8 +851,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) DeepImmutableField else state.referenceIsImmutable match { - case Some(false) | None ⇒ - MutableField + case Some(false) | None ⇒ MutableField case Some(true) ⇒ { if (state.tacDependees.isEmpty && !state.concreteClassTypeIsKnown) { if (state.typeIsImmutable) { @@ -920,17 +861,15 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) DeepImmutableField } else { state.dependentImmutability match { - case NotShallowOrMutable ⇒ - DependentImmutableField - case OnlyDeepImmutable ⇒ - DeepImmutableField - case _ ⇒ { - ShallowImmutableField - } + case NotShallowOrMutable ⇒ DependentImmutableField + case OnlyDeepImmutable ⇒ DeepImmutableField + case _ ⇒ ShallowImmutableField + } } } - } else DeepImmutableField + } else + DeepImmutableField } } @@ -947,8 +886,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (!state.escapesStillDetermined) state.noEscapePossibilityViaReference = false state.referenceIsImmutable match { - case Some(false) | None ⇒ - Result(field, MutableField) + case Some(false) | None ⇒ Result(field, MutableField) case Some(true) ⇒ { if (state.typeIsImmutable) { Result(field, DeepImmutableField) @@ -957,13 +895,9 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) Result(field, DeepImmutableField) } else { state.dependentImmutability match { - case NotShallowOrMutable ⇒ - Result(field, DependentImmutableField) - case OnlyDeepImmutable ⇒ - Result(field, DeepImmutableField) - case _ ⇒ { - Result(field, ShallowImmutableField) - } + case NotShallowOrMutable ⇒ Result(field, DependentImmutableField) + case OnlyDeepImmutable ⇒ Result(field, DeepImmutableField) + case _ ⇒ Result(field, ShallowImmutableField) } } } @@ -981,27 +915,23 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case EUBP(t, MutableType | ShallowImmutableType) ⇒ state.typeIsImmutable = false if (t != ObjectType.Object) { // in case of generic fields - state.dependentImmutability = DependentImmutabilityKind.Dependent + state.dependentImmutability = Dependent } if (t.isInstanceOf[ObjectType] && state.innerArrayTypes.contains(t.asInstanceOf[ObjectType])) state.noEscapePossibilityViaReference = false case FinalEP(t, DependentImmutableType) ⇒ { state.typeIsImmutable = false if (t != field.fieldType && state.dependentImmutability == OnlyDeepImmutable) - state.dependentImmutability = DependentImmutabilityKind.NotShallowOrMutable + state.dependentImmutability = NotShallowOrMutable if (t.isInstanceOf[ObjectType] && state.innerArrayTypes.contains(t.asInstanceOf[ObjectType])) state.noEscapePossibilityViaReference = false } - case UBP( - MutableFieldReference | LazyInitializedNotThreadSafeFieldReference - ) ⇒ { + case UBP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ { state.typeIsImmutable = false state.referenceIsImmutable = Some(false) return Result(field, MutableField); } - case FinalP( - ImmutableFieldReference | - LazyInitializedThreadSafeFieldReference | + case FinalP(ImmutableFieldReference | LazyInitializedThreadSafeFieldReference | LazyInitializedNotThreadSafeButDeterministicFieldReference ) ⇒ { state.referenceIsImmutable = Some(true) @@ -1016,7 +946,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val pcs = state.tacDependees(method)._2 if (eps.isFinal) { checkFieldWritesForEffImmutability(method, pcs._2)(state) - determineEscapePossibilityViaFieldReads(method, pcs._1)(state) + determineEscapeViaFieldReads(method, pcs._1)(state) } else { state.tacDependees += method -> ((newEP, pcs)) } @@ -1038,11 +968,12 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) implicit val state: State = State(field) propertyStore(field, FieldReferenceImmutability.key) match { case FinalP(ImmutableFieldReference) ⇒ state.referenceIsImmutable = Some(true) - case FinalP(LazyInitializedThreadSafeFieldReference | LazyInitializedNotThreadSafeButDeterministicFieldReference) ⇒ + case FinalP(LazyInitializedThreadSafeFieldReference | + LazyInitializedNotThreadSafeButDeterministicFieldReference) ⇒ state.referenceIsImmutable = Some(true) case FinalP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ return Result(field, MutableField); - case ep @ _ ⇒ { + case ep ⇒ { state.dependees += ep } } @@ -1050,7 +981,6 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) /** * Determines whether the field is dependent immutable */ - loadFormalTypeParameter() hasGenericType() // println("B") /** 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 index e10b86f465..cb47209531 100644 --- 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 @@ -163,7 +163,7 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { } } - def handleCalls( + def doCallsIntroduceNonDeterminism( calleesEOP: EOptionP[DeclaredMethod, Callees], pc: PC )( @@ -171,8 +171,8 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { state: State ): Boolean = { calleesEOP match { - case FinalP(callees) ⇒ handleCallees(callees, pc) - case InterimUBP(callees) ⇒ handleCallees(callees.asInstanceOf[Callees], pc) + case FinalP(callees) ⇒ isACalleeNotDeterministic(callees, pc) + case InterimUBP(callees) ⇒ isACalleeNotDeterministic(callees.asInstanceOf[Callees], pc) case _ ⇒ state.calleesDependee += calleesEOP.e → ((calleesEOP, pc :: state.calleesDependee(calleesEOP.e)._2)) @@ -180,7 +180,7 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { } } - def handleCallees(callees: Callees, pc: PC)(implicit state: State): Boolean = { + def isACalleeNotDeterministic(callees: Callees, pc: PC)(implicit state: State): Boolean = { if (callees.isIncompleteCallSite(pc)) { state.referenceImmutability = MutableFieldReference true 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 index b967a76080..26ea7957c5 100644 --- 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 @@ -150,7 +150,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr // println("C") // println (s"field: ${state.field}") if (method.returnType == state.field.fieldType && - !isTheFieldsValueReturned(write, writeIndex, readIndex, cfg, tacCode)) { + !isTheFieldValueReturned(write, writeIndex, readIndex, cfg, tacCode)) { return MutableFieldReference; } // println("D") @@ -237,18 +237,16 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr * 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) - * The third element of the triple returns: * @author Tobias Roth */ def findCaughtsThrowsAndResults( tacCode: TACode[TACMethodParameter, V], cfg: CFG[Stmt[V], TACStmts[V]] - ): (List[(Int, ObjectType, IntTrieSet, CFGNode)], List[(Int, IntTrieSet, CFGNode)]) = { //}, List[CFGNode]) = { + ): (List[(Int, ObjectType, IntTrieSet, CFGNode)], List[(Int, IntTrieSet, CFGNode)]) = { var caughtExceptions: List[(Int, ObjectType, IntTrieSet, CFGNode)] = List.empty var throwStatements: List[(Int, IntTrieSet, CFGNode)] = List.empty - //var returnNodes: List[CFGNode] = List.empty for (stmt ← tacCode.stmts) { - if (!stmt.isNop) { //pc < ... + if (!stmt.isNop) { // to prevent the handling of partially negative pcs of nops val currentBB = cfg.bb(tacCode.pcToIndex(stmt.pc)) (stmt.astID: @switch) match { case CaughtException.ASTID ⇒ @@ -256,32 +254,32 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val exceptionType = if (caughtException.exceptionType.isDefined) { val intermediateExceptionType = caughtException.exceptionType.get - //if (intermediateExceptionType.isObjectType) intermediateExceptionType.asObjectType - //else - //ObjectType.Throwable // ObjectType.Exception } else - ObjectType.Throwable // ObjectType.Exception + ObjectType.Throwable caughtExceptions = - ((caughtException.pc, exceptionType, caughtException.origins, currentBB)) :: caughtExceptions + (caughtException.pc, exceptionType, caughtException.origins, currentBB) :: caughtExceptions case Throw.ASTID ⇒ val throwStatement = stmt.asThrow - val throwStatementDefinedBys = - // if (throwStatement.exception.isVar) { - throwStatement.exception.asVar.definedBy - // } else - // IntTrieSet.empty - throwStatements = ((throwStatement.pc, throwStatementDefinedBys, currentBB)) :: throwStatements - //case ReturnValue.ASTID ⇒ - // returnNodes = (currentBB) :: returnNodes + val throwStatementDefinedBys = throwStatement.exception.asVar.definedBy + throwStatements = (throwStatement.pc, throwStatementDefinedBys, currentBB) :: throwStatements case _ ⇒ } - //} } } - (caughtExceptions, throwStatements) //, returnNodes) + (caughtExceptions, throwStatements) } + /** + * + * @param fieldWrite + * @param defaultValue + * @param code + * @param cfg + * @param tacCode + * @param state + * @return + */ def findMonitors( fieldWrite: Int, defaultValue: Any, @@ -297,6 +295,12 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr var monitorExitQueuedBBs: Set[CFGNode] = startBB.successors var worklistMonitorExit = getSuccessors(startBB, Set.empty) + /** + * + * @param v + * @param state + * @return + */ def checkMonitor(v: V)(implicit state: State): Boolean = { v.definedBy .forall(definedByIndex ⇒ { @@ -422,8 +426,6 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr if (!seen.contains(curBB)) { seen += curBB - // println("curBB: "+curBB) - //val startPC = curBB.startPC val endPC = curBB.endPC @@ -434,16 +436,6 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr // println("ifstmt: ") // println(cfStmt) val ifStmt = cfStmt.asIf - //ifStmt.condition match { - // println("AA") - /* println(s"isguard: ${ - isGuard( - ifStmt, - defaultValue, - code, - tacCode - ) - }") */ if (ifStmt.condition.equals(EQ) && curBB != startBB && isGuard( ifStmt, defaultValue, @@ -521,25 +513,16 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr else ifStmt.leftExpr val definitions = expr.asVar.definedBy - if (definitions.exists(_ < 0)) //.head < 0) - return finalResult; - println( - s"""definitions: - |field read - | ${code(definitions.head).asAssignment.expr} - | - | - |""".stripMargin - ) - fieldReadIndex = definitions.head - val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy //TODO ... - val fieldReadUsedCorrectly = - fieldReadUses.forall(use ⇒ use == result._1 || use == result._2) - - if (definitions.size == 1 && definitions.head >= 0 && fieldReadUsedCorrectly) - finalResult = (result._1, result._2, definitions.head, result._3, result._4, fieldReadIndex) :: finalResult // Found proper guard - else finalResult - }) // else None) + if (!definitions.exists(_ < 0)) { + //return finalResult; + fieldReadIndex = definitions.head + val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy + val fieldReadUsedCorrectly = + fieldReadUses.forall(use ⇒ use == result._1 || use == result._2) + if (definitions.size == 1 && definitions.head >= 0 && fieldReadUsedCorrectly) + finalResult = (result._1, result._2, definitions.head, result._3, result._4, fieldReadIndex) :: finalResult // Found proper guard + } + }) finalResult } @@ -559,27 +542,16 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr } def isTransitivePredecessor(possiblePredecessor: CFGNode, node: CFGNode): Boolean = { + var visited: Set[CFGNode] = Set.empty - def isTransitivePredecessorInternal(possiblePredecessor: CFGNode, node: CFGNode): Boolean = { - /*println( - s""" - |is transitive predecessor: - |possiblePredecessor: $possiblePredecessor - | node: $node - | visited: ${visited.mkString("\n")} - |""".stripMargin - ) */ + def isTransitivePredecessorInternal(possiblePredecessor: CFGNode, node: CFGNode): Boolean = { if (possiblePredecessor == node) { true } else if (visited.contains(node)) false else { visited += node - //val predecessors = node.predecessors //getPredecessors(node, Set.empty) - //val tmpVisited2 = tmpVisited ++ predecessors - //println("predecessors: "+predecessors.mkString(", \n")) - //println("tmpVisited2: "+tmpVisited2) node.predecessors.exists( currentNode ⇒ { isTransitivePredecessorInternal(possiblePredecessor, currentNode) @@ -587,7 +559,6 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr ) } } - println("--------------------------------------------------------------------------------------------------------") isTransitivePredecessorInternal(possiblePredecessor, node) } @@ -669,7 +640,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr true //TODO look at it case Var.ASTID ⇒ { val varValue = value.asVar - varValue.definedBy.size == 1 && //stronger requirement -> only one definition side + varValue.definedBy.size == 1 && //no different values due to different control flows varValue.definedBy.forall(i ⇒ i >= 0 && isNonConstDeterministic(code(i).asAssignment.expr)) } case New.ASTID ⇒ { @@ -678,8 +649,10 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr //TODO check val nonVirtualMethodCallIndex = origin.asAssignment.targetVar.usedBy.iterator.filter(i ⇒ code(i).isNonVirtualMethodCall).toList.head - val resultCallees = propertyStore(declaredMethods(method), Callees.key) - !handleCalls(resultCallees, nonVirtualMethodCallIndex) && + + //code(nonVirtualMethodCallIndex).asNonVirtualMethodCall. + val propertyStoreResultCallees = propertyStore(declaredMethods(method), Callees.key) + !doCallsIntroduceNonDeterminism(propertyStoreResultCallees, nonVirtualMethodCallIndex) && code(nonVirtualMethodCallIndex).asNonVirtualMethodCall.params.forall(isConstant) } case _ ⇒ @@ -701,10 +674,8 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr def isReadOfCurrentField(expr: Expr[V], tacCode: TACode[TACMethodParameter, V])(implicit state: State): Boolean = { var seenExpressions: Set[Expr[V]] = Set.empty def isReadOfCurrentFieldInternal(expr: Expr[V], tacCode: TACode[TACMethodParameter, V])(implicit state: State): Boolean = { - - if (seenExpressions.contains(expr)) { - return false - }; + if (seenExpressions.contains(expr)) + return false; seenExpressions += expr def isExprReadOfCurrentField(pc: Int): Int ⇒ Boolean = index ⇒ index == tacCode.pcToIndex(pc) || @@ -712,22 +683,11 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr isReadOfCurrentFieldInternal(tacCode.stmts(index).asAssignment.expr, tacCode) (expr.astID: @switch) match { case GetField.ASTID ⇒ - - //f (state.field.classFile.thisType.simpleName.contains("DoubleCheckedLocking")) { - /*println( - s""" - | field: ${state.field} - | expr: $expr - | objectDefinition: ${expr.asGetField.objRef.asVar.definedBy} - | SelfReferenceParameter: ${SelfReferenceParameter} - | resolve: ${if (expr.asGetField.objRef.asVar.definedBy == SelfReferenceParameter) expr.asGetField.resolveField(project)} - | field: ${state.field} - |""".stripMargin - ) */ - //} val objRefDefinition = expr.asGetField.objRef.asVar.definedBy - if (objRefDefinition != SelfReferenceParameter) false - else expr.asGetField.resolveField(project).contains(state.field) + if (objRefDefinition != SelfReferenceParameter) + false + else + expr.asGetField.resolveField(project).contains(state.field) case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project).contains(state.field) case Compare.ASTID ⇒ { val leftExpr = expr.asCompare.left @@ -744,27 +704,19 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr case VirtualFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | StaticFunctionCall.ASTID ⇒ { - // val (receiverDefSites, val (params, pc) = if (expr.isVirtualFunctionCall) { val virtualFunctionCall = expr.asVirtualFunctionCall - //(virtualFunctionCall.receiver.asVar.definedBy, (virtualFunctionCall.params, virtualFunctionCall.pc) } else if (expr.isStaticFunctionCall) { val staticFunctionCall = expr.asStaticFunctionCall - //val receiverDefSites = staticFunctionCall.receiverOption - //if (receiverDefSites.isEmpty) - // return false; - //(receiverDefSites.get.asVar.definedBy, (staticFunctionCall.params, staticFunctionCall.pc) } else { val nonVirtualFunctionCall = expr.asNonVirtualFunctionCall - //(nonVirtualFunctionCall.receiver.asVar.definedBy, (nonVirtualFunctionCall.params, nonVirtualFunctionCall.pc) } params.forall(expr ⇒ expr.asVar.definedBy.forall(isExprReadOfCurrentField(pc))) - //isReadOfCurrentFieldInternal(expr, tacCode)) } case _ ⇒ false } @@ -839,7 +791,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val calleesResult = propertyStore(declaredMethods(method), Callees.key) - if (handleCalls(calleesResult, code(index).asAssignment.pc)) + if (doCallsIntroduceNonDeterminism(calleesResult, code(index).asAssignment.pc)) return false; if (isVirtualFunctionCall) { @@ -916,11 +868,9 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr } /** - * * Checks that the value of the field is returned. - * */ - def isTheFieldsValueReturned( + def isTheFieldValueReturned( write: FieldWriteAccessStmt[V], writeIndex: Int, readIndex: Int, @@ -928,7 +878,6 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr tacCode: TACode[TACMethodParameter, V] )(implicit state: State): Boolean = { val startBB = cfg.bb(0).asBasicBlock - //var curBB = startBB var queuedNodes: Set[CFGNode] = Set.empty var workList = getSuccessors(startBB, queuedNodes) workList ++= Set(startBB) 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 index abd36e3444..c6c7e6f987 100644 --- 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 @@ -6,6 +6,8 @@ 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 @@ -45,7 +47,6 @@ import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS import org.opalj.tac.common.DefinitionSite import org.opalj.tac.fpcf.properties.TACAI -import scala.annotation.switch import org.opalj.br.ReferenceType import org.opalj.br.ByteType import org.opalj.br.CharType @@ -58,8 +59,8 @@ import org.opalj.br.ShortType /** * - * Determines the immutability of the reference of a classes field. - * A field reference can either refer to an reference object or store a value. + * 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 ... { @@ -70,7 +71,7 @@ import org.opalj.br.ShortType * } * * 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 integer-value. + * 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. * @@ -106,14 +107,14 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP private[analyses] def determineFieldReferenceImmutability( field: Field ): ProperPropertyComputationResult = { - println( + /* println( s""" | determine field reference immutability: | field ${field} | | |""".stripMargin - ) + )*/ if (field.isFinal) return Result(field, ImmutableFieldReference); @@ -166,7 +167,7 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP } if (state.lazyInitInvocation.isDefined) { val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) - handleCalls(calleesEOP, state.lazyInitInvocation.get._2) + doCallsIntroduceNonDeterminism(calleesEOP, state.lazyInitInvocation.get._2) } createResult() } @@ -241,7 +242,7 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP //state.lazyInitInvocation val newEPS = eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]] val pcs = state.calleesDependee(newEPS.e)._2 - isNotFinal = pcs.forall(pc ⇒ handleCalls(newEPS, pc)) + isNotFinal = pcs.forall(pc ⇒ doCallsIntroduceNonDeterminism(newEPS, pc)) //state.calleesDependee+= (calleesEOP.e → (calleesEOP,pc :: state.calleesDependee(calleesEOP.e)._2)) // isNotFinal = handleCalls() //else { @@ -443,7 +444,7 @@ object EagerL0FieldReferenceImmutabilityAnalysis extends L0FieldReferenceImmutab 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 + val fields = p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldReferenceImmutability) analysis } From d6f1a4523ee6f76dfd9fd23b2db91f03248b2a42 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 16 Oct 2020 16:20:14 +0200 Subject: [PATCH 283/327] revised, still wip --- .../L3FieldImmutabilityAnalysis.scala | 619 ++++++++---------- ...mutabilityAnalysisLazyInitialization.scala | 359 +++++----- 2 files changed, 476 insertions(+), 502 deletions(-) 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 index 03b78632bb..6c94715e18 100755 --- 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 @@ -128,7 +128,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e final val definitionSites = project.get(DefinitionSitesKey) implicit final val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) - def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = { + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = entity match { case field: Field ⇒ determineFieldImmutability(field) @@ -136,7 +136,6 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e val m = s"${entity.getClass.getName} is not an org.opalj.br.Field" throw new IllegalArgumentException(m) } - } private[analyses] def determineFieldImmutability( field: Field @@ -145,18 +144,17 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e /** * Returns the formal type parameters from the class' and outer class' signature */ - def getFormalTypeParameters(): Set[String] = { + def getFormalTypeParameters: Set[String] = { /** * * Extract the formal type parameters if it exist of a class attribute */ def collectFormalTypeParameterFromClassAttribute(attribute: Attribute): Iterator[String] = attribute match { - case ClassSignature(typeParameters, _, _) ⇒ { - typeParameters.iterator.map { - case FormalTypeParameter(identifier, _, _) ⇒ identifier - } - } + + case ClassSignature(typeParameters, _, _) ⇒ + typeParameters.iterator.map { case FormalTypeParameter(identifier, _, _) ⇒ identifier } + case _ ⇒ Iterator.empty } @@ -180,14 +178,13 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e getAllFormalParameters(field.classFile).toSet } - val classFormalTypeParameters: Set[String] = getFormalTypeParameters() + 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) + def isInClassesGenericTypeParameters(string: String): Boolean = classFormalTypeParameters.contains(string) /** * Determines the immutability of a fields type. Adjusts the state and registers the dependencies if necessary. @@ -217,57 +214,61 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } } - def hasGenericType()(implicit state: State): Unit = { + def handleGenericity()(implicit state: State): Unit = { var noShallowOrMutableTypesInGenericTypeFound = true var onlyDeepImmutableTypesInGenericTypeFound = true var genericParameters: List[ObjectType] = List() var noRelevantAttributesFound = true - state.field.attributes.foreach( - _ match { - case RuntimeInvisibleAnnotationTable(_) | SourceFile(_) ⇒ - case TypeVariableSignature(t) ⇒ - noRelevantAttributesFound = false - onlyDeepImmutableTypesInGenericTypeFound = false - if (!isInClassesGenericTypeParameters(t)) - noShallowOrMutableTypesInGenericTypeFound = false - case ClassTypeSignature(_, SimpleClassTypeSignature(_, typeArguments), _) ⇒ - noRelevantAttributesFound = false - typeArguments.foreach({ - _ match { - 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 _ ⇒ + state.field.attributes.foreach { + + case RuntimeInvisibleAnnotationTable(_) | SourceFile(_) ⇒ + + case TypeVariableSignature(t) ⇒ + noRelevantAttributesFound = false + onlyDeepImmutableTypesInGenericTypeFound = false + if (!isInClassesGenericTypeParameters(t)) noShallowOrMutableTypesInGenericTypeFound = false - onlyDeepImmutableTypesInGenericTypeFound = 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 + case FinalP(DependentImmutableType) ⇒ onlyDeepImmutableTypesInGenericTypeFound = false state.typeIsImmutable = false - case UBP(ShallowImmutableType | MutableType) ⇒ { + + case UBP(ShallowImmutableType | MutableType) ⇒ noShallowOrMutableTypesInGenericTypeFound = false onlyDeepImmutableTypesInGenericTypeFound = false state.typeIsImmutable = false - } + case ep ⇒ state.dependees += ep } }) @@ -296,7 +297,9 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e isRead: Boolean )(implicit state: State): Option[TACode[TACMethodParameter, V]] = { propertyStore(method, TACAI.key) match { + case finalEP: FinalEP[Method, TACAI] ⇒ finalEP.ub.tac + case epk ⇒ var reads = IntTrieSet.empty var writes = IntTrieSet.empty @@ -324,15 +327,17 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e )(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 _: InterimEP[DefinitionSite, EscapeProperty] ⇒ - true // Escape state is worse than via return + + 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 _: InterimEP[DefinitionSite, EscapeProperty] ⇒ true // Escape state is worse than via return + case epk ⇒ state.dependees += epk false @@ -351,21 +356,17 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e //has to be determined before the following foreach loop because the information is needed state.totalNumberOfFieldWrites = writes.foldLeft(0)(_ + _._2.size) - //println("total amaount ouf writes: "+state.totalNumberOfFieldWrites) writes.foreach(writeAccess ⇒ { - val method = writeAccess._1 - val pcs = writeAccess._2 + val (method, pcs) = writeAccess checkFieldWritesForEffImmutability(method, pcs) }) - //println("state no escape 1: "+state.noEscapePossibilityViaReference) if (state.noEscapePossibilityViaReference) { val reads = fieldAccessInformation.readAccesses(state.field) reads.foreach(read ⇒ { - val method = read._1 - val pcs = read._2 + val (method, pcs) = read determineEscapeViaFieldReads(method, pcs) }) - //println("state no escape 2: "+state.noEscapePossibilityViaReference) + } } } @@ -376,9 +377,10 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e def determineEscapeViaFieldReads(method: Method, pcs: PCs)(implicit state: State): Unit = { val taCodeOption = getTACAI(method, pcs, isRead = true) + // if the taCode is not defined this function is again called in the continuation function if (taCodeOption.isDefined) { val taCode = taCodeOption.get - pcs.foreach(pc ⇒ { + 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) { @@ -391,84 +393,61 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e state.noEscapePossibilityViaReference = false println("false1") return ; - } else */ for { - useSite ← assignment.targetVar.usedBy - } { + } else */ + + assignment.targetVar.usedBy.exists(useSite ⇒ { + val fieldsUseSiteStmt = taCode.stmts(useSite) - //println("fieldUseSiteStmt: "+fieldsUseSiteStmt) + if (fieldsUseSiteStmt.isAssignment) { + val assignment = fieldsUseSiteStmt.asAssignment - if (assignment.expr.isVirtualFunctionCall) { - val virtualFunctionCall = assignment.expr.asVirtualFunctionCall - if (field.fieldType.isObjectType) { - //val m = virtualFunctionCall.resolveCallTargets(field.fieldType.asObjectType) - if (virtualFunctionCall.params.exists(!_.isConst)) { - state.noEscapePossibilityViaReference = false - } - } - // val propertyResult = propertyStore(declaredMethods(method), Purity.key) - // println("propertyResult: "+propertyResult) - } else if (assignment.expr.isArrayLoad) { + if (assignment.expr.isVirtualFunctionCall || + assignment.expr.isStaticFunctionCall) { + val functionCall = assignment.expr.asFunctionCall + field.fieldType.isObjectType && functionCall.params.exists(!_.isConst) + + } 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) { - // nothing to do, because it can not be mutated + false // nothing to do, because it can not be mutated } else if (innerArrayType.isArrayType) { - state.noEscapePossibilityViaReference = false // to be sound - // println("false2") - return ; + true // to be sound } else if (innerArrayType.isObjectType) { //If a deep immutable object escapes, it can not be mutated propertyStore(innerArrayType, TypeImmutability.key) match { - case FinalP(DeepImmutableType) ⇒ //nothing to do - case UBP(DependentImmutableType | - ShallowImmutableType | + + case FinalP(DeepImmutableType) ⇒ false //nothing to do + + case UBP(DependentImmutableType | ShallowImmutableType | MutableType) ⇒ - state.noEscapePossibilityViaReference = false - return ; - case ep ⇒ { + true + + case ep ⇒ state.innerArrayTypes += innerArrayType.asObjectType state.dependees += ep - } + false } - } else { - state.noEscapePossibilityViaReference = false - return ; - } - case _ ⇒ { - state.noEscapePossibilityViaReference = false - return ; - } + } else true + case _ ⇒ true } - } else { - state.noEscapePossibilityViaReference = false - return ; - } - } else if (fieldsUseSiteStmt.isMonitorEnter || - fieldsUseSiteStmt.isMonitorExit || - fieldsUseSiteStmt.isIf) { - //nothing to do - } else { - state.noEscapePossibilityViaReference = false - // println("false7") - return ; - } - } - } else if (stmt.isExprStmt) { - //nothing to do here, because the value is only read but not assigned to another one - } else { - state.noEscapePossibilityViaReference = false - // println("false8") - return ; - } - } else { - //nothing to do - // -1 means NOTHING as a placeholder - } - }) + } else true + } else + !fieldsUseSiteStmt.isMonitorEnter && + !fieldsUseSiteStmt.isMonitorExit && + !fieldsUseSiteStmt.isIf + }) + } else + //nothing to do in case of Expr; The value is only read but not assigned to another one + !stmt.isExprStmt + } else false //nothing to do; -1 means NOTHING as a placeholder + }) { + state.noEscapePossibilityViaReference = false + } } } @@ -476,6 +455,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e * Determine if the referenced object can escape via field writes */ def checkFieldWritesForEffImmutability(method: Method, pcs: PCs)(implicit state: State): Unit = { + //Needed because of cyclic calls of the functions - to prevent infinite cycles var seen: Set[Stmt[V]] = Set.empty @@ -483,50 +463,47 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e * Checks if the parameters of a static function call are no parameters from an outer * function and are constants */ - def handleStaticFunctionCall( + def doesStaticFunctionCallEnableEscape( staticFunctionCall: StaticFunctionCall[V], tacCode: TACode[TACMethodParameter, V] - ): Unit = { - if (staticFunctionCall.params.exists(p ⇒ - p.asVar.definedBy.size != 1 || - p.asVar.definedBy.head < 0 || - !tacCode.stmts(p.asVar.definedBy.head).asAssignment.expr.isConst)) { - state.noEscapePossibilityViaReference = false - return ; - } - } + ): Boolean = staticFunctionCall.params.exists(_.asVar.definedBy.exists( + definedByIndex ⇒ definedByIndex < 0 || + !tacCode.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)(implicit state: State): Unit = { - val result = propertyStore(objectType, ClassImmutability.key) - //println(s"handle type imm result: $result") - result match { - case UBP(MutableClass | - ShallowImmutableClass | - DependentImmutableClass) ⇒ state.typeIsImmutable = false - case FinalP(DeepImmutableClass) ⇒ state.typeIsImmutable = true - case eps ⇒ state.dependees += eps + + propertyStore(objectType, ClassImmutability.key) match { + + case UBP(MutableClass | ShallowImmutableClass | DependentImmutableClass) ⇒ + state.typeIsImmutable = false + + case FinalP(DeepImmutableClass) ⇒ + state.typeIsImmutable = true + + case eps ⇒ + state.dependees += eps } } /** * Checks if the referenced object or elements from it can escape via the nonvirtualmethod-call */ - def handleNonVirtualMethodCall( + def doesNonVirtualMethodCallEnablesEscape( method: Method, nonVirtualMethodCall: NonVirtualMethodCall[V], tacCode: TACode[TACMethodParameter, V] - ): Unit = { - nonVirtualMethodCall.params.foreach( //TODO forall - param ⇒ { - param.asVar.definedBy.foreach( + )(implicit state: State): Boolean = + nonVirtualMethodCall.params.exists { + param ⇒ + param.asVar.definedBy.exists { paramDefinedByIndex ⇒ if (paramDefinedByIndex < 0) { - state.noEscapePossibilityViaReference = false - return ; + true } else { val paramDefinitionStmt = tacCode.stmts(paramDefinedByIndex) if (paramDefinitionStmt.isAssignment) { @@ -539,14 +516,17 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e else if (assignmentExpression.isGetStatic) assignedField = assignmentExpression.asGetStatic.resolveField - if (assignedField.isDefined && assignedField.get != state.field) + if (assignedField.isDefined && assignedField.get != state.field) { propertyStore(assignedField.get, FieldImmutability.key) match { - case FinalP(DeepImmutableField) ⇒ //nothing to do here + case FinalP(DeepImmutableField) ⇒ false //nothing to do here case FinalP(_) ⇒ - state.noEscapePossibilityViaReference = false - return ; - case ep ⇒ state.dependees += ep + true + + case ep ⇒ + state.dependees += ep + false } + } else false } else if (assignmentExpression.isVirtualFunctionCall) { val virtualFunctionCall = assignmentExpression.asVirtualFunctionCall virtualFunctionCall.params.exists( @@ -554,92 +534,77 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst ) } else if (assignmentExpression.isStaticFunctionCall) { - handleStaticFunctionCall(assignmentExpression.asStaticFunctionCall, tacCode) - return ; + doesStaticFunctionCallEnableEscape(assignmentExpression.asStaticFunctionCall, tacCode) } 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) } - for (usedSiteIndex ← paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy) { + + paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy.exists { usedSiteIndex ⇒ val stmt = tacCode.stmts(usedSiteIndex) if (stmt.isNonVirtualMethodCall) { if (!seen.contains(stmt)) { seen += stmt - handleNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) - } - } else { - state.noEscapePossibilityViaReference = false - return ; - } + doesNonVirtualMethodCallEnablesEscape(method, stmt.asNonVirtualMethodCall, tacCode) + } else false + } else true } - } else if (assignmentExpression.isConst) { - //nothing to do - } else { - state.noEscapePossibilityViaReference = false - return ; - } + + } else + assignmentExpression.isConst } else { val definitionSitesOfParam = definitionSites(method, paramDefinedByIndex) val stmt = tacCode.stmts(tacCode.pcToIndex(definitionSitesOfParam.pc)) if (stmt.isNonVirtualMethodCall) { if (!seen.contains(stmt)) { seen += stmt - handleNonVirtualMethodCall(method, stmt.asNonVirtualMethodCall, tacCode) - } + doesNonVirtualMethodCallEnablesEscape(method, stmt.asNonVirtualMethodCall, tacCode) + } else false } else if (stmt.isPutField || stmt.isPutStatic) { if (!seen.contains(stmt)) { seen += stmt - handlePut(stmt, method, tacCode) - } - return ; + doesPutEnableEscape(stmt, method, tacCode) + } else false } else if (stmt.isArrayStore) { - state.noEscapePossibilityViaReference = false //TODO handling that case more precise - return ; + true //TODO handling that case more precise } else { // other cases that the purity analysis can not handle - if (doesItEscapeViaMethod( - propertyStore(definitionSitesOfParam, EscapeProperty.key) - )) { - state.noEscapePossibilityViaReference = false - return ; - } + doesItEscapeViaMethod(propertyStore(definitionSitesOfParam, EscapeProperty.key)) + //false } } //TODO go further } - ) - } - ) - } + } + } /** * Checks if a reference object can escape via a given putfield or putstatic */ - def handlePut(putStmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Unit = { + def doesPutEnableEscape(putStmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Boolean = { val (putDefinitionSites, putValue) = { if (putStmt.isPutField) { val putField = putStmt.asPutField - (putField.value.asVar.definedBy, putField.value) + (putField.value.asVar.definedBy, putField.value.asVar) } else if (putStmt.isPutStatic) { val putStatic = putStmt.asPutStatic - (putStatic.value.asVar.definedBy, putStatic.value) + (putStatic.value.asVar.definedBy, putStatic.value.asVar) } else { - state.noEscapePossibilityViaReference = false - return ; + //state.noEscapePossibilityViaReference = false + return true; } } - val putValueDefinedByIndex = putValue.asVar.definedBy.head - if (putValue.asVar.value.isArrayValue == Yes) { + val putValueDefinedByIndex = putValue.definedBy.head + if (putValue.value.isArrayValue.isYes) { if (putValueDefinedByIndex >= 0) { //necessary - tacCode.stmts(putValueDefinedByIndex).asAssignment.targetVar.usedBy.foreach(usedByIndex ⇒ { //TODO forall + tacCode.stmts(putValueDefinedByIndex).asAssignment.targetVar.usedBy.exists { usedByIndex ⇒ val arrayStmt = tacCode.stmts(usedByIndex) - if (arrayStmt != putStmt) + if (arrayStmt != putStmt) { if (arrayStmt.isArrayStore) { val arrayStore = arrayStmt.asArrayStore val arrayStoreIndex = arrayStore.index @@ -650,111 +615,88 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e val valueAssignment = tacCode.stmts(assignedValue.asVar.definedBy.head).asAssignment val assignedExpr = valueAssignment.expr val useSites = valueAssignment.targetVar.usedBy.map(tacCode.stmts(_)) - for (useSite ← useSites) { + useSites.exists { useSite ⇒ if (useSite.isNonVirtualMethodCall) { val nonVirtualMethodCall = useSite.asNonVirtualMethodCall - nonVirtualMethodCall.params.foreach(param ⇒ { - if (!param.isConst && - param.asVar.definedBy.head > -1 && - !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst) { - state.noEscapePossibilityViaReference = false - return ; - } - }) + nonVirtualMethodCall.params.exists(param ⇒ + !param.isConst && + param.asVar.definedBy.head > -1 && //TODO look at more than the head + !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst) } else if (useSite == arrayStore) { - //nothing to do + false } else if (useSite.isReturnValue) { //assigned array-element escapes - state.noEscapePossibilityViaReference = false - return ; + true } else { - state.noEscapePossibilityViaReference = false - return ; + true //to be sound } } + if (!isArrayIndexConst) { - state.noEscapePossibilityViaReference = false - return ; + true } else if (assignedExpr.isStaticFunctionCall) { - handleStaticFunctionCall(assignedExpr.asStaticFunctionCall, tacCode) + doesStaticFunctionCallEnableEscape(assignedExpr.asStaticFunctionCall, tacCode) } 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.foreach(index ⇒ { - val tmpStmt = tacCode.stmts(index) - if (tmpStmt.isArrayStore) { - // can be ingored - } else if (tmpStmt.isNonVirtualMethodCall) { - val nonVirtualMethodcall = tmpStmt.asNonVirtualMethodCall - if (!seen.contains(tmpStmt)) { - seen += tmpStmt - handleNonVirtualMethodCall(method, nonVirtualMethodcall, tacCode) - } //nothing to do in the else case. Stmt has still been handled - } else { - state.noEscapePossibilityViaReference = false - return ; - } - }) - } else { - state.noEscapePossibilityViaReference = false - return ; - } - } else { - state.noEscapePossibilityViaReference = false - return ; - } - } else { - state.noEscapePossibilityViaReference = false - return ; - } - }) - } else { - state.noEscapePossibilityViaReference = false - } - } else - for { - i ← putDefinitionSites - } { - if (i >= 0) { //necessary - val definitionSiteStatement = tacCode.stmts(i) + + valueAssignment.targetVar.asVar.usedBy.exists { + index ⇒ + val tmpStmt = tacCode.stmts(index) + if (tmpStmt.isArrayStore) { + false // can be ingored + } else if (tmpStmt.isNonVirtualMethodCall) { + val nonVirtualMethodcall = tmpStmt.asNonVirtualMethodCall + if (!seen.contains(tmpStmt)) { + seen += tmpStmt + doesNonVirtualMethodCallEnablesEscape(method, nonVirtualMethodcall, tacCode) + } + false //nothing to do in the else case. Stmt has still been handled + } else true + } + } else true + } else true + } else true + } else false + } + } else true + } else { + + putDefinitionSites.exists { putDefinitionSite ⇒ + if (putDefinitionSite >= 0) { //necessary + val definitionSiteStatement = tacCode.stmts(putDefinitionSite) //println("def site stmt: "+definitionSiteStatement) val definitionSiteAssignment = definitionSiteStatement.asAssignment //println("def site assignement: "+definitionSiteAssignment) if (definitionSiteAssignment.expr.isStaticFunctionCall) { // println("handle static function call") - handleStaticFunctionCall(definitionSiteAssignment.expr.asStaticFunctionCall, tacCode) - return ; + doesStaticFunctionCallEnableEscape(definitionSiteAssignment.expr.asStaticFunctionCall, tacCode) + } else if (definitionSiteAssignment.expr.isVar) { // println("def site is var") val definitionSiteVar = definitionSiteAssignment.expr.asVar - for (definitionSiteVarUseSite ← definitionSiteVar.usedBy) { - val definitionSiteVarUseSiteStmt = tacCode.stmts(definitionSiteVarUseSite) - if (definitionSiteVarUseSiteStmt.isNonVirtualMethodCall) { - val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall - handleNonVirtualMethodCall(method, nonVirtualMethodCall, tacCode) - } else { - state.noEscapePossibilityViaReference = false - return ; - } - //TODO andere Fälle bedenken + definitionSiteVar.usedBy.exists { + definitionSiteVarUseSite ⇒ + val definitionSiteVarUseSiteStmt = tacCode.stmts(definitionSiteVarUseSite) + if (definitionSiteVarUseSiteStmt.isNonVirtualMethodCall) { + val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall + doesNonVirtualMethodCallEnablesEscape(method, nonVirtualMethodCall, tacCode) + } else true + //TODO handle all cases } + + /*for (definitionSiteVarUseSite ← definitionSiteVar.usedBy) { + + } */ } else if (definitionSiteAssignment.expr.isNew) { - //println("is new") val newStmt = definitionSiteAssignment.expr.asNew - /*println( - s""" - | newStmt: $newStmt - | newStmt.tpe.mostPreciseObjectType: ${newStmt.tpe.mostPreciseObjectType} - | fhandleKnownClassTypeieldType: ${field.fieldType.asObjectType} - | state.totalNumberOfFieldWrites - |""".stripMargin - ) */ if (field.fieldType.isObjectType && newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && state.totalNumberOfFieldWrites == 1) { @@ -762,77 +704,62 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e handleKnownClassType(newStmt.tpe.mostPreciseObjectType) } if (!method.isConstructor) { - definitionSiteAssignment.targetVar.asVar.usedBy.foreach(x ⇒ { - val tmpStmt = tacCode.stmts(x) - if (tmpStmt.isPutStatic || tmpStmt.isPutField) { - if (!seen.contains(tmpStmt)) { - seen += tmpStmt - handlePut(tmpStmt, method, tacCode) - } - } else if (tmpStmt.isNonVirtualMethodCall) { - if (!seen.contains(tmpStmt)) { - seen += tmpStmt - handleNonVirtualMethodCall(method, tmpStmt.asNonVirtualMethodCall, tacCode) - } - } else { - state.noEscapePossibilityViaReference = false - return ; - } - }) + definitionSiteAssignment.targetVar.asVar.usedBy.exists { + x ⇒ + val tmpStmt = tacCode.stmts(x) + if (tmpStmt.isPutStatic || tmpStmt.isPutField) { + if (!seen.contains(tmpStmt)) { + seen += tmpStmt + doesPutEnableEscape(tmpStmt, method, tacCode) + } else false + } else if (tmpStmt.isNonVirtualMethodCall) { + if (!seen.contains(tmpStmt)) { + seen += tmpStmt + doesNonVirtualMethodCallEnablesEscape(method, tmpStmt.asNonVirtualMethodCall, tacCode) + } else false + } else true + } } else { - val useSites = - definitionSiteAssignment.targetVar.usedBy - for (i ← useSites) { - val useSiteStmt = tacCode.stmts(i) + definitionSiteAssignment.targetVar.usedBy.exists { useSite ⇒ + val useSiteStmt = tacCode.stmts(useSite) if (useSiteStmt.isNonVirtualMethodCall) { - handleNonVirtualMethodCall(method, useSiteStmt.asNonVirtualMethodCall, tacCode) + doesNonVirtualMethodCallEnablesEscape(method, useSiteStmt.asNonVirtualMethodCall, tacCode) } else if (useSiteStmt.isPutStatic || useSiteStmt.isPutField) { if (!seen.contains(useSiteStmt)) { seen += useSiteStmt - handlePut(useSiteStmt, method, tacCode) - } + doesPutEnableEscape(useSiteStmt, method, tacCode) + } else false } else if (useSiteStmt.isAssignment) { - state.noEscapePossibilityViaReference = false //TODO - return ; + true //TODO } else { - state.noEscapePossibilityViaReference = false - return ; + true } } } - //TODO alle Fälle abdecken - } else if (!definitionSiteAssignment.expr.isConst) { - state.noEscapePossibilityViaReference = false - return ; - } - } else { - state.noEscapePossibilityViaReference = false - return ; - } + //TODO handle all cases!! + } else !definitionSiteAssignment.expr.isConst + } else true } + } } /** * Begin of method check field writes */ - val tacCodeOption = getTACAI(method, pcs, isRead = false) + val tacCodeOption = getTACAI(method, pcs, false) if (tacCodeOption.isDefined) { val taCode = tacCodeOption.get - - pcs.foreach(pc ⇒ { //TODO forall + if (pcs.exists { pc ⇒ val index = taCode.pcToIndex(pc) if (index >= 0) { val stmt = taCode.stmts(index) - //println("stmt: "+stmt) if (!seen.contains(stmt)) { seen += stmt - handlePut(stmt, method, taCode) - } - } else { - state.noEscapePossibilityViaReference = false - return ; - } + doesPutEnableEscape(stmt, method, taCode) + } else false + } else true }) + state.noEscapePossibilityViaReference = false } } @@ -847,12 +774,14 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e else MutableField val upperBound = { - if (!state.referenceIsImmutable.isDefined) + if (state.referenceIsImmutable.isEmpty) DeepImmutableField - else + else { state.referenceIsImmutable match { + case Some(false) | None ⇒ MutableField - case Some(true) ⇒ { + + case Some(true) ⇒ if (state.tacDependees.isEmpty && !state.concreteClassTypeIsKnown) { if (state.typeIsImmutable) { DeepImmutableField @@ -870,8 +799,8 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } } else DeepImmutableField - } } + } } //TODO check DeepImmutableField @@ -886,8 +815,10 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e if (!state.escapesStillDetermined) state.noEscapePossibilityViaReference = false state.referenceIsImmutable match { + case Some(false) | None ⇒ Result(field, MutableField) - case Some(true) ⇒ { + + case Some(true) ⇒ if (state.typeIsImmutable) { Result(field, DeepImmutableField) } else { @@ -901,7 +832,6 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } } } - } } } } @@ -912,33 +842,42 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e state.dependees = state.dependees.filter(_.e ne eps.e) eps match { case FinalP(DeepImmutableType) ⇒ //nothing to do -> is default + case EUBP(t, MutableType | ShallowImmutableType) ⇒ state.typeIsImmutable = false if (t != ObjectType.Object) { // in case of generic fields state.dependentImmutability = Dependent } - if (t.isInstanceOf[ObjectType] && state.innerArrayTypes.contains(t.asInstanceOf[ObjectType])) - state.noEscapePossibilityViaReference = false - case FinalEP(t, DependentImmutableType) ⇒ { + t match { + case objectType: ObjectType if state.innerArrayTypes.contains(objectType) ⇒ + state.noEscapePossibilityViaReference = false + case _ ⇒ + } + + case FinalEP(t, DependentImmutableType) ⇒ state.typeIsImmutable = false if (t != field.fieldType && state.dependentImmutability == OnlyDeepImmutable) state.dependentImmutability = NotShallowOrMutable - if (t.isInstanceOf[ObjectType] && state.innerArrayTypes.contains(t.asInstanceOf[ObjectType])) - state.noEscapePossibilityViaReference = false - } - case UBP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ { + t match { + case objectType: ObjectType if state.innerArrayTypes.contains(objectType) ⇒ + state.noEscapePossibilityViaReference = false + case _ ⇒ + } + + case UBP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ state.typeIsImmutable = false state.referenceIsImmutable = Some(false) return Result(field, MutableField); - } + case FinalP(ImmutableFieldReference | LazyInitializedThreadSafeFieldReference | - LazyInitializedNotThreadSafeButDeterministicFieldReference - ) ⇒ { + LazyInitializedNotThreadSafeButDeterministicFieldReference) ⇒ state.referenceIsImmutable = Some(true) - } + case FinalP(DeepImmutableField) ⇒ // nothing to do + case UBP(DependentImmutableField | ShallowImmutableField | MutableField) ⇒ state.noEscapePossibilityViaReference = false + case eps if eps.asEPS.pk == TACAI.key ⇒ val newEP = eps.asInstanceOf[EOptionP[Method, TACAI]] val method = newEP.e @@ -954,8 +893,13 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e case eps if eps.isFinal && eps.asEPS.pk == EscapeProperty.key ⇒ if (doesItEscapeViaMethod(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) state.noEscapePossibilityViaReference = false - case FinalP(DeepImmutableClass) ⇒ state.typeIsImmutable = true - case UBP(ShallowImmutableClass | DependentImmutableClass | MutableClass) ⇒ state.typeIsImmutable = false + + case FinalP(DeepImmutableClass) ⇒ + state.typeIsImmutable = true + + case UBP(ShallowImmutableClass | DependentImmutableClass | MutableClass) ⇒ + state.typeIsImmutable = false + case eps ⇒ state.dependees += eps } @@ -973,15 +917,14 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e state.referenceIsImmutable = Some(true) case FinalP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ return Result(field, MutableField); - case ep ⇒ { + case ep ⇒ state.dependees += ep - } } // println("A") /** * Determines whether the field is dependent immutable */ - hasGenericType() + handleGenericity() // println("B") /** * Determines whether the reference object escapes or can be mutated. 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 index 26ea7957c5..1843040f50 100644 --- 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 @@ -24,6 +24,7 @@ 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 /** * @@ -50,7 +51,6 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr method: Method, code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]], - pcToIndex: Array[Int], tacCai: TACode[TACMethodParameter, V] )(implicit state: State): Boolean = { println("handle lazy initialization") @@ -61,6 +61,8 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr ) val lazyInitializationResult: FieldReferenceImmutability = determineLazyInitialization(writeIndex, defaultValue, method, code, cfg, tacCai) + + println(s"lazy initialization result: $lazyInitializationResult") state.referenceImmutability = lazyInitializationResult lazyInitializationResult == MutableFieldReference } @@ -81,21 +83,27 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val write = code(writeIndex).asFieldWriteAccessStmt val writeBB = cfg.bb(writeIndex).asBasicBlock val domTree = cfg.dominatorTree - //println("1") + println("1") val resultCatchesAndThrows = findCaughtsThrowsAndResults(tacCode, cfg) - //println("2") + println("2") def noInterferingExceptions: Boolean = { resultCatchesAndThrows._1.forall(bbCatch ⇒ resultCatchesAndThrows._2.exists(bbThrow ⇒ - ((domTree.strictlyDominates(bbCatch._4.nodeId, bbThrow._3.nodeId) || //domination + (domTree.strictlyDominates(bbCatch._4.nodeId, bbThrow._3.nodeId) || //domination (bbCatch._4 == bbThrow._3 && bbCatch._1 < bbThrow._1)) && // or equal and successor - bbThrow._2 == bbCatch._3))) + bbThrow._2 == bbCatch._3)) } - // println("3") - val findGuardResult: (List[(Int, Int, Int, CFGNode, Int, Int)]) = { + println("3") + val findGuardResult: List[(Int, Int, Int, CFGNode, Int, Int)] = { findGuard(method, writeIndex, defaultValue, code, cfg, tacCode) } - // println("4") + println("4") + println( + s""" + |find guard result: + |${findGuardResult.mkString("\n")} + |""".stripMargin + ) val (guardedIndex, readIndex, afterGuardRecognizedTheDefaultValueIndex, fieldReadIndex) = { if (findGuardResult.nonEmpty) @@ -110,14 +118,14 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr return MutableFieldReference; } } - //println("5") + println("5") val guardedBB = cfg.bb(afterGuardRecognizedTheDefaultValueIndex) val elseBB = cfg.bb(guardedIndex) - //println("6") + println("6") if (isTransitivePredecessor(elseBB, writeBB)) { return MutableFieldReference; } - //println("7") + println("7") //prevents that the field is seen with another value if (method.returnType == state.field.fieldType && { !tacCode.stmts.forall(stmt ⇒ { //TODO @@ -136,49 +144,49 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr }) { return MutableFieldReference; } - //println("8") + println("8") val reads = fieldAccessInformation.readAccesses(state.field) - // println("9") - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) { + println("9") + if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) return MutableFieldReference; - } - // println("B") + + println("B") val writes = fieldAccessInformation.writeAccesses(state.field) if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { return MutableFieldReference; } - // println("C") + println("C") // println (s"field: ${state.field}") if (method.returnType == state.field.fieldType && - !isTheFieldValueReturned(write, writeIndex, readIndex, cfg, tacCode)) { + !isFieldValueReturned(write, writeIndex, readIndex, cfg, tacCode)) { return MutableFieldReference; } - // println("D") + println("D") //when the method is synchronized the monitor has not to be searched if (method.isSynchronized) { println("E") if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { - // println("F") + println("F") if (noInterferingExceptions) { - // println("G") + println("G") LazyInitializedThreadSafeFieldReference // result //DCL } else { - // println("H") + println("H") MutableFieldReference } } else { - // println("I") + println("I") MutableFieldReference } } else { - //println("else") + println("else") val monitorResult: ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = { - // println("find monitors start") + println("find monitors start") findMonitors(writeIndex, defaultValue, code, cfg, tacCode) //... } - // println("find monitors end") + println("find monitors end") if ((monitorResult._1._1.isDefined && monitorResult._1._2.isDefined && monitorResult._2._1.isDefined) && ( @@ -191,22 +199,27 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr monitorResult._2._1.get == cfg.bb(fieldReadIndex) && monitorResult._1._1.get < fieldReadIndex) )) { if (noInterferingExceptions) { - // println("J") + println("J") LazyInitializedThreadSafeFieldReference // result //DCL } else { - // println("K") + println("K") MutableFieldReference } } else { - //println("check write is deterministic: "+checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code)) + // println("check write is deterministic: "+checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code)) if ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) && write.value.asVar.definedBy.size >= 0 && (( //IsDeepImmutable //state.field.isFinal ||write.value.asVar.definedBy.head > -1 write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.nonEmpty && - checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code) + checkWriteIsDeterministic( + code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, + method, + code, + tacCode + ) ))) { - // println("L") + println("L") if (noInterferingExceptions) { if (state.field.fieldType.computationalType != ComputationalTypeInt && state.field.fieldType.computationalType != ComputationalTypeFloat) { @@ -215,11 +228,11 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr } else LazyInitializedNotThreadSafeButDeterministicFieldReference } else { - // println("M") + println("M") MutableFieldReference } } else { - // println("N") + println("N") MutableFieldReference } } @@ -253,8 +266,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val caughtException = stmt.asCaughtException val exceptionType = if (caughtException.exceptionType.isDefined) { - val intermediateExceptionType = caughtException.exceptionType.get - intermediateExceptionType.asObjectType + caughtException.exceptionType.get.asObjectType } else ObjectType.Throwable caughtExceptions = @@ -307,19 +319,13 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr if (definedByIndex >= 0) { val stmt = tacCode.stmts(definedByIndex) stmt match { - case Assignment(_, DVar(_, _), classConst: ClassConst) ⇒ { + case Assignment(_, DVar(_, _), classConst: ClassConst) ⇒ state.field.classFile.thisType == classConst.value || state.field.fieldType == classConst.value - } - case Assignment( - _, - DVar(_, _), - GetField(_, _, _, - classType, - UVar(_, _)) - ) ⇒ - classType == - state.field.classFile.thisType + + case Assignment(_, DVar(_, _), GetField(_, _, _, classType, UVar(_, _))) ⇒ + classType == state.field.classFile.thisType + case _ ⇒ false } } else // (definedByIndex <= -1) @@ -343,7 +349,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr case MonitorEnter.ASTID ⇒ val me = code(i).asMonitorEnter if (checkMonitor(me.objRef.asVar)) { //me.pc, , curBB - result = (Some(tacCode.pcToIndex(me.pc)), (result._2)) + result = (Some(tacCode.pcToIndex(me.pc)), result._2) dclEnterBBs = curBB :: dclEnterBBs flag = false } @@ -410,11 +416,11 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr tacCode: TACode[TACMethodParameter, V] )(implicit state: State): (List[(Int, Int, Int, CFGNode, Int, Int)]) = { - // println(s"start findguard defaultValue: $defaultValue") + println(s"start findguard defaultValue: $defaultValue") val startBB = cfg.bb(fieldWrite).asBasicBlock var enqueuedBBs: Set[CFGNode] = startBB.predecessors - var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty).toList + var worklist: List[BasicBlock] = getPredecessors(startBB, Set.empty) var seen: Set[BasicBlock] = Set.empty var result: List[(Int, Int, CFGNode, Int)] = List.empty //None @@ -433,8 +439,8 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr (cfStmt.astID: @switch) match { case If.ASTID ⇒ - // println("ifstmt: ") - // println(cfStmt) + println("ifstmt: ") + println(cfStmt) val ifStmt = cfStmt.asIf if (ifStmt.condition.equals(EQ) && curBB != startBB && isGuard( ifStmt, @@ -443,6 +449,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr tacCode, method )) { + println("EQ") // println("BB") //case EQ @@ -459,6 +466,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr tacCode, method )) { + println("NE") // println("CC") //case NE //if => @@ -469,6 +477,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr result = (endPC, ifStmt.targetStmt, curBB, endPC + 1) :: result ///} } else { + println("else") // println("DD") // Otherwise, we have to ensure that a guard is present for all predecessors //case _ => @@ -480,8 +489,11 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr // // println("befor transitive call...") if ((cfg.bb(fieldWrite) != cfg.bb(ifStmt.target) || fieldWrite < ifStmt.target) && - isTransitivePredecessor(cfg.bb(fieldWrite), cfg.bb(ifStmt.target))) //(ifStmt.target > fieldWrite) + isTransitivePredecessor(cfg.bb(fieldWrite), cfg.bb(ifStmt.target))) { //(ifStmt.target > fieldWrite) + println("is transitive predecessor: ") return List.empty //in cases where other if-statements destroy + } + } //} //} @@ -500,7 +512,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr } } - + println(s"tmpResult: ${result.mkString("\n")}") var finalResult: List[(Int, Int, Int, CFGNode, Int, Int)] = List.empty var fieldReadIndex = 0 result.foreach(result ⇒ { @@ -567,12 +579,12 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr */ def getSuccessors(node: CFGNode, visited: Set[CFGNode]): List[BasicBlock] = { def getSuccessorsInternal(node: CFGNode, visited: Set[CFGNode]): Iterator[BasicBlock] = { - node.successors.iterator flatMap ({ currentNode ⇒ + 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 } @@ -586,7 +598,8 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr def checkWriteIsDeterministic( origin: Assignment[V], method: Method, - code: Array[Stmt[V]] + code: Array[Stmt[V]], + taCode: TACode[TACMethodParameter, V] )(implicit state: State): Boolean = { def isConstant(uvar: Expr[V]): Boolean = { @@ -614,7 +627,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val value = origin.expr - def isNonConstDeterministic(value: Expr[V]): Boolean = { + def isNonConstDeterministic(value: Expr[V], taCode: TACode[TACMethodParameter, V])(implicit state: State): Boolean = { (value.astID: @switch) match { case BinaryExpr.ASTID ⇒ isConstant(value.asBinaryExpr.left) && @@ -631,7 +644,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr // 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(_))) { + if (value.asFunctionCall.allParams.forall(isConstant)) { state.lazyInitInvocation = Some((declaredMethods(method), origin.pc)) true } else @@ -641,28 +654,54 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr 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)) + varValue.definedBy. + forall(i ⇒ i >= 0 && isNonConstDeterministic(code(i).asAssignment.expr, taCode)) } case New.ASTID ⇒ { //TODO constructor must be deterministic //TODO check that the nonvirtualmethod call calls the constructor //TODO check - val nonVirtualMethodCallIndex = - origin.asAssignment.targetVar.usedBy.iterator.filter(i ⇒ code(i).isNonVirtualMethodCall).toList.head + val nonVirtualMethodCallIndexes = + origin.asAssignment.targetVar.usedBy.iterator. + filter(i ⇒ code(i).isNonVirtualMethodCall) //TODO head + + nonVirtualMethodCallIndexes.forall { nonVirtualMethodCallIndex ⇒ + import org.opalj.br.fpcf.properties.Purity + println( + s""" + | field: ${state.field} + | method: $method + | nonVirtualMethodCallIndex: $nonVirtualMethodCallIndex + | nonVirtualMethodCall: ${taCode.stmts(nonVirtualMethodCallIndex).asNonVirtualMethodCall} + |""".stripMargin + ) + val callTargetResult = + taCode.stmts(nonVirtualMethodCallIndex).asNonVirtualMethodCall.resolveCallTarget( + state.field.classFile.thisType + ) + + !callTargetResult.value.isConstructor || + !isNonDeterministic(propertyStore(declaredMethods(callTargetResult.value), Purity.key)) + } + //val result = taCode.stmts(nonVirtualMethodCallIndex).asNonVirtualMethodCall.resolveCallTarget(state.field.classFile.thisType) + //if (!result.isEmpty) + // result.value.isConstructor && //code(nonVirtualMethodCallIndex).asNonVirtualMethodCall. - val propertyStoreResultCallees = propertyStore(declaredMethods(method), Callees.key) - !doCallsIntroduceNonDeterminism(propertyStoreResultCallees, nonVirtualMethodCallIndex) && - code(nonVirtualMethodCallIndex).asNonVirtualMethodCall.params.forall(isConstant) + //val propertyStoreResultCallees = propertyStore(declaredMethods(method), Callees.key) + //!doCallsIntroduceNonDeterminism(propertyStoreResultCallees, nonVirtualMethodCallIndex) && + // code(nonVirtualMethodCallIndex).asNonVirtualMethodCall.params.forall(isConstant) + // } } + 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, code) + lazyInitializerIsDeterministic(method) } } - val result = value.isConst || isNonConstDeterministic(value) + val result = value.isConst || isNonConstDeterministic(value, taCode) result } @@ -671,59 +710,68 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr * 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])(implicit state: State): Boolean = { - var seenExpressions: Set[Expr[V]] = Set.empty - def isReadOfCurrentFieldInternal(expr: Expr[V], tacCode: TACode[TACMethodParameter, V])(implicit state: State): Boolean = { - if (seenExpressions.contains(expr)) - return false; - seenExpressions += expr - def isExprReadOfCurrentField(pc: Int): Int ⇒ Boolean = index ⇒ - index == tacCode.pcToIndex(pc) || - index >= 0 && - isReadOfCurrentFieldInternal(tacCode.stmts(index).asAssignment.expr, tacCode) - (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 Compare.ASTID ⇒ { - val leftExpr = expr.asCompare.left - val rightExpr = expr.asCompare.right - - leftExpr.asVar.definedBy.forall(isExprReadOfCurrentField(expr.asCompare.pc)) || - rightExpr.asVar.definedBy.forall(isExprReadOfCurrentField(expr.asCompare.pc)) - //TODO - //TODO check if sufficient - //TODO check if handling of methods needed - //TODO check if semantic is correct - //TODO check if handling of method arguments needed - } - case VirtualFunctionCall.ASTID - | NonVirtualFunctionCall.ASTID - | StaticFunctionCall.ASTID ⇒ { - val (params, pc) = - if (expr.isVirtualFunctionCall) { - val virtualFunctionCall = expr.asVirtualFunctionCall - (virtualFunctionCall.params, virtualFunctionCall.pc) - } else if (expr.isStaticFunctionCall) { - val staticFunctionCall = expr.asStaticFunctionCall - (staticFunctionCall.params, staticFunctionCall.pc) - } else { - val nonVirtualFunctionCall = expr.asNonVirtualFunctionCall - (nonVirtualFunctionCall.params, nonVirtualFunctionCall.pc) - } - params.forall(expr ⇒ - expr.asVar.definedBy.forall(isExprReadOfCurrentField(pc))) - } - case _ ⇒ false - } + def isReadOfCurrentField( + expr: Expr[V], + tacCode: TACode[TACMethodParameter, V], + index: Int + )(implicit state: State): Boolean = { + println("is read of current field: "+expr) + def isExprReadOfCurrentField: Int ⇒ Boolean = exprIndex ⇒ + exprIndex == index || + exprIndex >= 0 && isReadOfCurrentField(tacCode.stmts(exprIndex).asAssignment.expr, tacCode, exprIndex) + println(s"expr: "+expr) + (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 ⇒ + val primitiveTypecastExpr = expr.asPrimitiveTypeCastExpr + //val targetType = primitiveTypecastExpr.targetTpe + //targetType.si + //val originType = primitiveTypecastExpr.operand.asVar.cTpe.category. + //println("originType: "+originType) + throw new Exception("-------------------------------") + //TODO check lossless typecast + /*println("operand: "+primitiveTypecastExpr.operand) + val result = primitiveTypecastExpr.operand.asVar.definedBy.forall(isExprReadOfCurrentField) + println("result: "+result) + //throw new Exception("primitive type cast expr") + result*/ + primitiveTypecastExpr.operand.asVar.definedBy.forall(isExprReadOfCurrentField) + case Compare.ASTID ⇒ + /*println("expr: "+expr) + expr.asCompare.left.asVar.definedBy.foreach(isExprReadOfCurrentField) + expr.asCompare.right.asVar.definedBy.foreach(isExprReadOfCurrentField) + throw new Exception("compare") */ + 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 //.asFunctionCall + val fieldType = state.field.fieldType + + functionCall.params.isEmpty && ( + fieldType == ObjectType.Integer && functionCall.name == "intValue" || + fieldType == ObjectType.Float && functionCall.name == "floatValue" || + fieldType == ObjectType.Double && functionCall.name == "doubleValue" || + fieldType == ObjectType.Byte && functionCall.name == "byteValue" || + fieldType == ObjectType.Long && functionCall.name == "longValue" + ) && functionCall.receiver.asVar.definedBy.forall(isExprReadOfCurrentField) + + case _ ⇒ false } - isReadOfCurrentFieldInternal(expr, tacCode) } - /** * Determines if an if-Statement is actually a guard for the current field, i.e. it compares * the current field to the default value. @@ -738,6 +786,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr import org.opalj.br.FieldType import scala.annotation.tailrec + println("is guard") /** * Checks if an expression */ @@ -749,20 +798,6 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val head = defSites.head defSites.size == 1 && head >= 0 && isDefaultConst(code(head).asAssignment.expr) } else { - /* - println( - s""" - | isIntConst: ${expr.isIntConst} - | defaultValue: $defaultValue - | expr: ${expr.asIntConst.value} - | isFloatConst: ${expr.isFloatConst} - | isDoublConst: ${expr.isDoubleConst} - | isLongConst: ${expr.isLongConst} - | isNullExpr: ${expr.isNullExpr} - | expr.isIntConst && defaultValue == expr.asIntConst.value: ${expr.isIntConst && defaultValue == expr.asIntConst.value} - |""".stripMargin - )*/ - expr.isIntConst && defaultValue == expr.asIntConst.value || //defaultValue == expr.asIntConst.value || expr.isFloatConst && defaultValue == expr.asFloatConst.value || expr.isDoubleConst && defaultValue == expr.asDoubleConst.value || @@ -776,36 +811,35 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr * field. */ def isGuardInternal(expr: V, tacCode: TACode[TACMethodParameter, V], method: Method): Boolean = { - // println("i1") + println("i1") expr.definedBy forall { index ⇒ - // println("index: "+index) + println("index: "+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 (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)) - } else { //if(isStaticFunctionCall){ + receiverDefSite >= 0 && isReadOfCurrentField(code(receiverDefSite).asAssignment.expr, tacCode, index)) + } else { + //if(isStaticFunctionCall){ //val staticFunctionCall = code(index).asAssignment.expr.asStaticFunctionCall //staticFunctionCall.receiverOption. - isReadOfCurrentField(code(index).asAssignment.expr, tacCode) + isReadOfCurrentField(code(index).asAssignment.expr, tacCode, index) } } else { - method.asMethod.isVirtualCallTarget - isReadOfCurrentField(code(index).asAssignment.expr, tacCode) + //method.asMethod.isVirtualCallTarget + isReadOfCurrentField(code(index).asAssignment.expr, tacCode, index) } } } @@ -817,24 +851,24 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr if (ifStmt.rightExpr.isVar && isFloatDoubleOrLong(state.field.fieldType) && ifStmt.rightExpr.asVar.definedBy.head > 0 && tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.isCompare) { //ifStmt.leftExpr.isIntConst //ifStmt.leftExpr.asIntConst.value==0 - // println("1") + // println("-1") val left = tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.asCompare.left.asVar - // println("2") + // println("-2") val right = tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.asCompare.right.asVar - // println("3") + // println("-3") val leftExpr = tacCode.stmts(left.definedBy.head).asAssignment.expr - // println("4") + // println("-4") val rightExpr = tacCode.stmts(right.definedBy.head).asAssignment.expr - // println("5") + // println("-5") if (leftExpr.isGetField) { - // println("6") + // println("-6") val result = isDefaultConst(rightExpr) // println("result: "+result) result } else if (rightExpr.isGetField) { - // println("7") + // ("-7") val result = isDefaultConst(leftExpr) - // println("result: "+result) + // println("result-: "+result) result } else { false } //TODO reasoning } else if (ifStmt.leftExpr.isVar && ifStmt.rightExpr.isVar && ifStmt.leftExpr.asVar.definedBy.head >= 0 && @@ -853,7 +887,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr } else if (rightExpr.isGetField) { isDefaultConst(leftExpr) } else false //TODO reasoning - } else if ((ifStmt.rightExpr.isVar) && isDefaultConst(ifStmt.leftExpr)) { + } else if (ifStmt.rightExpr.isVar && isDefaultConst(ifStmt.leftExpr)) { // println("12") isGuardInternal(ifStmt.rightExpr.asVar, tacCode, method) } else if (ifStmt.leftExpr.isVar && isDefaultConst(ifStmt.rightExpr)) { @@ -870,7 +904,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr /** * Checks that the value of the field is returned. */ - def isTheFieldValueReturned( + def isFieldValueReturned( write: FieldWriteAccessStmt[V], writeIndex: Int, readIndex: Int, @@ -881,7 +915,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr var queuedNodes: Set[CFGNode] = Set.empty var workList = getSuccessors(startBB, queuedNodes) workList ++= Set(startBB) - var tmp = -1 + var potentiallyReadIndex = -1 while (workList.nonEmpty) { val currentBB = workList.head workList = workList.tail @@ -892,14 +926,13 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr currentBB.startPC } val endPC = currentBB.endPC - var pc = startPC - while (pc <= endPC) { - val index = pc + var index = startPC + while (index <= endPC) { val stmt = tacCode.stmts(index) if (stmt.isAssignment) { val assignment = stmt.asAssignment - if (isReadOfCurrentField(assignment.expr, tacCode)) { - tmp = pc + if (isReadOfCurrentField(assignment.expr, tacCode, index)) { + potentiallyReadIndex = index } } else if (stmt.isReturnValue) { val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy @@ -908,17 +941,15 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr returnValueDefs.contains(readIndex)) { return true; } // direct return of the written value - else if (tmp >= 0 && (returnValueDefs == IntTrieSet(tmp) || - returnValueDefs == IntTrieSet(readIndex, tmp))) { + else if (potentiallyReadIndex >= 0 && (returnValueDefs == IntTrieSet(potentiallyReadIndex) || + returnValueDefs == IntTrieSet(readIndex, potentiallyReadIndex))) { return true; } // return of field value loaded by field read else { return false; } // return of different value - } else { - } - pc = pc + 1 + index = index + 1 } val successors = getSuccessors(currentBB, queuedNodes) workList ++= successors From 66d5751aa80072920fb186d6881e7b29146c6d76 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 20 Oct 2020 12:15:44 +0200 Subject: [PATCH 284/327] reformatted --- OPAL/tac/src/main/resources/reference.conf | 58 +++++++++++----------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/OPAL/tac/src/main/resources/reference.conf b/OPAL/tac/src/main/resources/reference.conf index 8c707ef617..a68f97cdd8 100644 --- a/OPAL/tac/src/main/resources/reference.conf +++ b/OPAL/tac/src/main/resources/reference.conf @@ -20,39 +20,39 @@ org.opalj { 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" - }, + 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" - }, + 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" - }, + description = "Determines if (instance and static) fields are deep immutable", + eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL0FieldReferenceImmutabilityAnalysis", + lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL0FieldReferenceImmutabilityAnalysis" + }, "L0ClassImmutabilityAnalysis" { - description = "", - eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL0ClassImmutabilityAnalysis", - lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL0ClassImmutabilityAnalysis" - }, + 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" - }, + 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" - }, + 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" + description = "", + eagerFactory = "org.opalj.tac.fpcf.analyses.EagerL0TypeImmutabilityAnalysis", + lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL0TypeImmutabilityAnalysis" }, "SimpleEscapeAnalysis" { description = "Determines whether objects escape a method.", @@ -121,8 +121,8 @@ org.opalj { domainSpecificRater = "org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater" }, L2PurityAnalysis_new { - domainSpecificRater = "org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater" - }, + domainSpecificRater = "org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater" + }, ConfiguredPurity { purities = [ # Native methods From 0d8f69b3e893a5cc3b824b68ef4a17a146135bf4 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 22 Oct 2020 14:02:35 +0200 Subject: [PATCH 285/327] revised, still wip --- .../L3FieldImmutabilityAnalysis.scala | 478 +++++++++--------- 1 file changed, 240 insertions(+), 238 deletions(-) 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 index 6c94715e18..6e0d675a5e 100755 --- 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 @@ -51,7 +51,6 @@ 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.value.ASArrayValue import org.opalj.br.fpcf.properties.DeepImmutableClass import org.opalj.br.fpcf.properties.DependentImmutableClass @@ -60,7 +59,6 @@ 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.InterimEP import org.opalj.fpcf.Entity import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyBounds @@ -76,12 +74,12 @@ import org.opalj.tac.common.DefinitionSitesKey import org.opalj.fpcf.InterimUBP import org.opalj.br.ClassFile import org.opalj.br.FormalTypeParameter +import org.opalj.br.fpcf.properties.ClassImmutability +import scala.collection.mutable /** * Analysis that determines the immutability of org.opalj.br.Field - * * @author Tobias Roth - * */ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { @@ -130,8 +128,9 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = entity match { - case field: Field ⇒ - determineFieldImmutability(field) + + case field: Field ⇒ determineFieldImmutability(field) + case _ ⇒ val m = s"${entity.getClass.getName} is not an org.opalj.br.Field" throw new IllegalArgumentException(m) @@ -142,13 +141,13 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e ): ProperPropertyComputationResult = { /** - * Returns the formal type parameters from the class' and outer class' signature + * Returns the formal type parameters from the class' and outer classes' signature */ def getFormalTypeParameters: Set[String] = { /** * - * Extract the formal type parameters if it exist of a class attribute + * Extract the formal type parameters if it exists of a class' attribute */ def collectFormalTypeParameterFromClassAttribute(attribute: Attribute): Iterator[String] = attribute match { @@ -187,8 +186,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e def isInClassesGenericTypeParameters(string: String): Boolean = classFormalTypeParameters.contains(string) /** - * Determines the immutability of a fields type. Adjusts the state and registers the dependencies if necessary. - * + * 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 @@ -202,13 +200,15 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e state.typeIsImmutable = false } else { propertyStore(objectType, TypeImmutability.key) match { - case FinalP(DeepImmutableType) ⇒ // deep immutable type is set as default - case FinalP(DependentImmutableType) ⇒ - state.typeIsImmutable = false + + case FinalP(DeepImmutableType) ⇒ // deep immutable type is set as default + case FinalP(DependentImmutableType) ⇒ state.typeIsImmutable = false + case UBP(ShallowImmutableType | MutableType) ⇒ state.typeIsImmutable = false if (field.fieldType != ObjectType.Object) state.dependentImmutability = Dependent + case epk ⇒ state.dependees += epk } } @@ -219,9 +219,10 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e var onlyDeepImmutableTypesInGenericTypeFound = true var genericParameters: List[ObjectType] = List() var noRelevantAttributesFound = true + state.field.attributes.foreach { - case RuntimeInvisibleAnnotationTable(_) | SourceFile(_) ⇒ + case RuntimeInvisibleAnnotationTable(_) | SourceFile(_) ⇒ // no including generic parameter case TypeVariableSignature(t) ⇒ noRelevantAttributesFound = false @@ -231,6 +232,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e case ClassTypeSignature(_, SimpleClassTypeSignature(_, typeArguments), _) ⇒ noRelevantAttributesFound = false + typeArguments.foreach { case ProperTypeArgument(_, TypeVariableSignature(identifier)) ⇒ @@ -301,18 +303,13 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e case finalEP: FinalEP[Method, TACAI] ⇒ finalEP.ub.tac case epk ⇒ - var reads = IntTrieSet.empty - var writes = IntTrieSet.empty - if (state.tacDependees.contains(method)) { - reads = state.tacDependees(method)._2._1 - writes = state.tacDependees(method)._2._2 - } - if (isRead) { - state.tacDependees += method -> ((epk, (reads ++ pcs, writes))) - } - if (!isRead) { - state.tacDependees += method -> ((epk, (reads, writes ++ pcs))) - } + 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 } } @@ -322,9 +319,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e * @return true if the object - on which a field write occurred - escapes, false otherwise. * @note (Re-)Adds dependees as necessary. */ - def doesItEscapeViaMethod( - ep: EOptionP[DefinitionSite, EscapeProperty] - )(implicit state: State): Boolean = { + def escapesViaMethod(ep: EOptionP[DefinitionSite, EscapeProperty])(implicit state: State): Boolean = { ep match { case FinalP(NoEscape) ⇒ false @@ -332,11 +327,10 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e 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 _: InterimEP[DefinitionSite, EscapeProperty] ⇒ true // Escape state is worse than via return + 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 @@ -345,7 +339,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } /** - * Determine if the referenced object can escape either via field reads or writes. + * Determines if the referenced object can escape either via field reads or writes. */ def determineEscapeOfReferencedObjectOrValue()(implicit state: State): Unit = { state.escapesStillDetermined = true @@ -354,19 +348,28 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e if (state.noEscapePossibilityViaReference) { val writes = fieldAccessInformation.writeAccesses(state.field) - //has to be determined before the following foreach loop because the information is needed + //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(writeAccess ⇒ { - val (method, pcs) = writeAccess - checkFieldWritesForEffImmutability(method, pcs) - }) - if (state.noEscapePossibilityViaReference) { - val reads = fieldAccessInformation.readAccesses(state.field) - reads.foreach(read ⇒ { - val (method, pcs) = read - determineEscapeViaFieldReads(method, pcs) - }) + writes.foreach { write ⇒ + val (method, pcs) = write + /** + * Begin of method check field writes + */ + val tacCodeOption = getTACAI(method, pcs, isRead = false) + if (tacCodeOption.isDefined) { + val taCode = tacCodeOption.get + checkFieldWritesForEffImmutabilityWithKnownTAC(method, pcs, taCode) + } + } + if (state.noEscapePossibilityViaReference) { + fieldAccessInformation.readAccesses(state.field). + foreach { read ⇒ + val (method, pcs) = read + val taCodeOption = getTACAI(method, pcs, isRead = true) + if (taCodeOption.isDefined) + determineEscapeViaFieldReadsWithKnownTAC(pcs, taCodeOption.get, method) + } } } } @@ -374,101 +377,96 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e /** * Determine if the referenced object can escape via field reads. */ - def determineEscapeViaFieldReads(method: Method, pcs: PCs)(implicit state: State): Unit = { - - val taCodeOption = getTACAI(method, pcs, isRead = true) - // if the taCode is not defined this function is again called in the continuation function - if (taCodeOption.isDefined) { - val taCode = taCodeOption.get - 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 - /*if (doesItEscapeViaMethod( - propertyStore(definitionSites(method, assignment.pc), EscapeProperty.key) - )) { - state.noEscapePossibilityViaReference = false - println("false1") - return ; - } else */ - - 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) - - } 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.isObjectType) { - //If a deep immutable object escapes, it can not be mutated - propertyStore(innerArrayType, TypeImmutability.key) match { - - case FinalP(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 - //nothing to do in case of Expr; The value is only read but not assigned to another one - !stmt.isExprStmt - } else false //nothing to do; -1 means NOTHING as a placeholder - }) { - state.noEscapePossibilityViaReference = false - } + def determineEscapeViaFieldReadsWithKnownTAC( + pcs: PCs, + taCode: TACode[TACMethodParameter, V], + method: Method + )(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) + + } 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.isObjectType) { + //If a deep immutable object escapes, it can not be mutated + propertyStore(innerArrayType, TypeImmutability.key) match { + + case FinalP(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 could 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 checkFieldWritesForEffImmutability(method: Method, pcs: PCs)(implicit state: State): Unit = { + def checkFieldWritesForEffImmutabilityWithKnownTAC( + method: Method, + pcs: PCs, + taCode: TACode[TACMethodParameter, V] + )(implicit state: State): Unit = { - //Needed because of cyclic calls of the functions - to prevent infinite cycles - var seen: Set[Stmt[V]] = Set.empty + //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], - tacCode: TACode[TACMethodParameter, V] + staticFunctionCall: StaticFunctionCall[V] ): Boolean = staticFunctionCall.params.exists(_.asVar.definedBy.exists( definedByIndex ⇒ definedByIndex < 0 || - !tacCode.stmts(definedByIndex).asAssignment.expr.isConst + !taCode.stmts(definedByIndex).asAssignment.expr.isConst )) /** @@ -494,9 +492,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e * Checks if the referenced object or elements from it can escape via the nonvirtualmethod-call */ def doesNonVirtualMethodCallEnablesEscape( - method: Method, - nonVirtualMethodCall: NonVirtualMethodCall[V], - tacCode: TACode[TACMethodParameter, V] + nonVirtualMethodCall: NonVirtualMethodCall[V] )(implicit state: State): Boolean = nonVirtualMethodCall.params.exists { param ⇒ @@ -505,19 +501,27 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e if (paramDefinedByIndex < 0) { true } else { - val paramDefinitionStmt = tacCode.stmts(paramDefinedByIndex) + val paramDefinitionStmt = taCode.stmts(paramDefinedByIndex) + // println(s"parameterDefinitionStmt: $paramDefinitionStmt") if (paramDefinitionStmt.isAssignment) { val assignmentExpression = paramDefinitionStmt.asAssignment.expr if (assignmentExpression.isGetField || assignmentExpression.isGetStatic) { - var assignedField: Option[Field] = None - if (assignmentExpression.isGetField) - assignedField = assignmentExpression.asGetField.resolveField - else if (assignmentExpression.isGetStatic) - assignedField = assignmentExpression.asGetStatic.resolveField + val assignedField: Option[Field] = + if (assignmentExpression.isGetField) + assignmentExpression.asGetField.resolveField + else if (assignmentExpression.isGetStatic) + assignmentExpression.asGetStatic.resolveField + else + None if (assignedField.isDefined && assignedField.get != state.field) { propertyStore(assignedField.get, FieldImmutability.key) match { + + case UBP(MutableField + | ShallowImmutableField + | DependentImmutableField) ⇒ true + case FinalP(DeepImmutableField) ⇒ false //nothing to do here case FinalP(_) ⇒ true @@ -531,10 +535,10 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e val virtualFunctionCall = assignmentExpression.asVirtualFunctionCall virtualFunctionCall.params.exists( param ⇒ param.asVar.definedBy.head < 0 || - !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst + !taCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst ) } else if (assignmentExpression.isStaticFunctionCall) { - doesStaticFunctionCallEnableEscape(assignmentExpression.asStaticFunctionCall, tacCode) + doesStaticFunctionCallEnableEscape(assignmentExpression.asStaticFunctionCall) } else if (assignmentExpression.isNew) { val newStmt = assignmentExpression.asNew if (field.fieldType.isObjectType && @@ -545,11 +549,11 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } paramDefinitionStmt.asAssignment.targetVar.asVar.usedBy.exists { usedSiteIndex ⇒ - val stmt = tacCode.stmts(usedSiteIndex) + val stmt = taCode.stmts(usedSiteIndex) if (stmt.isNonVirtualMethodCall) { if (!seen.contains(stmt)) { seen += stmt - doesNonVirtualMethodCallEnablesEscape(method, stmt.asNonVirtualMethodCall, tacCode) + doesNonVirtualMethodCallEnablesEscape(stmt.asNonVirtualMethodCall) } else false } else true } @@ -558,21 +562,21 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e assignmentExpression.isConst } else { val definitionSitesOfParam = definitionSites(method, paramDefinedByIndex) - val stmt = tacCode.stmts(tacCode.pcToIndex(definitionSitesOfParam.pc)) + val stmt = taCode.stmts(taCode.pcToIndex(definitionSitesOfParam.pc)) if (stmt.isNonVirtualMethodCall) { if (!seen.contains(stmt)) { seen += stmt - doesNonVirtualMethodCallEnablesEscape(method, stmt.asNonVirtualMethodCall, tacCode) + doesNonVirtualMethodCallEnablesEscape(stmt.asNonVirtualMethodCall) } else false } else if (stmt.isPutField || stmt.isPutStatic) { if (!seen.contains(stmt)) { seen += stmt - doesPutEnableEscape(stmt, method, tacCode) + doesPutEnableEscape(stmt) } else false } else if (stmt.isArrayStore) { true //TODO handling that case more precise } else { // other cases that the purity analysis can not handle - doesItEscapeViaMethod(propertyStore(definitionSitesOfParam, EscapeProperty.key)) + escapesViaMethod(propertyStore(definitionSitesOfParam, EscapeProperty.key)) //false } } //TODO go further @@ -583,7 +587,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e /** * Checks if a reference object can escape via a given putfield or putstatic */ - def doesPutEnableEscape(putStmt: Stmt[V], method: Method, tacCode: TACode[TACMethodParameter, V]): Boolean = { + def doesPutEnableEscape(putStmt: Stmt[V]): Boolean = { val (putDefinitionSites, putValue) = { if (putStmt.isPutField) { @@ -598,95 +602,96 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } } - val putValueDefinedByIndex = putValue.definedBy.head if (putValue.value.isArrayValue.isYes) { - - if (putValueDefinedByIndex >= 0) { //necessary - tacCode.stmts(putValueDefinedByIndex).asAssignment.targetVar.usedBy.exists { usedByIndex ⇒ - val arrayStmt = tacCode.stmts(usedByIndex) - if (arrayStmt != putStmt) { - if (arrayStmt.isArrayStore) { - val arrayStore = arrayStmt.asArrayStore - val arrayStoreIndex = arrayStore.index - val isArrayIndexConst = - tacCode.stmts(arrayStoreIndex.asVar.definedBy.head).asAssignment.expr.isConst - val assignedValue = arrayStore.value - if (assignedValue.asVar.definedBy.head >= 0) { - val valueAssignment = tacCode.stmts(assignedValue.asVar.definedBy.head).asAssignment - val assignedExpr = valueAssignment.expr - val useSites = valueAssignment.targetVar.usedBy.map(tacCode.stmts(_)) - useSites.exists { useSite ⇒ - if (useSite.isNonVirtualMethodCall) { - val nonVirtualMethodCall = useSite.asNonVirtualMethodCall - nonVirtualMethodCall.params.exists(param ⇒ - !param.isConst && - param.asVar.definedBy.head > -1 && //TODO look at more than the head - !tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst) - } else if (useSite == arrayStore) { - false - } else if (useSite.isReturnValue) { - //assigned array-element escapes - true - } else { - true //to be sound + 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.head >= 0) { + val valueAssignment = taCode.stmts(assignedValue.asVar.definedBy.head).asAssignment + val assignedExpr = valueAssignment.expr + val useSites = valueAssignment.targetVar.usedBy.map(taCode.stmts(_)) + 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)) + //param.asVar.definedBy.head > -1 && //TODO look at more than the head + //!tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst) + } else if (useSite == arrayStore) { + false + } else if (useSite.isReturnValue) { + //assigned array-element escapes + true + } else { + true //to be sound + } } - } - - if (!isArrayIndexConst) { - true - } else if (assignedExpr.isStaticFunctionCall) { - doesStaticFunctionCallEnableEscape(assignedExpr.asStaticFunctionCall, tacCode) - } 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) - } + 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 = tacCode.stmts(index) - if (tmpStmt.isArrayStore) { - false // can be ingored - } else if (tmpStmt.isNonVirtualMethodCall) { - val nonVirtualMethodcall = tmpStmt.asNonVirtualMethodCall - if (!seen.contains(tmpStmt)) { - seen += tmpStmt - doesNonVirtualMethodCallEnablesEscape(method, nonVirtualMethodcall, tacCode) - } - false //nothing to do in the else case. Stmt has still been handled - } else true - } + 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 + } + } else true } else true } else true - } else true - } else false + } else false + } } } else true } else { putDefinitionSites.exists { putDefinitionSite ⇒ if (putDefinitionSite >= 0) { //necessary - val definitionSiteStatement = tacCode.stmts(putDefinitionSite) - //println("def site stmt: "+definitionSiteStatement) + val definitionSiteStatement = taCode.stmts(putDefinitionSite) + // println("def site stmt: "+definitionSiteStatement) val definitionSiteAssignment = definitionSiteStatement.asAssignment //println("def site assignement: "+definitionSiteAssignment) if (definitionSiteAssignment.expr.isStaticFunctionCall) { // println("handle static function call") - doesStaticFunctionCallEnableEscape(definitionSiteAssignment.expr.asStaticFunctionCall, tacCode) + doesStaticFunctionCallEnableEscape(definitionSiteAssignment.expr.asStaticFunctionCall) } else if (definitionSiteAssignment.expr.isVar) { - // println("def site is var") val definitionSiteVar = definitionSiteAssignment.expr.asVar definitionSiteVar.usedBy.exists { definitionSiteVarUseSite ⇒ - val definitionSiteVarUseSiteStmt = tacCode.stmts(definitionSiteVarUseSite) + val definitionSiteVarUseSiteStmt = taCode.stmts(definitionSiteVarUseSite) if (definitionSiteVarUseSiteStmt.isNonVirtualMethodCall) { val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall - doesNonVirtualMethodCallEnablesEscape(method, nonVirtualMethodCall, tacCode) + doesNonVirtualMethodCallEnablesEscape(nonVirtualMethodCall) } else true //TODO handle all cases } @@ -706,28 +711,29 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e if (!method.isConstructor) { definitionSiteAssignment.targetVar.asVar.usedBy.exists { x ⇒ - val tmpStmt = tacCode.stmts(x) + val tmpStmt = taCode.stmts(x) if (tmpStmt.isPutStatic || tmpStmt.isPutField) { if (!seen.contains(tmpStmt)) { seen += tmpStmt - doesPutEnableEscape(tmpStmt, method, tacCode) + doesPutEnableEscape(tmpStmt) } else false } else if (tmpStmt.isNonVirtualMethodCall) { if (!seen.contains(tmpStmt)) { seen += tmpStmt - doesNonVirtualMethodCallEnablesEscape(method, tmpStmt.asNonVirtualMethodCall, tacCode) + doesNonVirtualMethodCallEnablesEscape(tmpStmt.asNonVirtualMethodCall) } else false } else true } } else { definitionSiteAssignment.targetVar.usedBy.exists { useSite ⇒ - val useSiteStmt = tacCode.stmts(useSite) + val useSiteStmt = taCode.stmts(useSite) + // println(s"constructor use site stmt: $useSiteStmt") if (useSiteStmt.isNonVirtualMethodCall) { - doesNonVirtualMethodCallEnablesEscape(method, useSiteStmt.asNonVirtualMethodCall, tacCode) + doesNonVirtualMethodCallEnablesEscape(useSiteStmt.asNonVirtualMethodCall) } else if (useSiteStmt.isPutStatic || useSiteStmt.isPutField) { if (!seen.contains(useSiteStmt)) { seen += useSiteStmt - doesPutEnableEscape(useSiteStmt, method, tacCode) + doesPutEnableEscape(useSiteStmt) } else false } else if (useSiteStmt.isAssignment) { true //TODO @@ -743,24 +749,19 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } } - /** - * Begin of method check field writes - */ - val tacCodeOption = getTACAI(method, pcs, false) - if (tacCodeOption.isDefined) { - val taCode = tacCodeOption.get - 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, method, taCode) - } else false - } else true - }) - state.noEscapePossibilityViaReference = false - } + if (pcs.exists { pc ⇒ + val index = taCode.pcToIndex(pc) + if (index >= 0) { + val stmt = taCode.stmts(index) + // println(s"stmt: $stmt") + if (!seen.contains(stmt)) { + seen += stmt + doesPutEnableEscape(stmt) + } else false + } else true + }) + state.noEscapePossibilityViaReference = false + } /** @@ -881,17 +882,18 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e case eps if eps.asEPS.pk == TACAI.key ⇒ val newEP = eps.asInstanceOf[EOptionP[Method, TACAI]] val method = newEP.e - state.tacDependees -= method val pcs = state.tacDependees(method)._2 + state.tacDependees -= method if (eps.isFinal) { - checkFieldWritesForEffImmutability(method, pcs._2)(state) - determineEscapeViaFieldReads(method, pcs._1)(state) + val finalEP = eps.asInstanceOf[FinalEP[Method, TACAI]] + checkFieldWritesForEffImmutabilityWithKnownTAC(method, pcs._2, finalEP.p.tac.get)(state) + determineEscapeViaFieldReadsWithKnownTAC(pcs._1, finalEP.p.tac.get, method)(state) } else { state.tacDependees += method -> ((newEP, pcs)) } case eps if eps.isFinal && eps.asEPS.pk == EscapeProperty.key ⇒ - if (doesItEscapeViaMethod(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) + if (escapesViaMethod(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) state.noEscapePossibilityViaReference = false case FinalP(DeepImmutableClass) ⇒ From 14412f7a93596a2e9b3497ff1003503cbce32cbd Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 23 Oct 2020 18:07:47 +0200 Subject: [PATCH 286/327] revised, still wip --- .../L1ClassImmutabilityAnalysis.scala | 98 ++- .../L1TypeImmutabilityAnalysis.scala | 20 +- ...ctFieldReferenceImmutabilityAnalysis.scala | 61 +- ...mutabilityAnalysisLazyInitialization.scala | 733 +++++++----------- ...L0FieldReferenceImmutabilityAnalysis.scala | 117 ++- 5 files changed, 431 insertions(+), 598 deletions(-) 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 index d3ebecd488..27a7d2b47e 100644 --- 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 @@ -117,7 +117,7 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis nextComputations ::= ( ( determineL1ClassImmutability(t, cfMutability, cfMutabilityIsFinal, - lazyComputation = false) _, scf + lazyComputation = false), scf ) ) case None ⇒ @@ -134,27 +134,19 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis 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(identifier, classBound, interfaceBound) ⇒ ftp - }) - .foreach({ - case FormalTypeParameter(identifier, classBound, interfaceBound) ⇒ - classBound match { - case Some( - ClassTypeSignature( - packageIdentifier, - SimpleClassTypeSignature(simpleName, typeArguments), - classTypeSignatureSuffix - ) - ) ⇒ { - genericTypeBounds += ((identifier, simpleName)) //+ typeBounds - } - case _ ⇒ - } + 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 } @@ -235,35 +227,36 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis var hasDependentImmutableFields = false val fieldsPropertyStoreInformation = propertyStore(instanceFields, FieldImmutability) - //println("class Inner: ") - //fieldsPropertyStoreInformation.foreach(println(_)) - - fieldsPropertyStoreInformation.foreach( - _ match { - 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); - } - ) + 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 @@ -336,13 +329,12 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis case LUBP(MutableClass, DeepImmutableClass) ⇒ // No information about superclass - case FinalEP(f, DependentImmutableField) ⇒ { + case FinalP(DependentImmutableField) ⇒ if (hasShallowImmutableFields) { maxLocalImmutability = ShallowImmutableClass } else if (maxLocalImmutability != MutableClass && maxLocalImmutability != ShallowImmutableClass) { maxLocalImmutability = DependentImmutableClass } - } // Field Immutability related dependencies: case FinalP(DeepImmutableField) ⇒ @@ -352,7 +344,7 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis 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) ⇒ + case UBP(DependentImmutableField) if maxLocalImmutability != ShallowImmutableClass ⇒ maxLocalImmutability = DependentImmutableClass case _ ⇒ Result(t, MutableClass) //TODO check } @@ -436,7 +428,7 @@ trait L1ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { // 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, DeepImmutableClass)) //ImmutableObject)) + allInterfaces.foreach(cf ⇒ set(cf.thisType, DeepImmutableClass)) // 2. // All classes that do not have complete superclass information are mutable 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 index 6451e4d574..734c396eb1 100644 --- 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 @@ -96,19 +96,18 @@ class L1TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAn } } - val resultToMatch = ps(t, ClassImmutability.key) - resultToMatch match { - case x @ FinalP(p) ⇒ { + 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, Seq(eps), c) - case epk ⇒ { + + case epk ⇒ InterimResult(t, MutableType, DeepImmutableType, Seq(epk), c) - } } } else { var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] @@ -221,19 +220,20 @@ class L1TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAn dependencies = dependencies - e nextResult() - case UBP(x) if (x == MutableType || x == MutableClass) ⇒ + case UBP(x) if x == MutableType || x == MutableClass ⇒ Result(t, MutableType) //MutableType) - case FinalEP(e, x) if (x == ShallowImmutableType || x == ShallowImmutableClass) ⇒ + case FinalEP(e, x) if x == ShallowImmutableType || x == ShallowImmutableClass ⇒ maxImmutability = ShallowImmutableType dependencies = dependencies - e nextResult() - case FinalEP(e, DependentImmutableClass | DependentImmutableType) ⇒ { + + 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 { 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 index cb47209531..10283916bb 100644 --- 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 @@ -49,7 +49,6 @@ import org.opalj.value.ValueInformation * @author Dominik Helm * @author Florian Kübler * @author Michael Eichberg - * */ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { @@ -73,9 +72,8 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { var typeDependees: Set[EOptionP[ObjectType, TypeImmutability]] = Set.empty ) { def hasDependees: Boolean = { - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - prematurelyReadDependee.isDefined || purityDependees.nonEmpty || - calleesDependee.nonEmpty || referenceImmutabilityDependees.nonEmpty || + prematurelyReadDependee.isDefined || purityDependees.nonEmpty || prematurelyReadDependee.isDefined || + purityDependees.nonEmpty || calleesDependee.nonEmpty || referenceImmutabilityDependees.nonEmpty || escapeDependees.nonEmpty || tacDependees.nonEmpty || typeDependees.nonEmpty } @@ -93,8 +91,9 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { pcs: PCs )(implicit state: State): Option[TACode[TACMethodParameter, V]] = { propertyStore(method, TACAI.key) match { - case finalEP: FinalEP[Method, TACAI] ⇒ - finalEP.ub.tac + + case finalEP: FinalEP[Method, TACAI] ⇒ finalEP.ub.tac + case epk ⇒ state.tacDependees += method -> ((epk, pcs)) None @@ -109,24 +108,20 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { 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 LBP(p: Purity) if p.isDeterministic ⇒ false + case UBP(p: Purity) if !p.isDeterministic ⇒ true + case _ ⇒ state.purityDependees += eop false } def lazyInitializerIsDeterministic( - method: Method, - code: Array[Stmt[V]] + 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)) + if (method.descriptor.parametersCount > 0) false + else !isNonDeterministic(propertyStore(declaredMethods(method), Purity.key)) } /** @@ -136,8 +131,10 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { 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 @@ -151,36 +148,46 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { 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 if 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 = { + )(implicit state: State): Boolean = { calleesEOP match { - case FinalP(callees) ⇒ isACalleeNotDeterministic(callees, pc) - case InterimUBP(callees) ⇒ isACalleeNotDeterministic(callees.asInstanceOf[Callees], pc) + + 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)) + state.calleesDependee += calleesEOP.e → ((calleesEOP, pc :: state.calleesDependee(calleesEOP.e)._2)) false } } - def isACalleeNotDeterministic(callees: Callees, pc: PC)(implicit state: State): Boolean = { + /** + * Checks if a callee is nondeterministic and sets the [[State.referenceImmutability]] + * if it is to MutableFieldReference. + * @return if the callee is nondeterministic + */ + def isNonDeterministicCallee(callees: Callees, pc: PC)(implicit state: State): Boolean = { if (callees.isIncompleteCallSite(pc)) { state.referenceImmutability = MutableFieldReference true @@ -189,9 +196,7 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { state.referenceImmutability = MutableFieldReference true - } else { - false - } + } 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 index 1843040f50..b381dc2c70 100644 --- 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 @@ -6,6 +6,9 @@ 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 @@ -24,7 +27,8 @@ 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.fpcf.properties.Purity +import org.opalj.br.FieldType /** * @@ -40,202 +44,140 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr with FPCFAnalysis { /** - * - * handles the lazy initialization determination for the write in a given method + * handles the lazy initialization determination for a field write in a given method * @author Tobias Roth - * @return true if we have no thread safe or deterministic lazy initialization + * @return true if there is no thread safe or deterministic lazy initialization */ def handleLazyInitialization( writeIndex: Int, defaultValue: Any, method: Method, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], tacCai: TACode[TACMethodParameter, V] )(implicit state: State): Boolean = { - println("handle lazy initialization") - println( - s""" - | field: ${state.field} - |""".stripMargin - ) val lazyInitializationResult: FieldReferenceImmutability = - determineLazyInitialization(writeIndex, defaultValue, method, code, cfg, tacCai) + determineLazyInitialization(writeIndex, defaultValue, method, tacCai) - println(s"lazy initialization result: $lazyInitializationResult") state.referenceImmutability = lazyInitializationResult lazyInitializationResult == MutableFieldReference } /** - * Determines if a given field is lazy initialized in the given method. + * 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, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], tacCode: TACode[TACMethodParameter, V] )(implicit state: State): FieldReferenceImmutability = { - //println("0") + val code = tacCode.stmts + val cfg = tacCode.cfg + val write = code(writeIndex).asFieldWriteAccessStmt val writeBB = cfg.bb(writeIndex).asBasicBlock val domTree = cfg.dominatorTree - println("1") - val resultCatchesAndThrows = findCaughtsThrowsAndResults(tacCode, cfg) - println("2") - def noInterferingExceptions: Boolean = { - resultCatchesAndThrows._1.forall(bbCatch ⇒ - resultCatchesAndThrows._2.exists(bbThrow ⇒ - (domTree.strictlyDominates(bbCatch._4.nodeId, bbThrow._3.nodeId) || //domination - (bbCatch._4 == bbThrow._3 && bbCatch._1 < bbThrow._1)) && // or equal and successor - bbThrow._2 == bbCatch._3)) + val resultCatchesAndThrows = findCatchesAndThrows(tacCode, cfg) //TODO remove values + + /** + * 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): Boolean = { + val bbPotentiallyDominator = cfg.bb(potentiallyDominator) + val bbPotentiallyDominated = cfg.bb(potentiallyDominated) + domTree.strictlyDominates(bbPotentiallyDominator.nodeId, bbPotentiallyDominated.nodeId) || + bbPotentiallyDominator == bbPotentiallyDominated && potentiallyDominator < potentiallyDominated } - println("3") - val findGuardResult: List[(Int, Int, Int, CFGNode, Int, Int)] = { - findGuard(method, writeIndex, defaultValue, code, cfg, tacCode) + + /** + * 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(tacCode.pcToIndex(catchPC), tacCode.pcToIndex(throwPC)) && + throwDefinitionSites == originsCaughtException //throwing and catching same exceptions + } + } } - println("4") - println( - s""" - |find guard result: - |${findGuardResult.mkString("\n")} - |""".stripMargin - ) - val (guardedIndex, readIndex, afterGuardRecognizedTheDefaultValueIndex, fieldReadIndex) = { + val findGuardResult = findGuard(method, writeIndex, defaultValue, tacCode) + + val (guardedIndex, readIndex, trueCaseIndex, fieldReadIndex) = if (findGuardResult.nonEmpty) - ( - //findGuardResult.head._1, - findGuardResult.head._2, - findGuardResult.head._3, - findGuardResult.head._5, - findGuardResult.head._6 - ) - else { + (findGuardResult.head._1, findGuardResult.head._2, findGuardResult.head._3, findGuardResult.head._4) + else return MutableFieldReference; - } - } - println("5") - val guardedBB = cfg.bb(afterGuardRecognizedTheDefaultValueIndex) + + if (!dominates(trueCaseIndex, writeIndex)) return MutableFieldReference; + val elseBB = cfg.bb(guardedIndex) - println("6") - if (isTransitivePredecessor(elseBB, writeBB)) { - return MutableFieldReference; - } - println("7") - //prevents that the field is seen with another value - if (method.returnType == state.field.fieldType && { - !tacCode.stmts.forall(stmt ⇒ { //TODO - if (stmt.isReturnValue) { - (findGuardResult.exists(x ⇒ { - isTransitivePredecessor( - cfg.bb(x._2), - cfg.bb(tacCode.pcToIndex(stmt.pc)) - ) - }) - || isTransitivePredecessor(writeBB, cfg.bb(tacCode.pcToIndex(stmt.pc)))) - } else - true - }) - }) { + // prevents a 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, cfg, tacCode)) 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 + tacCode.stmts.exists(stmt ⇒ stmt.isReturnValue && + !isTransitivePredecessor(writeBB, cfg.bb(tacCode.pcToIndex(stmt.pc))) && + findGuardResult.forall { + case (indexOfFieldRead, _, _, _) ⇒ + !isTransitivePredecessor(cfg.bb(indexOfFieldRead), cfg.bb(tacCode.pcToIndex(stmt.pc))) + })) + return MutableFieldReference; } - println("8") + val reads = fieldAccessInformation.readAccesses(state.field) - println("9") - if (reads.exists(mAndPCs ⇒ (mAndPCs._1 ne method) && !mAndPCs._1.isInitializer)) + + // prevents reads outside the method + if (reads.exists(_._1 ne method)) return MutableFieldReference; - println("B") val writes = fieldAccessInformation.writeAccesses(state.field) - if (writes.exists(x ⇒ ((x._1 eq method) && x._2.size > 1))) { - return MutableFieldReference; - } - println("C") - // println (s"field: ${state.field}") - if (method.returnType == state.field.fieldType && - !isFieldValueReturned(write, writeIndex, readIndex, cfg, tacCode)) { + + // 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; - } - println("D") - //when the method is synchronized the monitor has not to be searched + + // if the method is synchronized the monitor within the method doesn't have to be searched if (method.isSynchronized) { - println("E") - if (domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || - (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) { - println("F") - if (noInterferingExceptions) { - println("G") - LazyInitializedThreadSafeFieldReference // result //DCL - } else { - println("H") - MutableFieldReference - } - } else { - println("I") - MutableFieldReference - } + if (dominates(trueCaseIndex, writeIndex) && noInterferingExceptions()) + LazyInitializedThreadSafeFieldReference + else MutableFieldReference } else { - println("else") - val monitorResult: ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = { - println("find monitors start") - findMonitors(writeIndex, defaultValue, code, cfg, tacCode) //... - } - println("find monitors end") - if ((monitorResult._1._1.isDefined && monitorResult._1._2.isDefined && monitorResult._2._1.isDefined) - && - ( - (domTree.strictlyDominates(monitorResult._2._1.get.nodeId, guardedBB.nodeId) || - (monitorResult._2._1.get == guardedBB && monitorResult._1._1.get < afterGuardRecognizedTheDefaultValueIndex)) && //writeIndex)) && //monitor enter dominates guard1 - ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId)) - || guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex) && //&& //true case dominates Write - // The field read must be within the synchronized block - (domTree.strictlyDominates(monitorResult._2._1.get.nodeId, cfg.bb(fieldReadIndex).nodeId) || - monitorResult._2._1.get == cfg.bb(fieldReadIndex) && monitorResult._1._1.get < fieldReadIndex) - )) { - if (noInterferingExceptions) { - println("J") - LazyInitializedThreadSafeFieldReference // result //DCL - } else { - println("K") - MutableFieldReference - } + val (indexMonitorEnter, indexMonitorExit) = findMonitors(writeIndex, code, cfg, tacCode) + + val monitorResultsDefined = indexMonitorEnter.isDefined && indexMonitorExit.isDefined + + if (monitorResultsDefined && //dominates(indexMonitorEnter.get, trueCaseIndex) && + dominates(indexMonitorEnter.get, fieldReadIndex)) { + if (noInterferingExceptions()) LazyInitializedThreadSafeFieldReference + else MutableFieldReference } else { - // println("check write is deterministic: "+checkWriteIsDeterministic(code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, method, code)) - if ((domTree.strictlyDominates(guardedBB.nodeId, writeBB.nodeId) || - (guardedBB == writeBB && afterGuardRecognizedTheDefaultValueIndex < writeIndex)) && - write.value.asVar.definedBy.size >= 0 && - (( //IsDeepImmutable //state.field.isFinal ||write.value.asVar.definedBy.head > -1 - write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.nonEmpty && - checkWriteIsDeterministic( - code(write.value.asVar.definedBy.iterator.filter(n ⇒ n >= 0).toList.head).asAssignment, - method, - code, - tacCode - ) - ))) { - println("L") - if (noInterferingExceptions) { - if (state.field.fieldType.computationalType != ComputationalTypeInt && - state.field.fieldType.computationalType != ComputationalTypeFloat) { - - LazyInitializedNotThreadSafeFieldReference - } else - LazyInitializedNotThreadSafeButDeterministicFieldReference - } else { - println("M") - MutableFieldReference - } - } else { - println("N") - MutableFieldReference + if (write.value.asVar.definedBy.forall { defSite ⇒ + defSite >= 0 && checkWriteIsDeterministic(code(defSite).asAssignment, method, code, tacCode) } + && noInterferingExceptions()) { + val computationalFieldType = state.field.fieldType.computationalType + if (computationalFieldType != ComputationalTypeInt && + computationalFieldType != ComputationalTypeFloat) { + LazyInitializedNotThreadSafeFieldReference + } else + LazyInitializedNotThreadSafeButDeterministicFieldReference + } else MutableFieldReference } + } } @@ -252,29 +194,30 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr * The throw statements: (the pc, the definitionSites, the bb of the throw statement) * @author Tobias Roth */ - def findCaughtsThrowsAndResults( + def findCatchesAndThrows( tacCode: TACode[TACMethodParameter, V], cfg: CFG[Stmt[V], TACStmts[V]] - ): (List[(Int, ObjectType, IntTrieSet, CFGNode)], List[(Int, IntTrieSet, CFGNode)]) = { - var caughtExceptions: List[(Int, ObjectType, IntTrieSet, CFGNode)] = List.empty - var throwStatements: List[(Int, IntTrieSet, CFGNode)] = List.empty + ): (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 - val currentBB = cfg.bb(tacCode.pcToIndex(stmt.pc)) + //val currentBB = cfg.bb(tacCode.pcToIndex(stmt.pc)) (stmt.astID: @switch) match { + case CaughtException.ASTID ⇒ val caughtException = stmt.asCaughtException - val exceptionType = - if (caughtException.exceptionType.isDefined) { - caughtException.exceptionType.get.asObjectType - } else - ObjectType.Throwable + /*val exceptionType = + if (caughtException.exceptionType.isDefined) caughtException.exceptionType.get.asObjectType + else ObjectType.Throwable*/ caughtExceptions = - (caughtException.pc, exceptionType, caughtException.origins, currentBB) :: caughtExceptions + (caughtException.pc, caughtException.origins) :: caughtExceptions + case Throw.ASTID ⇒ val throwStatement = stmt.asThrow val throwStatementDefinedBys = throwStatement.exception.asVar.definedBy - throwStatements = (throwStatement.pc, throwStatementDefinedBys, currentBB) :: throwStatements + throwStatements = (throwStatement.pc, throwStatementDefinedBys) :: throwStatements + case _ ⇒ } } @@ -283,120 +226,95 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr } /** - * - * @param fieldWrite - * @param defaultValue - * @param code - * @param cfg - * @param tacCode - * @param state - * @return + * Searches the monitor enter and exit that is closest to the field write. + * @return the index of the monitor enter and exit + * @author Tobias Roth */ def findMonitors( - fieldWrite: Int, - defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - tacCode: TACode[TACMethodParameter, V] - )(implicit state: State): ((Option[Int], Option[Int]), (Option[CFGNode], Option[CFGNode])) = { + fieldWrite: Int, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], + tacCode: TACode[TACMethodParameter, V] + )(implicit state: State): (Option[Int], Option[Int]) = { var result: (Option[Int], Option[Int]) = (None, None) - var dclEnterBBs: List[CFGNode] = List.empty - var dclExitBBs: List[CFGNode] = List.empty val startBB = cfg.bb(fieldWrite) var monitorExitQueuedBBs: Set[CFGNode] = startBB.successors var worklistMonitorExit = getSuccessors(startBB, Set.empty) /** - * - * @param v - * @param state - * @return + * checks that a given monitor supports a thread safe lazy initialization */ def checkMonitor(v: V)(implicit state: State): Boolean = { - v.definedBy - .forall(definedByIndex ⇒ { - if (definedByIndex >= 0) { - val stmt = tacCode.stmts(definedByIndex) - stmt match { - case Assignment(_, DVar(_, _), classConst: ClassConst) ⇒ - state.field.classFile.thisType == classConst.value || - state.field.fieldType == classConst.value - - case Assignment(_, DVar(_, _), GetField(_, _, _, classType, UVar(_, _))) ⇒ - classType == state.field.classFile.thisType - - case _ ⇒ false - } - } else // (definedByIndex <= -1) - true - }) + v.definedBy.forall(definedByIndex ⇒ { + if (definedByIndex >= 0) { + //supports two ways of synchronization + tacCode.stmts(definedByIndex) match { + //synchronized(... //TODO + case Assignment(_, DVar(_, _), classConst: ClassConst) ⇒ + state.field.classFile.thisType == classConst.value || + state.field.fieldType == classConst.value + + //synchronized(... //TODO + case Assignment(_, DVar(_, _), GetField(_, _, _, classType, UVar(_, _))) ⇒ + classType == state.field.classFile.thisType + + case _ ⇒ false + } + } else true // (definedByIndex <= -1) + }) } var monitorEnterQueuedBBs: Set[CFGNode] = startBB.predecessors var worklistMonitorEnter = getPredecessors(startBB, Set.empty) - // println("find monitorenter") + //find monitorenter while (worklistMonitorEnter.nonEmpty) { val curBB = worklistMonitorEnter.head - // println("curBB: "+curBB) worklistMonitorEnter = worklistMonitorEnter.tail val startPC = curBB.startPC val endPC = curBB.endPC - var flag = true + var hasNotFoundAnyMonitorYet = true for (i ← startPC to endPC) { (code(i).astID: @switch) match { case MonitorEnter.ASTID ⇒ - val me = code(i).asMonitorEnter - if (checkMonitor(me.objRef.asVar)) { //me.pc, , curBB - result = (Some(tacCode.pcToIndex(me.pc)), result._2) - dclEnterBBs = curBB :: dclEnterBBs - flag = false + val monitorEnter = code(i).asMonitorEnter + if (checkMonitor(monitorEnter.objRef.asVar)) { + result = (Some(tacCode.pcToIndex(monitorEnter.pc)), result._2) + hasNotFoundAnyMonitorYet = false } case _ ⇒ } } - if (flag) { + if (hasNotFoundAnyMonitorYet) { val predecessor = getPredecessors(curBB, monitorEnterQueuedBBs) worklistMonitorEnter ++= predecessor monitorEnterQueuedBBs ++= predecessor } } - //println("find monitorexit") //find monitorexit while (worklistMonitorExit.nonEmpty) { val curBB = worklistMonitorExit.head - // println("curBB: "+curBB) worklistMonitorExit = worklistMonitorExit.tail val endPC = curBB.endPC val cfStmt = code(endPC) (cfStmt.astID: @switch) match { + case MonitorExit.ASTID ⇒ - val mex = cfStmt.asMonitorExit - if (checkMonitor(mex.objRef.asVar)) { - result = ((result._1), Some(tacCode.pcToIndex(mex.pc))) - dclExitBBs = curBB :: dclExitBBs + 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 } } - - val bbsEnter = { - if (dclEnterBBs.nonEmpty) - Some(dclEnterBBs.head) - else None - } - val bbsExit = { - if (dclExitBBs.nonEmpty) - Some(dclExitBBs.head) - else None - } - (result, (bbsEnter, bbsExit)) + result } /** @@ -404,27 +322,21 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr * 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 fieldread. */ - // var savedGuards: mutable.HashMap[(Int, Method), ((Option[(Int, Int, Int, CFGNode, Int)]))] = - // new mutable.HashMap[(Int, Method), ((Option[(Int, Int, Int, CFGNode, Int)]))]() - def findGuard( method: Method, fieldWrite: Int, defaultValue: Any, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], tacCode: TACode[TACMethodParameter, V] - )(implicit state: State): (List[(Int, Int, Int, CFGNode, Int, Int)]) = { + )(implicit state: State): List[(Int, Int, Int, Int)] = { + val cfg = tacCode.cfg + val code = tacCode.stmts - println(s"start findguard defaultValue: $defaultValue") 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, CFGNode, Int)] = List.empty //None - - // println("startBB: "+startBB) + var result: List[(Int, Int, Int)] = List.empty //None while (worklist.nonEmpty) { val curBB = worklist.head @@ -432,15 +344,12 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr if (!seen.contains(curBB)) { seen += curBB - //val startPC = curBB.startPC val endPC = curBB.endPC val cfStmt = code(endPC) (cfStmt.astID: @switch) match { - case If.ASTID ⇒ - println("ifstmt: ") - println(cfStmt) + case If.ASTID ⇒ val ifStmt = cfStmt.asIf if (ifStmt.condition.equals(EQ) && curBB != startBB && isGuard( ifStmt, @@ -449,16 +358,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr tacCode, method )) { - println("EQ") - // println("BB") - - //case EQ - //if => - ///if (result.size > 0) { //.isDefined) { - /// if (result.head._1 != endPC || result.head._2 != endPC + 1) {} //return result //None; - ///} else { - result = (endPC, endPC + 1, curBB, ifStmt.targetStmt) :: result - ///} + result = (endPC, endPC + 1, ifStmt.targetStmt) :: result } else if (ifStmt.condition.equals(NE) && curBB != startBB && isGuard( ifStmt, defaultValue, @@ -466,45 +366,19 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr tacCode, method )) { - println("NE") - // println("CC") - //case NE - //if => - ///if (result.size > 0) { //.isDefined) { - /// if (result.head._1 != endPC || result.head._2 != ifStmt.targetStmt) - /// result //None - ///} else { - result = (endPC, ifStmt.targetStmt, curBB, endPC + 1) :: result - ///} + result = (endPC, ifStmt.targetStmt, endPC + 1) :: result } else { - println("else") - // println("DD") - // Otherwise, we have to ensure that a guard is present for all predecessors - //case _ => - //if (startPC == 0) result //None; - - //isTransitivePredecessor(cfg.bb(fieldWrite), cfg.bb(ifStmt.target), Set.empty) - - //fieldWrite != ifStmt.target - // - // println("befor transitive call...") if ((cfg.bb(fieldWrite) != cfg.bb(ifStmt.target) || fieldWrite < ifStmt.target) && - isTransitivePredecessor(cfg.bb(fieldWrite), cfg.bb(ifStmt.target))) { //(ifStmt.target > fieldWrite) - println("is transitive predecessor: ") + 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 _ ⇒ - ///if (startPC == 0) result //None; - val predecessors = getPredecessors(curBB, enqueuedBBs) worklist ++= predecessors enqueuedBBs ++= predecessors @@ -512,27 +386,31 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr } } - println(s"tmpResult: ${result.mkString("\n")}") - var finalResult: List[(Int, Int, Int, CFGNode, Int, Int)] = List.empty + + var finalResult: List[(Int, Int, Int, Int)] = List.empty // 2 3 5 6 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 expr = if (ifStmt.leftExpr.isConst) ifStmt.rightExpr + else ifStmt.leftExpr + val definitions = expr.asVar.definedBy - if (!definitions.exists(_ < 0)) { - //return finalResult; + 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._2) - if (definitions.size == 1 && definitions.head >= 0 && fieldReadUsedCorrectly) - finalResult = (result._1, result._2, definitions.head, result._3, result._4, fieldReadIndex) :: finalResult // Found proper guard + + if (definitions.size == 1 && definitions.head >= 0 && fieldReadUsedCorrectly) { + // Found proper guard + finalResult = (result._2, definitions.head, result._3, fieldReadIndex) :: finalResult + } // 2 3 5 6 } }) finalResult @@ -555,19 +433,15 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr def isTransitivePredecessor(possiblePredecessor: CFGNode, node: CFGNode): Boolean = { - var visited: Set[CFGNode] = Set.empty + 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 + if (possiblePredecessor == node) true + else if (visited.contains(node)) false else { visited += node node.predecessors.exists( - currentNode ⇒ { - isTransitivePredecessorInternal(possiblePredecessor, currentNode) - } + currentNode ⇒ isTransitivePredecessorInternal(possiblePredecessor, currentNode) ) } } @@ -606,39 +480,37 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val defSites = uvar.asVar.definedBy def isConstantDef(index: Int) = { - if (index < 0) - false - else if (code(index).asAssignment.expr.isConst) - true + 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 _ ⇒ // Unknown field - false + state.field == field || + isImmutableReference(propertyStore(field, FieldReferenceImmutability.key)) + + case _ ⇒ false // Unknown field }) } } - val result = defSites == SelfReferenceParameter || - defSites.size == 1 && isConstantDef(defSites.head) - result + 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 _ ⇒ // Unknown field - false - } + 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 @@ -647,53 +519,30 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr 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 ⇒ { + } 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 ⇒ { - //TODO constructor must be deterministic - //TODO check that the nonvirtualmethod call calls the constructor - //TODO check + + case New.ASTID ⇒ val nonVirtualMethodCallIndexes = origin.asAssignment.targetVar.usedBy.iterator. - filter(i ⇒ code(i).isNonVirtualMethodCall) //TODO head - + filter(i ⇒ code(i).isNonVirtualMethodCall) nonVirtualMethodCallIndexes.forall { nonVirtualMethodCallIndex ⇒ - import org.opalj.br.fpcf.properties.Purity - println( - s""" - | field: ${state.field} - | method: $method - | nonVirtualMethodCallIndex: $nonVirtualMethodCallIndex - | nonVirtualMethodCall: ${taCode.stmts(nonVirtualMethodCallIndex).asNonVirtualMethodCall} - |""".stripMargin - ) val callTargetResult = taCode.stmts(nonVirtualMethodCallIndex).asNonVirtualMethodCall.resolveCallTarget( state.field.classFile.thisType ) - - !callTargetResult.value.isConstructor || - !isNonDeterministic(propertyStore(declaredMethods(callTargetResult.value), Purity.key)) + !callTargetResult.isEmpty && (!callTargetResult.value.isConstructor || + //if the constructor is called and it must be deterministic + !isNonDeterministic(propertyStore(declaredMethods(callTargetResult.value), Purity.key))) } - //val result = taCode.stmts(nonVirtualMethodCallIndex).asNonVirtualMethodCall.resolveCallTarget(state.field.classFile.thisType) - //if (!result.isEmpty) - // result.value.isConstructor && - //code(nonVirtualMethodCallIndex).asNonVirtualMethodCall. - //val propertyStoreResultCallees = propertyStore(declaredMethods(method), Callees.key) - //!doCallsIntroduceNonDeterminism(propertyStoreResultCallees, nonVirtualMethodCallIndex) && - // code(nonVirtualMethodCallIndex).asNonVirtualMethodCall.params.forall(isConstant) - // } - } - 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 @@ -701,9 +550,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr lazyInitializerIsDeterministic(method) } } - val result = value.isConst || isNonConstDeterministic(value, taCode) - - result + value.isConst || isNonConstDeterministic(value, taCode) } /** @@ -715,58 +562,86 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr tacCode: TACode[TACMethodParameter, V], index: Int )(implicit state: State): Boolean = { - println("is read of current field: "+expr) def isExprReadOfCurrentField: Int ⇒ Boolean = exprIndex ⇒ exprIndex == index || exprIndex >= 0 && isReadOfCurrentField(tacCode.stmts(exprIndex).asAssignment.expr, tacCode, exprIndex) - println(s"expr: "+expr) + //println("expr: "+expr) (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 ⇒ - val primitiveTypecastExpr = expr.asPrimitiveTypeCastExpr - //val targetType = primitiveTypecastExpr.targetTpe - //targetType.si - //val originType = primitiveTypecastExpr.operand.asVar.cTpe.category. - //println("originType: "+originType) - throw new Exception("-------------------------------") - //TODO check lossless typecast - /*println("operand: "+primitiveTypecastExpr.operand) - val result = primitiveTypecastExpr.operand.asVar.definedBy.forall(isExprReadOfCurrentField) - println("result: "+result) - //throw new Exception("primitive type cast expr") - result*/ - primitiveTypecastExpr.operand.asVar.definedBy.forall(isExprReadOfCurrentField) + /*val primitiveTypecastExpr = expr.asPrimitiveTypeCastExpr + val targetTypeBeforConversion = primitiveTypecastExpr.targetTpe + + if (primitiveTypecastExpr.operand.asVar.definedBy.forall(isExprReadOfCurrentField(_))) { + val fieldTypeBeforConversion = state.field.fieldType + + + val fieldType = + if(fieldTypeBeforConversion.isReferenceType) + fieldTypeBeforConversion + else + fieldTypeBeforConversion.asBaseType.WrapperType + + val targetType = + if(targetTypeBeforConversion.isReferenceType) + targetTypeBeforConversion + else + targetTypeBeforConversion.asBaseType.WrapperType + + //prevent lossy conversion of the field + + fieldType match { + case ObjectType.Integer ⇒ + } + + fieldType == ObjectType.Byte || + (fieldType == ObjectType.Short) && + (targetType != ObjectType.Byte) || + + (fieldType == ObjectType.Integer || fieldType == ObjectType.Float) && + (targetType != ObjectType.Short) && + (targetType != ObjectType.Byte) || + + (fieldType == ObjectType.Long || + fieldType == ObjectType.Double) && + (targetType == ObjectType.Long || + targetType == ObjectType.Double) + } else */ + /*println( + s""" + | class: ${state.field.classFile.thisType} + | field: ${state.field} + |""".stripMargin + ) */ + false case Compare.ASTID ⇒ - /*println("expr: "+expr) - expr.asCompare.left.asVar.definedBy.foreach(isExprReadOfCurrentField) - expr.asCompare.right.asVar.definedBy.foreach(isExprReadOfCurrentField) - throw new Exception("compare") */ val leftExpr = expr.asCompare.left val rightExpr = expr.asCompare.right - (leftExpr.asVar.definedBy.forall(index ⇒ + 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)) + leftExpr.asVar.definedBy.forall(isExprReadOfCurrentField) case VirtualFunctionCall.ASTID ⇒ - val functionCall = expr.asVirtualFunctionCall //.asFunctionCall + val functionCall = expr.asVirtualFunctionCall val fieldType = state.field.fieldType - functionCall.params.isEmpty && ( - fieldType == ObjectType.Integer && functionCall.name == "intValue" || - fieldType == ObjectType.Float && functionCall.name == "floatValue" || - fieldType == ObjectType.Double && functionCall.name == "doubleValue" || - fieldType == ObjectType.Byte && functionCall.name == "byteValue" || - fieldType == ObjectType.Long && functionCall.name == "longValue" + 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 @@ -783,10 +658,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr tacCode: TACode[TACMethodParameter, V], method: Method )(implicit state: State): Boolean = { - import org.opalj.br.FieldType - import scala.annotation.tailrec - println("is guard") /** * Checks if an expression */ @@ -811,11 +683,8 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr * field. */ def isGuardInternal(expr: V, tacCode: TACode[TACMethodParameter, V], method: Method): Boolean = { - println("i1") expr.definedBy forall { index ⇒ - println("index: "+index) - if (index < 0) - false // If the value is from a parameter, this can not be the guard + 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 @@ -823,14 +692,13 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr //in case of Integer etc.... .initValue() val calleesResult = propertyStore(declaredMethods(method), Callees.key) - if (doCallsIntroduceNonDeterminism(calleesResult, code(index).asAssignment.pc)) { - return false - }; + 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)) + receiverDefSite >= 0 && + isReadOfCurrentField(code(receiverDefSite).asAssignment.expr, tacCode, index)) } else { //if(isStaticFunctionCall){ //val staticFunctionCall = code(index).asAssignment.expr.asStaticFunctionCall @@ -849,56 +717,36 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr fieldType.isDoubleType || fieldType.isFloatType || fieldType.isLongType if (ifStmt.rightExpr.isVar && isFloatDoubleOrLong(state.field.fieldType) && ifStmt.rightExpr.asVar.definedBy.head > 0 && tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.isCompare) { - //ifStmt.leftExpr.isIntConst - //ifStmt.leftExpr.asIntConst.value==0 - // println("-1") + val left = tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.asCompare.left.asVar - // println("-2") val right = tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.asCompare.right.asVar - // println("-3") val leftExpr = tacCode.stmts(left.definedBy.head).asAssignment.expr - // println("-4") val rightExpr = tacCode.stmts(right.definedBy.head).asAssignment.expr - // println("-5") - if (leftExpr.isGetField) { - // println("-6") - val result = isDefaultConst(rightExpr) - // println("result: "+result) - result - } else if (rightExpr.isGetField) { - // ("-7") - val result = isDefaultConst(leftExpr) - // println("result-: "+result) - result - } else { false } //TODO reasoning + + if (leftExpr.isGetField) isDefaultConst(rightExpr) + else if (rightExpr.isGetField) isDefaultConst(leftExpr) + else false //TODO reasoning + } else if (ifStmt.leftExpr.isVar && ifStmt.rightExpr.isVar && ifStmt.leftExpr.asVar.definedBy.head >= 0 && ifStmt.rightExpr.asVar.definedBy.head >= 0 && isFloatDoubleOrLong(state.field.fieldType) && tacCode.stmts(ifStmt.leftExpr.asVar.definedBy.head). asAssignment.expr.isCompare && ifStmt.leftExpr.isVar && ifStmt.rightExpr.isVar) { - // println("9") + 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) { - val result = isDefaultConst(rightExpr) - result - } else if (rightExpr.isGetField) { - isDefaultConst(leftExpr) - } else false //TODO reasoning + + if (leftExpr.isGetField) isDefaultConst(rightExpr) + else if (rightExpr.isGetField) isDefaultConst(leftExpr) + else false //TODO reasoning + } else if (ifStmt.rightExpr.isVar && isDefaultConst(ifStmt.leftExpr)) { - // println("12") isGuardInternal(ifStmt.rightExpr.asVar, tacCode, method) } else if (ifStmt.leftExpr.isVar && isDefaultConst(ifStmt.rightExpr)) { - // println("13") - val result = isGuardInternal(ifStmt.leftExpr.asVar, tacCode, method) - // println(s"result: $result") - result - } else { - // println("14") - false - } + isGuardInternal(ifStmt.leftExpr.asVar, tacCode, method) + } else false } /** @@ -914,25 +762,29 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val startBB = cfg.bb(0).asBasicBlock var queuedNodes: Set[CFGNode] = Set.empty var workList = getSuccessors(startBB, queuedNodes) + workList ++= Set(startBB) - var potentiallyReadIndex = -1 + + var potentiallyReadIndex: Option[Int] = None + while (workList.nonEmpty) { val currentBB = workList.head workList = workList.tail + val startPC = { - if (currentBB == startBB) - writeIndex + 1 - else - currentBB.startPC + if (currentBB == startBB) writeIndex + 1 + else currentBB.startPC } + val endPC = currentBB.endPC var index = startPC + while (index <= endPC) { val stmt = tacCode.stmts(index) if (stmt.isAssignment) { val assignment = stmt.asAssignment if (isReadOfCurrentField(assignment.expr, tacCode, index)) { - potentiallyReadIndex = index + potentiallyReadIndex = Some(index) } } else if (stmt.isReturnValue) { val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy @@ -941,13 +793,12 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr returnValueDefs.contains(readIndex)) { return true; } // direct return of the written value - else if (potentiallyReadIndex >= 0 && (returnValueDefs == IntTrieSet(potentiallyReadIndex) || - returnValueDefs == IntTrieSet(readIndex, potentiallyReadIndex))) { + else if (potentiallyReadIndex.isDefined && + (returnValueDefs == IntTrieSet(potentiallyReadIndex.get) || + returnValueDefs == IntTrieSet(readIndex, potentiallyReadIndex.get))) { return true; } // return of field value loaded by field read - else { - return false; - } // return of different value + else return false; // return of different value } index = index + 1 } 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 index c6c7e6f987..20570e7fd0 100644 --- 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 @@ -88,9 +88,10 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP def doDetermineFieldReferenceImmutability(entity: Entity): PropertyComputationResult = { entity match { - case field: Field ⇒ { + + case field: Field ⇒ determineFieldReferenceImmutability(field) - } + case _ ⇒ val m = entity.getClass.getSimpleName+" is not an org.opalj.br.Field" throw new IllegalArgumentException(m) @@ -107,14 +108,6 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP private[analyses] def determineFieldReferenceImmutability( field: Field ): ProperPropertyComputationResult = { - /* println( - s""" - | determine field reference immutability: - | field ${field} - | - | - |""".stripMargin - )*/ if (field.isFinal) return Result(field, ImmutableFieldReference); @@ -217,12 +210,12 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { var isNotFinal = false - println( + /*println( s""" | enter continuation | eps: $eps |""".stripMargin - ) + ) */ eps.pk match { case EscapeProperty.key ⇒ val newEP = eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]] @@ -267,7 +260,7 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP isNotFinal = !isImmutableReference(newEP) } - println("result is not final: "+isNotFinal) + // println("result is not final: "+isNotFinal) if (isNotFinal) state.referenceImmutability = MutableFieldReference createResult() @@ -284,7 +277,7 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP )(implicit state: State): Boolean = { val field = state.field val stmts = taCode.stmts - for (pc ← pcs.iterator) { + 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) @@ -293,45 +286,34 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP case PutStatic.ASTID | PutField.ASTID ⇒ if (method.isInitializer) { if (field.isStatic) { - if (method.isConstructor) - return true; + method.isConstructor } else { val receiverDefs = stmt.asPutField.objRef.asVar.definedBy - if (receiverDefs != SelfReferenceParameter) - return true; + 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 - if (state.referenceImmutability == LazyInitializedThreadSafeFieldReference || - state.referenceImmutability == LazyInitializedNotThreadSafeButDeterministicFieldReference) //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; - - // A field written outside an initializer must be lazily - // initialized or it is non-final - val result = handleLazyInitialization( - index, - getDefaultValue(), - method, - taCode.stmts, - taCode.cfg, - taCode.pcToIndex, - taCode - ) - if (result) { - println("result7: "+result) - return result - }; + 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 + handleLazyInitialization( + index, + getDefaultValue(), + method, + taCode + ) } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { - println("reference has escaped") + // 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 @@ -342,7 +324,17 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP // 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. - return true; + true + } else { + false + /*println( + s""" + | class: ${state.field.classFile.thisType} + | field: ${state.field} + | method: $method + |""".stripMargin + ) + throw new Exception("else case in methodUpdatesField")*/ } } @@ -350,10 +342,11 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP } } else { // nothing to do as the put field is dead + false } - } + } else false } - false + //false } /** @@ -367,15 +360,12 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP )(implicit state: State): Boolean = { ref.definedBy.forall { defSite ⇒ if (defSite < 0) true - // Must be locally created - else { + 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)) - } + else handleEscapeProperty(propertyStore(definitionSites(method, definition.pc), EscapeProperty.key)) } } } @@ -388,30 +378,25 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP def handleEscapeProperty( ep: EOptionP[DefinitionSite, EscapeProperty] )(implicit state: State): Boolean = { - ep match { - case FinalP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ - false - - case FinalP(AtMost(_)) ⇒ - true - - case _: FinalEP[DefinitionSite, EscapeProperty] ⇒ - true // Escape state is worse than via return + //println(s"ep: $ep") + 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 InterimUBP(AtMost(_)) ⇒ true + case _: InterimEP[DefinitionSite, EscapeProperty] ⇒ true // Escape state is worse than via return case _ ⇒ state.escapeDependees += ep false } + println(s"result: $result") + result } } @@ -444,7 +429,7 @@ object EagerL0FieldReferenceImmutabilityAnalysis extends L0FieldReferenceImmutab final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new L0FieldReferenceImmutabilityAnalysis(p) - val fields = p.allFields + val fields = p.allFields // p.allProjectClassFiles.flatMap(_.fields) // p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldReferenceImmutability) analysis } From c8f8ac0d82ea9904bf5162e73351c99e0fd49b61 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 26 Oct 2020 09:36:51 +0100 Subject: [PATCH 287/327] replaced deprecated property --- .../opalj/br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala index 42a148f299..c8b86ff63f 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala @@ -27,11 +27,9 @@ import org.opalj.br.analyses.cg.TypeExtensibilityKey import org.opalj.br.fpcf.properties.ClassImmutability import org.opalj.br.fpcf.properties.MutableType import org.opalj.br.fpcf.properties.DeepImmutableClass -import org.opalj.br.fpcf.properties.DeepImmutableType 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.ImmutableContainerType import org.opalj.br.fpcf.properties.ShallowImmutableType import org.opalj.br.fpcf.properties.DeepImmutableType @@ -182,7 +180,7 @@ class L0TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAn } } if (joinedImmutability == maxImmutability) { - assert(maxImmutability == ImmutableContainerType) + assert(maxImmutability == ShallowImmutableType) Result(t, maxImmutability) } else { InterimResult( From dac59622295f5f310262e23e8fae15fb787e3b9b Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 26 Oct 2020 09:45:54 +0100 Subject: [PATCH 288/327] conversion and renaming; still wip --- .../fpcf/ClassAndTypeImmutabilityTests.scala | 24 +++--- .../opalj/fpcf/FieldImmutabilityTests.scala | 44 ++-------- .../FieldReferenceImmutabilityTests.scala | 66 ++++----------- ... _AllFieldImmutabilityAnalysisTests.scala} | 80 +++++++++++++++---- .../AbstractClassImmutabilityMatcher.scala | 31 +++++-- ....scala => _ClassImmutabilityMatcher.scala} | 12 +-- .../fields/FieldImmutabilityMatcher.scala | 24 +++--- .../fields/_FieldImmutabilityMatcher.scala} | 29 +++---- .../FieldReferenceImmutabilityMatcher.scala | 2 +- .../AbstractTypeImmutabilityMatcher.scala | 17 ++++ ...cala => _NewTypeImmutabilityMatcher.scala} | 12 +-- 11 files changed, 175 insertions(+), 166 deletions(-) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/{FieldMutabilityTests.scala => _AllFieldImmutabilityAnalysisTests.scala} (50%) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/{ClassImmutabilityMatcher.scala => _ClassImmutabilityMatcher.scala} (70%) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/{field_mutability/FieldMutabilityMatcher.scala => immutability/fields/_FieldImmutabilityMatcher.scala} (59%) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/{NewTypeImmutabilityMatcher.scala => _NewTypeImmutabilityMatcher.scala} (72%) 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 index ab0ff21e48..a7a7345b64 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala @@ -2,23 +2,27 @@ package org.opalj package fpcf -import org.opalj.ai.fpcf.analyses.LazyL0BaseAIAnalysis +//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.EagerL1ClassImmutabilityAnalysis import org.opalj.tac.fpcf.analyses.immutability.EagerL1TypeImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyL3FieldImmutabilityAnalysis +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.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.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 /** * Tests if the properties specified in the test project (the classes in the (sub-)package of @@ -46,10 +50,10 @@ class ClassAndTypeImmutabilityTests extends PropertiesTest { describe("the 0 class and type immutability analyses are executed") { val as = executeAnalyses(Set( - EagerL1ClassImmutabilityAnalysis, - EagerL1TypeImmutabilityAnalysis, + EagerL0ClassImmutabilityAnalysis, + EagerL0TypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL3FieldImmutabilityAnalysis, + LazyL2FieldImmutabilityAnalysis, LazyL0FieldReferenceImmutabilityAnalysis, LazyL0BaseAIAnalysis )) @@ -62,7 +66,7 @@ class ClassAndTypeImmutabilityTests extends PropertiesTest { Set("TypeImmutability", "ClassImmutability") ) } - + /* describe("the 1 class and type immutability analysis are executed") { val as = executeAnalyses(Set( @@ -86,5 +90,5 @@ class ClassAndTypeImmutabilityTests extends PropertiesTest { 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 index ddfaccbeed..8693572e10 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -19,8 +19,10 @@ import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldRefere 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.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 @@ -28,40 +30,6 @@ import org.opalj.tac.fpcf.analyses.EagerL2FieldImmutabilityAnalysis * @author Tobias Roth */ class FieldImmutabilityTests extends PropertiesTest { - /* - override def withRT = true - - override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") - }*/ - /* - override def createConfig(): Config = { - import com.typesafe.config.ConfigValueFactory.fromAnyRef - 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( - InitialInstantiatedTypesKey.ConfigKeyPrefix+ - "AllInstantiatedTypesFinder.projectClassesOnly", - ConfigValueFactory.fromAnyRef(true) - ) - .withValue( - "org.opalj.br.analyses.cg.ClosedPackagesKey", - fromAnyRef("org.opalj.br.analyses.cg.OpenCodeBase") - ) - .withValue("org.opalj.br.analyses.cg.ClassExtensibilityKey", ConfigValueFactory.fromAnyRef( - "org.opalj.br.analyses.cg.ConfiguredExtensibleClasses" - )) - } -*/ override def withRT = true @@ -86,9 +54,8 @@ class FieldImmutabilityTests extends PropertiesTest { validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) } - + /* describe("the org.opalj.fpcf.analyses.L0FieldMutabilityAnalysis is executed") { - import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis val as = executeAnalyses( Set( @@ -103,7 +70,6 @@ class FieldImmutabilityTests extends PropertiesTest { } describe("the org.opalj.fpcf.analyses.L1FieldMutabilityAnalysis is executed") { - import org.opalj.tac.fpcf.analyses.EagerL1FieldImmutabilityAnalysis val as = executeAnalyses( Set( @@ -132,7 +98,7 @@ class FieldImmutabilityTests extends PropertiesTest { validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) } - +*/ describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { val as = executeAnalyses( Set( 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 index 89bec7894e..c87a6c504c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldReferenceImmutabilityTests.scala @@ -3,69 +3,36 @@ package org.opalj package fpcf import java.net.URL -import com.typesafe.config.Config -import com.typesafe.config.ConfigValueFactory -import com.typesafe.config.ConfigValueFactory.fromAnyRef -import org.opalj.br.analyses.cg.InitialEntryPointsKey -import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey 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.LazyClassImmutabilityAnalysis 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.LazyUnsoundPrematurelyReadFieldsAnalysis 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.tac.fpcf.analyses.immutability.LazyL0FieldImmutabilityAnalysis -import org.opalj.tac.fpcf.analyses.immutability.LazyLxClassImmutabilityAnalysis_new -import org.opalj.tac.fpcf.analyses.immutability.LazyLxTypeImmutabilityAnalysis_new +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_new -import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis_new +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 Peter Roth + * @author Tobias Roth */ class FieldReferenceImmutabilityTests extends PropertiesTest { override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability/sandbox") - } - - override def createConfig(): Config = { - 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( - InitialInstantiatedTypesKey.ConfigKeyPrefix+ - "AllInstantiatedTypesFinder.projectClassesOnly", - ConfigValueFactory.fromAnyRef(true) - ) - .withValue( - "org.opalj.br.analyses.cg.ClosedPackagesKey", - fromAnyRef("org.opalj.br.analyses.cg.OpenCodeBase") - ) - .withValue("org.opalj.br.analyses.cg.ClassExtensibilityKey", ConfigValueFactory.fromAnyRef( - "org.opalj.br.analyses.cg.ConfiguredExtensibleClasses" - )) + List("org/opalj/fpcf/fixtures/immutability") } override def init(p: Project[URL]): Unit = { @@ -80,31 +47,28 @@ class FieldReferenceImmutabilityTests extends PropertiesTest { describe("no analysis is scheduled") { val as = executeAnalyses(Set.empty) as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldReferenceImmutability")) } - L2PurityAnalysis_new.setRater(Some(SystemOutLoggingAllExceptionRater)) + L2PurityAnalysis.setRater(Some(SystemOutLoggingAllExceptionRater)) describe("the org.opalj.fpcf.analyses.L0ReferenceImmutability is executed") { val as = executeAnalyses( Set( EagerL0FieldReferenceImmutabilityAnalysis, - LazyL0FieldImmutabilityAnalysis, - LazyLxClassImmutabilityAnalysis_new, - LazyLxTypeImmutabilityAnalysis_new, + LazyL3FieldImmutabilityAnalysis, + LazyL1ClassImmutabilityAnalysis, + LazyL1TypeImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyStaticDataUsageAnalysis, - LazyL2PurityAnalysis_new, + LazyL2PurityAnalysis, LazyL0CompileTimeConstancyAnalysis, LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis, - LazyFieldLocalityAnalysis, - LazyL1FieldMutabilityAnalysis, - LazyClassImmutabilityAnalysis, - LazyTypeImmutabilityAnalysis + LazyFieldLocalityAnalysis ) ) as.propertyStore.shutdown() - validateProperties(as, fieldsWithAnnotations(as.project), Set("ReferenceImmutability")) + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldReferenceImmutability")) } } 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..e8aa36cbe2 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") + } 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/immutability/classes/AbstractClassImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/AbstractClassImmutabilityMatcher.scala index a6ccea4225..3dc2eb6405 100644 --- 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 @@ -1,16 +1,33 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.immutability.classes +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 -import org.opalj.fpcf.Property -import org.opalj.fpcf.properties.AbstractPropertyMatcher -class AbstractClassImmutabilityMatcher( - val property: properties.ClassImmutability -) extends AbstractPropertyMatcher { +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[_], @@ -48,7 +65,7 @@ class MutableClassMatcher extends AbstractPropertyMatcher { properties: Traversable[Property] ): Option[String] = { if (properties.exists { - case _: MutableClass ⇒ true // MutableObject ⇒ true + case _: MutableClass ⇒ true case _ ⇒ false }) Some(a.elementValuePairs.head.value.asStringValue.value) 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 similarity index 70% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/ClassImmutabilityMatcher.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/classes/_ClassImmutabilityMatcher.scala index 469d507a30..0343f254b0 100644 --- 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 @@ -8,13 +8,13 @@ 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_new +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_new) extends AbstractPropertyMatcher { +class _ClassImmutabilityMatcher(val property: ClassImmutability) extends AbstractPropertyMatcher { final private val PropertyReasonID = 0 @@ -50,10 +50,10 @@ class ClassImmutabilityMatcher(val property: ClassImmutability_new) extends Abst } } -class MutableClassMatcher extends ClassImmutabilityMatcher(br.fpcf.properties.MutableClass) +class _MutableClassMatcher extends _ClassImmutabilityMatcher(br.fpcf.properties.MutableClass) -class DependentImmutableClassMatcher extends ClassImmutabilityMatcher(br.fpcf.properties.DependentImmutableClass) +class _DependentImmutableClassMatcher extends _ClassImmutabilityMatcher(br.fpcf.properties.DependentImmutableClass) -class ShallowImmutableClassMatcher extends ClassImmutabilityMatcher(br.fpcf.properties.ShallowImmutableClass) +class _ShallowImmutableClassMatcher extends _ClassImmutabilityMatcher(br.fpcf.properties.ShallowImmutableClass) -class DeepImmutableClassMatcher extends ClassImmutabilityMatcher(br.fpcf.properties.DeepImmutableClass) +class _DeepImmutableClassMatcher extends _ClassImmutabilityMatcher(br.fpcf.properties.DeepImmutableClass) 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 index 80f89aec6a..028d6adc4e 100644 --- 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 @@ -1,21 +1,20 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.properties.immutability.fields +package org.opalj +package fpcf +package properties +package immutability +package fields -import org.opalj.br import org.opalj.br.AnnotationLike import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.FieldImmutability -import org.opalj.fpcf.Entity -import org.opalj.fpcf.Property -import org.opalj.fpcf.properties.AbstractPropertyMatcher -/*import org.opalj.br.fpcf.properties.FieldMutability -import org.opalj.br.fpcf.properties.LazyInitializedField*/ /** * 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 */ @@ -52,16 +51,11 @@ class FieldImmutabilityMatcher(val property: FieldImmutability) extends Abstract None } } - } -/* -class DeclaredFinalMatcher extends FieldImmutabilityMatcher(ShallowImmutableField) -class EffectivelyFinalMatcher extends FieldImmutabilityMatcher(ShallowImmutableField) */ +class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.ShallowImmutableField) -//class MutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.MutableField) -class ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.ShallowImmutableField) //ShallowImmutableField) +class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.DependentImmutableField) -class DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.ShallowImmutableField) -class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.ShallowImmutableField) +class DeepImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.DeepImmutableField) 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 59% 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..d6c91a8e42 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,26 +2,21 @@ 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 - * given property and a sufficiently capable analysis was scheduled. - * - * @author Michael Eichberg - * @author Dominik Helm + * This is the basis for the matchers that match the immutability of a field + * @author Tobias Peter Roth */ -class FieldMutabilityMatcher(val property: FieldMutability) extends AbstractPropertyMatcher { +class _FieldImmutabilityMatcher(val property: FieldImmutability) extends AbstractPropertyMatcher { - private final val PropertyReasonID = 0 + final private val PropertyReasonID = 0 override def isRelevant( p: SomeProject, @@ -36,6 +31,7 @@ class FieldMutabilityMatcher(val property: FieldMutability) extends AbstractProp val analyses = analysesElementValues.map(ev ⇒ ev.asClassValue.value.asObjectType) analyses.exists(as.contains) + } def validateProperty( @@ -52,11 +48,12 @@ class FieldMutabilityMatcher(val property: FieldMutability) extends AbstractProp None } } - } -class DeclaredFinalMatcher extends FieldMutabilityMatcher(DeclaredFinalField) +class _MutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.MutableField) + +class _ShallowImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.ShallowImmutableField) -class EffectivelyFinalMatcher extends FieldMutabilityMatcher(EffectivelyFinalField) +class _DependentImmutableFieldMatcher extends FieldImmutabilityMatcher(br.fpcf.properties.DependentImmutableField) -class LazyInitializedMatcher extends FieldMutabilityMatcher(LazyInitializedField) +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 index 71d2acc9ce..d33fdb3f82 100644 --- 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 @@ -12,7 +12,7 @@ import org.opalj.br.fpcf.properties.FieldReferenceImmutability /** * This is the basis for the matchers that match the immutability of a field reference - * @author Tobias Peter Roth + * @author Tobias Roth */ class FieldReferenceImmutabilityMatcher(val property: FieldReferenceImmutability) extends AbstractPropertyMatcher { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/AbstractTypeImmutabilityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/AbstractTypeImmutabilityMatcher.scala index 8003f8508c..a112aa8a5e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/AbstractTypeImmutabilityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/AbstractTypeImmutabilityMatcher.scala @@ -12,6 +12,23 @@ 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], 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 similarity index 72% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/NewTypeImmutabilityMatcher.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/immutability/types/_NewTypeImmutabilityMatcher.scala index 940b8f4716..701c8234a9 100644 --- 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 @@ -8,13 +8,13 @@ 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_new +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_new) +class _NewTypeImmutabilityMatcher(val property: TypeImmutability) extends AbstractPropertyMatcher { final private val PropertyReasonID = 0 @@ -49,10 +49,10 @@ class NewTypeImmutabilityMatcher(val property: TypeImmutability_new) } } } -class NewMutableTypeMatcher extends NewTypeImmutabilityMatcher(br.fpcf.properties.MutableType_new) +class _NewMutableTypeMatcher extends _NewTypeImmutabilityMatcher(br.fpcf.properties.MutableType) -class ShallowImmutableTypeMatcher extends NewTypeImmutabilityMatcher(br.fpcf.properties.ShallowImmutableType) +class _ShallowImmutableTypeMatcher extends _NewTypeImmutabilityMatcher(br.fpcf.properties.ShallowImmutableType) -class DependentImmutableTypeMatcher extends NewTypeImmutabilityMatcher(br.fpcf.properties.DependentImmutableType) +class _DependentImmutableTypeMatcher extends _NewTypeImmutabilityMatcher(br.fpcf.properties.DependentImmutableType) -class DeepImmutableTypeMatcher extends NewTypeImmutabilityMatcher(br.fpcf.properties.DeepImmutableType) +class _DeepImmutableTypeMatcher extends _NewTypeImmutabilityMatcher(br.fpcf.properties.DeepImmutableType) From ea994eb670261ef98f09b4c8b7327cf6643477d0 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 26 Oct 2020 09:53:47 +0100 Subject: [PATCH 289/327] conversion and renaming; still wip --- .../fixtures/escape/EscapesOfExceptions.java | 4 ++-- .../field_mutability/DeclaredFinalFields.java | 14 ++++++------ .../field_mutability/LazyInitialization.java | 3 ++- .../field_mutability/PrivateFieldUpdater.java | 20 ++++++++--------- .../fixtures/field_mutability/Singleton.java | 22 +++++++++---------- 5 files changed, 32 insertions(+), 31 deletions(-) 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 087081d919..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,8 +1,8 @@ /* 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 @@ -26,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 54a9c96cb0..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 @@ -32,6 +32,7 @@ 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. @@ -41,7 +42,7 @@ class Simple { - @ShallowImmutableField("Simple lazy initialization") + @ShallowImmutableField(value = "Simple lazy initialization", analyses = L2FieldImmutabilityAnalysis.class) @MutableField(value = "Analysis doesn't recognize lazy initialization", analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) private int x; 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 { From d17c66902cff1fc6ac6a7e24087f99231c265f9e Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 26 Oct 2020 10:15:02 +0100 Subject: [PATCH 290/327] conversion and renaming; still wip --- .../PossibleExceptionInInitialization.java | 40 -- .../DependentClassWithGenericField.java | 391 +++++++++++ .../DependentClassWithGenericField_deep1.java | 255 ------- .../immutability/classes/generic/Nested.java | 78 +- .../classes/inheriting/EmptyClass.java | 56 +- .../classes/interfaces/EmptyInterface.java | 21 +- .../GenericBaseClass.java | 379 ++++++++++ .../GenericEscapesTransitive.java | 89 ++- .../GenericExt2Deep.java | 12 +- .../GenericExt2Shallow.java | 12 +- .../Generic_class1.java | 281 -------- .../MultipleNestedInnerClasses.java | 116 +++ .../classes/trivials/Container.java | 99 +++ .../trivials/ShallowImmutableClass.java | 102 ++- .../field_references/DeclaredFinalFields.java | 88 +++ .../field_references/LazyInitialization.java | 445 ++++++++++++ .../field_references/PrivateFieldUpdater.java | 53 ++ ...rivateFinalArrayEscapesViaConstructor.java | 15 +- .../field_references/Singleton.java | 57 ++ .../field_references/StaticFields.java | 20 + .../field_references/Template.java | 45 ++ .../DCL.java | 261 +++++++ .../DoubleCheckedLocking.java | 298 ++++++++ .../SimpleStringModel.java | 50 ++ .../simpleLazyInitialization.java | 50 ++ .../immutability/fields/ArrayAndString.java | 28 +- .../immutability/fields/ArrayClasses.java | 83 +-- .../fields/ArrayInitialization.java | 60 +- .../fields/ArrayWithOneEscapingObject.java | 39 +- ...utableFieldWhichIsSetInTheConstructor.java | 38 + ...ImmutableFieldWhereTheObjectCanEscape.java | 26 + ...ClassWithDeepImmutableFieldWithGetter.java | 35 + .../ClassWithEffectiveDeepImmutableField.java | 39 + ...ClassWithFieldReferringEscapingObject.java | 42 ++ ...dMutableTypeThatIsSetInTheConstructor.java | 33 + ...WithPrivateNotEffectiveImmutableField.java | 33 + ...veImmutableFieldWithDeepImmutableType.java | 23 + .../fields/ClassWithProtectedFields.java | 68 ++ .../ClassWithShallowImmutableField.java | 18 + ...FieldWhereTheObjectCanEscapeViaGetter.java | 27 + .../fields/ClassWithStaticFields.java | 24 +- ...lassWithdirectlySetDeepImmutableField.java | 33 + .../ConstructorWithEscapingParameters.java | 15 +- .../fields/DifferentModifier.java | 91 +++ .../fields/EffectivelyImmutableFields.java | 664 +++++++++--------- .../immutability/fields/Escapers.java | 132 ++-- .../immutability/fields/FinalEmptyClass.java | 15 +- .../immutability/fields/MethodCalls.java | 41 +- .../fields/privateFieldNotBlank_deep.java | 14 - .../fields/privateFieldNotBlank_shallow.java | 15 - ...FinalFieldBlank_costructorEscape_deep.java | 16 - ...alFieldBlank_costructorEscape_shallow.java | 17 - .../fields/private_getterEscape_deep.java | 17 - .../fields/private_getterEscape_shallow.java | 19 - .../fields/private_setter_deep.java | 20 - .../fields/private_setter_shallow.java | 20 - .../privatefinal_getterEscape_deep.java | 17 - .../privatefinal_getterEscape_shallow.java | 16 - .../fields/protectedClass_deep.java | 44 -- .../reference/DeclaredFinalFields.java | 92 --- .../reference/LazyInitialization.java | 377 ---------- .../reference/PrivateFieldUpdater.java | 34 - .../immutability/reference/Singleton.java | 39 - .../immutability/reference/StaticFields.java | 11 - .../immutability/reference/Template.java | 29 - .../DCL.java | 387 ---------- .../DoubleCheckedLocking.java | 322 --------- .../S.java | 30 - .../SimpleLazyInstantiation.java | 37 - .../WithMutableAndImmutableFieldType.java | 68 +- .../fpcf/fixtures/mutability/Container.java | 66 -- .../fpcf/fixtures/purity/DependentCalls.java | 16 +- .../fpcf/fixtures/purity/PrimitiveTypes.java | 4 +- .../fpcf/fixtures/purity/ReferenceTypes.java | 16 +- .../ImmutableContainerObject.java | 13 +- .../class_mutability/ImmutableObject.java | 13 +- .../class_mutability/MutableObject.java | 13 +- .../field_mutability/LazyInitialized.java | 56 -- .../properties/field_mutability/NonFinal.java | 39 - ...DeclaredFinal.java => _DeclaredFinal.java} | 17 +- ...ivelyFinal.java => _EffectivelyFinal.java} | 17 +- .../classes/DeepImmutableClass.java | 4 +- .../classes/DependentImmutableClass.java | 13 +- .../immutability/classes/MutableClass.java | 4 +- .../classes/ShallowImmutableClass.java | 14 +- .../fields/DeepImmutableField.java | 46 +- .../fields/DependentImmutableField.java | 43 +- .../immutability/fields/MutableField.java | 9 +- .../fields/ShallowImmutableField.java | 47 +- ...ence.java => ImmutableFieldReference.java} | 12 +- ...otThreadSafeButDeterministicReference.java | 14 +- ...itializedNotThreadSafeFieldReference.java} | 16 +- ...yInitializedThreadSafeFieldReference.java} | 14 +- ...erence.java => MutableFieldReference.java} | 12 +- .../immutability/types/DeepImmutableType.java | 16 +- .../types/DependentImmutableType.java | 10 +- .../immutability/types/MutableType.java | 14 +- .../types/ShallowImmutableType.java | 14 +- .../ImmutableContainerType.java | 12 +- .../type_mutability/ImmutableType.java | 8 +- ...{MutableType.java => MutableType_old.java} | 3 +- 101 files changed, 3943 insertions(+), 3213 deletions(-) delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/PossibleExceptionInInitialization.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/GenericBaseClass.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/MultipleNestedInnerClasses.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/Container.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/DeclaredFinalFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/LazyInitialization.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/PrivateFieldUpdater.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/{reference => field_references}/PrivateFinalArrayEscapesViaConstructor.java (62%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/Singleton.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/StaticFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/Template.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/DCL.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/DoubleCheckedLocking.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/SimpleStringModel.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/simpleLazyInitialization.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithADeepImmutableFieldWhichIsSetInTheConstructor.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithDeepImmutableFieldWhereTheObjectCanEscape.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithDeepImmutableFieldWithGetter.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithEffectiveDeepImmutableField.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithFieldReferringEscapingObject.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithPrivateFinalFieldAndMutableTypeThatIsSetInTheConstructor.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithPrivateNotEffectiveImmutableField.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithPrivateNotEffectiveImmutableFieldWithDeepImmutableType.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithProtectedFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithShallowImmutableField.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithShallowImmutableFieldWhereTheObjectCanEscapeViaGetter.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithdirectlySetDeepImmutableField.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/DifferentModifier.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_deep.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_shallow.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_deep.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_shallow.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_deep.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_shallow.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_deep.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_shallow.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_deep.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_shallow.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/protectedClass_deep.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFieldUpdater.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/StaticFields.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLocking.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/S.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/mutability/Container.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/LazyInitialized.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/NonFinal.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/{DeclaredFinal.java => _DeclaredFinal.java} (79%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/{EffectivelyFinal.java => _EffectivelyFinal.java} (56%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/{ImmutableReference.java => ImmutableFieldReference.java} (84%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/{LazyInitializedNotThreadSafeReference.java => LazyInitializedNotThreadSafeFieldReference.java} (79%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/{LazyInitializedThreadSafeReference.java => LazyInitializedThreadSafeFieldReference.java} (82%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/{MutableReference.java => MutableFieldReference.java} (90%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/type_mutability/{MutableType.java => MutableType_old.java} (85%) diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/PossibleExceptionInInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/PossibleExceptionInInitialization.java deleted file mode 100644 index 13367c6d95..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/PossibleExceptionInInitialization.java +++ /dev/null @@ -1,40 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.fixtures.immutability.classes; - -import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeButDeterministicReference; -import org.opalj.fpcf.properties.immutability.references.MutableReference; - -class PossibleExceptionInInitialization { - - @LazyInitializedNotThreadSafeButDeterministicReference("Incorrect because lazy initialization is may " + - "not happen due to exception") - private int x; - - public int init(int i) { - int y = this.x; - if (y == 0) { - int z = 10 / i; - y = x = 5; - } - return y; - } -} - -class CaughtExceptionInInitialization { - - @MutableReference("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; - } - } -} \ No newline at end of file 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..b3f08ed551 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField.java @@ -0,0 +1,391 @@ +/* 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) +@ShallowImmutableClass(value = "can not handle generics", analyses = L0ClassImmutabilityAnalysis.class) +final class ClassWithGenericField_shallow { + + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value="can not handle generic types", + analyses = {L1FieldImmutabilityAnalysis.class, + L2FieldImmutabilityAnalysis.class}) + @DeepImmutableField(value = "the generic type is concretised with deep immutable types", + analyses = 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/DependentClassWithGenericField_deep1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java deleted file mode 100644 index f84590bdef..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/generic/DependentClassWithGenericField_deep1.java +++ /dev/null @@ -1,255 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.generic; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; -import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -public final class DependentClassWithGenericField_deep1 { - - @DependentImmutableFieldAnnotation(value = "", genericString = "T1") - @ImmutableReferenceAnnotation("") - private T1 t1; - - @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") - @ImmutableReferenceAnnotation("eff imm ref") - private SimpleGenericClass gc; - - public DependentClassWithGenericField_deep1(T1 t1) { - this.t1 = t1; - gc = new SimpleGenericClass - (t1, new FinalEmptyClass(), new FinalEmptyClass()); - } -} - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -final class DependentClassWithGenericField_deep01 { - - @DeepImmutableFieldAnnotation(value = "") - private FinalEmptyClass fec; - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("eff imm ref") - private SimpleGenericClass gc; - - public DependentClassWithGenericField_deep01(FinalEmptyClass fec) { - this.fec = fec; - gc = new SimpleGenericClass - (fec, new FinalEmptyClass(), new FinalEmptyClass()); - } -} - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -final class DependentClassWithGenericField_deep2 { - - @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") - @ImmutableReferenceAnnotation("eff imm ref") - private SimpleGenericClass sgc; - - public DependentClassWithGenericField_deep2(T1 t1) { - sgc = new SimpleGenericClass - (t1, new FinalEmptyClass(), new FinalEmptyClass()); - } - -} - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -final class DependentClassWithGenericField_deep11 { - - @DependentImmutableFieldAnnotation(value = "", genericString = "T1") - @ImmutableReferenceAnnotation("") - private T1 t1; - - @DependentImmutableFieldAnnotation(value = "dep imm field", genericString = "T1") - @ImmutableReferenceAnnotation("eff imm ref") - private DependentClassWithGenericField_deep1 gc; - - public DependentClassWithGenericField_deep11(T1 t1) { - this.t1 = t1; - gc = new DependentClassWithGenericField_deep1(t1); - } -} - -@MutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") -class DependentClassWithGenericTypeArgument { - private final SimpleGenericClass sgc; - DependentClassWithGenericTypeArgument(SimpleGenericClass sgc) { - this.sgc = sgc; - } -} - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("It has no fields and is final") -final class FinalEmptyClass { - -} - -@DependentImmutableTypeAnnotation("Dependent Immutability of T1,...,T5") -@DependentImmutableClassAnnotation("Dependent Immutability of T1,...,T5") -final class SimpleGenericClass { - @DependentImmutableFieldAnnotation(value = "T1",genericString = "T1") - @ImmutableReferenceAnnotation("effectively") - private T1 t1; - @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") - @ImmutableReferenceAnnotation("effectively") - private T2 t2; - @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") - @ImmutableReferenceAnnotation("effectively") - private T3 t3; - - public SimpleGenericClass(T1 t1, T2 t2, T3 t3){ - this.t1 = t1; - this.t2 = t2; - this.t3 = t3; - } -} - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -final class GenericAndDeepImmutableFields { - - @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") - @ImmutableReferenceAnnotation("") - private T1 t1; - - @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") - @ImmutableReferenceAnnotation("") - private T2 t2; - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private FinalEmptyClass fec; - - GenericAndDeepImmutableFields(T1 t1, T2 t2, FinalEmptyClass fec){ - this.t1 = t1; - this.t2 = t2; - this.fec = fec; - } -} - -@MutableTypeAnnotation("") -@MutableClassAnnotation("Because of mutable field") -class GenericAndMutableFields { - @MutableFieldAnnotation("Because of mutable reference") - @MutableReferenceAnnotation("Because of public field") - public T1 t1; - @DependentImmutableFieldAnnotation(value = "Because of generic type", genericString = "T2") - @ImmutableReferenceAnnotation("Because of effectively immutable final") - private T2 t2; - GenericAndMutableFields(T1 t1, T2 t2){ - this.t1 = t1; - this.t2 = t2; - } -} - -@MutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") -class GenericAndShallowImmutableFields { - - @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") - private T1 t1; - @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") - private T2 t2; - @ShallowImmutableFieldAnnotation("") - private SimpleMutableClass smc; - GenericAndShallowImmutableFields(T1 t1, T2 t2, SimpleMutableClass smc){ - this.t1 = t1; - this.t2 = t2; - this.smc = smc; - } - -} - -@MutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -class GenericClassWithDeepImmParam { - @DeepImmutableFieldAnnotation("") - private A a; - GenericClassWithDeepImmParam(A a) { - this.a = a; - } -} - -@MutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") -class GenericClassWithExtendingFinalMutableType { - - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private A a; - - GenericClassWithExtendingFinalMutableType(A a){ - this.a = a; - } -} - -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -final class FinalMutableClass{ - public int n = 0; -} - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -final class ClassWithGenericField_deep { - @DeepImmutableFieldAnnotation("deep imm field") - @ImmutableReferenceAnnotation("eff imm ref") - private SimpleGenericClass gc = - new SimpleGenericClass - (new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); -} - -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -final class ClassWithGenericField_mutable { - @MutableFieldAnnotation("deep imm field") - @MutableReferenceAnnotation("eff imm ref") - public SimpleGenericClass gc = - new SimpleGenericClass - (new FinalEmptyClass(), new FinalEmptyClass(), new FinalEmptyClass()); -} - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -final class ClassWithGenericField_shallow { - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private SimpleGenericClass gc = - new SimpleGenericClass - (new SimpleMutableClass(), new FinalEmptyClass(), new FinalEmptyClass()); -} - -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -class DependentClassInheritingMutableOne extends SimpleMutableClass { - private final T field; - public DependentClassInheritingMutableOne(T field) { - this.field = field; - } -} - -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -class SimpleMutableClass{ public int n = 0;} - - - - - - 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 index b319e0ec0c..8d6e1ebd3e 100644 --- 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 @@ -1,33 +1,99 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.classes.generic; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; +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{ - @DependentImmutableFieldAnnotation(value= "", genericString = "T") + + @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 { - @DependentImmutableFieldAnnotation(value="", genericString = "T") - private GenericClass gc; + + @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 { - @DependentImmutableFieldAnnotation(value = "", genericString = "T") + + @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 index 34c33d88ef..b2a6da6568 100644 --- 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 @@ -1,33 +1,53 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.classes.inheriting; -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -import org.opalj.fpcf.properties.type_mutability.MutableType; +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; -@MutableTypeAnnotation("Not final class") -@DeepImmutableClassAnnotation("Class has no fields but is not final") +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "Class has no fields", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) public class EmptyClass { } -@MutableTypeAnnotation("Not final class") -@DeepImmutableClassAnnotation("empty class inheriting empty class") +@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("Because of mutable class") -@MutableClassAnnotation("Because of extending mutable class") -final class EmptyClassInheritingMutableClass extends MutableClass { +@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 { } -@MutableTypeAnnotation("Because of mutable class") -@MutableClassAnnotation("Because of mutable field") -class MutableClass { - @MutableFieldAnnotation("Mutable reference") - @MutableReferenceAnnotation("public field") +@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 index 9bc600028b..8a1a3eb2b1 100644 --- 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 @@ -1,15 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.classes.interfaces; -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +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; -@MutableTypeAnnotation("Not final interface") -@DeepImmutableClassAnnotation("Empty Interface") +@MutableType(value = "interface is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "empty interface", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) public interface EmptyInterface { } -@MutableTypeAnnotation("Not final Interface") -@DeepImmutableClassAnnotation("Interface") +@MutableType(value = "interface is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "interface", + analyses = {L0ClassImmutabilityAnalysis.class, L1ClassImmutabilityAnalysis.class}) interface TrivialInterfaceWithMethods { String getName(); 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..a2e0d76f72 --- /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); + } +} + +@DependentImmutableType(value = "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +@DependentImmutableClass(value = "class has only dependent immutable fields", + analyses = L1ClassImmutabilityAnalysis.class) +class TwoVirgin { + + @DependentImmutableField(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 index 831a7e1a82..1754559937 100644 --- 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 @@ -1,28 +1,71 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; +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.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; +@DeepImmutableType(value = "has only deep immutable fields and is not extensible", +analyses = L1TypeImmutabilityAnalysis.class) +@DeepImmutableClass(value = "has only deep immutable fields", analyses = L1ClassImmutabilityAnalysis.class) +final class ClassWithGenericField { -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -final class ClassWithGenericField_shallow { - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @DeepImmutableField("") + @ImmutableFieldReference("") private SimpleGenericClass gc = new SimpleGenericClass (new SimpleMutableClass(), new FinalEmptyClass(), new FinalEmptyClass()); } -class FinalEmptyClass{} -class SimpleMutableClass{ public int n = 10;} - +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DependentImmutableClass(value = "has only dependent immutable fields", analyses = L1ClassImmutabilityAnalysis.class) class SimpleGenericClass { - A a; - B b; - C c; + + @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; @@ -30,3 +73,19 @@ class SimpleGenericClass { } } +@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 index 8caf9d29ba..b7d08424f6 100644 --- 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 @@ -1,15 +1,13 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; - //TODO @DeepImmutableClassAnnotation("") public class GenericExt2Deep { - //TODO @DeepImmutableFieldAnnotation("") - private Generic_class1 gc; - GenericExt2Deep(Generic_class1 gc) { + /* //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 index b7c87d70fb..9d79040990 100644 --- 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 @@ -1,15 +1,13 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; - //TODO @ShallowImmutableClassAnnotation("") public class GenericExt2Shallow { - //TODO @ShallowImmutableFieldAnnotation("") - private Generic_class1 gc; - GenericExt2Shallow(Generic_class1 gc) { + /* //TODO @ShallowImmutableFieldAnnotation("") + private GenericClass gc; + GenericExt2Shallow(GenericClass gc) { this.gc = gc; - } + }*/ } final class FinalMutableClass{ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java deleted file mode 100644 index e77ee42648..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/multinested_genericClasses/Generic_class1.java +++ /dev/null @@ -1,281 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.classes.multinested_genericClasses; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; -import org.opalj.fpcf.properties.type_immutability.DependentImmutableTypeAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -import org.opalj.fpcf.properties.type_immutability.ShallowImmutableTypeAnnotation; - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -public final class Generic_class1 { - - @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") - private T1 t1; - - @DependentImmutableFieldAnnotation(value = "T2", genericString = "T2") - private T2 t2; - - @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") - private T3 t3; - - @DependentImmutableFieldAnnotation(value = "T4", genericString = "T4") - private T4 t4; - - @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") - private T5 t5; - - public Generic_class1(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ - this.t1 = t1; - this.t2 = t2; - this.t3 = t3; - this.t4 = t4; - this.t5 = t5; - } - -} - -@ShallowImmutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") -final class Generic_class1_extString { - - @ShallowImmutableFieldAnnotation("") - private T1 t1; - - @DeepImmutableFieldAnnotation(value = "T2") - private T2 t2; - - @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") - private T3 t3; - - @DependentImmutableFieldAnnotation(value = "T4", genericString = "T4") - private T4 t4; - - @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") - private T5 t5; - - public Generic_class1_extString(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ - this.t1 = t1; - this.t2 = t2; - this.t3 = t3; - this.t4 = t4; - this.t5 = t5; - } - -} - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -final class Generic_class2 { - - @ImmutableReferenceAnnotation("") - @DependentImmutableFieldAnnotation(value="T1", genericString = "T1") - private T1 t1; - - @ImmutableReferenceAnnotation("") - @DependentImmutableFieldAnnotation(value="T2", genericString = "T2") - private T2 t2; - - @ImmutableReferenceAnnotation("") - @DependentImmutableFieldAnnotation(value="T3", genericString = "T3") - private T3 t3; - - @ImmutableReferenceAnnotation("") - @DependentImmutableFieldAnnotation(value = "", genericString = "") - private Generic_class1 gc; - - - public Generic_class2(T1 t1, T2 t2, T3 t3, FinalClassWithoutFields fec1, FinalClassWithoutFields fec2){ - this.t1 = t1; - this.t2 = t2; - this.t3 = t3; - gc = new Generic_class1(fec1, fec2, t1,t2,t3); - } - -} - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -final class Generic_class3 { - - @ImmutableReferenceAnnotation("") - @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") - private T1 t1; - - @ImmutableReferenceAnnotation("") - @DependentImmutableFieldAnnotation(value = "", genericString = "") - private Generic_class2 gc; - - public Generic_class3(T1 t1, FinalClassWithoutFields fec1, FinalClassWithoutFields fec2, FinalClassWithoutFields fec3, FinalClassWithoutFields fec4){ - this.t1 = t1; - gc = new Generic_class2(t1, fec1, fec2, fec3, fec4); - } -} - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -final class Generic_class4_deep { - - @ImmutableReferenceAnnotation("") - @DeepImmutableFieldAnnotation("") - private Generic_class3 gc; - - public Generic_class4_deep(FinalClassWithoutFields fec1, FinalClassWithoutFields fec2, FinalClassWithoutFields fec3, FinalClassWithoutFields fec4, FinalClassWithoutFields fec5){ - gc = new Generic_class3(fec1, fec2, fec3, fec4, fec5); - } -} - -@ShallowImmutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") -final class Generic_class4_shallow { - - @ImmutableReferenceAnnotation("") - @ShallowImmutableFieldAnnotation("") - private Generic_class3 gc; - - public Generic_class4_shallow(ClassWithOneMutableField tmc1, FinalClassWithoutFields fec2, FinalClassWithoutFields fec3, FinalClassWithoutFields fec4, FinalClassWithoutFields fec5){ - gc = new Generic_class3(tmc1, fec2, fec3, fec4, fec5); - } -} - -@DeepImmutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") -final class DeepGeneric { - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private Generic_class1 gc1; - - public DeepGeneric(Generic_class1 gc1){ - this.gc1 = gc1; - } - -} - -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -class One { - @DependentImmutableFieldAnnotation(value="",genericString = "A") - @ImmutableReferenceAnnotation("") - private A a; - @DependentImmutableFieldAnnotation(value="",genericString = "B") - @ImmutableReferenceAnnotation("") - private B b; - @DependentImmutableFieldAnnotation(value="",genericString = "C") - @ImmutableReferenceAnnotation("") - private C c; - @DependentImmutableFieldAnnotation(value="",genericString = "D") - @ImmutableReferenceAnnotation("") - private D d; - @MutableFieldAnnotation("") - @MutableReferenceAnnotation("") - public ClassWithOneMutableField tmc; - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private Generic_class1 gc1; - public One(A a, B b, C c, D d, ClassWithOneMutableField tmc){ - this.a = a; - this.b = b; - this.c = c; - this.d = d; - this.tmc = tmc; - this.gc1 = new Generic_class1(this.a,this.b,this.c,this.d, this.tmc); - } -} - -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -class OneVirgin { - @DependentImmutableFieldAnnotation(value="",genericString = "A") - @ImmutableReferenceAnnotation("") - private A a; - @DependentImmutableFieldAnnotation(value="",genericString = "B") - @ImmutableReferenceAnnotation("") - private B b; - @DependentImmutableFieldAnnotation(value="",genericString = "C") - @ImmutableReferenceAnnotation("") - private C c; - @DependentImmutableFieldAnnotation(value="",genericString = "D") - @ImmutableReferenceAnnotation("") - private D d; - - @MutableFieldAnnotation("") - @MutableReferenceAnnotation("") - public ClassWithOneMutableField tmc; - - Generic_class1 gc1; - public OneVirgin(A a, B b, C c, D d, ClassWithOneMutableField e){ - this.a = a; - this.b = b; - this.c = c; - this.d = d; - this.tmc = tmc; - this.gc1 = new Generic_class1(this.a, this.b, this.c, this.d, this.tmc); - } - - -} - -@MutableTypeAnnotation("") -@ShallowImmutableClassAnnotation("") -class Two { - - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private Generic_class1, B, B, B, ClassWithOneMutableField> gc1; - - public Two(A a, B b, ClassWithOneMutableField tmc, Generic_class1 gc1) { - this.gc1 = new Generic_class1, B, B, B, ClassWithOneMutableField>(gc1,b,b,b,tmc); - } -} - -class TwoVirgin { - @DependentImmutableFieldAnnotation(value="",genericString = "") - @ImmutableReferenceAnnotation("") - private Generic_class1, B, C, C, C> gc1; - - public TwoVirgin(A a, B b, C c, Generic_class1 gc1) { - this.gc1 = new Generic_class1, B, C, C, C>(gc1,b,c,c,c); - } -} - -@DependentImmutableTypeAnnotation("") -@DependentImmutableClassAnnotation("") -final class TestTest { - - @DependentImmutableFieldAnnotation(value = "T1", genericString = "T1") - private Generic_class1 t1; - - @DeepImmutableFieldAnnotation(value = "T2") - private T2 t2; - - @DependentImmutableFieldAnnotation(value = "T3", genericString = "T3") - private T3 t3; - - @DependentImmutableFieldAnnotation(value = "T4", genericString = "T4") - private T4 t4; - - @DependentImmutableFieldAnnotation(value = "T5", genericString = "T5") - private T5 t5; - - public TestTest(Generic_class1 t1, T2 t2, T3 t3, T4 t4, T5 t5){ - this.t1 = t1; - this.t2 = t2; - this.t3 = t3; - this.t4 = t4; - this.t5 = t5; - } - -} - -class ClassWithOneMutableField { public int n = 0;} -final class FinalClassWithoutFields {} - 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/ShallowImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java index 250c92f4a3..080a472102 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java @@ -1,68 +1,96 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.classes.trivials; -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; -import org.opalj.fpcf.properties.type_mutability.MutableType; - -@MutableTypeAnnotation("Because of not final class") -@DeepImmutableClassAnnotation("has shallow immutable field") +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.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.DependentImmutableField; +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.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") +@DeepImmutableClass("has shallow immutable field") public class ShallowImmutableClass { - @DeepImmutableFieldAnnotation("Because object can not escape") - @ImmutableReferenceAnnotation("Because it is private") - private MutableClass mutableClass = new MutableClass(); + + @DeepImmutableField(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") -@MutableClassAnnotation("Because of mutable field") -class MutableClass { - @MutableFieldAnnotation("Because of mutable reference") - @MutableReferenceAnnotation("Because of public field") +@MutableClass("Because of mutable field") +class TrivialMutableClass { + + @MutableField("Because of mutable reference") + @MutableFieldReference("Because of public field") public int n = 0; } -@MutableTypeAnnotation("Because not final class") -@DeepImmutableClassAnnotation("Class has no fields but is not final") +@MutableType(value = "class is extensible", + analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) +@DeepImmutableClass(value = "class has no fields", analyses = L1ClassImmutabilityAnalysis.class) class EmptyClass { } -@DeepImmutableTypeAnnotation("Because of final deep immutable class") -@DeepImmutableClassAnnotation("Class has no fields and is final") +@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 { } -@MutableTypeAnnotation("Because of not final class") -@DependentImmutableClassAnnotation("Das dependent immutable field") -class DependentImmutableClass { - @DependentImmutableFieldAnnotation(value = "Because of type T",genericString = "T") - @ImmutableReferenceAnnotation("Private effectively final field") +@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 DependentImmutableClass(T t){ + + public TrivialDependentImmutableClass(T t){ this.t = t; } } -@MutableTypeAnnotation("Because of not final class") -@DeepImmutableClassAnnotation("Generic Type is not used as field Type") + +@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(); } } -@MutableTypeAnnotation("Because of mutable not final class") -@MutableClassAnnotation("Generic class but public field") +@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 { - @MutableFieldAnnotation("Because of mutable reference") - @MutableReferenceAnnotation("Field is public") + + @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..70a2be308d --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/LazyInitialization.java @@ -0,0 +1,445 @@ +/* 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.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", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) + @LazyInitializedNotThreadSafeButDeterministicReference("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", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) + @LazyInitializedNotThreadSafeButDeterministicReference("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 = "Analysis doesn't recognize lazy initialization", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) + @LazyInitializedNotThreadSafeButDeterministicReference(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 }) + @LazyInitializedNotThreadSafeButDeterministicReference( + 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", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) + @LazyInitializedNotThreadSafeButDeterministicReference(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 }) + @LazyInitializedNotThreadSafeButDeterministicReference("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}) + @MutableFieldReference(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) + @LazyInitializedNotThreadSafeButDeterministicReference(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}) + @MutableFieldReference(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}) + @MutableFieldReference(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", + analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) + @LazyInitializedNotThreadSafeButDeterministicReference(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) + @ImmutableFieldReference(value = "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/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/reference/PrivateFinalArrayEscapesViaConstructor.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/PrivateFinalArrayEscapesViaConstructor.java similarity index 62% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFinalArrayEscapesViaConstructor.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/PrivateFinalArrayEscapesViaConstructor.java index 0d58cea6f9..27faaf2e33 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFinalArrayEscapesViaConstructor.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/PrivateFinalArrayEscapesViaConstructor.java @@ -1,22 +1,23 @@ -package org.opalj.fpcf.fixtures.immutability.reference; +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.immutability.field_references; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; public class PrivateFinalArrayEscapesViaConstructor { - @ImmutableReferenceAnnotation("") + @ImmutableFieldReference("") private final char[] charArray; - @ImmutableReferenceAnnotation("") + @ImmutableFieldReference("") private final byte[] byteArray; - @ImmutableReferenceAnnotation("") + @ImmutableFieldReference("") private final int[] intArray; - @ImmutableReferenceAnnotation("") + @ImmutableFieldReference("") private final long[] longArray; - @ImmutableReferenceAnnotation("") + @ImmutableFieldReference("") private final Object[] objectArray; public PrivateFinalArrayEscapesViaConstructor(char[] charArray, byte[] byteArray, int[] intArray, 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..10656a2236 --- /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; + + @DeepImmutableField(value ="referenced object can not escape", analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField( + value = "only initialized once by the constructor", + analyses = { L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.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/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..f13afa950b --- /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; + } + + @MutableFieldReference(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/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..33be00af15 --- /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,50 @@ +/* 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.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeButDeterministicReference; +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; + } + + @DeepImmutableField(value="", analyses = L3FieldImmutabilityAnalysis.class) + @LazyInitializedNotThreadSafeButDeterministicReference(value = "", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + 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/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..cee7535279 --- /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,50 @@ +/* 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", + analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class}) + @LazyInitializedNotThreadSafeButDeterministicReference(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/fields/ArrayAndString.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ArrayAndString.java index 7a6eb14ab0..435440e406 100644 --- 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 @@ -1,23 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.fields; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; +import org.opalj.fpcf.properties.immutability.fields.DeepImmutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; public class ArrayAndString { - @ShallowImmutableFieldAnnotation("") + + @ShallowImmutableField("") private String[] stringArray; - @ShallowImmutableFieldAnnotation("") + + @ShallowImmutableField("") private int[] intArray; - @DeepImmutableFieldAnnotation("") + + @DeepImmutableField("") private String string; - @DeepImmutableFieldAnnotation("") + + @DeepImmutableField("") private int i; - @ShallowImmutableFieldAnnotation("") - private TrivialMutableClass[] tmc; - @ShallowImmutableFieldAnnotation("") + + @ShallowImmutableField("") + private ClassWithPublicFields[] tmc; + + @ShallowImmutableField("") private T[] tArray; - ArrayAndString(String[] stringArray, int[] intArray, String string, int i, TrivialMutableClass[] tmc, T[] tArray) { + ArrayAndString(String[] stringArray, int[] intArray, String string, int i, + ClassWithPublicFields[] tmc, T[] tArray) { this.stringArray = stringArray; this.intArray = intArray; this.string = string; 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 index c75009b61f..695e3ab6d7 100644 --- 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 @@ -1,29 +1,30 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.fields; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +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 { - @DeepImmutableFieldAnnotation("The elements of the array can not escape") - @ImmutableReferenceAnnotation("Array is eager initialized") + @DeepImmutableField("The elements of the array can not escape") + @ImmutableFieldReference("Array is eager initialized") private Object[] zzz = new Object[]{1, 2, 3}; - @DeepImmutableFieldAnnotation("The elements of the array can not escape") - @ImmutableReferenceAnnotation("Array is initialized in the constructor") + @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}; } - @ShallowImmutableFieldAnnotation("The elements of the array are manipulated after initialization and can escape.") - @ImmutableReferenceAnnotation("The array is eager initialized.") + @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() { @@ -31,8 +32,8 @@ public Object[] getB() { return b; } - @MutableFieldAnnotation("Array has a mutable reference.") - @MutableReferenceAnnotation("The array is initalized always when the InitC function is called") + @MutableField("Array has a mutable reference.") + @MutableFieldReference("The array is initalized always when the InitC function is called") private Object[] c; public void InitC() { @@ -40,8 +41,8 @@ public void InitC() { } - @ShallowImmutableFieldAnnotation("The elements of the array can escape.") - @ImmutableReferenceAnnotation("The array is eager initialized.") + @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() { @@ -49,8 +50,8 @@ public Object[] getD() { } - @ShallowImmutableFieldAnnotation("The elements of the array can escape.") - @LazyInitializedThreadSafeReferenceAnnotation("The array is initialized lazily.") + @ShallowImmutableField("The elements of the array can escape.") + @LazyInitializedThreadSafeFieldReference("The array is initialized lazily.") private Object[] e; public synchronized Object[] getE() { @@ -65,8 +66,8 @@ public synchronized Object[] getE() { } - @MutableFieldAnnotation("The reference is seen as mutable.") - @LazyInitializedNotThreadSafeReferenceAnnotation("The array is initialized lazily but not thread safe.") + @MutableField("The reference is seen as mutable.") + @LazyInitializedNotThreadSafeFieldReference("The array is initialized lazily but not thread safe.") private Object[] f; public void getF() { @@ -76,8 +77,8 @@ public void getF() { } - @ShallowImmutableFieldAnnotation("One element of the array is written after initialization.") - @LazyInitializedThreadSafeReferenceAnnotation("The array is initialized lazily and thread safe.") + @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() { @@ -88,13 +89,13 @@ public synchronized void getG() { } - @DeepImmutableFieldAnnotation("The elements of the array can not escape.") - @ImmutableReferenceAnnotation("The array is initialized eagerly.") + @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()}; - @ShallowImmutableFieldAnnotation("The elements of the array can escape.") - @LazyInitializedThreadSafeReferenceAnnotation("The array is initialized thread safe and eagerly.") + @ShallowImmutableField("The elements of the array can escape.") + @LazyInitializedThreadSafeFieldReference("The array is initialized thread safe and eagerly.") private Object[] i; public synchronized Object[] getI(){ @@ -104,8 +105,8 @@ public synchronized Object[] getI(){ } - @DeepImmutableFieldAnnotation("The elements of the array can not escape.") - @LazyInitializedThreadSafeReferenceAnnotation("The array is initialized thread safen and eagerly.") + @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[] @@ -116,13 +117,13 @@ public synchronized void getJ(){ //Object[] } - @DeepImmutableFieldAnnotation("The elements of the array can not escape") - @ImmutableReferenceAnnotation("The array is not initialized.") + @DeepImmutableField("The elements of the array can not escape") + @ImmutableFieldReference("The array is not initialized.") private Object[] k; - @MutableFieldAnnotation("The reference is seen as mutable.") - @LazyInitializedNotThreadSafeReferenceAnnotation("The array is initialized lazily but not thread-safe.") + @MutableField("The reference is seen as mutable.") + @LazyInitializedNotThreadSafeFieldReference("The array is initialized lazily but not thread-safe.") private int[] m; public int[] getM() { @@ -132,8 +133,8 @@ public int[] getM() { } - @MutableFieldAnnotation("The reference is seen as mutable.") - @LazyInitializedNotThreadSafeReferenceAnnotation("The array is initialized lazily but not thread-safe.") + @MutableField("The reference is seen as mutable.") + @LazyInitializedNotThreadSafeFieldReference("The array is initialized lazily but not thread-safe.") private Object[] n; public Object[] getN(){ //Object[] @@ -144,8 +145,8 @@ public Object[] getN(){ //Object[] } - @ShallowImmutableFieldAnnotation("The elements of the array can escape.") - @LazyInitializedThreadSafeReferenceAnnotation("The array is initialized thread safe and lazily.") + @ShallowImmutableField("The elements of the array can escape.") + @LazyInitializedThreadSafeFieldReference("The array is initialized thread safe and lazily.") private Object[] o; public synchronized Object[] getO(){ @@ -155,8 +156,8 @@ public synchronized Object[] getO(){ return o; } - @ShallowImmutableFieldAnnotation("One element of the array can escape") - @LazyInitializedThreadSafeReferenceAnnotation("The array is thread safe lazily intialized.") + @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) @@ -164,8 +165,8 @@ public synchronized Object getP(){ return p[2]; } - @DeepImmutableFieldAnnotation("The elements of the array can escape, but have a deep immutable reference.") - @LazyInitializedThreadSafeReferenceAnnotation("The array is thread safe lazily intialized.") + @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) 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 index 07b24418d2..306eb08d51 100644 --- 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 @@ -1,12 +1,15 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.fields; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +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 { - @MutableReferenceAnnotation("") + + @MutableFieldReference("") private Object[] array; public Object[] getArray(int n) { @@ -15,23 +18,28 @@ public Object[] getArray(int n) { } return array; } - //@MutableReferenceAnnotation("") + + @MutableFieldReference("") private Object[] b; - public Object[] getB(boolean flag) throws Exception{ + + public Object[] getB(boolean flag) throws Exception { if(b!=null) return b; else if(flag) - return null; //throw new Exception(""); + return b; //throw new Exception(""); else { this.b = new Object[5]; return b; } - } } + + class SimpleLazyObjectsInstantiation{ - @LazyInitializedNotThreadSafeReferenceAnnotation("") + + @LazyInitializedNotThreadSafeFieldReference("") private SimpleLazyObjectsInstantiation instance; + public SimpleLazyObjectsInstantiation getInstance() { if(instance==null) instance = new SimpleLazyObjectsInstantiation(); @@ -39,12 +47,11 @@ public SimpleLazyObjectsInstantiation getInstance() { } } - - - class EscapingObjectDeep { - @DeepImmutableFieldAnnotation("") + + @DeepImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) private Object o; + public synchronized Object getO(){ if(this.o==null) this.o = new Object(); @@ -52,15 +59,20 @@ public synchronized Object getO(){ } } -class EscapingObjectShallow { - @ShallowImmutableFieldAnnotation("") +class EscapingObjectThatIsShallow { + + @ShallowImmutableField(value = "there are more than one object possibly assigned", + analyses = L3FieldImmutabilityAnalysis.class) private final Object o; - public EscapingObjectShallow() { + + public EscapingObjectThatIsShallow() { this.o = new EmptyClass(); } - public EscapingObjectShallow(int n) { + + public EscapingObjectThatIsShallow(int n) { this.o = new Object(); } + public Object getO(){ return this.o; } @@ -68,7 +80,7 @@ public Object getO(){ class ClassUsingEmptyClass { - @DeepImmutableFieldAnnotation("") + @DeepImmutableField(value = "concrete object is known", analyses = L3FieldImmutabilityAnalysis.class) private EmptyClass emptyClass = new EmptyClass(); public EmptyClass getEmptyClass() { @@ -76,18 +88,18 @@ public EmptyClass getEmptyClass() { } } + class ClassUsingEmptyClassExtensible { - @ShallowImmutableFieldAnnotation("") + @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 { - - -class EmptyClass {} \ No newline at end of file +} 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 index ca224ade2a..f6234ad095 100644 --- 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 @@ -1,31 +1,37 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.fields; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +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 { - @MutableFieldAnnotation("Reference of the field is mutable") - @MutableReferenceAnnotation("Field is public") + + @MutableField("Reference of the field is mutable") + @MutableFieldReference("Field is public") public Object o = new Object(); - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("Reference is only initialized once") + @ShallowImmutableField("") + @ImmutableFieldReference("Reference is only initialized once") private Object[] array1 = new Object[]{o, new Object(), new Object()}; //TODO - @ShallowImmutableFieldAnnotation("Field is initialized with an Shallow immutable field") - @ImmutableReferenceAnnotation("Field is only initialized once.") + @ShallowImmutableField("Field is initialized with an Shallow immutable field") + @ImmutableFieldReference("Field is only initialized once.") private Object[] array2; public ArrayWithOneEscapingObject() { array2 = new Object[]{o}; } - @ShallowImmutableFieldAnnotation("Field is initialized with a shallow immutable field.") - @LazyInitializedThreadSafeReferenceAnnotation("Synchronized method with a guard-statement around the write") + @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){ @@ -33,14 +39,17 @@ public synchronized void initArray3(Object o){ array3 = new Object[]{o}; } - @ShallowImmutableFieldAnnotation("An array element escapes") - @LazyInitializedThreadSafeReferenceAnnotation("Synchronized method, with guarding if-statement.") + @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..3ed155c32c --- /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) +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}) + @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..89da1defee --- /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.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(); + + @DeepImmutableField(value = "field has an immutable reference and the concrete known " + + "referenced object can not escape", + 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 index 1352ff31b0..2606b03db2 100644 --- 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 @@ -1,25 +1,27 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.fields; -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +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; -@MutableTypeAnnotation("") -@DeepImmutableClassAnnotation("") +@MutableType("") +@DeepImmutableClass("") public class ClassWithStaticFields { - @MutableFieldAnnotation("") - @MutableReferenceAnnotation("") + @MutableField("") + @MutableFieldReference("") public static String name = "Class with static fields"; // @ShallowImmutableFieldAnnotation("") // @ImmutableReferenceAnnotation("") private static int counter; - ClassWithStaticFields(){ + ClassWithStaticFields() { counter++; } - public void setCounter(int n) {counter =n;} - + 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 index 257f69f8ed..25ac828a8a 100644 --- 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 @@ -1,17 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.fields; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; +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 { - @ShallowImmutableFieldAnnotation("The field is init") - @ImmutableReferenceAnnotation("The field is only assigned in the constructor.") + @ShallowImmutableField("The field is init") + @ImmutableFieldReference("The field is only assigned in the constructor.") private TrivialClass tc1; - @DeepImmutableFieldAnnotation("The construtor pararameter of the assigned object not escape") - @ImmutableReferenceAnnotation("The field is only assigned in the constructor.") + @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){ 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 index 2409f96fa3..1df52cd7ef 100644 --- 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 @@ -1,280 +1,312 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.fields; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; - -import java.util.*; +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.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 { - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private int n1 = 5; + @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; - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private int n2; + @DeepImmutableField("field has a primitive type and is synchronized lazy initialized") + @LazyInitializedThreadSafeFieldReference("field is thread safe lazy initialized") + private int lazyInitializedFieldWithPrimitiveType; - public synchronized void initN2(){ - if(n2==0) - n2 = 5; + public synchronized void initLazyInitializedFieldWithPrimitiveType(){ + if(lazyInitializedFieldWithPrimitiveType == 0) + lazyInitializedFieldWithPrimitiveType = 5; } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private int n3; + @DeepImmutableField("Lazy initialized field with primitive type.") + @LazyInitializedThreadSafeFieldReference("field is thread safely lazy initialized") + private int inTheGetterLazyInitializedFieldWithPrimitiveType; - public synchronized int getN3(){ - if(n3==0) - n3 = 5; - return n3; + 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) + @MutableFieldReference(value = "write of reference objects is not atomic", + analyses = L0FieldReferenceImmutabilityAnalysis.class) + private Integer simpleLazyInitializedIntegerField; - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private Integer nO = 5; + public void initSimpleLazyInitializedIntegerField(){ + if(simpleLazyInitializedIntegerField==0) + simpleLazyInitializedIntegerField = 5; + } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Integer nO2; + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized method") + private Integer synchronizedSimpleLazyInitializedIntegerField; public synchronized void initNO2(){ - if(nO2==0) - nO2 = 5; //TODO test again + if(synchronizedSimpleLazyInitializedIntegerField==0) + synchronizedSimpleLazyInitializedIntegerField = 5; } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Integer nO3; + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private Integer inGetterSynchronizedSimpleLazyInitializedIntegerField; - public synchronized Integer getNO3(){ - if(nO3==0) - nO3 = 5; - return nO3; + 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; - - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private double d = 5d; - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private double d2; + @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(d2==0d) - d2 = 5d; + if(synchronizedLazyInitializedDoubleField ==0d) + synchronizedLazyInitializedDoubleField = 5d; } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private double d3; + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private double inGetterSynchronizedLazyInitializedDoubleField; public synchronized double getD3(){ - if(d3==0d) - d3 = 5; - return d3; + 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; - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private Double dO = 5d; - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Double dO2; - - public synchronized void initDO2(){ - if(dO2==0) - dO2 = 5d; + public synchronized void initLazyInitializedObjectDoubleField(){ + if(lazyInitializedObjectDoubleField==0) + lazyInitializedObjectDoubleField = 5d; } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Double dO3; + @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 getDO3(){ - if(dO3==0) - dO3 = 5d; - return dO3; + 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; - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private float f = 5; - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private float f2; + @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(f2==0) - f2 = 5f; + if(synchronizedLazyInitializedFloatField ==0) + synchronizedLazyInitializedFloatField = 5f; } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private float f3; + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private float inGetterSynchronizedLazyInitializedFloatField; public synchronized float getf3(){ - if(f3==0) - f3 = 5f; - return f3; + 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; - - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private Float fO = 5f; - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Float fO2; + @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(fO2==0) - fO2 = 5f; + if(lazyInitializedFloatObjectField ==0) + lazyInitializedFloatObjectField = 5f; } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private float fO3; + @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 getfO3(){ - if(fO3==0) - fO3 = 5f; - return fO3; + 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; - - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private byte b = 5; - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private byte b2; + @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(b2==0) - b2 = 5; + if(synchronizedLazyInitializedByteField ==0) + synchronizedLazyInitializedByteField = 5; } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private byte b3; + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private byte inGetterSynchronizedLazyInitializedByteField; - public synchronized byte b3(){ - if(b3==0) - b3 = 5; - return b3; + 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; - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private Byte b0 = 5; - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Byte bO2; + @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(bO2==0) - bO2 = 5; + if(lazyInitializedByteObjectField ==0) + lazyInitializedByteObjectField = 5; } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Byte bO3; + @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 bO3(){ - if(bO3==0) - bO3 = 5; - return bO3; + public synchronized Byte getInAGetterLazyInitializedByteObjectField(){ + if(inAGetterLazyInitializedByteObjectField==0) + inAGetterLazyInitializedByteObjectField = 5; + return inAGetterLazyInitializedByteObjectField; } - - - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @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'; - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private char c2; + @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(c2 == '\u0000') - c2 = 'a'; + if(synchronizedLazyInitializedCharField == '\u0000') + synchronizedLazyInitializedCharField = 'a'; } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private char c3; + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private char inGetterSynchronizedLazyInitializedCharField; public synchronized char c3(){ - if(c3 == '\u0000') - c3 = 5; - return 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; - -//------------------------------------------------------------------------------------------------------ - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private long l = 5; - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private long l2; + @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(l2 == 0l) - l2 = 5l; + if(sychronizedLazyInitializedLongField == 0l) + sychronizedLazyInitializedLongField = 5l; } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private long l3; + @DeepImmutableField("immutable reference and deep immutable type") + @LazyInitializedThreadSafeFieldReference("lazy initialization in a synchronized getter method") + private long inGetterSynchronizedLazyInitializedLongField; - public synchronized long l3(){ - if(l3 == 0l) - l3 = 5; - return l3; + public synchronized long getInGetterSynchronizedLazyInitializedLongField(){ + if(inGetterSynchronizedLazyInitializedLongField == 0l) + inGetterSynchronizedLazyInitializedLongField = 5; + return inGetterSynchronizedLazyInitializedLongField; } - - - - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @DeepImmutableField("") + @ImmutableFieldReference("") private Long lO = 5l; - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("") private Long lO2; public synchronized void initLO2(){ @@ -282,8 +314,8 @@ public synchronized void initLO2(){ lO2 = 5l; } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") + @DeepImmutableField("") + @LazyInitializedThreadSafeFieldReference("") private Long lO3; public synchronized Long lO3(){ @@ -292,201 +324,185 @@ public synchronized Long lO3(){ 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; - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private String s = "abc"; - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private String s2; - - public synchronized void initS2(){ - if(s2 == null) - s2 = "abc"; + public synchronized void initLazyInitializedString(){ + if(lazyInitializedString == null) + lazyInitializedString = "abc"; } - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private String s3; + @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 getS3(){ - if(s3 == null) - s3 = "abc"; - return s3; + 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; - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private Object o = new Object(); - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Object o2; - - public synchronized void initO2(){ - if(o2 == null) - o2 = new Object(); + public synchronized void initLazyInitializedObjectReference(){ + if(lazyInitializedObjectReference == null) + lazyInitializedObjectReference = new Object(); } - @DeepImmutableFieldAnnotation("The concrete type of the object that is assigned is known") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Object o3; + @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 getO3(){ - if(o3 == null) - o3 = new Object(); - return o3; + 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(); - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private List linkedList = new LinkedList(); - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private List linkedList2; + @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private List lazyInitializedLinkedList; public synchronized void initLinkedList2(){ - if(linkedList2 == null) - linkedList2 = new LinkedList(); + if(lazyInitializedLinkedList == null) + lazyInitializedLinkedList = new LinkedList(); } - @ShallowImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private List linkedList3; + @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(linkedList3 == null) - linkedList3 = new LinkedList(); - linkedList3.add(new Object()); + if(lazyInitializedLinkedListWithManipulationAfterwards == null) + lazyInitializedLinkedListWithManipulationAfterwards = new LinkedList(); + lazyInitializedLinkedListWithManipulationAfterwards.add(new Object()); } - @DeepImmutableFieldAnnotation("The concrete type of the object that is assigned is known") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Object linkedList4; + @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 getLinkedList4(){ - if(linkedList4 == null) - linkedList4 = new Object(); - return linkedList4; + 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; - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private List arrayList = new ArrayList(); - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private List arrayList2; - - public synchronized void initArrayList2(){ - if(arrayList2 == null) - arrayList2 = new ArrayList(); + public synchronized void initLazyInitializedArrayList(){ + if(lazyInitializedArrayList == null) + lazyInitializedArrayList = new ArrayList(); } - @ShallowImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private List arrayList3; + @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 initArrayList3(){ - if(arrayList3 == null) - arrayList3 = new ArrayList(); - arrayList3.add(new Object()); + public synchronized void initLazyInitializedArrayListWithManipulationAfterwards(){ + if(lazyInitializedArrayListWithManipulationAfterwards == null) + lazyInitializedArrayListWithManipulationAfterwards = new ArrayList(); + lazyInitializedArrayListWithManipulationAfterwards.add(new Object()); } - @ShallowImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private List arrayList4; + @ShallowImmutableField("immutable reference but assigned object escapes via getter") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private List inTheGetterLazyInitializedArrayList; - public synchronized List getArrayList4(){ - if(arrayList4 == null) - arrayList4 = new ArrayList(); - return arrayList4; + 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; - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private Set set = new HashSet(); - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Set set2; - - public synchronized void initSet2(){ - if(set2 == null) - set2 = new HashSet(); + public synchronized void initLazyInitializedSet(){ + if(lazyInitializedSet == null) + lazyInitializedSet = new HashSet(); } - @ShallowImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Set set3; + @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(set3 == null) - set3 = new HashSet(); - set3.add(new Object()); + if(lazyInitializedSetWithManipulationAfterwards == null) + lazyInitializedSetWithManipulationAfterwards = new HashSet(); + lazyInitializedSetWithManipulationAfterwards.add(new Object()); } - @ShallowImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private Set set4; + @ShallowImmutableField("immutable reference but assigned object escapes via getter") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private Set inTheGetterLazyInitializedSet; - public synchronized Set getSet4(){ - if(set4 == null) - set4 = new HashSet(); - return set4; + 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; - - - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private HashMap hashMap = new HashMap(); - - @DeepImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private HashMap hashMap2; - - public synchronized void initHashMap2(){ - if(hashMap2 == null) - hashMap2 = new HashMap(); + public synchronized void initLazyInitializedHashMap(){ + if(lazyInitializedHashMap == null) + lazyInitializedHashMap = new HashMap(); } - @ShallowImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private HashMap hashMap3; + @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(hashMap3 == null) - hashMap3 = new HashMap(); - hashMap3.put(new Object(), new Object()); + if(lazyInitializedHashMapWithManipulationAfterwards == null) + lazyInitializedHashMapWithManipulationAfterwards = new HashMap(); + lazyInitializedHashMapWithManipulationAfterwards.put(new Object(), new Object()); } - @ShallowImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private HashMap hashMap4; + @ShallowImmutableField("immutable reference but assigned object escapes via getter") + @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") + private HashMap inTheGetterLazyInitializedHashMap; - public synchronized HashMap getHashMap4(){ - if(hashMap4 == null) - hashMap4 = new HashMap(); - return hashMap4; + 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 index f6307a7dd9..7f94cec401 100644 --- 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 @@ -1,64 +1,76 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.fields; -import org.opalj.fpcf.properties.class_immutability.DependentImmutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.DependentImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; +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 { - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private TrivialMutableClass tmc = new TrivialMutableClass(); + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private ClassWithPublicFields tmc = new ClassWithPublicFields(); public void printTMC(){ System.out.println(tmc.name); } - public TrivialMutableClass get(){ - TrivialMutableClass tmc1 = this.tmc; + public ClassWithPublicFields get(){ + ClassWithPublicFields tmc1 = this.tmc; return tmc1; } } class TransitiveEscape2 { - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private TrivialMutableClass tmc = new TrivialMutableClass(); + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private ClassWithPublicFields tmc = new ClassWithPublicFields(); public void printTMC(){ System.out.println(tmc.name); } - public TrivialMutableClass get(){ - TrivialMutableClass tmc1 = this.tmc; - TrivialMutableClass tmc2 = tmc1; + public ClassWithPublicFields get(){ + ClassWithPublicFields tmc1 = this.tmc; + ClassWithPublicFields tmc2 = tmc1; return tmc2; } } class OneThatNotEscapesAndOneWithDCL { - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private TrivialMutableClass tmc1 = new TrivialMutableClass(); - - @ShallowImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") - private TrivialMutableClass tmc2; - - public TrivialMutableClass set() { - TrivialMutableClass tmc11 = tmc1; - TrivialMutableClass tmc22 = tmc2; - if (tmc11 != null) { + @DeepImmutableField(value = "immutable field reference and the assigned object is known", + 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 TrivialMutableClass(); + tmc2 = new ClassWithPublicFields(); } } } @@ -66,39 +78,43 @@ public TrivialMutableClass set() { } } class GenericEscapes { - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + + @ShallowImmutableField("") + @ImmutableFieldReference("") private SimpleGenericClass sgc; - public GenericEscapes(TrivialMutableClass tmc){ + public GenericEscapes(ClassWithPublicFields tmc){ sgc = new SimpleGenericClass(tmc); } } -@ShallowImmutableClassAnnotation("") +@ShallowImmutableClass("") class GenericEscapesTransitive { - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @ShallowImmutableField("") + @ImmutableFieldReference("") private SimpleGenericClass gc1; - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private TrivialMutableClass tmc; + @ShallowImmutableField("") + @ImmutableFieldReference("") + private ClassWithPublicFields tmc; - public GenericEscapesTransitive(TrivialMutableClass tmc){ + public GenericEscapesTransitive(ClassWithPublicFields tmc){ this.tmc = tmc; gc1 = new SimpleGenericClass(this.tmc); } } class GenericNotEscapesMutualEscapeDependencyNotAbleToResolve{ - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") - private TrivialMutableClass tmc = new TrivialMutableClass(); - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + + @ShallowImmutableField("") + @ImmutableFieldReference("") + private ClassWithPublicFields tmc = new ClassWithPublicFields(); + + @ShallowImmutableField("") + @ImmutableFieldReference("") private SimpleGenericClass sgc; + public GenericNotEscapesMutualEscapeDependencyNotAbleToResolve() { this.sgc = new SimpleGenericClass(this.tmc); @@ -106,27 +122,23 @@ public GenericNotEscapesMutualEscapeDependencyNotAbleToResolve() { } class GenericNotEscapesMutualEscapeDependencyAbleToResolve{ - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + + @DeepImmutableField("") + @ImmutableFieldReference("") private FinalEmptyClass fec = new FinalEmptyClass(); - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + + @DeepImmutableField("") + @ImmutableFieldReference("") private SimpleGenericClass sgc; - public GenericNotEscapesMutualEscapeDependencyAbleToResolve() { + public GenericNotEscapesMutualEscapeDependencyAbleToResolve() { this.sgc = new SimpleGenericClass(this.fec); } } - - - - - - -@DependentImmutableClassAnnotation("") +@DependentImmutableClass("") class SimpleGenericClass { - @DependentImmutableFieldAnnotation(value = "", genericString = "T") + @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 index c8bbbac54c..57236095a6 100644 --- 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 @@ -1,9 +1,16 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.fields; -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; +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; -@DeepImmutableTypeAnnotation("Because of deep immutable final class") -@DeepImmutableClassAnnotation("Because of Emptiness") +@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 index 01a4f73cc8..01f26b79c3 100644 --- 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 @@ -1,16 +1,17 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.fields; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; +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 { - @ShallowImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") + @ShallowImmutableField("") + @LazyInitializedThreadSafeFieldReference("") private TestMutable tm1; public synchronized void getTM1(){ @@ -20,8 +21,8 @@ public synchronized void getTM1(){ tm1.nop(); } - @ShallowImmutableFieldAnnotation("") - @LazyInitializedThreadSafeReferenceAnnotation("") + @ShallowImmutableField("") + @LazyInitializedThreadSafeFieldReference("") private TestMutable tm2; public synchronized TestMutable getTM2(){ @@ -31,8 +32,8 @@ public synchronized TestMutable getTM2(){ return tm2; } - @MutableFieldAnnotation("") - @LazyInitializedNotThreadSafeReferenceAnnotation("") + @MutableField("") + @LazyInitializedNotThreadSafeFieldReference("") private TestMutable tm3; public void getTm3() { @@ -41,8 +42,8 @@ public void getTm3() { } } - @MutableReferenceAnnotation("") - @MutableFieldAnnotation("") + @MutableFieldReference("") + @MutableField("") private TestMutable tm4; public synchronized TestMutable getTm4() { @@ -59,7 +60,7 @@ public synchronized TestMutable getTm42() { return tm4; } - @ShallowImmutableFieldAnnotation("") + @ShallowImmutableField("") private TestMutable tm5; public synchronized void getTm5() { @@ -69,12 +70,12 @@ public synchronized void getTm5() { tm5.nop(); } - @DeepImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @DeepImmutableField("") + @ImmutableFieldReference("") private TestMutable tm6 = new TestMutable(); - @ShallowImmutableFieldAnnotation("") - @ImmutableReferenceAnnotation("") + @ShallowImmutableField("") + @ImmutableFieldReference("") private TestMutable tm7 = new TestMutable(); public void foo(){ diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_deep.java deleted file mode 100644 index c3af344bc4..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_deep.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.fields; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Because of not final class") -@DeepImmutableClassAnnotation("Because it has only Deep Immutable Field Types") -public class privateFieldNotBlank_deep { - @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceAnnotation("Effectively Immutable Reference") - private FinalEmptyClass name = new FinalEmptyClass(); -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_shallow.java deleted file mode 100644 index a16d5fc7b6..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFieldNotBlank_shallow.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.fields; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Because of not final class") -@DeepImmutableClassAnnotation("Because it has only Shallow Immutable Fields") -public class privateFieldNotBlank_shallow { - - @DeepImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") - @ImmutableReferenceAnnotation("Effectively Immutable Reference") - private TrivialMutableClass tmc = new TrivialMutableClass(); -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_deep.java deleted file mode 100644 index df3941d3f4..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_deep.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.fields; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; - -@DeepImmutableClassAnnotation("It has only Deep Immutable Fields") -public class privateFinalFieldBlank_costructorEscape_deep { - - @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceAnnotation("Declared final Reference") - private final FinalEmptyClass fec; - public privateFinalFieldBlank_costructorEscape_deep(FinalEmptyClass fec) { - this.fec = fec; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_shallow.java deleted file mode 100644 index ab78b53a3a..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privateFinalFieldBlank_costructorEscape_shallow.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.fields; - -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; - -@ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") -public class privateFinalFieldBlank_costructorEscape_shallow { - - @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") - @ImmutableReferenceAnnotation("Declared final Field") - private final TrivialMutableClass tmc; - - public privateFinalFieldBlank_costructorEscape_shallow(TrivialMutableClass tmc) { - this.tmc = tmc; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_deep.java deleted file mode 100644 index d1bd253dfb..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_deep.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.fields; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Because of not final class") -@DeepImmutableClassAnnotation("Only Deep Immutable Fields") -public class private_getterEscape_deep { - public FinalEmptyClass getFec() { - return fec; - } - @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceAnnotation("It is effectively immutable") - private FinalEmptyClass fec = new FinalEmptyClass(); -} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_shallow.java deleted file mode 100644 index 87d093b1f3..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_getterEscape_shallow.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.fields; - -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Because of not final class") -@ShallowImmutableClassAnnotation("Because it has only Shallow Immutable Fields") -public class private_getterEscape_shallow { - public TrivialMutableClass getTmc() { - return tmc; - } - - @ShallowImmutableFieldAnnotation("Because of Immutable Reference and Mutable Field Type") - @ImmutableReferenceAnnotation("effectively immutable") - private TrivialMutableClass tmc = new TrivialMutableClass(); - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_deep.java deleted file mode 100644 index b0844417bb..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_deep.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.fields; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Because the class is mutable and not final") -@MutableClassAnnotation("Because it has Mutable Fields") -public class private_setter_deep { - - public void setFec(FinalEmptyClass fec) { - this.fec = fec; - } - - @MutableFieldAnnotation("Because of Mutable Reference") - @MutableReferenceAnnotation("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/private_setter_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_shallow.java deleted file mode 100644 index a82821aa8b..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/private_setter_shallow.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.fields; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; - -@MutableTypeAnnotation("Because of not final class") -@MutableClassAnnotation("Because it has Mutable Fields") -public class private_setter_shallow { - - public void setTmc(TrivialMutableClass tmc) { - this.tmc = tmc; - } - - @MutableFieldAnnotation("Because of Mutable Reference") - @MutableReferenceAnnotation("Not final field could be set via setter") - private TrivialMutableClass tmc = new TrivialMutableClass(); - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_deep.java deleted file mode 100644 index 1f0e03aaf5..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_deep.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.fields; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; - -@DeepImmutableClassAnnotation("It has only Deep Immutable Fields") -public class privatefinal_getterEscape_deep { - public FinalEmptyClass getFec() { - return fec; - } - - @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceAnnotation("Declared final Field") - private final FinalEmptyClass fec = new FinalEmptyClass(); - -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_shallow.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_shallow.java deleted file mode 100644 index 1b2b14f107..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/privatefinal_getterEscape_shallow.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.fields; - -import org.opalj.fpcf.properties.class_immutability.ShallowImmutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.ShallowImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; - -@ShallowImmutableClassAnnotation("It has only Shallow Immutable Fields") -public class privatefinal_getterEscape_shallow { - public TrivialMutableClass getTmc() { - return tmc; - } - - @ShallowImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") - @ImmutableReferenceAnnotation("Declared final Reference") - private final TrivialMutableClass tmc = new TrivialMutableClass(); -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/protectedClass_deep.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/protectedClass_deep.java deleted file mode 100644 index 2191d20935..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/protectedClass_deep.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.fields; - -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; -import org.opalj.fpcf.properties.field_immutability.MutableFieldAnnotation; - - -public class protectedClass_deep { - @MutableFieldAnnotation("Because of Mutable Reference") - @MutableReferenceAnnotation("Because it is declared as protected") - protected FinalEmptyClass fec1 = new FinalEmptyClass(); - - @MutableFieldAnnotation("Because of Mutable Reference") - @MutableReferenceAnnotation("Because it is declared as protected") - protected TrivialMutableClass tmc1 = new TrivialMutableClass(); - - @DeepImmutableFieldAnnotation("Immutable Reference and Mutable Field Type") - @ImmutableReferenceAnnotation("Declared final Field") - private final TrivialMutableClass tmc2 = new TrivialMutableClass(); - - @DeepImmutableFieldAnnotation("Immutable Reference and Immutable Field Type") - @ImmutableReferenceAnnotation("Declared final Field") - private final FinalEmptyClass fec2 = new FinalEmptyClass(); -} - - - - - -@MutableClassAnnotation("It has Mutable Fields") -class TrivialMutableClass { - - @MutableFieldAnnotation("Because of Mutable Reference") - @MutableReferenceAnnotation("Because it is public") - public int n = 0; - - @MutableFieldAnnotation("Because of Mutable Reference") - @MutableReferenceAnnotation("Because it is public") - public String name = "name"; -} - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java deleted file mode 100644 index cd16812cc3..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/DeclaredFinalFields.java +++ /dev/null @@ -1,92 +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.fixtures.immutability.reference; - -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; - -/** - * 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 Peter Roth - */ -public class DeclaredFinalFields extends Super { - - @ImmutableReferenceAnnotation("Initialized directly") - private final int a = 1; - - @ImmutableReferenceAnnotation("Initialized through instance initializer") - private final int b; - - @ImmutableReferenceAnnotation("Initialized through constructor") - private final int c; - - @MutableReferenceAnnotation(value = "Prematurely read through super constructor", prematurelyRead = true) - private final int d; - - @MutableReferenceAnnotation(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/reference/LazyInitialization.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java deleted file mode 100644 index da0acbeed6..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/LazyInitialization.java +++ /dev/null @@ -1,377 +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.fixtures.immutability.reference; - -import org.opalj.fpcf.properties.field_mutability.LazyInitialized; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; - -/** - * Test classes for simple lazy initialization patterns and anti-patterns regarding reference immutability analysis. - * - * @author Dominik Helm - * @author Tobias Peter Roth - */ - -class Simple { - - @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Simple lazy initialization") - private int x; - - public int init() { - if (x == 0) { - x = 5; - } - return x; - } -} - -class Local { - - @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Lazy initialization with local") - private int x; - - public int init() { - int y = this.x; - if (y == 0) { - x = y = 5; - } - return y; - } -} - -class LocalWrong { - - @MutableReferenceAnnotation("Incorrect lazy initialization with local") - private int x; - - public int init() { - int y = this.x; - if (y == 0) { - x = 5; - } - return y; - } -} - -class LocalReversed { - - @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("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 { - - @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("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 { - - @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Simple lazy initialization (reversed)") - private int x; - - public int init() { - if (x != 0) - return x; - x = 5; - return x; - } -} - -class SimpleWithDifferentDefault { - - @LazyInitialized(value = "Simple lazy initialization, but different default value", - analyses = {}) - @MutableReferenceAnnotation(value = "Analysis doesn't recognize lazy initialization with different default") - //TODO - private int x; - - public SimpleWithDifferentDefault() { - x = -1; - } - - public SimpleWithDifferentDefault(int a) { - this(); - } - - public int init() { - if (x == -1) { - x = 5; - } - return x; - } -} - -class WrongDefault { - - @MutableReferenceAnnotation("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 { - - @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("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 { - - @MutableReferenceAnnotation("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 { - - @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("Lazy initialization with call to deterministic method on final field") - private int x; - - @ImmutableReferenceAnnotation("Declared final field") - private final Inner inner; - - public DeterministicCallOnFinalField(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 DeterministicCallOnNonFinalField { - - @MutableReferenceAnnotation("Wrong lazy initialization with call to non-deterministic method on final field") - private int x; - - @MutableReferenceAnnotation("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 NodeterministicCall { - - @MutableReferenceAnnotation("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 { - - @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("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 { - - @MutableReferenceAnnotation("Field can be observed partially updated") - private int x; - - public int init() { - if (x == 0) { - x = 5; - x ^= -1; - } - return x; - } -} - -class VisibleInitialization { - - @MutableReferenceAnnotation("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. - */ - @ImmutableReferenceAnnotation(value = "Field is never initialized, so it stays on its default value") - 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 - @MutableReferenceAnnotation("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/immutability/reference/PrivateFieldUpdater.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFieldUpdater.java deleted file mode 100644 index 3513eb4956..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/PrivateFieldUpdater.java +++ /dev/null @@ -1,34 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.fixtures.immutability.reference; - -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; - -/** - * Simple demo class which updates the private field of another instance of this class. - */ -public class PrivateFieldUpdater { - - @ImmutableReferenceAnnotation("only initialized by the constructor") - private String name; - - @MutableReferenceAnnotation("incremented whenever `this` object is passed to another `NonFinal` object") - 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/reference/Singleton.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java deleted file mode 100644 index 105a9d77c5..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Singleton.java +++ /dev/null @@ -1,39 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.fixtures.immutability.reference; - -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; - -public class Singleton { - - @MutableReferenceAnnotation("written by static initializer after the field becomes (indirectly) readable") - private String name; - - @ImmutableReferenceAnnotation("only initialized once by the constructor") - private Object mutex = new Object(); - - private Singleton() { - this.name = ""; - } - - public String getName() { - synchronized (mutex) { - return name; - } - } - - // STATIC FUNCTIONALITY - - @ImmutableReferenceAnnotation("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/reference/StaticFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/StaticFields.java deleted file mode 100644 index 3843500c02..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/StaticFields.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference; - -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; - -@DeepImmutableClassAnnotation("") -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/reference/Template.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java deleted file mode 100644 index bd8309afea..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference/Template.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference; - -//import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; - -public class Template { - //@LazyInitializedNotThreadSafeReferenceAnnotation("") - private Template _template; - 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/reference_immutability_lazy_initialization/DCL.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java deleted file mode 100644 index e7a93bbfd9..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DCL.java +++ /dev/null @@ -1,387 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; - -import java.util.Random; - -public class DCL { - -} - -class DCLint { - @LazyInitializedThreadSafeReferenceAnnotation("") - private int n; - public int getN() { - if(n==0){ - synchronized(this) { - if(n==0){ - n = 42; - } - } - } - return n; - } -} - -class DCLIntRandom { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private int r; - - public int getR(){ - if(r==0){ - synchronized(this){ - if(r==0){ - r = new Random().nextInt(); - } - } - } - return r; - } -} -class NoDCLIntRandom { - - @MutableReferenceAnnotation("") - private int r; - - public int getR(){ - if(r==0){ - r = new Random().nextInt(); - } - return r; - } -} - -class DCLwithEarlyReturns { - @LazyInitializedThreadSafeReferenceAnnotation("") - private DCLwithEarlyReturns instance; - public DCLwithEarlyReturns getInstance(){ - if(instance!=null) - return instance; - synchronized(this) { - if(instance==null) - instance = new DCLwithEarlyReturns(); - } - return instance; - } - } - - - class DCLChallenge { - @LazyInitializedThreadSafeReferenceAnnotation("") - private static org.omg.CORBA.TypeCode __typeCode = null; - private static boolean __active = false; - private static String _id = "IDL:omg.org/CORBA/ValueMember:1.0"; - synchronized public static org.omg.CORBA.TypeCode type () - { - if (__typeCode == null) - { - synchronized (org.omg.CORBA.TypeCode.class) - { - if (__typeCode == null) - { - if (__active) - { - return org.omg.CORBA.ORB.init().create_recursive_tc ( _id ); - } - __active = true; - org.omg.CORBA.StructMember[] _members0 = new org.omg.CORBA.StructMember [7]; - org.omg.CORBA.TypeCode _tcOf_members0 = null; - _tcOf_members0 = org.omg.CORBA.ORB.init ().create_string_tc (0); - _tcOf_members0 = org.omg.CORBA.ORB.init ().create_alias_tc (org.omg.CORBA.IdentifierHelper.id (), "Identifier", _tcOf_members0); - _members0[0] = new org.omg.CORBA.StructMember ( - "name", - _tcOf_members0, - null); - _tcOf_members0 = org.omg.CORBA.ORB.init ().create_string_tc (0); - _tcOf_members0 = org.omg.CORBA.ORB.init ().create_alias_tc (org.omg.CORBA.RepositoryIdHelper.id (), "RepositoryId", _tcOf_members0); - _members0[1] = new org.omg.CORBA.StructMember ( - "id", - _tcOf_members0, - null); - _tcOf_members0 = org.omg.CORBA.ORB.init ().create_string_tc (0); - _tcOf_members0 = org.omg.CORBA.ORB.init ().create_alias_tc (org.omg.CORBA.RepositoryIdHelper.id (), "RepositoryId", _tcOf_members0); - _members0[2] = new org.omg.CORBA.StructMember ( - "defined_in", - _tcOf_members0, - null); - _tcOf_members0 = org.omg.CORBA.ORB.init ().create_string_tc (0); - _tcOf_members0 = org.omg.CORBA.ORB.init ().create_alias_tc (org.omg.CORBA.VersionSpecHelper.id (), "VersionSpec", _tcOf_members0); - _members0[3] = new org.omg.CORBA.StructMember ( - "version", - _tcOf_members0, - null); - _tcOf_members0 = org.omg.CORBA.ORB.init ().get_primitive_tc (org.omg.CORBA.TCKind.tk_TypeCode); - _members0[4] = new org.omg.CORBA.StructMember ( - "type", - _tcOf_members0, - null); - _tcOf_members0 = org.omg.CORBA.IDLTypeHelper.type (); - _members0[5] = new org.omg.CORBA.StructMember ( - "type_def", - _tcOf_members0, - null); - _tcOf_members0 = org.omg.CORBA.ORB.init ().get_primitive_tc (org.omg.CORBA.TCKind.tk_short); - _tcOf_members0 = org.omg.CORBA.ORB.init ().create_alias_tc (org.omg.CORBA.VisibilityHelper.id (), "Visibility", _tcOf_members0); - _members0[6] = new org.omg.CORBA.StructMember ( - "access", - _tcOf_members0, - null); - __typeCode = org.omg.CORBA.ORB.init ().create_struct_tc (org.omg.CORBA.ValueMemberHelper.id (), "ValueMember", _members0); - __active = false; - } - } - } - return __typeCode; - } - } - -class DCL5 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DCL5 instance; - - public DCL5 DCL5(){ - if(instance==null){ - synchronized(this){ - if(instance==null){ - try{ - instance = new DCL5(); - } - catch (Exception e) - { - throw e; - } - } - } - } - return instance; - } -} - -class DCL6 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DCL6 instance; - - public DCL6 getInstance() throws Exception{ - if(instance==null){ - synchronized(this){ - if(instance==null){ - instance = new DCL6(); - } - } - } - return instance; - } -} - -class SimpleLockingSynchronizedBlock { - @LazyInitializedThreadSafeReferenceAnnotation("") - private SimpleLockingSynchronizedBlock instance; - private SimpleLockingSynchronizedBlock() {} - public SimpleLockingSynchronizedBlock getInstance(){ - synchronized(this){ - if(instance==null) - instance = new SimpleLockingSynchronizedBlock(); - } - return instance; - } -} - -class SimpleLockingSynchronizedFunction1 { - @LazyInitializedThreadSafeReferenceAnnotation("") - private SimpleLockingSynchronizedFunction1 instance; - private SimpleLockingSynchronizedFunction1() {} - public synchronized SimpleLockingSynchronizedFunction1 getInstance() { - if(instance ==null) - instance = new SimpleLockingSynchronizedFunction1(); - return instance; - } -} - - - - -class NoDCL1 { - - @MutableReferenceAnnotation("") - NoDCL1 instance; - - public NoDCL1 NoDCL1(){ - if(instance==null){ - synchronized(this){ - if(instance==null){ - try{ - instance = new NoDCL1(); - } - catch(Exception e){} - }}} - return instance; - } - -} - -class NoDCL2 { - - @MutableReferenceAnnotation("") - NoDCL2 instance; - - public NoDCL2 NoDCL1(){ - try{ - if(instance==null){ - synchronized(this){ - if(instance==null){ - - instance = new NoDCL2(); - } - - }}}catch(Exception e){} - return instance; - } - -} - -class NoDCL3 { - - @MutableReferenceAnnotation("") - NoDCL3 instance; - - public NoDCL3 NoDCL1(){ - - if(instance==null){ - try{ - synchronized(this){ - if(instance==null){ - - instance = new NoDCL3(); - } - }}catch(Exception e){}} - return instance; - } - -} - -class NoDCL4 { - - @MutableReferenceAnnotation("") - NoDCL4 instance; - - public NoDCL4 NoDCL1(){ - - if(instance==null){ - try{ - synchronized(this){ - if(instance==null){ - try{ - instance = new NoDCL4(); - } - catch (Exception e) - { - throw e; - } - } - }}catch(Exception e){}} - return instance; - } - -} - - -class NoDCL6 { - - @MutableReferenceAnnotation("") - NoDCL6 instance; - - public NoDCL6 NoDCL6() throws IndexOutOfBoundsException{ - if(instance==null){ - synchronized(this){ - if(instance==null){ - try{ - instance = new NoDCL6(); - } - catch (Exception e) - { - throw new IndexOutOfBoundsException(); - } - } - } - } - return instance; - } -} - -class DoubleCheckedLockingClassWithStaticFields { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private static DoubleCheckedLockingClassWithStaticFields instance; - public static DoubleCheckedLockingClassWithStaticFields getInstance() { - if(instance==null){ - synchronized(DoubleCheckedLockingClassWithStaticFields.class) { - if(instance==null){ - instance = new DoubleCheckedLockingClassWithStaticFields(); - } - } - } - return instance; - } -} - -class DoubleCheckedLockingClassArray1 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private Object[] instance; - public Object[] getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - instance = new Object[10]; - } - } - } - return instance; - } -} - -class DoubleCheckedLockingClass19 { - - @LazyInitializedNotThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass19 instance; - - public DoubleCheckedLockingClass19 getInstance() { - if (instance == null) { - synchronized (this) { - if (instance == null) { - } - } - instance = new DoubleCheckedLockingClass19(); - } - return instance; - } -} - -class ArrayLazyInitializationNotThreadSafe { - @LazyInitializedNotThreadSafeReferenceAnnotation("During the initialization phase there is no lock") - private int[] values; - - public int[] getValues(){ - if(values==null){ - values = new int[] {1,2,3,4,5,6,7,8,9,10}; - } - return values; - } -} - -class ArrayLazyInitializationThreadSafe { - @LazyInitializedThreadSafeReferenceAnnotation("it is a lock via the synchronized method") - private int[] values; - - public synchronized int[] getValues(){ - if(values==null){ - values = new int[] {1,2,3,4,5,6,7,8,9,10}; - } - return values; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLocking.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLocking.java deleted file mode 100644 index ce1f8353cc..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/DoubleCheckedLocking.java +++ /dev/null @@ -1,322 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedThreadSafeReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.MutableReferenceAnnotation; - -public class DoubleCheckedLocking { -} - -class DoubleCheckedLockingClass1{ - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass1 instance; - public DoubleCheckedLockingClass1 getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - instance = new DoubleCheckedLockingClass1(); - } - } - } - return instance; - } -} - -class DoubleCheckedLockingClass2 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass2 instance; - private DoubleCheckedLockingClass2 instance2 = new DoubleCheckedLockingClass2(); - public DoubleCheckedLockingClass2 getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - instance = new DoubleCheckedLockingClass2(); - } - } - } - return instance; - } -} - -class DoubleCheckedLockingClass3 { - - @LazyInitializedNotThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass3 instance; - public DoubleCheckedLockingClass3 getInstance() { - if(instance==null){ - synchronized(DoubleCheckedLockingClass3.class) { - instance = new DoubleCheckedLockingClass3(); - } - } - return instance; - } -} - -class DoubleCheckedLockingClass4 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass4 instance; - public DoubleCheckedLockingClass4 getInstance() { - synchronized(DoubleCheckedLockingClass4.class) { - if(instance==null){ - instance = new DoubleCheckedLockingClass4(); - } - } - return instance; - } -} - -class DoubleCheckedLockingClass5 { - - @LazyInitializedNotThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass5 instance; - public DoubleCheckedLockingClass5 getInstance() { - if(instance==null){ - if(instance==null){ - instance = new DoubleCheckedLockingClass5(); - } - } - return instance; - } -} - -class DoubleCheckedLockingClass6 { - - @LazyInitializedNotThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass6 instance; - public DoubleCheckedLockingClass6 getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - //instance = new DoubleCheckedLockingClass6(); - } - instance = new DoubleCheckedLockingClass6(); - } - - } - return instance; - } -} - -class DoubleCheckedLockingClass7 { - - @MutableReferenceAnnotation("") - private DoubleCheckedLockingClass7 instance; - public DoubleCheckedLockingClass7 getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - instance = new DoubleCheckedLockingClass7(); - } - - } - instance = new DoubleCheckedLockingClass7(); - } - return instance; - } -} - -class DoubleCheckedLockingClass8 { - - @MutableReferenceAnnotation("") - private DoubleCheckedLockingClass8 instance; - public DoubleCheckedLockingClass8 getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - instance = new DoubleCheckedLockingClass8(); - } - } - } - instance = new DoubleCheckedLockingClass8(); - return instance; - } -} - -class DoubleCheckedLockingClass9 { - - @MutableReferenceAnnotation("") - private DoubleCheckedLockingClass9 instance; - - public DoubleCheckedLockingClass9 getInstance() { - if (instance != null) { - synchronized (this) { - if (instance != null) { - instance = new DoubleCheckedLockingClass9(); - } - } - } - return instance; - } -} - -class DoubleCheckedLockingClass10 { - - @MutableReferenceAnnotation("") - private DoubleCheckedLockingClass10 instance; - - public DoubleCheckedLockingClass10 getInstance() { - if (instance == null) { - synchronized (this) { - if (instance == null) { - } - } - } - instance = new DoubleCheckedLockingClass10(); - return instance; - } -} - -class DoubleCheckedLockingClass11 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass11 instance; - public DoubleCheckedLockingClass11 getInstance() { - for(int i=0; i<1000; i++){} - if(instance==null){ - for(int i=0; i<1000; i++){} - synchronized(this) { - for(int i=0; i<1000; i++){} - if(instance==null){ - for(int i=0; i<1000; i++){} - instance = new DoubleCheckedLockingClass11(); - } - } - } - return instance; - } -} - -class DoubleCheckedLockingClass12 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass12 instance; - public DoubleCheckedLockingClass12 getInstance() { - if(instance==null){ - } - synchronized(this) { - if(instance==null){ - instance = new DoubleCheckedLockingClass12(); - } - } - return instance; - } -} - -class DoubleCheckedLockingClass13 { - - @LazyInitializedNotThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass13 instance; - public DoubleCheckedLockingClass13 getInstance() { - if(instance==null){ - } - synchronized(this) { - } - if(instance==null){ - instance = new DoubleCheckedLockingClass13(); - } - return instance; - } -} - -class DoubleCheckedLockingClass14 { - - @MutableReferenceAnnotation("") - private DoubleCheckedLockingClass14 instance; - public DoubleCheckedLockingClass14 getInstance() { - if(instance==null){ - } - synchronized(this) { - } - if(instance==null){ - - } - instance = new DoubleCheckedLockingClass14(); - return instance; - } -} - -class DoubleCheckedLockingClass15 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass15 instance; - public DoubleCheckedLockingClass15 getInstance() { - if(instance==null){ - synchronized(this) { - if(instance==null){ - if(instance==null) { - instance = new DoubleCheckedLockingClass15(); - } - } - } - } - return instance; - } -} - -class DoubleCheckedLockingClass16 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass16 instance; - public DoubleCheckedLockingClass16 getInstance() { - if(instance==null){ - if(instance==null){ - synchronized(this) { - if(instance==null) { - instance = new DoubleCheckedLockingClass16(); - } - } - } - } - return instance; - } -} - -class DoubleCheckedLockingClass17 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass17 instance; - public DoubleCheckedLockingClass17 getInstance() { - if(instance==null){ - if(instance==null) { - if (instance == null) { - synchronized (this) { - if (instance == null) { - if (instance == null) { - instance = new DoubleCheckedLockingClass17(); - } - } - } - } - } - } - return instance; - } -} - -class DoubleCheckedLockingClass18 { - - @LazyInitializedThreadSafeReferenceAnnotation("") - private DoubleCheckedLockingClass18 instance; - private boolean lock = true; - public DoubleCheckedLockingClass18 getInstance() { - if(instance==null && lock){ - synchronized(this) { - if(instance==null && lock){ - instance = new DoubleCheckedLockingClass18(); - } - } - } - return instance; - } -} - - - - - - - - - diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/S.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/S.java deleted file mode 100644 index 0700afb37a..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/S.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; - -public class S { - private final char value[]; - - /** Cache the hash code for the string */ - - //@LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("") - private int hash; // Default to 0 - - - public S(S original) { - this.value = original.value; - this.hash = original.hash; - } - - public int hashCode() { - int h = hash; - - if (h == 0 && value.length > 0) { - char val[] = value; - - for (int i = 0; i < value.length; i++) { - h = 31 * h + val[i]; - } - hash = h; - } - return h; - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java deleted file mode 100644 index a394de9568..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/reference_immutability_lazy_initialization/SimpleLazyInstantiation.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.opalj.fpcf.fixtures.immutability.reference_immutability_lazy_initialization; - -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation; -import org.opalj.fpcf.properties.reference_immutability.LazyInitializedNotThreadSafeReferenceAnnotation; - -public class SimpleLazyInstantiation{ - @LazyInitializedNotThreadSafeReferenceAnnotation("") - private static SimpleLazyInstantiation instance; - - public static SimpleLazyInstantiation init() { - if(instance==null) - instance = new SimpleLazyInstantiation(); - return instance; - } -} - -class SimpleLazyIntInstantiation{ - @LazyInitializedNotThreadSafeButDeterministicReferenceAnnotation("") - private int i = 0; - public int hashcode() { - if(i==0) - i = 5; - return i; - } -} - -class SimpleLazyObjectsInstantiation{ - @LazyInitializedNotThreadSafeReferenceAnnotation("") - private static SimpleLazyObjectsInstantiation instance; - public static SimpleLazyObjectsInstantiation getInstance() { - if(instance==null) - instance = new SimpleLazyObjectsInstantiation(); - return instance; - } -} - - 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 index a8d58b3b59..484b488a4d 100644 --- 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 @@ -1,35 +1,65 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.immutability.type; -import org.opalj.fpcf.properties.class_immutability.DeepImmutableClassAnnotation; -import org.opalj.fpcf.properties.class_immutability.MutableClassAnnotation; -import org.opalj.fpcf.properties.field_immutability.DeepImmutableFieldAnnotation; -import org.opalj.fpcf.properties.reference_immutability.ImmutableReferenceAnnotation; -import org.opalj.fpcf.properties.type_immutability.DeepImmutableTypeAnnotation; -import org.opalj.fpcf.properties.type_immutability.MutableTypeAnnotation; +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.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.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; -@DeepImmutableTypeAnnotation("has shallow and mutable fields") -@DeepImmutableClassAnnotation("has shallow and immutable fields") +@DeepImmutableType("has only deep immutable fields and is final") +@DeepImmutableClass("has only deep immutable fields") public final class WithMutableAndImmutableFieldType { - @DeepImmutableFieldAnnotation("immutable reference and deep immutable type") - @ImmutableReferenceAnnotation("private field") + @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(); - @DeepImmutableFieldAnnotation("imm reference and mutable type") - @ImmutableReferenceAnnotation("private field") + @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) + @DeepImmutableField(value = "assigned object is known and can not escape", + analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "has mutable type but is effective final", + analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class}) + @ImmutableFieldReference(value = "private, effectively final field", + analyses = L0FieldReferenceImmutabilityAnalysis.class) private SimpleMutableClass tmc = new SimpleMutableClass(); } -@MutableTypeAnnotation("Class has no fields but is not final") -@DeepImmutableClassAnnotation("Class has no fields") +@MutableType("class is deep immutable but extensible") +@DeepImmutableClass("has no fields") class EmptyClass { } -@DeepImmutableClassAnnotation("Class has no fields and is final") -@DeepImmutableTypeAnnotation("Class has no fields") +@DeepImmutableClass("Class has no fields") +@DeepImmutableType("Class has no fields and is final") final class FinalEmptyClass { } -@MutableTypeAnnotation("") -@MutableClassAnnotation("") -class SimpleMutableClass{ public int n=0;} +@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; +} 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/NonFinal.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/NonFinal.java deleted file mode 100644 index 23a8f5dfe1..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/NonFinal.java +++ /dev/null @@ -1,39 +0,0 @@ -/* 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; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Annotation to state that the annotated field is not final. - * - * @author Michael Eichberg - */ -@PropertyValidator(key = "FieldMutability",validator = NonFinalMatcher.class) -@Documented -@Retention(RetentionPolicy.CLASS) -public @interface NonFinal{ - - /** - * A short reasoning of this property. - */ - String value();// default = "N/A"; - - /** - * True if the field is non-final because it is read prematurely. - * Tests may ignore @NonFinal annotations if the FieldPrematurelyRead property for the field - * did not identify the premature read. - */ - boolean prematurelyRead() default false; - - Class[] analyses() default { L0FieldMutabilityAnalysis.class, - L1FieldMutabilityAnalysis.class, 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 index d39e1a9ad1..1cc3d3950f 100644 --- 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 @@ -12,9 +12,9 @@ /** * Annotation to state that the annotated class is deep immutable * - * @author Tobias Peter Roth + * @author Tobias Roth */ -@PropertyValidator(key = "ClassImmutability_new",validator = DeepImmutableClassMatcher.class) +@PropertyValidator(key = "ClassImmutability",validator = DeepImmutableClassMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface DeepImmutableClass { 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 index 6453efc7e8..b168d277c5 100644 --- 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 @@ -1,19 +1,20 @@ /* 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; +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 Peter Roth + * @author Tobias Roth */ -@PropertyValidator(key = "ClassImmutability_new",validator = DependentImmutableClassMatcher.class) +@PropertyValidator(key = "ClassImmutability",validator = DependentImmutableClassMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface DependentImmutableClass { @@ -21,7 +22,7 @@ /** * A short reasoning of this property. */ - String value();// default = "N/A"; + 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 index 1844c0ec00..c66f776a1e 100644 --- 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 @@ -12,7 +12,7 @@ /** * Annotation to state that the annotated class is mutable * - * @author Tobias Peter Roth + * @author Tobias Roth */ @PropertyValidator(key = "ClassImmutability",validator = MutableClassMatcher.class) @Documented @@ -22,7 +22,7 @@ /** * A short reasoning of this property. */ - String value();// default = "N/A"; + 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 index 9b33618d4e..245ce617f7 100644 --- 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 @@ -1,20 +1,20 @@ /* 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; +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 Peter Roth + * @author Tobias Roth */ -@PropertyValidator(key = "ClassImmutability_new",validator = ShallowImmutableClassMatcher.class) +@PropertyValidator(key = "ClassImmutability",validator = ShallowImmutableClassMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface ShallowImmutableClass { @@ -22,7 +22,7 @@ /** * A short reasoning of this property. */ - String value();// default = "N/A"; + 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 index 2963236aef..6729680215 100644 --- 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 @@ -1,48 +1,18 @@ -/* 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.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; +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 lazily initialized. - * - * @author Michael Eichberg - * @author Dominik Helm + * Annotation to state that the annotated field is deep immutable. */ -@PropertyValidator(key="FieldMutability",validator= DeepImmutableFieldMatcher.class) +@PropertyValidator(key="FieldImmutability",validator= DeepImmutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface DeepImmutableField { @@ -50,7 +20,7 @@ /** * A short reasoning of this property. */ - String value() ; // default = "N/A"; + 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 index 0e479402a9..b219badec0 100644 --- 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 @@ -1,36 +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.properties.immutability.fields; import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.fpcf.properties.immutability.fields.DependentImmutableFieldMatcher; import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; import java.lang.annotation.Documented; @@ -38,12 +10,9 @@ import java.lang.annotation.RetentionPolicy; /** - * Annotation to state that the annotated field is lazily initialized. - * - * @author Michael Eichberg - * @author Dominik Helm + * Annotation to state that the annotated field is dependent immutable. */ -@PropertyValidator(key="FieldMutability",validator= DependentImmutableFieldMatcher.class) +@PropertyValidator(key="FieldImmutability",validator= DependentImmutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface DependentImmutableField { @@ -51,9 +20,7 @@ /** * A short reasoning of this property. */ - String value() ; // default = "N/A"; - - String genericString(); + String value(); - Class[] analyses() default {L3FieldImmutabilityAnalysis.class }; + 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 index 018b440086..18c33cafae 100644 --- 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 @@ -4,7 +4,6 @@ import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.br.fpcf.analyses.L0FieldImmutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.fpcf.properties.immutability.fields.MutableFieldMatcher; import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; @@ -14,9 +13,7 @@ import java.lang.annotation.RetentionPolicy; /** - * Annotation to state that the annotated field is not mutable. - * - * @author Michael Eichberg + * Annotation to state that the annotated field is mutable. */ @PropertyValidator(key = "FieldImmutability",validator = MutableFieldMatcher.class) @Documented @@ -26,11 +23,11 @@ /** * A short reasoning of this property. */ - String value();// default = "N/A"; + String value(); /** * True if the field is non-final because it is read prematurely. - * Tests may ignore @NonFinal annotations if the FieldPrematurelyRead property for the field + * Tests may ignore @Mutable annotations if the FieldPrematurelyRead property for the field * did not identify the premature read. */ boolean prematurelyRead() default false; 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 index 3d571f4fdf..0a5ce32a83 100644 --- 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 @@ -1,49 +1,18 @@ -/* 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.properties.immutability.fields; -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableFieldMatcher; -import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; - 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 lazily initialized. - * - * @author Michael Eichberg - * @author Dominik Helm + * Annotation to state that the annotated field is shallow immutable. */ -@PropertyValidator(key="FieldMutability",validator= ShallowImmutableFieldMatcher.class) +@PropertyValidator(key="FieldImmutability",validator= ShallowImmutableFieldMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface ShallowImmutableField { @@ -51,7 +20,7 @@ /** * A short reasoning of this property. */ - String value() ; // default = "N/A"; + String value(); Class[] analyses() default { L3FieldImmutabilityAnalysis.class }; } diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableReference.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableFieldReference.java similarity index 84% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableReference.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableFieldReference.java index f06adc6240..204d93772d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableReference.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/ImmutableFieldReference.java @@ -1,27 +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; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; /** - * Annotation to state that the annotated reference is immutable + * 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 ImmutableReference { +public @interface ImmutableFieldReference { /** * A short reasoning of this property. */ - String value();// default = "N/A"; + 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 index 8c5171a60a..11a807ab84 100644 --- 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 @@ -1,18 +1,16 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.immutability.references; -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; - 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 lazy initialized - * - * @author Tobias Peter Roth + * Annotation to state that the annotated field reference is not thread safe but deterministic lazy initialized */ @PropertyValidator(key = "FieldReferenceImmutability",validator = LazyInitializedNotThreadSafeButDeterministicFieldReferenceMatcher.class) @Documented @@ -22,7 +20,7 @@ /** * A short reasoning of this property. */ - String value();// default = "N/A"; + String value(); Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeReference.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeFieldReference.java similarity index 79% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeReference.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeFieldReference.java index 541398a8e3..079d6ff291 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeReference.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedNotThreadSafeFieldReference.java @@ -1,28 +1,26 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.immutability.references; -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; - 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 lazy initialized - * - * @author Tobias Peter Roth + * 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 LazyInitializedNotThreadSafeReference { +public @interface LazyInitializedNotThreadSafeFieldReference { /** * A short reasoning of this property. */ - String value();// default = "N/A"; + String value(); Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeReference.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeFieldReference.java similarity index 82% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeReference.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeFieldReference.java index 29db7f584d..da5f42d9af 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeReference.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/LazyInitializedThreadSafeFieldReference.java @@ -1,28 +1,28 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.immutability.references; -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; - 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 lazy initialized + * 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 LazyInitializedThreadSafeReference { +public @interface LazyInitializedThreadSafeFieldReference { /** * A short reasoning of this property. */ - String value();// default = "N/A"; + String value(); Class[] analyses() default {L0FieldReferenceImmutabilityAnalysis.class}; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableReference.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableFieldReference.java similarity index 90% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableReference.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableFieldReference.java index 9f10c45e07..3e36f1bd0d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableReference.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/immutability/references/MutableFieldReference.java @@ -1,23 +1,23 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.immutability.references; -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; - 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 reference is mutable + * Annotation to state that the annotated field reference is mutable * * @author Tobias Peter Roth */ @PropertyValidator(key = "FieldReferenceImmutability",validator = MutableFieldReferenceMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) -public @interface MutableReference { +public @interface MutableFieldReference { /** 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 index 1c199a3421..a80f8ea26d 100644 --- 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 @@ -1,20 +1,18 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.immutability.types; -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; - 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. - * - * @author Tobias Peter Roth - */ -@PropertyValidator(key = "TypeImmutability_new", validator = DeepImmutableTypeMatcher.class) + * */ +@PropertyValidator(key = "TypeImmutability", validator = DeepImmutableTypeMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface DeepImmutableType { @@ -22,7 +20,7 @@ /** * A short reasoning of this property. */ - String value();// default = "N/A"; + 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 index 357a51eff0..dbc9efce54 100644 --- 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 @@ -1,14 +1,14 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.immutability.types; -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; - 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. * @@ -22,7 +22,7 @@ /** * A short reasoning of this property. */ - String value();// default = "N/A"; + 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 index faa9c19f66..f18910ffb1 100644 --- 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 @@ -1,20 +1,20 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.immutability.types; -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; - 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 Peter Roth + * @author Tobias Roth */ -@PropertyValidator(key = "TypeImmutability_new", validator = MutableTypeMatcher.class) +@PropertyValidator(key = "TypeImmutability", validator = MutableTypeMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface MutableType { @@ -22,7 +22,7 @@ /** * A short reasoning of this property. */ - String value();// default = "N/A"; + 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 index 2ceb3256be..8cadda3c67 100644 --- 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 @@ -1,20 +1,18 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.immutability.types; -import org.opalj.br.fpcf.FPCFAnalysis; -import org.opalj.fpcf.properties.PropertyValidator; -import org.opalj.tac.fpcf.analyses.immutability.L1TypeImmutabilityAnalysis; - 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 = ShallowImmutableTypeMatcher.class) +@PropertyValidator(key = "TypeImmutability", validator = ShallowImmutableTypeMatcher.class) @Documented @Retention(RetentionPolicy.CLASS) public @interface ShallowImmutableType { @@ -22,7 +20,7 @@ /** * A short reasoning of this property. */ - String value();// default = "N/A"; + 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. From 8c273f2d9638c8e6f90be40c71e1955d7ff50d12 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 26 Oct 2020 14:26:39 +0100 Subject: [PATCH 291/327] removed println --- .../fieldreference/L0FieldReferenceImmutabilityAnalysis.scala | 1 - 1 file changed, 1 deletion(-) 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 index 20570e7fd0..1302fed43a 100644 --- 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 @@ -395,7 +395,6 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP state.escapeDependees += ep false } - println(s"result: $result") result } From 8e6616dca803bb4519c416cec5a6b88ee5d554de Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 26 Oct 2020 14:42:41 +0100 Subject: [PATCH 292/327] revised; still wip --- ...mutabilityAnalysisLazyInitialization.scala | 48 +------------------ 1 file changed, 2 insertions(+), 46 deletions(-) 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 index b381dc2c70..edf50884e8 100644 --- 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 @@ -571,54 +571,10 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr 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 ⇒ - /*val primitiveTypecastExpr = expr.asPrimitiveTypeCastExpr - val targetTypeBeforConversion = primitiveTypecastExpr.targetTpe - if (primitiveTypecastExpr.operand.asVar.definedBy.forall(isExprReadOfCurrentField(_))) { - val fieldTypeBeforConversion = state.field.fieldType + case GetStatic.ASTID ⇒ expr.asGetStatic.resolveField(project).contains(state.field) + case PrimitiveTypecastExpr.ASTID ⇒ false - - val fieldType = - if(fieldTypeBeforConversion.isReferenceType) - fieldTypeBeforConversion - else - fieldTypeBeforConversion.asBaseType.WrapperType - - val targetType = - if(targetTypeBeforConversion.isReferenceType) - targetTypeBeforConversion - else - targetTypeBeforConversion.asBaseType.WrapperType - - //prevent lossy conversion of the field - - fieldType match { - case ObjectType.Integer ⇒ - } - - fieldType == ObjectType.Byte || - (fieldType == ObjectType.Short) && - (targetType != ObjectType.Byte) || - - (fieldType == ObjectType.Integer || fieldType == ObjectType.Float) && - (targetType != ObjectType.Short) && - (targetType != ObjectType.Byte) || - - (fieldType == ObjectType.Long || - fieldType == ObjectType.Double) && - (targetType == ObjectType.Long || - targetType == ObjectType.Double) - } else */ - /*println( - s""" - | class: ${state.field.classFile.thisType} - | field: ${state.field} - |""".stripMargin - ) */ - false case Compare.ASTID ⇒ val leftExpr = expr.asCompare.left val rightExpr = expr.asCompare.right From a25e715bf96b6661e71567f748e106f1f38c48a4 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 26 Oct 2020 14:44:10 +0100 Subject: [PATCH 293/327] revised; still wip --- .../ClassImmutabilityAnalysisDemo.scala | 4 - ...eldReferenceImmutabilityAnalysisDemo.scala | 2 +- .../org/opalj/support/info/Immutability.scala | 213 +++++++++--------- .../fpcf/properties/ClassImmutability.scala | 3 +- .../properties/ClassImmutability_old.scala | 175 ++++++++++++++ .../fpcf/properties/FieldImmutability.scala | 8 +- .../FieldReferenceImmutability.scala | 4 +- .../br/fpcf/properties/TypeImmutability.scala | 5 +- .../properties/TypeImmutability_old.scala | 104 +++++++++ 9 files changed, 398 insertions(+), 120 deletions(-) create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/ClassImmutability_old.scala create mode 100644 OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/TypeImmutability_old.scala 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 index 19f38f566a..7b00949af4 100644 --- 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 @@ -8,7 +8,6 @@ import java.io.File import java.io.FileWriter import java.net.URL import java.util.Calendar -import java.io.IOException import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis @@ -59,11 +58,8 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { def analyze(project: Project[URL]): String = { var propertyStore: PropertyStore = null - var analysisTime: Seconds = Seconds.None - val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) time { 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 index 88db90581b..d783bed48f 100644 --- 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 @@ -60,9 +60,9 @@ object FieldReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None - val analysesManager = project.get(FPCFAnalysesManagerKey) analysesManager.project.get(RTACallGraphKey) + time { propertyStore = analysesManager .runAll( 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 index 6b8e750790..53575725fd 100644 --- 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 @@ -80,28 +80,26 @@ import org.opalj.br.fpcf.properties.TypeImmutability import org.opalj.fpcf.EPS /** - * Determines the immutability of field references, fields, classes or/and types + * Determines the immutability of field references, fields, classes and types * - * @author Tobias Peter Roth + * @author Tobias Roth */ object Immutability { - sealed trait RunningAnalysis - case object References extends RunningAnalysis - case object Fields extends RunningAnalysis - case object Classes extends RunningAnalysis - case object Types extends RunningAnalysis - case object All extends RunningAnalysis + 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: RunningAnalysis, + analysis: Analyses, numThreads: Int, projectDir: Option[String], libDir: Option[String], resultsFolder: Path, - timeEvaluation: Boolean, - threadEvaluation: Boolean, withoutJDK: Boolean, isLibrary: Boolean, closedWorldAssumption: Boolean, @@ -144,17 +142,10 @@ object Immutability { var callGraphTime: Seconds = Seconds.None val project = time { - Project( - classFiles, - libFiles ++ JDKFiles, - libraryClassFilesAreInterfacesOnly = false, - Traversable.empty - ) - } { - t ⇒ projectTime = t.toSeconds - } + Project(classFiles, libFiles ++ JDKFiles, libraryClassFilesAreInterfacesOnly = false, Traversable.empty) + } { t ⇒ projectTime = t.toSeconds } - val referenceDependencies: List[FPCFAnalysisScheduler] = List( + val fieldReferenceDependencies: List[FPCFAnalysisScheduler] = List( EagerL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, LazyL2PurityAnalysis, @@ -223,11 +214,11 @@ object Immutability { ) val dependencies = analysis match { - case References ⇒ referenceDependencies - case Fields ⇒ fieldDependencies - case Classes ⇒ classDepencencies - case Types ⇒ typeDependencies - case All ⇒ allImmAnalysisDependencies + case FieldReferences ⇒ fieldReferenceDependencies + case Fields ⇒ fieldDependencies + case Classes ⇒ classDepencencies + case Types ⇒ typeDependencies + case All ⇒ allImmAnalysisDependencies } L2PurityAnalysis.setRater(Some(SystemOutLoggingAllExceptionRater)) @@ -252,13 +243,12 @@ object Immutability { else if (level == 2) Set[Class[_ <: AnyRef]](classOf[domain.l2.DefaultPerformInvocationsDomainWithCFG[URL]]) else - throw new Exception("wrong level") + 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: _*) @@ -271,54 +261,50 @@ object Immutability { ) time { - propertyStore = analysesManager - .runAll( - dependencies - ) - ._1 + propertyStore = analysesManager.runAll(dependencies)._1 propertyStore.waitOnPhaseCompletion() - } { - t ⇒ analysisTime = t.toSeconds - } + } { t ⇒ analysisTime = t.toSeconds } val stringBuilderResults: StringBuilder = new StringBuilder() - val allProjectClassTypes = project.allProjectClassFiles.map(_.thisType).toSet + val allProjectClassTypes = project.allProjectClassFiles.toIterator.map(_.thisType).toSet val allFieldsInProjectClassFiles = { if (reImInferComparison) { project.allProjectClassFiles.toIterator.flatMap { _.fields }. filter(f ⇒ !f.isTransient && !f.isSynthetic).toSet - } else { + } else project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet - } } val fieldReferenceGroupedResults = propertyStore.entities(FieldReferenceImmutability.key). - filter(field ⇒ allFieldsInProjectClassFiles.contains(field.asInstanceOf[Field])). - toTraversable.groupBy(_.toFinalELBP.p) + filter(field ⇒ allFieldsInProjectClassFiles.contains(field.e.asInstanceOf[Field])). + toTraversable.groupBy(_.asFinal.p) - val fieldReferenceOrder = - (eps1: EPS[Entity, FieldReferenceImmutability], eps2: EPS[Entity, FieldReferenceImmutability]) ⇒ - eps1.e.toString < eps2.e.toString + val fieldReferenceOrder: (EPS[Entity, FieldReferenceImmutability], EPS[Entity, FieldReferenceImmutability]) ⇒ Boolean = + (epsLeftHandSide, epsRightHandSide) ⇒ epsLeftHandSide.e.toString < epsRightHandSide.e.toString val mutableFieldReferences = - fieldReferenceGroupedResults(MutableFieldReference).toSeq.sortWith(fieldReferenceOrder) + fieldReferenceGroupedResults.getOrElse(MutableFieldReference, Iterator.empty).toSeq. + sortWith(fieldReferenceOrder) val notThreadSafeLazyInitializedFieldReferences = - fieldReferenceGroupedResults(LazyInitializedNotThreadSafeFieldReference).toSeq.sortWith(fieldReferenceOrder) + fieldReferenceGroupedResults.getOrElse(LazyInitializedNotThreadSafeFieldReference, Iterator.empty).toSeq. + sortWith(fieldReferenceOrder) val lazyInitializedReferencesNotThreadSafeButDeterministic = - fieldReferenceGroupedResults(LazyInitializedNotThreadSafeButDeterministicFieldReference). - toSeq.sortWith(fieldReferenceOrder) + fieldReferenceGroupedResults. + getOrElse(LazyInitializedNotThreadSafeButDeterministicFieldReference, Iterator.empty).toSeq. + sortWith(fieldReferenceOrder) val threadSafeLazyInitializedFieldReferences = - fieldReferenceGroupedResults(LazyInitializedThreadSafeFieldReference).toSeq.sortWith(fieldReferenceOrder) + fieldReferenceGroupedResults.getOrElse(LazyInitializedThreadSafeFieldReference, Iterator.empty).toSeq. + sortWith(fieldReferenceOrder) - val immutableReferences = fieldReferenceGroupedResults(ImmutableFieldReference). + val immutableReferences = fieldReferenceGroupedResults.getOrElse(ImmutableFieldReference, Iterator.empty). toSeq.sortWith(fieldReferenceOrder) - if (analysis == All || analysis == References) { + if (analysis == All || analysis == FieldReferences) { stringBuilderResults.append( s""" | Mutable References: @@ -350,19 +336,22 @@ object Immutability { } val fieldGroupedResults = propertyStore.entities(FieldImmutability.key). - filter(field ⇒ allFieldsInProjectClassFiles.contains(field.asInstanceOf[Field])). - toTraversable.groupBy(_.toFinalELBP.p) + filter(eps ⇒ allFieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field])). + toTraversable.groupBy(_.asFinal.p) - val fieldOrder = (eps1: EPS[Entity, FieldImmutability], eps2: EPS[Entity, FieldImmutability]) ⇒ - eps1.e.toString < eps2.e.toString + val fieldOrder: (EPS[Entity, FieldImmutability], EPS[Entity, FieldImmutability]) ⇒ Boolean = + (epsLeftHandSide, epsRightHandSide) ⇒ epsLeftHandSide.e.toString < epsRightHandSide.e.toString - val mutableFields = fieldGroupedResults(MutableField).toSeq.sortWith(fieldOrder) + val mutableFields = fieldGroupedResults.getOrElse(MutableField, Iterator.empty).toSeq.sortWith(fieldOrder) - val shallowImmutableFields = fieldGroupedResults(ShallowImmutableField).toSeq.sortWith(fieldOrder) + val shallowImmutableFields = fieldGroupedResults.getOrElse(ShallowImmutableField, Iterator.empty).toSeq. + sortWith(fieldOrder) - val dependentImmutableFields = fieldGroupedResults(DependentImmutableField).toSeq.sortWith(fieldOrder) + val dependentImmutableFields = fieldGroupedResults.getOrElse(DependentImmutableField, Iterator.empty).toSeq. + sortWith(fieldOrder) - val deepImmutableFields = fieldGroupedResults(DeepImmutableField).toSeq.sortWith(fieldOrder) + val deepImmutableFields = fieldGroupedResults.getOrElse(DeepImmutableField, Iterator.empty).toSeq. + sortWith(fieldOrder) if (analysis == All || analysis == Fields) { stringBuilderResults.append( @@ -383,31 +372,35 @@ object Immutability { ) } + //val classGroupedResults = propertyStore.entities(ClassImmutability.key). + // filter(eps ⇒ allProjectClassTypes.contains(eps.e.asInstanceOf[ObjectType])).toTraversable.groupBy(_.p) + val classGroupedResults = propertyStore.entities(ClassImmutability.key). - filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) + filter(eps ⇒ allProjectClassTypes.contains(eps.e.asInstanceOf[ObjectType])).toTraversable.groupBy(_.asFinal.p) - val order = (eps1: EPS[Entity, ClassImmutability], eps2: EPS[Entity, ClassImmutability]) ⇒ - eps1.e.toString < eps2.e.toString + // println(s"cgr size: ${classGroupedResults.size}") + val order: (EPS[Entity, ClassImmutability], EPS[Entity, ClassImmutability]) ⇒ Boolean = + (epsLeftHandSide, epsRightHandSide) ⇒ epsLeftHandSide.e.toString < epsRightHandSide.e.toString val mutableClasses = - classGroupedResults(MutableClass).toSeq.sortWith(order) + classGroupedResults.getOrElse(MutableClass, Iterator.empty).toSeq.sortWith(order) val shallowImmutableClasses = - classGroupedResults(ShallowImmutableClass).toSeq.sortWith(order) + classGroupedResults.getOrElse(ShallowImmutableClass, Iterator.empty).toSeq.sortWith(order) val dependentImmutableClasses = - classGroupedResults(DependentImmutableClass).toSeq.sortWith(order) + classGroupedResults.getOrElse(DependentImmutableClass, Iterator.empty).toSeq.sortWith(order) - val deepImmutables = classGroupedResults(DeepImmutableClass) + val deepImmutables = classGroupedResults.getOrElse(DeepImmutableClass, Iterator.empty) val allInterfaces = project.allProjectClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet val deepImmutableClassesInterfaces = deepImmutables - .filter(eps ⇒ allInterfaces.contains(eps.asInstanceOf[ObjectType])).toSeq.sortWith(order) + .filter(eps ⇒ allInterfaces.contains(eps.e.asInstanceOf[ObjectType])).toSeq.sortWith(order) val deepImmutableClasses = deepImmutables - .filter(eps ⇒ !allInterfaces.contains(eps.asInstanceOf[ObjectType])).toSeq.sortWith(order) + .filter(eps ⇒ !allInterfaces.contains(eps.e.asInstanceOf[ObjectType])).toSeq.sortWith(order) if (analysis == All || analysis == Classes) { stringBuilderResults.append( @@ -430,19 +423,26 @@ object Immutability { ) } + // val typeGroupedResults = propertyStore.entities(TypeImmutability.key). + // filter(eps ⇒ allProjectClassTypes.contains(eps.e.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) + val typeGroupedResults = propertyStore.entities(TypeImmutability.key). - filter(x ⇒ allProjectClassTypes.contains(x.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) + filter(eps ⇒ allProjectClassTypes.contains(eps.e.asInstanceOf[ObjectType])).toTraversable.groupBy(_.asFinal.p) - val typeOrder = (eps1: EPS[Entity, TypeImmutability], eps2: EPS[Entity, TypeImmutability]) ⇒ - eps1.e.toString < eps2.e.toString + val typeOrder: (EPS[Entity, TypeImmutability], EPS[Entity, TypeImmutability]) ⇒ Boolean = + (epsLeftHandSide, epsRightHandSide) ⇒ epsLeftHandSide.e.toString < epsRightHandSide.e.toString - val mutableTypes = typeGroupedResults(MutableType).toSeq.sortWith(typeOrder) + val mutableTypes = typeGroupedResults.getOrElse(MutableType, Iterator.empty).toSeq. + sortWith(typeOrder) - val shallowImmutableTypes = typeGroupedResults(ShallowImmutableType).toSeq.sortWith(typeOrder) + val shallowImmutableTypes = typeGroupedResults.getOrElse(ShallowImmutableType, Iterator.empty).toSeq. + sortWith(typeOrder) - val dependentImmutableTypes = typeGroupedResults(DependentImmutableType).toSeq.sortWith(typeOrder) + val dependentImmutableTypes = typeGroupedResults.getOrElse(DependentImmutableType, Iterator.empty).toSeq. + sortWith(typeOrder) - val deepImmutableTypes = typeGroupedResults(DeepImmutableType).toSeq.sortWith(typeOrder) + val deepImmutableTypes = typeGroupedResults.getOrElse(DeepImmutableType, Iterator.empty).toSeq. + sortWith(typeOrder) if (analysis == All || analysis == Types) { stringBuilderResults.append( @@ -462,10 +462,10 @@ object Immutability { ) } - val stringBuilderAmounts: StringBuilder = new StringBuilder + val stringBuilderNumber: StringBuilder = new StringBuilder - if (analysis == References || analysis == All) { - stringBuilderAmounts.append( + if (analysis == FieldReferences || analysis == All) { + stringBuilderNumber.append( s""" | Mutable References: ${mutableFieldReferences.size} | Lazy Initialized Not Thread Safe Field References: ${notThreadSafeLazyInitializedFieldReferences.size} @@ -478,7 +478,7 @@ object Immutability { ) } if (analysis == Fields || analysis == All) { - stringBuilderAmounts.append( + stringBuilderNumber.append( s""" | Mutable Fields: ${mutableFields.size} | Shallow Immutable Fields: ${shallowImmutableFields.size} @@ -490,7 +490,7 @@ object Immutability { } if (analysis == Classes || analysis == All) { - stringBuilderAmounts.append( + stringBuilderNumber.append( s""" | Mutable Classes: ${mutableClasses.size} | Shallow Immutable Classes: ${shallowImmutableClasses.size} @@ -504,7 +504,7 @@ object Immutability { } if (analysis == Types || analysis == All) - stringBuilderAmounts.append( + stringBuilderNumber.append( s""" | Mutable Types: ${mutableTypes.size} | Shallow Immutable Types: ${shallowImmutableTypes.size} @@ -516,7 +516,7 @@ object Immutability { val totalTime = projectTime + callGraphTime + analysisTime - stringBuilderAmounts.append( + stringBuilderNumber.append( s""" | running ${analysis.toString} analysis | took: @@ -530,7 +530,7 @@ object Immutability { println( s""" | - | ${stringBuilderAmounts.toString()} + | ${stringBuilderNumber.toString()} | | time results: | @@ -541,16 +541,24 @@ object Immutability { ) if (resultsFolder != null) { + import java.text.SimpleDateFormat - val file = new File(s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt") + 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)}.txt") + println(s"filepath: ${file.getAbsolutePath}") val bw = new BufferedWriter(new FileWriter(file)) try { bw.write( s""" ${stringBuilderResults.toString()} | - | ${stringBuilderAmounts.toString()} + | ${stringBuilderNumber.toString()} + | + | level: $level | | jdk folder: $JRELibraryFolder | @@ -559,20 +567,18 @@ object Immutability { bw.close() } catch { - case e: IOException ⇒ println(s"could not write file: ${file.getName}") + case _: IOException ⇒ println(s"could not write file: ${file.getName}") } finally { - bw.close + bw.close() } } - println(s"propertyStore: ${propertyStore.getClass.toString()}") + println(s"propertyStore: ${propertyStore.getClass.toString}") println(s"jdk folder: $JRELibraryFolder") - BasicReport( - stringBuilderAmounts.toString() - ) + BasicReport(stringBuilderNumber.toString()) } def main(args: Array[String]): Unit = { @@ -621,16 +627,17 @@ object Immutability { } } - var analysis: RunningAnalysis = All + var analysis: Analyses = All while (i < args.length) { args(i) match { - case "-analysis" ⇒ { + + case "-analysis" ⇒ val result = readNextArg() if (result == "All") analysis = All - else if (result == "References") - analysis = References + else if (result == "FieldReferences") + analysis = FieldReferences else if (result == "Fields") analysis = Fields else if (result == "Classes") @@ -638,11 +645,10 @@ object Immutability { else if (result == "Types") analysis = Types else 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 "-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()) @@ -653,8 +659,11 @@ object Immutability { case "-callGraph" ⇒ callGraphName = Some(readNextArg()) case "-level" ⇒ level = Integer.parseInt(readNextArg()) case "-ReImInferComparison" ⇒ reImInferComparison = true + case "-JDK" ⇒ - cp = JRELibraryFolder; withoutJDK = true + cp = JRELibraryFolder + withoutJDK = true + case unknown ⇒ Console.println(usage) throw new IllegalArgumentException(s"unknown parameter: $unknown") @@ -681,8 +690,6 @@ object Immutability { projectDir, libDir, resultFolder, - timeEvaluation, - threadEvaluation, withoutJDK, isLibrary, closedWorldAssumption, 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 c9f2c25925..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 @@ -91,7 +91,8 @@ case object ShallowImmutableClass extends ClassImmutability { } case object MutableClass extends ClassImmutability { - def correspondingTypeImmutability = MutableType + + def correspondingTypeImmutability: TypeImmutability = MutableType def meet(other: ClassImmutability): ClassImmutability = 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 index 1455e2e50a..794ad59a2f 100644 --- 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 @@ -26,17 +26,13 @@ sealed trait FieldImmutabilityPropertyMetaInformation extends PropertyMetaInform * [[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 Peter Roth + * @author Tobias Roth */ -sealed trait FieldImmutability - extends OrderedProperty - with FieldImmutabilityPropertyMetaInformation { - +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] = { 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 index 0cb03ea050..7822994046 100644 --- 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 @@ -94,6 +94,7 @@ case object LazyInitializedNotThreadSafeButDeterministicFieldReference extends F } } } + case object LazyInitializedNotThreadSafeFieldReference extends FieldReferenceImmutability { def meet(other: FieldReferenceImmutability): properties.FieldReferenceImmutability = { @@ -115,8 +116,7 @@ case object LazyInitializedNotThreadSafeFieldReference extends FieldReferenceImm } case object MutableFieldReference extends FieldReferenceImmutability { - - def meet(other: FieldReferenceImmutability): this.type = this + def meet(other: FieldReferenceImmutability): FieldReferenceImmutability = this override def checkIsEqualOrBetterThan(e: Entity, other: FieldReferenceImmutability): Unit = { if (other != MutableFieldReference) { 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 cb565e0d98..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 @@ -16,6 +16,7 @@ sealed trait TypeImmutabilityPropertyMetaInformation extends PropertyMetaInforma /** * 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., * the collection as a whole is only immutable if only immutable objects are stored in the @@ -107,7 +108,6 @@ case object ShallowImmutableType extends TypeImmutability { this override def checkIsEqualOrBetterThan(e: Entity, other: Self): Unit = { - if (other == DeepImmutableType || other == DependentImmutableType) { throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") } @@ -121,10 +121,9 @@ case object MutableType extends TypeImmutability { 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") } 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"); + } + } +} + From fd598d68b6682a81ba3beff48718a0f1c82cfdf6 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 26 Oct 2020 15:16:54 +0100 Subject: [PATCH 294/327] optimizing comments if->whether --- ...stractFieldReferenceImmutabilityAnalysis.scala | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) 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 index 10283916bb..8762b8e6ab 100644 --- 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 @@ -101,7 +101,7 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { } /** - * Checks if the method that defines the value assigned to a (potentially) lazily initialized + * 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. */ @@ -125,7 +125,7 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { } /** - * Checks if the field the value assigned to a (potentially) lazily initialized field is final, + * 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( @@ -141,7 +141,7 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { } /** - * Checks if the field is prematurely read, i.e. read before it is initialized in the + * Checks whether the field is prematurely read, i.e. read before it is initialized in the * constructor, using the corresponding property. */ def isPrematurelyRead( @@ -164,7 +164,7 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { } /** - * Checks if the calls at a given pc within a given method introduce non determinism. + * Checks whether the calls at a given pc within a given method introduce non determinism. * @return if the calls introduce nondeterminism */ def doCallsIntroduceNonDeterminism( @@ -183,8 +183,8 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { } /** - * Checks if a callee is nondeterministic and sets the [[State.referenceImmutability]] - * if it is to MutableFieldReference. + * 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 = { @@ -196,7 +196,8 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { if (targets.exists(target ⇒ isNonDeterministic(propertyStore(target, Purity.key)))) { state.referenceImmutability = MutableFieldReference true - } else false + } else + false } } } From 3a295087345680c9a504bea63443161a95b0bab9 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 6 Nov 2020 15:45:27 +0100 Subject: [PATCH 295/327] wip --- .../org/opalj/support/info/Immutability.scala | 252 +++++++++------ .../SimpleStringModel.java | 9 +- OPAL/tac/src/main/resources/reference.conf | 10 +- .../L2FieldImmutabilityAnalysis.scala | 2 +- .../L3FieldImmutabilityAnalysis.scala | 63 ++-- ...ctFieldReferenceImmutabilityAnalysis.scala | 22 +- ...mutabilityAnalysisLazyInitialization.scala | 292 +++++++++--------- ...L0FieldReferenceImmutabilityAnalysis.scala | 24 +- 8 files changed, 393 insertions(+), 281 deletions(-) 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 index 53575725fd..b3511e2d85 100644 --- 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 @@ -78,9 +78,11 @@ 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.tac.fpcf.analyses.purity.EagerL2PurityAnalysis /** - * Determines the immutability of field references, fields, classes and types + * Determines the immutability of field references, fields, classes and types and gives several setting options for the + * evaluation. * * @author Tobias Roth */ @@ -94,19 +96,23 @@ object Immutability { 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, - reImInferComparison: Boolean + cp: File, + analysis: Analyses, + numThreads: Int, + projectDir: Option[String], + libDir: Option[String], + resultsFolder: Path, + withoutJDK: Boolean, + isLibrary: Boolean, + closedWorldAssumption: Boolean, + callGraphKey: AbstractCallGraphKey, + level: Int, + reImInferComparison: Boolean, + withoutConsiderEscape: Boolean, + withoutConsiderGenericity: Boolean, + withoutConsiderLazyInitialization: Boolean ): BasicReport = { + import org.opalj.fpcf.OrderedProperty OPALLogger.updateLogger(GlobalLogContext, DevNullLogger) @@ -137,6 +143,28 @@ object Immutability { ) } + if (withoutConsiderEscape) { + config = config.withValue( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape", + ConfigValueFactory.fromAnyRef("false") + ) + } + + if (withoutConsiderGenericity) { + config = config.withValue( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity", + ConfigValueFactory.fromAnyRef("false") + ) + } + + if (withoutConsiderLazyInitialization) { + config = + config.withValue( + "org.opalj.fpcf.analyses.L0FieldReferenceImmutabilityAnalysis.considerLazyInitialization", + ConfigValueFactory.fromAnyRef("false") + ) + } + var projectTime: Seconds = Seconds.None var analysisTime: Seconds = Seconds.None var callGraphTime: Seconds = Seconds.None @@ -145,10 +173,16 @@ object Immutability { Project(classFiles, libFiles ++ JDKFiles, libraryClassFilesAreInterfacesOnly = false, Traversable.empty) } { t ⇒ projectTime = t.toSeconds } + val purityAnalysis = + if (reImInferComparison) + EagerL2PurityAnalysis + else + LazyL2PurityAnalysis + val fieldReferenceDependencies: List[FPCFAnalysisScheduler] = List( EagerL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, + purityAnalysis, LazyInterProceduralEscapeAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, @@ -159,7 +193,7 @@ object Immutability { val fieldDependencies: List[FPCFAnalysisScheduler] = List( LazyL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, + purityAnalysis, EagerL3FieldImmutabilityAnalysis, LazyL1ClassImmutabilityAnalysis, LazyL1TypeImmutabilityAnalysis, @@ -172,7 +206,7 @@ object Immutability { val classDepencencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, + purityAnalysis, LazyL0FieldReferenceImmutabilityAnalysis, LazyL3FieldImmutabilityAnalysis, LazyL1TypeImmutabilityAnalysis, @@ -186,7 +220,7 @@ object Immutability { val typeDependencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, + purityAnalysis, LazyL0FieldReferenceImmutabilityAnalysis, LazyL3FieldImmutabilityAnalysis, LazyL1ClassImmutabilityAnalysis, @@ -201,7 +235,7 @@ object Immutability { val allImmAnalysisDependencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, - LazyL2PurityAnalysis, + purityAnalysis, EagerL0FieldReferenceImmutabilityAnalysis, EagerL3FieldImmutabilityAnalysis, EagerL1ClassImmutabilityAnalysis, @@ -227,13 +261,16 @@ object Immutability { val analysesManager = project.get(FPCFAnalysesManagerKey) - println(s"callgraph $callGraphKey") - time { analysesManager.project.get(callGraphKey) } { t ⇒ callGraphTime = t.toSeconds } - println(s"level: $level") + println( + s""" + | level: $level + | callgraph: $callGraphKey + |""".stripMargin + ) analysesManager.project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ if (level == 0) @@ -281,28 +318,33 @@ object Immutability { filter(field ⇒ allFieldsInProjectClassFiles.contains(field.e.asInstanceOf[Field])). toTraversable.groupBy(_.asFinal.p) - val fieldReferenceOrder: (EPS[Entity, FieldReferenceImmutability], EPS[Entity, FieldReferenceImmutability]) ⇒ Boolean = - (epsLeftHandSide, epsRightHandSide) ⇒ epsLeftHandSide.e.toString < epsRightHandSide.e.toString + def unpackFieldEPS(eps: EPS[Entity, OrderedProperty]): String = { + 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).toSeq. - sortWith(fieldReferenceOrder) + fieldReferenceGroupedResults.getOrElse(MutableFieldReference, Iterator.empty).map(unpackFieldEPS). + toSeq.sortWith(_ < _) val notThreadSafeLazyInitializedFieldReferences = fieldReferenceGroupedResults.getOrElse(LazyInitializedNotThreadSafeFieldReference, Iterator.empty).toSeq. - sortWith(fieldReferenceOrder) + map(unpackFieldEPS).sortWith(_ < _) val lazyInitializedReferencesNotThreadSafeButDeterministic = fieldReferenceGroupedResults. - getOrElse(LazyInitializedNotThreadSafeButDeterministicFieldReference, Iterator.empty).toSeq. - sortWith(fieldReferenceOrder) + getOrElse(LazyInitializedNotThreadSafeButDeterministicFieldReference, Iterator.empty). + toSeq.map(unpackFieldEPS).sortWith(_ < _) val threadSafeLazyInitializedFieldReferences = - fieldReferenceGroupedResults.getOrElse(LazyInitializedThreadSafeFieldReference, Iterator.empty).toSeq. - sortWith(fieldReferenceOrder) + fieldReferenceGroupedResults.getOrElse(LazyInitializedThreadSafeFieldReference, Iterator.empty). + toSeq.map(unpackFieldEPS).sortWith(_ < _) val immutableReferences = fieldReferenceGroupedResults.getOrElse(ImmutableFieldReference, Iterator.empty). - toSeq.sortWith(fieldReferenceOrder) + toSeq.map(unpackFieldEPS).sortWith(_ < _) if (analysis == All || analysis == FieldReferences) { stringBuilderResults.append( @@ -339,57 +381,56 @@ object Immutability { filter(eps ⇒ allFieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field])). toTraversable.groupBy(_.asFinal.p) - val fieldOrder: (EPS[Entity, FieldImmutability], EPS[Entity, FieldImmutability]) ⇒ Boolean = - (epsLeftHandSide, epsRightHandSide) ⇒ epsLeftHandSide.e.toString < epsRightHandSide.e.toString - - val mutableFields = fieldGroupedResults.getOrElse(MutableField, Iterator.empty).toSeq.sortWith(fieldOrder) + val mutableFields = fieldGroupedResults.getOrElse(MutableField, Iterator.empty).toSeq.map(unpackFieldEPS). + sortWith(_ < _) val shallowImmutableFields = fieldGroupedResults.getOrElse(ShallowImmutableField, Iterator.empty).toSeq. - sortWith(fieldOrder) + map(unpackFieldEPS).sortWith(_ < _) val dependentImmutableFields = fieldGroupedResults.getOrElse(DependentImmutableField, Iterator.empty).toSeq. - sortWith(fieldOrder) + map(unpackFieldEPS).sortWith(_ < _) val deepImmutableFields = fieldGroupedResults.getOrElse(DeepImmutableField, Iterator.empty).toSeq. - sortWith(fieldOrder) + map(unpackFieldEPS).sortWith(_ < _) if (analysis == All || analysis == Fields) { stringBuilderResults.append( s""" | Mutable Fields: - | ${mutableFields.mkString(" || Mutable Field \n")} + | ${mutableFields.mkString(" | Mutable Field \n")} | | Shallow Immutable Fields: - | ${shallowImmutableFields.mkString(" || Shallow Immutable Field \n")} + | ${shallowImmutableFields.mkString(" | Shallow Immutable Field \n")} | | Dependent Immutable Fields: - | ${dependentImmutableFields.mkString(" || Dependent Immutable Field \n")} + | ${dependentImmutableFields.mkString(" | Dependent Immutable Field \n")} | | Deep Immutable Fields: - | ${deepImmutableFields.mkString(" || Deep Immutable Field \n")} + | ${deepImmutableFields.mkString(" | Deep Immutable Field \n")} | |""".stripMargin ) } - //val classGroupedResults = propertyStore.entities(ClassImmutability.key). - // filter(eps ⇒ allProjectClassTypes.contains(eps.e.asInstanceOf[ObjectType])).toTraversable.groupBy(_.p) - val classGroupedResults = propertyStore.entities(ClassImmutability.key). filter(eps ⇒ allProjectClassTypes.contains(eps.e.asInstanceOf[ObjectType])).toTraversable.groupBy(_.asFinal.p) - // println(s"cgr size: ${classGroupedResults.size}") - val order: (EPS[Entity, ClassImmutability], EPS[Entity, ClassImmutability]) ⇒ Boolean = - (epsLeftHandSide, epsRightHandSide) ⇒ epsLeftHandSide.e.toString < epsRightHandSide.e.toString + 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.sortWith(order) + classGroupedResults.getOrElse(MutableClass, Iterator.empty).toSeq.map(unpackClass).sortWith(_ < _) val shallowImmutableClasses = - classGroupedResults.getOrElse(ShallowImmutableClass, Iterator.empty).toSeq.sortWith(order) + classGroupedResults.getOrElse(ShallowImmutableClass, Iterator.empty).toSeq. + map(unpackClass).sortWith(_ < _) val dependentImmutableClasses = - classGroupedResults.getOrElse(DependentImmutableClass, Iterator.empty).toSeq.sortWith(order) + classGroupedResults.getOrElse(DependentImmutableClass, Iterator.empty).toSeq. + map(unpackClass).sortWith(_ < _) val deepImmutables = classGroupedResults.getOrElse(DeepImmutableClass, Iterator.empty) @@ -397,52 +438,48 @@ object Immutability { project.allProjectClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet val deepImmutableClassesInterfaces = deepImmutables - .filter(eps ⇒ allInterfaces.contains(eps.e.asInstanceOf[ObjectType])).toSeq.sortWith(order) + .filter(eps ⇒ allInterfaces.contains(eps.e.asInstanceOf[ObjectType])).toSeq. + map(unpackClass).sortWith(_ < _) val deepImmutableClasses = deepImmutables - .filter(eps ⇒ !allInterfaces.contains(eps.e.asInstanceOf[ObjectType])).toSeq.sortWith(order) + .filter(eps ⇒ !allInterfaces.contains(eps.e.asInstanceOf[ObjectType])).toSeq. + map(unpackClass).sortWith(_ < _) if (analysis == All || analysis == Classes) { stringBuilderResults.append( s""" | Mutable Classes: - | ${mutableClasses.mkString(" || Mutable Class \n")} + | ${mutableClasses.mkString(" | Mutable Class \n")} | | Shallow Immutable Classes: - | ${shallowImmutableClasses.mkString(" || Shallow Immutable Class \n")} + | ${shallowImmutableClasses.mkString(" | Shallow Immutable Class \n")} | | Dependent Immutable Classes: - | ${dependentImmutableClasses.mkString(" || Dependent Immutable Class \n")} + | ${dependentImmutableClasses.mkString(" | Dependent Immutable Class \n")} | | Deep Immutable Classes: - | ${deepImmutableClasses.mkString(" || Deep Immutable Classes \n")} + | ${deepImmutableClasses.mkString(" | Deep Immutable Classes \n")} | | Deep Immutable Interfaces: - | ${deepImmutableClassesInterfaces.mkString(" || Deep Immutable Interfaces \n")} + | ${deepImmutableClassesInterfaces.mkString(" | Deep Immutable Interfaces \n")} |""".stripMargin ) } - // val typeGroupedResults = propertyStore.entities(TypeImmutability.key). - // filter(eps ⇒ allProjectClassTypes.contains(eps.e.asInstanceOf[ObjectType])).toTraversable.groupBy(_.e) - val typeGroupedResults = propertyStore.entities(TypeImmutability.key). filter(eps ⇒ allProjectClassTypes.contains(eps.e.asInstanceOf[ObjectType])).toTraversable.groupBy(_.asFinal.p) - val typeOrder: (EPS[Entity, TypeImmutability], EPS[Entity, TypeImmutability]) ⇒ Boolean = - (epsLeftHandSide, epsRightHandSide) ⇒ epsLeftHandSide.e.toString < epsRightHandSide.e.toString - - val mutableTypes = typeGroupedResults.getOrElse(MutableType, Iterator.empty).toSeq. - sortWith(typeOrder) + val mutableTypes = typeGroupedResults.getOrElse(MutableType, Iterator.empty).toSeq.map(unpackClass). + sortWith(_ < _) val shallowImmutableTypes = typeGroupedResults.getOrElse(ShallowImmutableType, Iterator.empty).toSeq. - sortWith(typeOrder) + map(unpackClass).sortWith(_ < _) val dependentImmutableTypes = typeGroupedResults.getOrElse(DependentImmutableType, Iterator.empty).toSeq. - sortWith(typeOrder) + map(unpackClass).sortWith(_ < _) val deepImmutableTypes = typeGroupedResults.getOrElse(DeepImmutableType, Iterator.empty).toSeq. - sortWith(typeOrder) + map(unpackClass).sortWith(_ < _) if (analysis == All || analysis == Types) { stringBuilderResults.append( @@ -539,6 +576,24 @@ object Immutability { | results folder: $resultsFolder |""".stripMargin ) + val fileNameExtension = { + { + if (withoutConsiderEscape) { + println("withoutConsiderEscape") + "_withoutConsiderEscape" + } else "" + } + { + if (withoutConsiderGenericity) { + println("withoutConsiderGenericity") + "_withoutConsiderGenericity" + } else "" + } + { + if (withoutConsiderLazyInitialization) { + println("withoutConsiderLazyInitialization") + "_withoutConsiderLazyInitialization" + } else "" + } + } if (resultsFolder != null) { import java.text.SimpleDateFormat @@ -547,7 +602,10 @@ object Immutability { 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)}.txt") + + 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)) @@ -562,7 +620,7 @@ object Immutability { | | jdk folder: $JRELibraryFolder | - |"""".stripMargin + |""".stripMargin ) bw.close() @@ -598,7 +656,10 @@ object Immutability { | [-noJDK] (running without the JDK) | [-callGraph (Default: RTA) | [-level] <0|1|2> (domain level Default: 2) - | [-ReImInferComparison] (without transient fields) + | [-ReImInferComparison] (without transient fields and with eager L2 purity analysis) + | [-withoutConsiderGenericity] + | [-withoutConsiderEscape] + | [-withoutConsiderLazyInitialization] |""".stripMargin } @@ -616,6 +677,9 @@ object Immutability { var callGraphName: Option[String] = None var level = 2 var reImInferComparison = false + var withoutConsiderLazyInitialization = false + var withoutConsiderGenericity = false + var withoutConsiderEscape = false def readNextArg(): String = { i = i + 1 @@ -644,28 +708,33 @@ object Immutability { analysis = Classes else if (result == "Types") analysis = Types - else 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 "-ReImInferComparison" ⇒ reImInferComparison = true - + 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 "-ReImInferComparison" ⇒ reImInferComparison = true + case "-withoutConsiderGenericity" ⇒ withoutConsiderGenericity = true + case "-withoutConsiderEscape" ⇒ withoutConsiderEscape = true + case "-withoutConsiderLazyInitialization" ⇒ withoutConsiderLazyInitialization = true case "-JDK" ⇒ cp = JRELibraryFolder withoutJDK = true case unknown ⇒ - Console.println(usage) + println(usage) throw new IllegalArgumentException(s"unknown parameter: $unknown") } i += 1 @@ -695,7 +764,10 @@ object Immutability { closedWorldAssumption, callGraphKey, level, - reImInferComparison + reImInferComparison, + withoutConsiderEscape, + withoutConsiderGenericity, + withoutConsiderLazyInitialization ) } } 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 index 33be00af15..1e050b0332 100644 --- 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 @@ -3,9 +3,11 @@ 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.MutableFieldReference; import org.opalj.tac.fpcf.analyses.L1FieldImmutabilityAnalysis; import org.opalj.tac.fpcf.analyses.L2FieldImmutabilityAnalysis; import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; @@ -26,9 +28,10 @@ char[] getValue(){ return value; } - @DeepImmutableField(value="", analyses = L3FieldImmutabilityAnalysis.class) - @LazyInitializedNotThreadSafeButDeterministicReference(value = "", - analyses = L0FieldReferenceImmutabilityAnalysis.class) + //TODO @DeepImmutableField + //TODO @LazyInitializedNotThreadSafeButDeterministicReference + @MutableField("Mutable field reference") + @MutableFieldReference(value="The two def sites of h are conservatively handled as mutable") private int hash; // Default value 0 diff --git a/OPAL/tac/src/main/resources/reference.conf b/OPAL/tac/src/main/resources/reference.conf index a68f97cdd8..b041bbd3ba 100644 --- a/OPAL/tac/src/main/resources/reference.conf +++ b/OPAL/tac/src/main/resources/reference.conf @@ -32,7 +32,8 @@ org.opalj { "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" + lazyFactory = "org.opalj.tac.fpcf.analyses.LazyL0FieldReferenceImmutabilityAnalysis", + considerLazyInitialization = true }, "L0ClassImmutabilityAnalysis" { description = "", @@ -114,6 +115,13 @@ org.opalj { } ] }, + L0FieldReferenceImmutabilityAnalysis { + considerLazyInitialization = "true" + }, + L3FieldImmutabilityAnalysis { + considerGenericity = true, + considerEscape = true + }, L1PurityAnalysis { domainSpecificRater = "org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater" }, diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala index 0aea0f705f..0e585126bc 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala @@ -1030,7 +1030,7 @@ class L2FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } /** - * 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. */ 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 index 6e0d675a5e..3c1b8028f2 100755 --- 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 @@ -126,6 +126,16 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e final val definitionSites = project.get(DefinitionSitesKey) implicit final val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + val considerEscape = + project.config.getBoolean( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape" + ) + + val considerGenericity = + project.config.getBoolean( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity" + ) + def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = entity match { @@ -368,7 +378,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e val (method, pcs) = read val taCodeOption = getTACAI(method, pcs, isRead = true) if (taCodeOption.isDefined) - determineEscapeViaFieldReadsWithKnownTAC(pcs, taCodeOption.get, method) + determineEscapeViaFieldReadsWithKnownTAC(pcs, taCodeOption.get) } } } @@ -379,8 +389,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e */ def determineEscapeViaFieldReadsWithKnownTAC( pcs: PCs, - taCode: TACode[TACMethodParameter, V], - method: Method + taCode: TACode[TACMethodParameter, V] )(implicit state: State): Unit = { if (pcs.exists { pc ⇒ val readIndex = taCode.pcToIndex(pc) @@ -727,7 +736,6 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } else { definitionSiteAssignment.targetVar.usedBy.exists { useSite ⇒ val useSiteStmt = taCode.stmts(useSite) - // println(s"constructor use site stmt: $useSiteStmt") if (useSiteStmt.isNonVirtualMethodCall) { doesNonVirtualMethodCallEnablesEscape(useSiteStmt.asNonVirtualMethodCall) } else if (useSiteStmt.isPutStatic || useSiteStmt.isPutField) { @@ -839,6 +847,14 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { import org.opalj.fpcf.EUBP + + def typeMatch(entity: Entity) = + entity match { + case objectType: ObjectType if state.innerArrayTypes.contains(objectType) ⇒ + state.noEscapePossibilityViaReference = false + case _ ⇒ + } + if (eps.asEPS.pk != TACAI.key) state.dependees = state.dependees.filter(_.e ne eps.e) eps match { @@ -849,21 +865,13 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e if (t != ObjectType.Object) { // in case of generic fields state.dependentImmutability = Dependent } - t match { - case objectType: ObjectType if state.innerArrayTypes.contains(objectType) ⇒ - state.noEscapePossibilityViaReference = false - case _ ⇒ - } + typeMatch(t) case FinalEP(t, DependentImmutableType) ⇒ state.typeIsImmutable = false if (t != field.fieldType && state.dependentImmutability == OnlyDeepImmutable) state.dependentImmutability = NotShallowOrMutable - t match { - case objectType: ObjectType if state.innerArrayTypes.contains(objectType) ⇒ - state.noEscapePossibilityViaReference = false - case _ ⇒ - } + typeMatch(t) case UBP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ state.typeIsImmutable = false @@ -887,7 +895,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e if (eps.isFinal) { val finalEP = eps.asInstanceOf[FinalEP[Method, TACAI]] checkFieldWritesForEffImmutabilityWithKnownTAC(method, pcs._2, finalEP.p.tac.get)(state) - determineEscapeViaFieldReadsWithKnownTAC(pcs._1, finalEP.p.tac.get, method)(state) + determineEscapeViaFieldReadsWithKnownTAC(pcs._1, finalEP.p.tac.get)(state) } else { state.tacDependees += method -> ((newEP, pcs)) } @@ -922,27 +930,34 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e case ep ⇒ state.dependees += ep } - // println("A") /** - * Determines whether the field is dependent immutable + * Determines whether the field is dependent immutable if the flag is set */ - handleGenericity() - // println("B") + if (considerGenericity) { + handleGenericity() + } else { + state.dependentImmutability = Dependent + } + /** * Determines whether the reference object escapes or can be mutated. */ - if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) { - determineEscapeOfReferencedObjectOrValue() + if (considerEscape) { + if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) { + determineEscapeOfReferencedObjectOrValue() + } + } else { + state.noEscapePossibilityViaReference = false + state.concreteClassTypeIsKnown = false + state.escapesStillDetermined = true } - // println("c") /** * In cases where we know the concrete class type assigned to the field we could use the immutabiltiy of this. */ if (!state.concreteClassTypeIsKnown) { - // println("C1") handleTypeImmutability(state) } - // println("D") + createResult } } 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 index 8762b8e6ab..1777dfe976 100644 --- 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 @@ -105,9 +105,8 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { * 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 { + 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 @@ -128,17 +127,16 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { * 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 { + def isImmutableReference(eop: EOptionP[Field, FieldReferenceImmutability])(implicit state: State): Boolean = + eop match { - case LBP(ImmutableFieldReference) ⇒ true - case UBP(MutableFieldReference) ⇒ false + case LBP(ImmutableFieldReference) ⇒ true + case UBP(MutableFieldReference) ⇒ false - case _ ⇒ - state.referenceImmutabilityDependees += eop - true - } + case _ ⇒ + state.referenceImmutabilityDependees += eop + true + } /** * Checks whether the field is prematurely read, i.e. read before it is initialized in the 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 index edf50884e8..a532d43042 100644 --- 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 @@ -15,7 +15,6 @@ 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.CFG import org.opalj.br.cfg.CFGNode import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeButDeterministicFieldReference @@ -52,15 +51,30 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr writeIndex: Int, defaultValue: Any, method: Method, - tacCai: TACode[TACMethodParameter, V] + taCode: TACode[TACMethodParameter, V] )(implicit state: State): Boolean = { val lazyInitializationResult: FieldReferenceImmutability = - determineLazyInitialization(writeIndex, defaultValue, method, tacCai) + 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 @@ -69,26 +83,14 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr writeIndex: Int, defaultValue: Any, method: Method, - tacCode: TACode[TACMethodParameter, V] + taCode: TACode[TACMethodParameter, V] )(implicit state: State): FieldReferenceImmutability = { - val code = tacCode.stmts - val cfg = tacCode.cfg + val code = taCode.stmts + val cfg = taCode.cfg val write = code(writeIndex).asFieldWriteAccessStmt val writeBB = cfg.bb(writeIndex).asBasicBlock - val domTree = cfg.dominatorTree - val resultCatchesAndThrows = findCatchesAndThrows(tacCode, cfg) //TODO remove values - - /** - * 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): Boolean = { - val bbPotentiallyDominator = cfg.bb(potentiallyDominator) - val bbPotentiallyDominated = cfg.bb(potentiallyDominated) - domTree.strictlyDominates(bbPotentiallyDominator.nodeId, bbPotentiallyDominated.nodeId) || - bbPotentiallyDominator == bbPotentiallyDominated && potentiallyDominator < potentiallyDominated - } + val resultCatchesAndThrows = findCatchesAndThrows(taCode) /** * Determines whether all exceptions that are caught are thrown @@ -98,39 +100,40 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr case (catchPC, originsCaughtException) ⇒ resultCatchesAndThrows._2.exists { case (throwPC, throwDefinitionSites) ⇒ - dominates(tacCode.pcToIndex(catchPC), tacCode.pcToIndex(throwPC)) && + dominates(taCode.pcToIndex(catchPC), taCode.pcToIndex(throwPC), taCode) && throwDefinitionSites == originsCaughtException //throwing and catching same exceptions } } } - val findGuardResult = findGuard(method, writeIndex, defaultValue, tacCode) + val findGuardResult = findGuards(method, writeIndex, defaultValue, taCode) - val (guardedIndex, readIndex, trueCaseIndex, fieldReadIndex) = + 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(trueCaseIndex, writeIndex)) return MutableFieldReference; + if (!dominates(defaultCaseIndex, writeIndex, taCode)) return MutableFieldReference; - val elseBB = cfg.bb(guardedIndex) + val elseBB = cfg.bb(elseCaseIndex) - // prevents a wrong control flow + // 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, cfg, tacCode)) return MutableFieldReference; + 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 - tacCode.stmts.exists(stmt ⇒ stmt.isReturnValue && - !isTransitivePredecessor(writeBB, cfg.bb(tacCode.pcToIndex(stmt.pc))) && + taCode.stmts.exists(stmt ⇒ stmt.isReturnValue && + !isTransitivePredecessor(writeBB, cfg.bb(taCode.pcToIndex(stmt.pc))) && findGuardResult.forall { case (indexOfFieldRead, _, _, _) ⇒ - !isTransitivePredecessor(cfg.bb(indexOfFieldRead), cfg.bb(tacCode.pcToIndex(stmt.pc))) + !isTransitivePredecessor(cfg.bb(indexOfFieldRead), cfg.bb(taCode.pcToIndex(stmt.pc))) })) return MutableFieldReference; } @@ -151,22 +154,25 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr // if the method is synchronized the monitor within the method doesn't have to be searched if (method.isSynchronized) { - if (dominates(trueCaseIndex, writeIndex) && noInterferingExceptions()) + if (dominates(defaultCaseIndex, writeIndex, taCode) && noInterferingExceptions()) LazyInitializedThreadSafeFieldReference - else MutableFieldReference + else + MutableFieldReference } else { - val (indexMonitorEnter, indexMonitorExit) = findMonitors(writeIndex, code, cfg, tacCode) + val (indexMonitorEnter, indexMonitorExit) = findMonitors(writeIndex, taCode, method) val monitorResultsDefined = indexMonitorEnter.isDefined && indexMonitorExit.isDefined if (monitorResultsDefined && //dominates(indexMonitorEnter.get, trueCaseIndex) && - dominates(indexMonitorEnter.get, fieldReadIndex)) { - if (noInterferingExceptions()) LazyInitializedThreadSafeFieldReference - else MutableFieldReference + dominates(indexMonitorEnter.get, readIndex, taCode)) { + if (noInterferingExceptions()) + LazyInitializedThreadSafeFieldReference + else + MutableFieldReference } else { if (write.value.asVar.definedBy.forall { defSite ⇒ - defSite >= 0 && checkWriteIsDeterministic(code(defSite).asAssignment, method, code, tacCode) + defSite >= 0 && checkWriteIsDeterministic(code(defSite).asAssignment, method, code, taCode) } && noInterferingExceptions()) { val computationalFieldType = state.field.fieldType.computationalType @@ -177,7 +183,6 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr LazyInitializedNotThreadSafeButDeterministicFieldReference } else MutableFieldReference } - } } @@ -195,23 +200,17 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr * @author Tobias Roth */ def findCatchesAndThrows( - tacCode: TACode[TACMethodParameter, V], - cfg: CFG[Stmt[V], TACStmts[V]] + 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 - //val currentBB = cfg.bb(tacCode.pcToIndex(stmt.pc)) (stmt.astID: @switch) match { case CaughtException.ASTID ⇒ val caughtException = stmt.asCaughtException - /*val exceptionType = - if (caughtException.exceptionType.isDefined) caughtException.exceptionType.get.asObjectType - else ObjectType.Throwable*/ - caughtExceptions = - (caughtException.pc, caughtException.origins) :: caughtExceptions + caughtExceptions = (caughtException.pc, caughtException.origins) :: caughtExceptions case Throw.ASTID ⇒ val throwStatement = stmt.asThrow @@ -226,42 +225,44 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr } /** - * Searches the monitor enter and exit that is closest to the field write. + * 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, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], - tacCode: TACode[TACMethodParameter, V] + fieldWrite: Int, tacCode: TACode[TACMethodParameter, V], method: Method )(implicit state: State): (Option[Int], Option[Int]) = { var result: (Option[Int], Option[Int]) = (None, None) - val startBB = cfg.bb(fieldWrite) + 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 + * 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) { - //supports two ways of synchronization tacCode.stmts(definedByIndex) match { - //synchronized(... //TODO - case Assignment(_, DVar(_, _), classConst: ClassConst) ⇒ - state.field.classFile.thisType == classConst.value || - state.field.fieldType == classConst.value - - //synchronized(... //TODO - case Assignment(_, DVar(_, _), GetField(_, _, _, classType, UVar(_, _))) ⇒ - classType == state.field.classFile.thisType - case _ ⇒ false + //synchronized(Foo.class) + case Assignment(_, _, _: ClassConst) ⇒ true + case _ ⇒ false } - } else true // (definedByIndex <= -1) + } else { + //synchronized(this) + state.field.isNotStatic && IntTrieSet(definedByIndex) == SelfReferenceParameter + } }) } @@ -276,9 +277,9 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val endPC = curBB.endPC var hasNotFoundAnyMonitorYet = true for (i ← startPC to endPC) { - (code(i).astID: @switch) match { + (tacCode.stmts(i).astID: @switch) match { case MonitorEnter.ASTID ⇒ - val monitorEnter = code(i).asMonitorEnter + val monitorEnter = tacCode.stmts(i).asMonitorEnter if (checkMonitor(monitorEnter.objRef.asVar)) { result = (Some(tacCode.pcToIndex(monitorEnter.pc)), result._2) hasNotFoundAnyMonitorYet = false @@ -299,7 +300,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr worklistMonitorExit = worklistMonitorExit.tail val endPC = curBB.endPC - val cfStmt = code(endPC) + val cfStmt = tacCode.stmts(endPC) (cfStmt.astID: @switch) match { case MonitorExit.ASTID ⇒ @@ -320,23 +321,23 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr /** * 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 fieldread. + * field read used for the guard and the index of the field-read. */ - def findGuard( + def findGuards( method: Method, fieldWrite: Int, defaultValue: Any, - tacCode: TACode[TACMethodParameter, V] + taCode: TACode[TACMethodParameter, V] )(implicit state: State): List[(Int, Int, Int, Int)] = { - val cfg = tacCode.cfg - val code = tacCode.stmts + 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 //None + var result: List[(Int, Int, Int)] = List.empty while (worklist.nonEmpty) { val curBB = worklist.head @@ -355,18 +356,18 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr ifStmt, defaultValue, code, - tacCode, + taCode, method )) { - result = (endPC, endPC + 1, ifStmt.targetStmt) :: result + result = (endPC, ifStmt.targetStmt, endPC + 1) :: result } else if (ifStmt.condition.equals(NE) && curBB != startBB && isGuard( ifStmt, defaultValue, code, - tacCode, + taCode, method )) { - result = (endPC, ifStmt.targetStmt, endPC + 1) :: result + 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))) { @@ -387,7 +388,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr } - var finalResult: List[(Int, Int, Int, Int)] = List.empty // 2 3 5 6 + 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 @@ -405,12 +406,12 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val fieldReadUses = code(definitions.head).asAssignment.targetVar.usedBy val fieldReadUsedCorrectly = - fieldReadUses.forall(use ⇒ use == result._1 || use == result._2) + fieldReadUses.forall(use ⇒ use == result._1 || use == result._3) if (definitions.size == 1 && definitions.head >= 0 && fieldReadUsedCorrectly) { // Found proper guard - finalResult = (result._2, definitions.head, result._3, fieldReadIndex) :: finalResult - } // 2 3 5 6 + finalResult = (fieldReadIndex, result._1, result._2, result._3) :: finalResult + } } }) finalResult @@ -499,7 +500,10 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val value = origin.expr - def isNonConstDeterministic(value: Expr[V], taCode: TACode[TACMethodParameter, V])(implicit state: State): Boolean = { + 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) @@ -565,7 +569,6 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr def isExprReadOfCurrentField: Int ⇒ Boolean = exprIndex ⇒ exprIndex == index || exprIndex >= 0 && isReadOfCurrentField(tacCode.stmts(exprIndex).asAssignment.expr, tacCode, exprIndex) - //println("expr: "+expr) (expr.astID: @switch) match { case GetField.ASTID ⇒ val objRefDefinition = expr.asGetField.objRef.asVar.definedBy @@ -648,7 +651,8 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr //in case of Integer etc.... .initValue() val calleesResult = propertyStore(declaredMethods(method), Callees.key) - if (doCallsIntroduceNonDeterminism(calleesResult, code(index).asAssignment.pc)) return false; + if (doCallsIntroduceNonDeterminism(calleesResult, code(index).asAssignment.pc)) + return false; if (isVirtualFunctionCall) { val virtualFunctionCall = code(index).asAssignment.expr.asVirtualFunctionCall @@ -656,36 +660,34 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr receiverDefSite >= 0 && isReadOfCurrentField(code(receiverDefSite).asAssignment.expr, tacCode, index)) } else { - //if(isStaticFunctionCall){ - //val staticFunctionCall = code(index).asAssignment.expr.asStaticFunctionCall - //staticFunctionCall.receiverOption. isReadOfCurrentField(code(index).asAssignment.expr, tacCode, index) } } else { - //method.asMethod.isVirtualCallTarget isReadOfCurrentField(code(index).asAssignment.expr, tacCode, index) } } } } - def isFloatDoubleOrLong(fieldType: FieldType): Boolean = + //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 && isFloatDoubleOrLong(state.field.fieldType) && ifStmt.rightExpr.asVar.definedBy.head > 0 && tacCode.stmts(ifStmt.rightExpr.asVar.definedBy.head).asAssignment.expr.isCompare) { + 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) isDefaultConst(rightExpr) - else if (rightExpr.isGetField) isDefaultConst(leftExpr) - else false //TODO reasoning + 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 && - isFloatDoubleOrLong(state.field.fieldType) && tacCode.stmts(ifStmt.leftExpr.asVar.definedBy.head). + hasFloatDoubleOrLongType(state.field.fieldType) && tacCode.stmts(ifStmt.leftExpr.asVar.definedBy.head). asAssignment.expr.isCompare && ifStmt.leftExpr.isVar && ifStmt.rightExpr.isVar) { @@ -694,9 +696,8 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val leftExpr = tacCode.stmts(left.definedBy.head).asAssignment.expr val rightExpr = tacCode.stmts(right.definedBy.head).asAssignment.expr - if (leftExpr.isGetField) isDefaultConst(rightExpr) - else if (rightExpr.isGetField) isDefaultConst(leftExpr) - else false //TODO reasoning + 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) @@ -706,63 +707,68 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr } /** - * Checks that the value of the field is returned. + * Checks that the returned value is definitely read from the field. */ def isFieldValueReturned( - write: FieldWriteAccessStmt[V], - writeIndex: Int, - readIndex: Int, - cfg: CFG[Stmt[V], TACStmts[V]], - tacCode: TACode[TACMethodParameter, V] + write: FieldWriteAccessStmt[V], + writeIndex: Int, + readIndex: Int, + taCode: TACode[TACMethodParameter, V], + guardIndexes: List[(Int, Int, Int, Int)] )(implicit state: State): Boolean = { - val startBB = cfg.bb(0).asBasicBlock - var queuedNodes: Set[CFGNode] = Set.empty - var workList = getSuccessors(startBB, queuedNodes) - - workList ++= Set(startBB) - var potentiallyReadIndex: Option[Int] = None + 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) - while (workList.nonEmpty) { - val currentBB = workList.head - workList = workList.tail + case GetStatic.ASTID ⇒ + expr.asGetStatic.resolveField(project).contains(state.field) - val startPC = { - if (currentBB == startBB) writeIndex + 1 - else currentBB.startPC + case _ ⇒ false } + } - val endPC = currentBB.endPC - var index = startPC - - while (index <= endPC) { - val stmt = tacCode.stmts(index) - if (stmt.isAssignment) { - val assignment = stmt.asAssignment - if (isReadOfCurrentField(assignment.expr, tacCode, index)) { - potentiallyReadIndex = Some(index) + 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)) + } + } + } + } } - } else if (stmt.isReturnValue) { - val returnValueDefs = stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefs.size == 2 && - returnValueDefs.contains(write.value.asVar.definedBy.head) && - returnValueDefs.contains(readIndex)) { - return true; - } // direct return of the written value - else if (potentiallyReadIndex.isDefined && - (returnValueDefs == IntTrieSet(potentiallyReadIndex.get) || - returnValueDefs == IntTrieSet(readIndex, potentiallyReadIndex.get))) { - return true; - } // return of field value loaded by field read - else return false; // return of different value - } - index = index + 1 } - val successors = getSuccessors(currentBB, queuedNodes) - workList ++= successors - queuedNodes ++= successors } - false } - } 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 index 1302fed43a..8fbd4b4fa4 100644 --- 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 @@ -86,6 +86,11 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP with AbstractFieldReferenceImmutabilityAnalysis with FPCFAnalysis { + val considerLazyInitialization: Boolean = + project.config.getBoolean( + "org.opalj.fpcf.analyses.L0FieldReferenceImmutabilityAnalysis.considerLazyInitialization" + ) + def doDetermineFieldReferenceImmutability(entity: Entity): PropertyComputationResult = { entity match { @@ -305,12 +310,17 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP stmt.asPutField.objRef.asVar.definedBy != SelfReferenceParameter || // A field written outside an initializer must be lazily // initialized or it is non-final - handleLazyInitialization( - index, - getDefaultValue(), - method, - taCode - ) + { + if (considerLazyInitialization) { + handleLazyInitialization( + index, + getDefaultValue(), + method, + taCode + ) + } else + true + } } else if (referenceHasEscaped(stmt.asPutField.objRef.asVar, stmts, method)) { // println("reference has escaped") @@ -378,7 +388,7 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP def handleEscapeProperty( ep: EOptionP[DefinitionSite, EscapeProperty] )(implicit state: State): Boolean = { - //println(s"ep: $ep") + val result = ep match { case FinalP(NoEscape | EscapeInCallee | EscapeViaReturn) ⇒ false case FinalP(AtMost(_)) ⇒ true From e311dec9d7a17f1320cc725eb38536ac990f32b4 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Mon, 9 Nov 2020 11:27:00 +0100 Subject: [PATCH 296/327] Avoid double notification of final results for non-suppressed PKs --- .../scala/org/opalj/fpcf/par/PKECPropertyStore.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 bdfce92925..91cd8555b2 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 @@ -839,15 +839,15 @@ case class EPKState( 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). - if ((theEOptP ne dependee) && - (theEOptP.isFinal || !suppressedPKs(dependeePK))) { - if (theEOptP.isFinal) + 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 (suppressedPKs(dependeePK)) { + if (isSuppressed) { suppressedDependers.add(depender) } else { dependers.add(depender) @@ -882,7 +882,7 @@ case class EPKState( def applyContinuation(oldDependee: SomeEOptionP)(implicit ps: PKECPropertyStore): Unit = { this.synchronized { val theDependees = dependees - // We are still interessted in that dependee? + // 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. From 3335d5358573255b2e2b9c6184c313e563290656 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Mon, 9 Nov 2020 11:29:01 +0100 Subject: [PATCH 297/327] Improved contains check --- .../main/scala/org/opalj/fpcf/PropertyComputationResult.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 ca4fceb4a0..adab86b8aa 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyComputationResult.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyComputationResult.scala @@ -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,6 +190,7 @@ final class InterimResult[P >: Null <: Property] private ( s"InterimResult($eps,dependees=${dependees.mkString("[", ", ", "]")},c=$c)" } } + object InterimResult { private[fpcf] final val id = 3 From 34036670b563066f2ebd6bc99b210171d2619fe9 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 1 Dec 2020 09:51:07 +0100 Subject: [PATCH 298/327] using set for the new parallel store and formatting --- .../L2FieldImmutabilityAnalysis.scala | 10 +- .../L1ClassImmutabilityAnalysis.scala | 4 +- .../L1TypeImmutabilityAnalysis.scala | 10 +- ...ctFieldReferenceImmutabilityAnalysis.scala | 6 +- ...mutabilityAnalysisLazyInitialization.scala | 208 +++++++++++------- ...L0FieldReferenceImmutabilityAnalysis.scala | 25 +-- 6 files changed, 150 insertions(+), 113 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala index 0e585126bc..3a3d06dfe7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldImmutabilityAnalysis.scala @@ -21,7 +21,6 @@ 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.fpcf.InterimEP import org.opalj.fpcf.InterimResult import org.opalj.fpcf.InterimUBP @@ -95,15 +94,18 @@ class L2FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e 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 || fieldImmutabilityDependees.nonEmpty || escapeDependees.nonEmpty || tacDependees.nonEmpty } - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ - fieldImmutabilityDependees ++ escapeDependees ++ tacDependees.valuesIterator.map(_._1) + def dependees: Set[SomeEOptionP] = { + (tacDependees.valuesIterator.map(_._1) ++ prematurelyReadDependee ++ purityDependees ++ calleesDependee ++ + fieldImmutabilityDependees ++ escapeDependees).toSet } } 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 index 27a7d2b47e..f1fdb4df72 100644 --- 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 @@ -382,12 +382,12 @@ class L1ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis */ Result(t, maxLocalImmutability) } else { - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values.toSet, c) } } val result = - InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values, c) + InterimResult(t, minLocalImmutability, maxLocalImmutability, dependees.values.toSet, c) if (lazyComputation) result else { 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 index 734c396eb1..479f2e65ec 100644 --- 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 @@ -91,7 +91,7 @@ class L1TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAn if (eps.isFinal) Result(t, thisUB) else - InterimResult(t, thisLB, thisUB, Seq(eps), c) + InterimResult(t, thisLB, thisUB, Set(eps), c) } } } @@ -104,10 +104,10 @@ class L1TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAn 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, DeepImmutableType, Seq(epk), c) + InterimResult(t, MutableType, DeepImmutableType, Set(epk), c) } } else { var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] @@ -208,7 +208,7 @@ class L1TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAn t, joinedImmutability, maxImmutability, - dependencies.values, + dependencies.values.toSet, c ) } @@ -245,7 +245,7 @@ class L1TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAn nextResult() } } - InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) + InterimResult(t, joinedImmutability, maxImmutability, dependencies.values.toSet, c) } } } 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 index 1777dfe976..92a6709b68 100644 --- 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 @@ -77,9 +77,9 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { escapeDependees.nonEmpty || tacDependees.nonEmpty || typeDependees.nonEmpty } - def dependees: Traversable[EOptionP[Entity, Property]] = { - prematurelyReadDependee ++ purityDependees ++ referenceImmutabilityDependees ++ escapeDependees ++ - calleesDependee.valuesIterator.map(_._1) ++ tacDependees.valuesIterator.map(_._1) ++ typeDependees + def dependees: Set[EOptionP[Entity, Property]] = { + (prematurelyReadDependee ++ purityDependees ++ referenceImmutabilityDependees ++ escapeDependees ++ + calleesDependee.valuesIterator.map(_._1) ++ tacDependees.valuesIterator.map(_._1) ++ typeDependees).toSet } } 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 index a532d43042..41ff0f68b4 100644 --- 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 @@ -39,7 +39,8 @@ import org.opalj.br.FieldType * @author Michael Eichberg * */ -trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends AbstractFieldReferenceImmutabilityAnalysis +trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization + extends AbstractFieldReferenceImmutabilityAnalysis with FPCFAnalysis { /** @@ -66,12 +67,14 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr */ def dominates( potentiallyDominator: Int, - potentiallyDominated: Int, taCode: TACode[TACMethodParameter, V] + 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) || + taCode.cfg.dominatorTree + .strictlyDominates(bbPotentiallyDominator.nodeId, bbPotentiallyDominated.nodeId) || bbPotentiallyDominator == bbPotentiallyDominated && potentiallyDominator < potentiallyDominated } @@ -108,13 +111,19 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr val findGuardResult = findGuards(method, writeIndex, defaultValue, taCode) - val (readIndex, guardIndex, defaultCaseIndex, elseCaseIndex) = //guardIndex: for debugging purposes + val (readIndex, _ /* guardIndex */ , defaultCaseIndex, elseCaseIndex) = //guardIndex: for debugging purposes if (findGuardResult.nonEmpty) - (findGuardResult.head._1, findGuardResult.head._2, findGuardResult.head._3, findGuardResult.head._4) + ( + findGuardResult.head._1, + findGuardResult.head._2, + findGuardResult.head._3, + findGuardResult.head._4 + ) else return MutableFieldReference; - if (!dominates(defaultCaseIndex, writeIndex, taCode)) return MutableFieldReference; + if (!dominates(defaultCaseIndex, writeIndex, taCode)) + return MutableFieldReference; val elseBB = cfg.bb(elseCaseIndex) @@ -129,12 +138,18 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr //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))) - })) + 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; } @@ -148,8 +163,11 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr // 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))) + 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 @@ -160,7 +178,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr MutableFieldReference } else { - val (indexMonitorEnter, indexMonitorExit) = findMonitors(writeIndex, taCode, method) + val (indexMonitorEnter, indexMonitorExit) = findMonitors(writeIndex, taCode) val monitorResultsDefined = indexMonitorEnter.isDefined && indexMonitorExit.isDefined @@ -172,7 +190,12 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr MutableFieldReference } else { if (write.value.asVar.definedBy.forall { defSite ⇒ - defSite >= 0 && checkWriteIsDeterministic(code(defSite).asAssignment, method, code, taCode) + defSite >= 0 && checkWriteIsDeterministic( + code(defSite).asAssignment, + method, + code, + taCode + ) } && noInterferingExceptions()) { val computationalFieldType = state.field.fieldType.computationalType @@ -230,7 +253,8 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr * @author Tobias Roth */ def findMonitors( - fieldWrite: Int, tacCode: TACode[TACMethodParameter, V], method: Method + fieldWrite: Int, + tacCode: TACode[TACMethodParameter, V] )(implicit state: State): (Option[Int], Option[Int]) = { var result: (Option[Int], Option[Int]) = (None, None) @@ -395,8 +419,9 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr // 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 expr = + if (ifStmt.leftExpr.isConst) ifStmt.rightExpr + else ifStmt.leftExpr val definitions = expr.asVar.definedBy if (definitions.forall(_ >= 0)) { @@ -505,15 +530,17 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr taCode: TACode[TACMethodParameter, V] )(implicit state: State): Boolean = { (value.astID: @switch) match { - case BinaryExpr.ASTID ⇒ isConstant(value.asBinaryExpr.left) && isConstant(value.asBinaryExpr.right) + 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 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 _ ⇒ 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 @@ -530,20 +557,23 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr 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)) + 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) + origin.asAssignment.targetVar.usedBy.iterator.filter(i ⇒ code(i).isNonVirtualMethodCall) nonVirtualMethodCallIndexes.forall { nonVirtualMethodCallIndex ⇒ val callTargetResult = - taCode.stmts(nonVirtualMethodCallIndex).asNonVirtualMethodCall.resolveCallTarget( - state.field.classFile.thisType - ) + taCode + .stmts(nonVirtualMethodCallIndex) + .asNonVirtualMethodCall + .resolveCallTarget( + state.field.classFile.thisType + ) !callTargetResult.isEmpty && (!callTargetResult.value.isConstructor || - //if the constructor is called and it must be deterministic + //if the constructor is called it must be deterministic !isNonDeterministic(propertyStore(declaredMethods(callTargetResult.value), Purity.key))) } @@ -566,9 +596,14 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr 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) + 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 @@ -581,11 +616,11 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr 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) && + 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) && + rightExpr.asVar.definedBy + .forall(index ⇒ index >= 0 && tacCode.stmts(index).asAssignment.expr.isConst) && leftExpr.asVar.definedBy.forall(isExprReadOfCurrentField) case VirtualFunctionCall.ASTID ⇒ @@ -606,6 +641,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr 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. @@ -641,9 +677,14 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr * 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 = { + 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 + 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 @@ -656,9 +697,11 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr if (isVirtualFunctionCall) { val virtualFunctionCall = code(index).asAssignment.expr.asVirtualFunctionCall - virtualFunctionCall.receiver.asVar.definedBy.forall(receiverDefSite ⇒ - receiverDefSite >= 0 && - isReadOfCurrentField(code(receiverDefSite).asAssignment.expr, tacCode, index)) + virtualFunctionCall.receiver.asVar.definedBy.forall( + receiverDefSite ⇒ + receiverDefSite >= 0 && + isReadOfCurrentField(code(receiverDefSite).asAssignment.expr, tacCode, index) + ) } else { isReadOfCurrentField(code(index).asAssignment.expr, tacCode, index) } @@ -677,8 +720,10 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr 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 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 @@ -687,12 +732,17 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr } 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 && + 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 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 @@ -738,36 +788,34 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization extends Abstr 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)) - } - } - } + 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 index 8fbd4b4fa4..e6eed64531 100644 --- 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 @@ -215,17 +215,14 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { var isNotFinal = false - /*println( - s""" - | enter continuation - | eps: $eps - |""".stripMargin - ) */ + 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 @@ -234,29 +231,20 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP if (eps.isRefinable) state.tacDependees += method -> ((newEP, pcs)) isNotFinal = methodUpdatesField(method, newEP.ub.tac.get, pcs) - case Callees.key ⇒ - //if (eps.e.isInstanceOf[DeclaredMethod]) - //state.lazyInitInvocation + case Callees.key ⇒ val newEPS = eps.asInstanceOf[EOptionP[DeclaredMethod, Callees]] val pcs = state.calleesDependee(newEPS.e)._2 isNotFinal = pcs.forall(pc ⇒ doCallsIntroduceNonDeterminism(newEPS, pc)) - //state.calleesDependee+= (calleesEOP.e → (calleesEOP,pc :: state.calleesDependee(calleesEOP.e)._2)) - // isNotFinal = handleCalls() - //else { - //-T ODO callees handling - //} + 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) - //if (!r) state.referenceImmutability = LazyInitializedReference - //if (state.referenceImmutability != LazyInitializedNotThreadSafeReference && - // state.referenceImmutability != LazyInitializedThreadSafeReference) { // both dont need determinism isNotFinal = nonDeterministicResult - //} case FieldReferenceImmutability.key ⇒ val newEP = eps.asInstanceOf[EOptionP[Field, FieldReferenceImmutability]] @@ -265,7 +253,6 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP isNotFinal = !isImmutableReference(newEP) } - // println("result is not final: "+isNotFinal) if (isNotFinal) state.referenceImmutability = MutableFieldReference createResult() From ab46ab8e70cd13539288a2deacac1b551870716c Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 1 Dec 2020 11:32:18 +0100 Subject: [PATCH 299/327] using set for the new parallel store --- .../br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala index c8b86ff63f..fa476f9bfd 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0TypeImmutabilityAnalysis.scala @@ -79,7 +79,7 @@ class L0TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAn if (eps.isFinal) Result(t, thisUB) else - InterimResult(t, thisLB, thisUB, Seq(eps), c) + InterimResult(t, thisLB, thisUB, Set(eps), c) } } } @@ -90,9 +90,9 @@ class L0TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAn 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, DeepImmutableType, Seq(epk), c) + InterimResult(t, MutableType, DeepImmutableType, Set(epk), c) } } else { var dependencies = Map.empty[Entity, EOptionP[Entity, Property]] @@ -185,7 +185,7 @@ class L0TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAn } else { InterimResult( t, joinedImmutability, maxImmutability, - dependencies.values, c + dependencies.values.toSet, c ) } } @@ -218,7 +218,7 @@ class L0TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAn } } - InterimResult(t, joinedImmutability, maxImmutability, dependencies.values, c) + InterimResult(t, joinedImmutability, maxImmutability, dependencies.values.toSet, c) } } } From 7824d573d9467703275a4000e1356c40a6d5df5e Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 1 Dec 2020 11:58:15 +0100 Subject: [PATCH 300/327] revised; still wip --- .../L3FieldImmutabilityAnalysis.scala | 686 +++++++++--------- 1 file changed, 352 insertions(+), 334 deletions(-) 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 index 3c1b8028f2..ecd68c57a1 100755 --- 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 @@ -61,7 +61,6 @@ 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.PropertyBounds import org.opalj.fpcf.PropertyStore import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey @@ -76,12 +75,16 @@ import org.opalj.br.ClassFile import org.opalj.br.FormalTypeParameter import org.opalj.br.fpcf.properties.ClassImmutability import scala.collection.mutable +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.EUBP +import org.opalj.fpcf.LBP /** * Analysis that determines the immutability of org.opalj.br.Field * @author Tobias Roth */ -class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { +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 @@ -104,6 +107,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e case class State( field: Field, var typeIsImmutable: Boolean = true, + var classTypeIsImmutable: Boolean = true, var referenceIsImmutable: Option[Boolean] = None, var noEscapePossibilityViaReference: Boolean = true, var dependentImmutability: DependentImmutabilityKind = OnlyDeepImmutable, @@ -111,13 +115,14 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e 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 ) { def hasDependees: Boolean = dependees.nonEmpty || tacDependees.nonEmpty - - def getDependees: Traversable[EOptionP[Entity, Property]] = dependees ++ tacDependees.valuesIterator.map(_._1) + def getDependees: Set[EOptionP[Entity, Property]] = + dependees ++ tacDependees.valuesIterator.map(_._1) } final val typeExtensibility = project.get(TypeExtensibilityKey) @@ -136,15 +141,11 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity" ) - def doDetermineFieldImmutability(entity: Entity): PropertyComputationResult = - entity match { - - case field: Field ⇒ determineFieldImmutability(field) - - case _ ⇒ - val m = s"${entity.getClass.getName} is not an org.opalj.br.Field" - throw new IllegalArgumentException(m) - } + 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 @@ -159,30 +160,30 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e * * Extract the formal type parameters if it exists of a class' attribute */ - def collectFormalTypeParameterFromClassAttribute(attribute: Attribute): Iterator[String] = attribute match { + def collectFormalTypeParameterFromClassAttribute(attribute: Attribute): Iterator[String] = + attribute match { - case ClassSignature(typeParameters, _, _) ⇒ - typeParameters.iterator.map { case FormalTypeParameter(identifier, _, _) ⇒ identifier } + case ClassSignature(typeParameters, _, _) ⇒ + typeParameters.iterator.map { case FormalTypeParameter(identifier, _, _) ⇒ identifier } - case _ ⇒ Iterator.empty - } + 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 + 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 } @@ -193,7 +194,8 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e * 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) + 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. @@ -214,9 +216,9 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e case FinalP(DeepImmutableType) ⇒ // deep immutable type is set as default case FinalP(DependentImmutableType) ⇒ state.typeIsImmutable = false - case UBP(ShallowImmutableType | MutableType) ⇒ + case EUBP(t, ShallowImmutableType | MutableType) ⇒ state.typeIsImmutable = false - if (field.fieldType != ObjectType.Object) + if (t == field.fieldType && field.fieldType != ObjectType.Object) state.dependentImmutability = Dependent case epk ⇒ state.dependees += epk @@ -250,12 +252,16 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e if (!isInClassesGenericTypeParameters(identifier)) noShallowOrMutableTypesInGenericTypeFound = false - case ProperTypeArgument(_, ClassTypeSignature(outerPackageIdentifier, - SimpleClassTypeSignature(innerPackageIdentifier, _), _)) ⇒ - val objectPath = outerPackageIdentifier match { - case Some(prepackageIdentifier) ⇒ prepackageIdentifier + innerPackageIdentifier - case _ ⇒ innerPackageIdentifier - } + case ProperTypeArgument( + _, + ClassTypeSignature( + outerPackageIdentifier, + SimpleClassTypeSignature(innerPackageIdentifier, _), + _ + ) + ) ⇒ + val objectPath = outerPackageIdentifier.getOrElse("") + innerPackageIdentifier + genericParameters ::= ObjectType(objectPath) case _ ⇒ @@ -270,18 +276,21 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e genericParameters.foreach(objectType ⇒ { propertyStore(objectType, TypeImmutability.key) match { - case FinalP(DeepImmutableType) ⇒ //nothing to do here: default value is deep immutable + case LBP(DeepImmutableType) ⇒ //nothing to do here: default value is deep immutable - case FinalP(DependentImmutableType) ⇒ - onlyDeepImmutableTypesInGenericTypeFound = false - state.typeIsImmutable = false + /* FinalP(DependentImmutableType) => + onlyDeepImmutableTypesInGenericTypeFound = false + state.typeIsImmutable = false */ + //conservative overapproximation that allows no nested generic classes - case UBP(ShallowImmutableType | MutableType) ⇒ + case UBP(DependentImmutableType | ShallowImmutableType | MutableType) ⇒ noShallowOrMutableTypesInGenericTypeFound = false onlyDeepImmutableTypesInGenericTypeFound = false state.typeIsImmutable = false - case ep ⇒ state.dependees += ep + case ep ⇒ + state.concreteGenericTypes += ep.e + state.dependees += ep } }) @@ -293,11 +302,12 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } else if (noShallowOrMutableTypesInGenericTypeFound && state.dependentImmutability != Dependent) { state.dependentImmutability = NotShallowOrMutable - } else + } else { state.dependentImmutability = Dependent - - } else + } + } else { state.dependentImmutability = Dependent + } } /** @@ -313,11 +323,8 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e 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) + val (reads: IntTrieSet, writes: IntTrieSet) = + state.tacDependees.getOrElse(method, (IntTrieSet.empty, IntTrieSet.empty)) if (isRead) state.tacDependees += method -> ((epk, (reads ++ pcs, writes))) if (!isRead) state.tacDependees += method -> ((epk, (reads, writes ++ pcs))) None @@ -329,7 +336,9 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e * @return true if the object - on which a field write occurred - escapes, false otherwise. * @note (Re-)Adds dependees as necessary. */ - def escapesViaMethod(ep: EOptionP[DefinitionSite, EscapeProperty])(implicit state: State): Boolean = { + def doesEscapeViaMethod( + ep: EOptionP[DefinitionSite, EscapeProperty] + )(implicit state: State): Boolean = { ep match { case FinalP(NoEscape) ⇒ false @@ -337,10 +346,11 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e 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 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 @@ -353,33 +363,27 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e */ def determineEscapeOfReferencedObjectOrValue()(implicit state: State): Unit = { state.escapesStillDetermined = true - state.noEscapePossibilityViaReference = state.field.isPrivate 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 - /** - * Begin of method check field writes - */ - val tacCodeOption = getTACAI(method, pcs, isRead = false) - if (tacCodeOption.isDefined) { - val taCode = tacCodeOption.get - checkFieldWritesForEffImmutabilityWithKnownTAC(method, pcs, taCode) - } + + writes.foreach { + case (method, pcs) ⇒ + val taCodeOption = getTACAI(method, pcs, isRead = false) + if (taCodeOption.isDefined) + determineEscapeViaFieldWritesWithKnownTAC(method, pcs, taCodeOption.get) } if (state.noEscapePossibilityViaReference) { - fieldAccessInformation.readAccesses(state.field). - foreach { read ⇒ - val (method, pcs) = read + fieldAccessInformation.readAccesses(state.field).foreach { + case (method, pcs) ⇒ val taCodeOption = getTACAI(method, pcs, isRead = true) if (taCodeOption.isDefined) - determineEscapeViaFieldReadsWithKnownTAC(pcs, taCodeOption.get) - } + determineEscapeViaFieldReadsWithKnownTAC(method, pcs, taCodeOption.get) + } } } } @@ -388,6 +392,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e * Determine if the referenced object can escape via field reads. */ def determineEscapeViaFieldReadsWithKnownTAC( + method: Method, pcs: PCs, taCode: TACode[TACMethodParameter, V] )(implicit state: State): Unit = { @@ -405,12 +410,16 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e 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) - + 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 { @@ -423,11 +432,10 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } else if (innerArrayType.isObjectType) { //If a deep immutable object escapes, it can not be mutated propertyStore(innerArrayType, TypeImmutability.key) match { - - case FinalP(DeepImmutableType) ⇒ false //nothing to do - - case UBP(DependentImmutableType | ShallowImmutableType | - MutableType) ⇒ + case LBP(DeepImmutableType) ⇒ false //nothing to do + case UBP( + DependentImmutableType | ShallowImmutableType | MutableType + ) ⇒ true case ep ⇒ @@ -445,7 +453,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e !fieldsUseSiteStmt.isIf }) } else { - //there could occur other cases; see issue #44 + //there can occur other cases; see issue #44 //TODO make it more precise when issue #44 is fixed !(stmt.isExprStmt || stmt.isNop) } @@ -458,7 +466,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e /** * Determine if the referenced object can escape via field writes */ - def checkFieldWritesForEffImmutabilityWithKnownTAC( + def determineEscapeViaFieldWritesWithKnownTAC( method: Method, pcs: PCs, taCode: TACode[TACMethodParameter, V] @@ -473,24 +481,28 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e */ def doesStaticFunctionCallEnableEscape( staticFunctionCall: StaticFunctionCall[V] - ): Boolean = staticFunctionCall.params.exists(_.asVar.definedBy.exists( - definedByIndex ⇒ definedByIndex < 0 || - !taCode.stmts(definedByIndex).asAssignment.expr.isConst - )) + ): 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)(implicit state: State): Unit = { + def handleKnownClassType(objectType: ObjectType): Unit = { - propertyStore(objectType, ClassImmutability.key) match { + val result = propertyStore(objectType, ClassImmutability.key) + result match { - case UBP(MutableClass | ShallowImmutableClass | DependentImmutableClass) ⇒ - state.typeIsImmutable = false + case FinalP(MutableClass | ShallowImmutableClass | DependentImmutableClass) ⇒ + state.classTypeIsImmutable = false - case FinalP(DeepImmutableClass) ⇒ - state.typeIsImmutable = true + case FinalP(DeepImmutableClass) ⇒ //nothing to do ; true ist default case eps ⇒ state.dependees += eps @@ -503,94 +515,87 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e 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) - // println(s"parameterDefinitionStmt: $paramDefinitionStmt") - 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 - None - - if (assignedField.isDefined && assignedField.get != state.field) { - propertyStore(assignedField.get, FieldImmutability.key) match { - - case UBP(MutableField - | ShallowImmutableField - | DependentImmutableField) ⇒ true - - case FinalP(DeepImmutableField) ⇒ false //nothing to do here - case FinalP(_) ⇒ - true + 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 FinalP(_) ⇒ 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 - } + 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) + } - } else - assignmentExpression.isConst - } else { - val definitionSitesOfParam = definitionSites(method, paramDefinedByIndex) - val stmt = taCode.stmts(taCode.pcToIndex(definitionSitesOfParam.pc)) + 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 if (stmt.isPutField || stmt.isPutStatic) { - if (!seen.contains(stmt)) { - seen += stmt - doesPutEnableEscape(stmt) - } else false - } else if (stmt.isArrayStore) { - true //TODO handling that case more precise - } else { // other cases that the purity analysis can not handle - escapesViaMethod(propertyStore(definitionSitesOfParam, EscapeProperty.key)) - //false - } - } //TODO go further + } else 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) { + 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 } + } } /** @@ -605,63 +610,66 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } else if (putStmt.isPutStatic) { val putStatic = putStmt.asPutStatic (putStatic.value.asVar.definedBy, putStatic.value.asVar) - } else { - //state.noEscapePossibilityViaReference = false - return true; - } + } 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.head >= 0) { - val valueAssignment = taCode.stmts(assignedValue.asVar.definedBy.head).asAssignment - val assignedExpr = valueAssignment.expr - val useSites = valueAssignment.targetVar.usedBy.map(taCode.stmts(_)) - 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)) - //param.asVar.definedBy.head > -1 && //TODO look at more than the head - //!tacCode.stmts(param.asVar.definedBy.head).asAssignment.expr.isConst) - } else if (useSite == arrayStore) { + 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 (useSite.isReturnValue) { - //assigned array-element escapes + 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 { - true //to be sound - } - } - - 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 ⇒ + 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 @@ -673,11 +681,11 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } false //nothing to do in the else case. Stmt has still been handled } else true - } + } + } else true } else true } else true - } else true - } else false + } else false } } } else true @@ -686,28 +694,22 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e putDefinitionSites.exists { putDefinitionSite ⇒ if (putDefinitionSite >= 0) { //necessary val definitionSiteStatement = taCode.stmts(putDefinitionSite) - // println("def site stmt: "+definitionSiteStatement) val definitionSiteAssignment = definitionSiteStatement.asAssignment - //println("def site assignement: "+definitionSiteAssignment) if (definitionSiteAssignment.expr.isStaticFunctionCall) { - // println("handle static function call") - doesStaticFunctionCallEnableEscape(definitionSiteAssignment.expr.asStaticFunctionCall) + 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 + definitionSiteVar.usedBy.exists { definitionSiteVarUseSite ⇒ + val definitionSiteVarUseSiteStmt = taCode.stmts(definitionSiteVarUseSite) + if (definitionSiteVarUseSiteStmt.isNonVirtualMethodCall) { + val nonVirtualMethodCall = definitionSiteVarUseSiteStmt.asNonVirtualMethodCall + doesNonVirtualMethodCallEnablesEscape(nonVirtualMethodCall) + } else true //TODO handle all cases } - - /*for (definitionSiteVarUseSite ← definitionSiteVar.usedBy) { - - } */ } else if (definitionSiteAssignment.expr.isNew) { val newStmt = definitionSiteAssignment.expr.asNew @@ -718,20 +720,19 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e handleKnownClassType(newStmt.tpe.mostPreciseObjectType) } if (!method.isConstructor) { - 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 + 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 { definitionSiteAssignment.targetVar.usedBy.exists { useSite ⇒ @@ -761,7 +762,6 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e val index = taCode.pcToIndex(pc) if (index >= 0) { val stmt = taCode.stmts(index) - // println(s"stmt: $stmt") if (!seen.contains(stmt)) { seen += stmt doesPutEnableEscape(stmt) @@ -775,44 +775,38 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e /** * If there are no dependencies left, this method can be called to create the result. */ - def createResult(implicit state: State): ProperPropertyComputationResult = { + def createResult()(implicit state: State): ProperPropertyComputationResult = { + if (state.hasDependees) { val lowerBound = - if (state.referenceIsImmutable.isDefined && state.referenceIsImmutable.get) + if (state.referenceIsImmutable.getOrElse(false)) ShallowImmutableField else MutableField - val upperBound = { - if (state.referenceIsImmutable.isEmpty) - DeepImmutableField + val upperBound = + if (state.referenceIsImmutable.isEmpty) DeepImmutableField else { state.referenceIsImmutable match { - case Some(false) | None ⇒ MutableField + case Some(false) ⇒ MutableField - case Some(true) ⇒ + case Some(true) | None ⇒ if (state.tacDependees.isEmpty && !state.concreteClassTypeIsKnown) { - if (state.typeIsImmutable) { + if (state.typeIsImmutable || state.noEscapePossibilityViaReference) DeepImmutableField - } else { - if (state.noEscapePossibilityViaReference) { - DeepImmutableField - } else { - state.dependentImmutability match { - case NotShallowOrMutable ⇒ DependentImmutableField - case OnlyDeepImmutable ⇒ DeepImmutableField - case _ ⇒ ShallowImmutableField - - } + else { + state.dependentImmutability match { + case NotShallowOrMutable ⇒ DependentImmutableField + case OnlyDeepImmutable ⇒ DeepImmutableField + case _ ⇒ ShallowImmutableField } } + } else DeepImmutableField } } - } //TODO check - DeepImmutableField InterimResult( field, lowerBound, @@ -823,21 +817,23 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } else { if (!state.escapesStillDetermined) state.noEscapePossibilityViaReference = false + state.referenceIsImmutable match { case Some(false) | None ⇒ Result(field, MutableField) case Some(true) ⇒ - if (state.typeIsImmutable) { + if (!state.concreteClassTypeIsKnown && state.typeIsImmutable || + state.concreteClassTypeIsKnown && state.classTypeIsImmutable) { Result(field, DeepImmutableField) } else { if (state.noEscapePossibilityViaReference) { Result(field, DeepImmutableField) } else { state.dependentImmutability match { - case NotShallowOrMutable ⇒ Result(field, DependentImmutableField) case OnlyDeepImmutable ⇒ Result(field, DeepImmutableField) - case _ ⇒ Result(field, ShallowImmutableField) + case NotShallowOrMutable ⇒ Result(field, DependentImmutableField) + case Dependent ⇒ Result(field, ShallowImmutableField) } } } @@ -846,130 +842,150 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) e } def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { - import org.opalj.fpcf.EUBP - - def typeMatch(entity: Entity) = - entity match { - case objectType: ObjectType if state.innerArrayTypes.contains(objectType) ⇒ - state.noEscapePossibilityViaReference = false - case _ ⇒ - } if (eps.asEPS.pk != TACAI.key) - state.dependees = state.dependees.filter(_.e ne eps.e) + state.dependees = state.dependees.filter(ep ⇒ (ep.e != eps.e) || (ep.pk != eps.pk)) eps match { - case FinalP(DeepImmutableType) ⇒ //nothing to do -> is default + case LBP(DeepImmutableType) ⇒ //nothing to do -> is default case EUBP(t, MutableType | ShallowImmutableType) ⇒ - state.typeIsImmutable = false - if (t != ObjectType.Object) { // in case of generic fields + val tpe = t.asInstanceOf[ObjectType] + if (state.innerArrayTypes.contains(tpe)) + state.noEscapePossibilityViaReference = false + if (state.concreteGenericTypes.contains(tpe) || + t == state.field.fieldType && t != ObjectType.Object) { + //TODO reasoning + // if (t != ObjectType.Object) { // in case of generic fields state.dependentImmutability = Dependent + // } } - typeMatch(t) + // else if(t!=ObjectType.Object) + // state.dependentImmutability = Dependent + + state.typeIsImmutable = false case FinalEP(t, DependentImmutableType) ⇒ + val tpe = t.asInstanceOf[ObjectType] + if (state.innerArrayTypes.contains(tpe)) { + state.noEscapePossibilityViaReference = false + } + if (state.concreteGenericTypes.contains(tpe)) { + //TODO reasoning + if (state.dependentImmutability == OnlyDeepImmutable) + state.dependentImmutability = NotShallowOrMutable + } state.typeIsImmutable = false - if (t != field.fieldType && state.dependentImmutability == OnlyDeepImmutable) - state.dependentImmutability = NotShallowOrMutable - typeMatch(t) case UBP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ - state.typeIsImmutable = false + //state.typeIsImmutable = false state.referenceIsImmutable = Some(false) return Result(field, MutableField); - case FinalP(ImmutableFieldReference | LazyInitializedThreadSafeFieldReference | - LazyInitializedNotThreadSafeButDeterministicFieldReference) ⇒ + case LBP( + ImmutableFieldReference | LazyInitializedThreadSafeFieldReference | + LazyInitializedNotThreadSafeButDeterministicFieldReference + ) ⇒ state.referenceIsImmutable = Some(true) - case FinalP(DeepImmutableField) ⇒ // nothing to do + case LBP(DeepImmutableField) ⇒ // nothing to do case UBP(DependentImmutableField | ShallowImmutableField | MutableField) ⇒ state.noEscapePossibilityViaReference = false - case eps if eps.asEPS.pk == TACAI.key ⇒ - val newEP = eps.asInstanceOf[EOptionP[Method, TACAI]] + 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 (eps.isFinal) { - val finalEP = eps.asInstanceOf[FinalEP[Method, TACAI]] - checkFieldWritesForEffImmutabilityWithKnownTAC(method, pcs._2, finalEP.p.tac.get)(state) - determineEscapeViaFieldReadsWithKnownTAC(pcs._1, finalEP.p.tac.get)(state) + 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 eps if eps.isFinal && eps.asEPS.pk == EscapeProperty.key ⇒ - if (escapesViaMethod(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])(state)) + case epk if epk.isFinal && epk.asEPS.pk == EscapeProperty.key ⇒ + if (doesEscapeViaMethod(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])) state.noEscapePossibilityViaReference = false case FinalP(DeepImmutableClass) ⇒ - state.typeIsImmutable = true + case FinalEP(t, ShallowImmutableClass | DependentImmutableClass | MutableClass) ⇒ + state.classTypeIsImmutable = false + val tpe = t.asInstanceOf[ObjectType] + if (state.concreteGenericTypes.contains(tpe) || t != ObjectType.Object && t != field.fieldType) + //TODO reasoning + // if (t != ObjectType.Object) { // in case of generic fields + state.dependentImmutability = Dependent + // } - case UBP(ShallowImmutableClass | DependentImmutableClass | MutableClass) ⇒ - state.typeIsImmutable = false + case FinalP(DependentImmutableClass) ⇒ + state.classTypeIsImmutable = false - case eps ⇒ - state.dependees += eps + case epk ⇒ + state.dependees += epk } createResult } + implicit val state: State = State(field) + /** - * Requests the immutability of the field reference and registers the dependees if necessary + * Determines the immutability of the field reference and registers the dependees if necessary */ - implicit val state: State = State(field) propertyStore(field, FieldReferenceImmutability.key) match { - case FinalP(ImmutableFieldReference) ⇒ state.referenceIsImmutable = Some(true) - case FinalP(LazyInitializedThreadSafeFieldReference | - LazyInitializedNotThreadSafeButDeterministicFieldReference) ⇒ + + case LBP( + ImmutableFieldReference | LazyInitializedThreadSafeFieldReference | + LazyInitializedNotThreadSafeButDeterministicFieldReference + ) ⇒ state.referenceIsImmutable = Some(true) - case FinalP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ + + case UBP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ return Result(field, MutableField); - case ep ⇒ - state.dependees += ep + + case ep ⇒ state.dependees += ep } + /** - * Determines whether the field is dependent immutable if the flag is set + * Determines whether the field is dependent immutable if the flag [[considerGenericity]] is set */ - if (considerGenericity) { + if (considerGenericity) handleGenericity() - } else { + else state.dependentImmutability = Dependent - } /** - * Determines whether the reference object escapes or can be mutated. + * Determines whether the reference object escapes or can be mutated if the flag [[considerEscape]] is set */ if (considerEscape) { - if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) { + state.noEscapePossibilityViaReference = state.field.isPrivate + if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) determineEscapeOfReferencedObjectOrValue() - } } 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 immutabiltiy of this. + * In cases where we know the concrete class type assigned to the field we could use the immutability of this. */ - if (!state.concreteClassTypeIsKnown) { + if (!state.concreteClassTypeIsKnown) handleTypeImmutability(state) - } + else + state.typeIsImmutable = false - createResult + createResult() } } trait L3FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { - import org.opalj.fpcf.PropertyBounds - final override def uses: Set[PropertyBounds] = Set( PropertyBounds.ub(TACAI), PropertyBounds.ub(EscapeProperty), - PropertyBounds.lub(FieldReferenceImmutability), + PropertyBounds.ub(FieldReferenceImmutability), PropertyBounds.lub(TypeImmutability), PropertyBounds.lub(FieldImmutability) ) @@ -982,7 +998,8 @@ trait L3FieldImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { * Executor for the eager field immutability analysis. * */ -object EagerL3FieldImmutabilityAnalysis extends L3FieldImmutabilityAnalysisScheduler +object EagerL3FieldImmutabilityAnalysis + extends L3FieldImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -991,7 +1008,7 @@ object EagerL3FieldImmutabilityAnalysis extends L3FieldImmutabilityAnalysisSched 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 + val fields = p.allFields // p.allProjectClassFiles.flatMap(classfile => classfile.fields) // p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) analysis } @@ -1002,7 +1019,8 @@ object EagerL3FieldImmutabilityAnalysis extends L3FieldImmutabilityAnalysisSched * Executor for the lazy field immutability analysis. * */ -object LazyL3FieldImmutabilityAnalysis extends L3FieldImmutabilityAnalysisScheduler +object LazyL3FieldImmutabilityAnalysis + extends L3FieldImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) From ef6888bd785d03bf4d9099be0ca610e068074c4e Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 11 Dec 2020 18:47:41 +0100 Subject: [PATCH 301/327] fixing several bugs and optimization --- .../org/opalj/ai/common/DomainRegistry.scala | 3 +- .../L3FieldImmutabilityAnalysis.scala | 271 +++++++++--------- 2 files changed, 144 insertions(+), 130 deletions(-) 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/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 index ecd68c57a1..b19f0196f1 100755 --- 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 @@ -76,8 +76,8 @@ import org.opalj.br.FormalTypeParameter import org.opalj.br.fpcf.properties.ClassImmutability import scala.collection.mutable import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.EUBP import org.opalj.fpcf.LBP +import org.opalj.fpcf.EUBP /** * Analysis that determines the immutability of org.opalj.br.Field @@ -90,7 +90,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) * Describes the different kinds of dependent immutable fields, that depend on the concrete types that replace * the generic parameters. * - * [[Dependent]] Shallow or mutable types could still exist. + * [[NotDependentImmutable]] Shallow or mutable types could still exist. * Example: Foo f * * [[NotShallowOrMutable]] There are no shallow and no mutable types. @@ -99,26 +99,31 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) * [[OnlyDeepImmutable]] There are only deep immutable types and no generic parameters left. * Example: Foo f */ - sealed trait DependentImmutabilityKind - case object Dependent extends DependentImmutabilityKind - case object NotShallowOrMutable extends DependentImmutabilityKind - case object OnlyDeepImmutable extends DependentImmutabilityKind + 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 typeIsImmutable: Boolean = true, - var classTypeIsImmutable: Boolean = true, + var typeImmutability: ImmutabilityTypes = DeepImmutable, + var classTypeImmutability: ImmutabilityTypes = DeepImmutable, var referenceIsImmutable: Option[Boolean] = None, var noEscapePossibilityViaReference: Boolean = true, - var dependentImmutability: DependentImmutabilityKind = OnlyDeepImmutable, - var genericTypeSetNotDeepImmutable: Boolean = false, + 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 totalNumberOfFieldWrites: Int = 0, + var fieldTypeIsDependentImmutable: Boolean = false ) { def hasDependees: Boolean = dependees.nonEmpty || tacDependees.nonEmpty def getDependees: Set[EOptionP[Entity, Property]] = @@ -131,16 +136,6 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) final val definitionSites = project.get(DefinitionSitesKey) implicit final val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) - val considerEscape = - project.config.getBoolean( - "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 _ ⇒ @@ -151,6 +146,15 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) field: Field ): ProperPropertyComputationResult = { + val considerEscape = project.config.getBoolean( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape" + ) + + val considerGenericity = + project.config.getBoolean( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity" + ) + /** * Returns the formal type parameters from the class' and outer classes' signature */ @@ -203,24 +207,23 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) def handleTypeImmutability(state: State): Unit = { val objectType = field.fieldType.asFieldType if (objectType == ObjectType.Object) { - state.typeIsImmutable = false //handling generic fields + 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.typeIsImmutable = false + state.typeImmutability = Mutable //false } else { propertyStore(objectType, TypeImmutability.key) match { - case FinalP(DeepImmutableType) ⇒ // deep immutable type is set as default - case FinalP(DependentImmutableType) ⇒ state.typeIsImmutable = false - - case EUBP(t, ShallowImmutableType | MutableType) ⇒ - state.typeIsImmutable = false - if (t == field.fieldType && field.fieldType != ObjectType.Object) - state.dependentImmutability = Dependent - + 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 } } @@ -239,6 +242,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case TypeVariableSignature(t) ⇒ noRelevantAttributesFound = false onlyDeepImmutableTypesInGenericTypeFound = false + state.fieldTypeIsDependentImmutable = true //generic type T is dependent immutable if (!isInClassesGenericTypeParameters(t)) noShallowOrMutableTypesInGenericTypeFound = false @@ -260,8 +264,10 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) _ ) ) ⇒ - val objectPath = outerPackageIdentifier.getOrElse("") + innerPackageIdentifier - + val objectPath = outerPackageIdentifier match { + case Some(prepackageIdentifier) ⇒ prepackageIdentifier + innerPackageIdentifier + case _ ⇒ innerPackageIdentifier + } genericParameters ::= ObjectType(objectPath) case _ ⇒ @@ -276,17 +282,12 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) genericParameters.foreach(objectType ⇒ { propertyStore(objectType, TypeImmutability.key) match { - case LBP(DeepImmutableType) ⇒ //nothing to do here: default value is deep immutable - - /* FinalP(DependentImmutableType) => - onlyDeepImmutableTypesInGenericTypeFound = false - state.typeIsImmutable = false */ - //conservative overapproximation that allows no nested generic classes + 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 - state.typeIsImmutable = false case ep ⇒ state.concreteGenericTypes += ep.e @@ -300,13 +301,13 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (onlyDeepImmutableTypesInGenericTypeFound) { //nothing to do... } else if (noShallowOrMutableTypesInGenericTypeFound && - state.dependentImmutability != Dependent) { + state.dependentImmutability != NotDependentImmutable) { state.dependentImmutability = NotShallowOrMutable } else { - state.dependentImmutability = Dependent + state.dependentImmutability = NotDependentImmutable } } else { - state.dependentImmutability = Dependent + state.dependentImmutability = NotDependentImmutable } } @@ -323,8 +324,11 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case finalEP: FinalEP[Method, TACAI] ⇒ finalEP.ub.tac case epk ⇒ - val (reads: IntTrieSet, writes: IntTrieSet) = - state.tacDependees.getOrElse(method, (IntTrieSet.empty, IntTrieSet.empty)) + 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 @@ -370,19 +374,19 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) //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 { - case (method, pcs) ⇒ - val taCodeOption = getTACAI(method, pcs, isRead = false) - if (taCodeOption.isDefined) - determineEscapeViaFieldWritesWithKnownTAC(method, pcs, taCodeOption.get) + writes.foreach { write ⇒ + val (method, pcs) = write + val taCodeOption = getTACAI(method, pcs, isRead = false) + if (taCodeOption.isDefined) + determineEscapeViaFieldWritesWithKnownTAC(method, pcs, taCodeOption.get) } if (state.noEscapePossibilityViaReference) { - fieldAccessInformation.readAccesses(state.field).foreach { - case (method, pcs) ⇒ - val taCodeOption = getTACAI(method, pcs, isRead = true) - if (taCodeOption.isDefined) - determineEscapeViaFieldReadsWithKnownTAC(method, pcs, taCodeOption.get) + 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) } } } @@ -429,9 +433,12 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) 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 - propertyStore(innerArrayType, TypeImmutability.key) match { + val result = propertyStore(innerArrayType, TypeImmutability.key) + result match { case LBP(DeepImmutableType) ⇒ false //nothing to do case UBP( DependentImmutableType | ShallowImmutableType | MutableType @@ -496,13 +503,14 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) */ def handleKnownClassType(objectType: ObjectType): Unit = { - val result = propertyStore(objectType, ClassImmutability.key) - result match { - - case FinalP(MutableClass | ShallowImmutableClass | DependentImmutableClass) ⇒ - state.classTypeIsImmutable = false + propertyStore(objectType, ClassImmutability.key) match { + case LBP(DeepImmutableClass) ⇒ //nothing to do ; true ist default - case FinalP(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 @@ -539,8 +547,6 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case FinalP(DeepImmutableField) ⇒ false //nothing to do here case UBP(MutableField | ShallowImmutableField | DependentImmutableField) ⇒ true - case FinalP(_) ⇒ true - case ep ⇒ state.dependees += ep false @@ -589,7 +595,8 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) doesPutEnableEscape(stmt) } else false } else if (stmt.isArrayStore) { - true //TODO handling that case more precise + !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)) } @@ -745,13 +752,13 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) doesPutEnableEscape(useSiteStmt) } else false } else if (useSiteStmt.isAssignment) { - true //TODO + true //conservative handling to reduce complexity } else { true } } } - //TODO handle all cases!! + //TODO handle all cases } else !definitionSiteAssignment.expr.isConst } else true } @@ -776,10 +783,9 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) * If there are no dependencies left, this method can be called to create the result. */ def createResult()(implicit state: State): ProperPropertyComputationResult = { - if (state.hasDependees) { val lowerBound = - if (state.referenceIsImmutable.getOrElse(false)) + if (state.referenceIsImmutable.isDefined && state.referenceIsImmutable.get) ShallowImmutableField else MutableField @@ -792,28 +798,33 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(true) | None ⇒ if (state.tacDependees.isEmpty && !state.concreteClassTypeIsKnown) { - if (state.typeIsImmutable || state.noEscapePossibilityViaReference) + if (state.typeImmutability == DeepImmutable || + state.noEscapePossibilityViaReference) { DeepImmutableField - else { + } else if (state.typeImmutability == Mutable && + !state.fieldTypeIsDependentImmutable) { + ShallowImmutableField + } else { state.dependentImmutability match { case NotShallowOrMutable ⇒ DependentImmutableField case OnlyDeepImmutable ⇒ DeepImmutableField case _ ⇒ ShallowImmutableField } } - } else DeepImmutableField } } - - InterimResult( - field, - lowerBound, - upperBound, - state.getDependees, - c - ) + if (lowerBound == upperBound) + Result(field, lowerBound) + else + InterimResult( + field, + lowerBound, + upperBound, + state.getDependees, + c + ) } else { if (!state.escapesStillDetermined) state.noEscapePossibilityViaReference = false @@ -823,17 +834,23 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(false) | None ⇒ Result(field, MutableField) case Some(true) ⇒ - if (!state.concreteClassTypeIsKnown && state.typeIsImmutable || - state.concreteClassTypeIsKnown && state.classTypeIsImmutable) { + if (!state.concreteClassTypeIsKnown && state.typeImmutability == DeepImmutable || + state.concreteClassTypeIsKnown && state.classTypeImmutability == DeepImmutable) { Result(field, DeepImmutableField) } else { if (state.noEscapePossibilityViaReference) { Result(field, DeepImmutableField) } else { - state.dependentImmutability match { - case OnlyDeepImmutable ⇒ Result(field, DeepImmutableField) - case NotShallowOrMutable ⇒ Result(field, DependentImmutableField) - case Dependent ⇒ Result(field, ShallowImmutableField) + 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) } } } @@ -841,6 +858,15 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } + 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) @@ -848,38 +874,26 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) eps match { case LBP(DeepImmutableType) ⇒ //nothing to do -> is default - case EUBP(t, MutableType | ShallowImmutableType) ⇒ - val tpe = t.asInstanceOf[ObjectType] - if (state.innerArrayTypes.contains(tpe)) - state.noEscapePossibilityViaReference = false - if (state.concreteGenericTypes.contains(tpe) || - t == state.field.fieldType && t != ObjectType.Object) { - //TODO reasoning - // if (t != ObjectType.Object) { // in case of generic fields - state.dependentImmutability = Dependent - // } + case FinalEP(t, DependentImmutableType) ⇒ + import org.opalj.br.FieldType + if (t.asInstanceOf[FieldType] == state.field.fieldType) { + state.fieldTypeIsDependentImmutable = true + state.typeImmutability = DependentImmutable } - // else if(t!=ObjectType.Object) - // state.dependentImmutability = Dependent + val tpe = t.asInstanceOf[ObjectType] + handleMutableType(tpe) - state.typeIsImmutable = false + case EUBP(t, MutableType | ShallowImmutableType) ⇒ + import org.opalj.br.FieldType + if (t.asInstanceOf[FieldType] == state.field.fieldType) + state.typeImmutability = Mutable - case FinalEP(t, DependentImmutableType) ⇒ val tpe = t.asInstanceOf[ObjectType] - if (state.innerArrayTypes.contains(tpe)) { - state.noEscapePossibilityViaReference = false - } - if (state.concreteGenericTypes.contains(tpe)) { - //TODO reasoning - if (state.dependentImmutability == OnlyDeepImmutable) - state.dependentImmutability = NotShallowOrMutable - } - state.typeIsImmutable = false + handleMutableType(tpe) case UBP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ - //state.typeIsImmutable = false state.referenceIsImmutable = Some(false) - return Result(field, MutableField); + return Result(state.field, MutableField); case LBP( ImmutableFieldReference | LazyInitializedThreadSafeFieldReference | @@ -909,18 +923,15 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) if (doesEscapeViaMethod(eps.asInstanceOf[EOptionP[DefinitionSite, EscapeProperty]])) state.noEscapePossibilityViaReference = false - case FinalP(DeepImmutableClass) ⇒ - case FinalEP(t, ShallowImmutableClass | DependentImmutableClass | MutableClass) ⇒ - state.classTypeIsImmutable = false - val tpe = t.asInstanceOf[ObjectType] - if (state.concreteGenericTypes.contains(tpe) || t != ObjectType.Object && t != field.fieldType) - //TODO reasoning - // if (t != ObjectType.Object) { // in case of generic fields - state.dependentImmutability = Dependent - // } + case LBP(DeepImmutableClass) ⇒ //nothing to do - case FinalP(DependentImmutableClass) ⇒ - state.classTypeIsImmutable = false + 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 @@ -935,13 +946,13 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) */ propertyStore(field, FieldReferenceImmutability.key) match { - case LBP( + case FinalP( ImmutableFieldReference | LazyInitializedThreadSafeFieldReference | LazyInitializedNotThreadSafeButDeterministicFieldReference ) ⇒ state.referenceIsImmutable = Some(true) - case UBP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ + case FinalP(MutableFieldReference | LazyInitializedNotThreadSafeFieldReference) ⇒ return Result(field, MutableField); case ep ⇒ state.dependees += ep @@ -952,8 +963,9 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) */ if (considerGenericity) handleGenericity() - else - state.dependentImmutability = Dependent + else { + state.dependentImmutability = NotDependentImmutable + } /** * Determines whether the reference object escapes or can be mutated if the flag [[considerEscape]] is set @@ -973,8 +985,9 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) */ if (!state.concreteClassTypeIsKnown) handleTypeImmutability(state) - else - state.typeIsImmutable = false + else { + state.typeImmutability = Mutable + } createResult() } @@ -1008,7 +1021,7 @@ object EagerL3FieldImmutabilityAnalysis 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 + val fields = p.allFields // p.allProjectClassFiles.flatMap(classfile ⇒ classfile.fields) //p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldImmutability) analysis } From 52e192f86330191219e415802a83cb788908a62d Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 11 Dec 2020 18:50:33 +0100 Subject: [PATCH 302/327] fixing order of settings and first project call and optimization --- .../org/opalj/support/info/Immutability.scala | 169 ++++++++++-------- 1 file changed, 96 insertions(+), 73 deletions(-) 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 index b3511e2d85..dc62016f33 100644 --- 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 @@ -60,25 +60,23 @@ 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.log.DevNullLogger -import org.opalj.log.GlobalLogContext -import org.opalj.log.OPALLogger 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.ai.domain 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.ai.fpcf.properties.AIDomainFactoryKey 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.tac.fpcf.analyses.purity.EagerL2PurityAnalysis +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 @@ -112,9 +110,6 @@ object Immutability { withoutConsiderGenericity: Boolean, withoutConsiderLazyInitialization: Boolean ): BasicReport = { - import org.opalj.fpcf.OrderedProperty - - OPALLogger.updateLogger(GlobalLogContext, DevNullLogger) val classFiles = projectDir match { case Some(dir) ⇒ JavaClassFileReader().ClassFiles(cp.toPath.resolve(dir).toFile) @@ -143,27 +138,21 @@ object Immutability { ) } - if (withoutConsiderEscape) { - config = config.withValue( - "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape", - ConfigValueFactory.fromAnyRef("false") - ) - } + config = config.withValue( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape", + ConfigValueFactory.fromAnyRef(!withoutConsiderEscape) + ) - if (withoutConsiderGenericity) { - config = config.withValue( - "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity", - ConfigValueFactory.fromAnyRef("false") - ) - } + config = config.withValue( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity", + ConfigValueFactory.fromAnyRef(!withoutConsiderGenericity) + ) - if (withoutConsiderLazyInitialization) { - config = - config.withValue( - "org.opalj.fpcf.analyses.L0FieldReferenceImmutabilityAnalysis.considerLazyInitialization", - ConfigValueFactory.fromAnyRef("false") - ) - } + config = + config.withValue( + "org.opalj.fpcf.analyses.L0FieldReferenceImmutabilityAnalysis.considerLazyInitialization", + ConfigValueFactory.fromAnyRef(!withoutConsiderLazyInitialization) + ) var projectTime: Seconds = Seconds.None var analysisTime: Seconds = Seconds.None @@ -204,7 +193,7 @@ object Immutability { LazyFieldLocalityAnalysis ) - val classDepencencies: List[FPCFAnalysisScheduler] = List( + val classDependencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, purityAnalysis, LazyL0FieldReferenceImmutabilityAnalysis, @@ -250,29 +239,14 @@ object Immutability { val dependencies = analysis match { case FieldReferences ⇒ fieldReferenceDependencies case Fields ⇒ fieldDependencies - case Classes ⇒ classDepencencies + case Classes ⇒ classDependencies case Types ⇒ typeDependencies case All ⇒ allImmAnalysisDependencies } L2PurityAnalysis.setRater(Some(SystemOutLoggingAllExceptionRater)) - var propertyStore: PropertyStore = null - - val analysesManager = project.get(FPCFAnalysesManagerKey) - - time { - analysesManager.project.get(callGraphKey) - } { t ⇒ callGraphTime = t.toSeconds } - - println( - s""" - | level: $level - | callgraph: $callGraphKey - |""".stripMargin - ) - - analysesManager.project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ if (level == 0) Set[Class[_ <: AnyRef]](classOf[domain.l0.BaseDomainWithDefUse[URL]]) else if (level == 1) @@ -290,12 +264,17 @@ object Immutability { 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: _*) } } ) + println("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + time { project.get(callGraphKey) } { t ⇒ callGraphTime = t.toSeconds } + println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") + var propertyStore: PropertyStore = null + + val analysesManager = project.get(FPCFAnalysesManagerKey) time { propertyStore = analysesManager.runAll(dependencies)._1 @@ -319,6 +298,8 @@ object Immutability { 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("/", ".") @@ -350,33 +331,40 @@ object Immutability { stringBuilderResults.append( s""" | Mutable References: - | ${mutableFieldReferences.mkString(" || Mutable Reference \n")} + | ${mutableFieldReferences.mkString(" | Mutable Reference \n")} | - | Lazy Initalized Not Thread Safe And Not Deterministic References: + | Lazy Initialized Not Thread Safe And Not Deterministic Field References: | ${ notThreadSafeLazyInitializedFieldReferences. - mkString(" | Lazy Initialized Not Thread Safe And Not Deterministic Reference\n") + mkString(" | Lazy Initialized Not Thread Safe And Not Deterministic Field Reference\n") } | - | Lazy Initialized Not Thread Safe But Deterministic References: + | Lazy Initialized Not Thread Safe But Deterministic Field References: | ${ lazyInitializedReferencesNotThreadSafeButDeterministic. - mkString(" || Lazy Initialized Not Thread Safe But Deterministic Reference\n") + mkString(" | Lazy Initialized Not Thread Safe But Deterministic Field Reference\n") } | | Lazy Initialized Thread Safe References: | ${ threadSafeLazyInitializedFieldReferences. - mkString(" || Lazy Initialized Thread Safe Reference\n") + mkString(" | Lazy Initialized Thread Safe Field Reference\n") } | | Immutable References: - | ${immutableReferences.mkString(" || immutable Reference\n")} + | ${immutableReferences.mkString(" | immutable field Reference\n")} | |""".stripMargin ) } + for (eps ← propertyStore.entities(FieldImmutability.key)) { + if (eps.e.asInstanceOf[Field].name == "resolverFields") { + println("eps: "+eps) + // throw new Exception("...") + } + } + val fieldGroupedResults = propertyStore.entities(FieldImmutability.key). filter(eps ⇒ allFieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field])). toTraversable.groupBy(_.asFinal.p) @@ -514,6 +502,7 @@ object Immutability { |""".stripMargin ) } + if (analysis == Fields || analysis == All) { stringBuilderNumber.append( s""" @@ -574,8 +563,26 @@ object Immutability { | $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 (withoutConsiderEscape) { @@ -592,7 +599,16 @@ object Immutability { 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) { @@ -600,7 +616,7 @@ object Immutability { val calender = Calendar.getInstance() calender.add(Calendar.ALL_STYLES, 1) - val date = calender.getTime(); + val date = calender.getTime() val simpleDateFormat = new SimpleDateFormat("dd_MM_yyyy_HH_mm_ss") val file = new File( @@ -729,6 +745,7 @@ object Immutability { case "-withoutConsiderGenericity" ⇒ withoutConsiderGenericity = true case "-withoutConsiderEscape" ⇒ withoutConsiderEscape = true case "-withoutConsiderLazyInitialization" ⇒ withoutConsiderLazyInitialization = true + case "-JDK" ⇒ cp = JRELibraryFolder withoutJDK = true @@ -751,24 +768,30 @@ object Immutability { Console.println(usage) return ; } + var nIndex = 0 + val end = 0 + while (nIndex <= end) { + println(s"start $nIndex") + nIndex = nIndex + 1 + evaluate( + cp, + analysis, + numThreads, + projectDir, + libDir, + resultFolder, + withoutJDK, + isLibrary, + closedWorldAssumption, + callGraphKey, + level, + reImInferComparison, + withoutConsiderEscape, + withoutConsiderGenericity, + withoutConsiderLazyInitialization + ) + } - evaluate( - cp, - analysis, - numThreads, - projectDir, - libDir, - resultFolder, - withoutJDK, - isLibrary, - closedWorldAssumption, - callGraphKey, - level, - reImInferComparison, - withoutConsiderEscape, - withoutConsiderGenericity, - withoutConsiderLazyInitialization - ) } } From a6873fe2ab9f957a160e07c4591b6f9980e5d2e3 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 11 Dec 2020 18:51:24 +0100 Subject: [PATCH 303/327] extending for new immutability analysis --- .../scala/org/opalj/support/info/Purity.scala | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) 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 6784f573eb..a9fb29f1d5 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 @@ -79,6 +79,11 @@ import org.opalj.tac.fpcf.analyses.purity.SystemOutLoggingAllExceptionRater 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. @@ -527,13 +532,14 @@ object Purity { Console.println(usage) return ; } - - if (eager) { - support ::= EagerL0ClassImmutabilityAnalysis - support ::= EagerL0TypeImmutabilityAnalysis - } else { - support ::= LazyL0ClassImmutabilityAnalysis - support ::= LazyL0TypeImmutabilityAnalysis + if (!fieldMutabilityAnalysisName.contains("L3")) { + if (eager) { + support ::= EagerL0ClassImmutabilityAnalysis + support ::= EagerL0TypeImmutabilityAnalysis + } else { + support ::= LazyL0ClassImmutabilityAnalysis + support ::= LazyL0TypeImmutabilityAnalysis + } } escapeAnalysisName match { @@ -568,6 +574,18 @@ object Purity { support ::= LazyL2FieldImmutabilityAnalysis support ::= LazyUnsoundPrematurelyReadFieldsAnalysis + case Some("L3") ⇒ + support ::= LazyL3FieldImmutabilityAnalysis + support ::= LazyUnsoundPrematurelyReadFieldsAnalysis + + if (eager) { + support ::= EagerL1ClassImmutabilityAnalysis + support ::= EagerL1TypeImmutabilityAnalysis + } else { + support ::= LazyL1ClassImmutabilityAnalysis + support ::= LazyL1TypeImmutabilityAnalysis + } + case Some("none") ⇒ case None ⇒ analysis match { From 27ca712a6a0dbcb2f1451640d08a5339463fd9bc Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 11 Dec 2020 18:52:58 +0100 Subject: [PATCH 304/327] enable all analysis tests --- .../opalj/fpcf/FieldImmutabilityTests.scala | 170 +++++++++--------- 1 file changed, 85 insertions(+), 85 deletions(-) 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 index 8693572e10..9898b8da75 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -19,10 +19,10 @@ import org.opalj.tac.fpcf.analyses.immutability.fieldreference.LazyL0FieldRefere 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.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 */ +import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis /** * Tests the field immutability analyses @@ -31,92 +31,92 @@ import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis */ */ class FieldImmutabilityTests extends PropertiesTest { - override def withRT = true + override def withRT = true - override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") - } - - 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")) - } - /* - describe("the org.opalj.fpcf.analyses.L0FieldMutabilityAnalysis is executed") { + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/immutability") + } - val as = executeAnalyses( - Set( - EagerL0FieldImmutabilityAnalysis, - LazyUnsoundPrematurelyReadFieldsAnalysis - ) - ) + override def init(p: Project[URL]): Unit = { - 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")) + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ => + Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) } - 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")) - } + p.get(RTACallGraphKey) + } + + describe("no analysis is scheduled") { + + val as = executeAnalyses(Set.empty) + + as.propertyStore.shutdown() + + validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) + } + + 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")) + } } From e1bff23b766a4acc41d2eea574ec5f6fda42e4dc Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 11 Dec 2020 19:51:30 +0100 Subject: [PATCH 305/327] fixing fake entity bug --- .../src/main/scala/org/opalj/fpcf/par/PKECPropertyStore.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 91cd8555b2..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 @@ -511,6 +511,7 @@ class PKECPropertyStore( startThreads(new PartialPropertiesFinalizerThread(_)) subPhaseId += 1 + ps(AnalysisKeyId).clear() } idle = true @@ -1044,4 +1045,4 @@ object PKECPropertyStore extends PropertyStoreFactory[PKECPropertyStore] { val ps = new PKECPropertyStore(contextMap, MaxThreads, maxEvaluationDepth) ps } -} \ No newline at end of file +} From 5795b36e7aa13d51eb90bba2a0b1b37f85237aec Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 14 Dec 2020 11:15:29 +0100 Subject: [PATCH 306/327] formatting --- .../WithMutableAndImmutableFieldType.java | 13 ++ .../opalj/fpcf/FieldImmutabilityTests.scala | 166 +++++++++--------- 2 files changed, 96 insertions(+), 83 deletions(-) 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 index 484b488a4d..cdf234708c 100644 --- 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 @@ -62,4 +62,17 @@ class SimpleMutableClass{ 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/scala/org/opalj/fpcf/FieldImmutabilityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala index 9898b8da75..2a449d6a8a 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -31,92 +31,92 @@ import org.opalj.br.fpcf.analyses.EagerL0FieldImmutabilityAnalysis */ class FieldImmutabilityTests extends PropertiesTest { - override def withRT = true + override def withRT = true - override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") - } + override def fixtureProjectPackage: List[String] = { + List("org/opalj/fpcf/fixtures/immutability") + } + + 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")) + } + + describe("the org.opalj.fpcf.analyses.L0FieldMutabilityAnalysis is executed") { - override def init(p: Project[URL]): Unit = { + val as = executeAnalyses( + Set( + EagerL0FieldImmutabilityAnalysis, + LazyUnsoundPrematurelyReadFieldsAnalysis + ) + ) - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ => - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + 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")) } - p.get(RTACallGraphKey) - } - - describe("no analysis is scheduled") { - - val as = executeAnalyses(Set.empty) - - as.propertyStore.shutdown() - - validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) - } - - 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")) - } + 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")) + } } From 914a27208ad91c1e83f273b9483fc68356d8d804 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 14 Dec 2020 13:35:26 +0100 Subject: [PATCH 307/327] adjustment; nested generic classes seen as shaddow immutable. --- .../GenericBaseClass.java | 6 +++--- .../immutability/fields/ArrayClasses.java | 4 ++-- .../fpcf/ClassAndTypeImmutabilityTests.scala | 15 +++++++++++++-- 3 files changed, 18 insertions(+), 7 deletions(-) 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 index a2e0d76f72..3621c4aef8 100644 --- 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 @@ -308,12 +308,12 @@ public Two(A a, B b, ClassWithOneMutableField tmc, GenericBaseClass gc1) { } } -@DependentImmutableType(value = "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) -@DependentImmutableClass(value = "class has only dependent immutable fields", +@MutableType(value = "class is not extensible", analyses = L1TypeImmutabilityAnalysis.class) +@ShallowImmutableClass(value = "class has only dependent immutable fields", analyses = L1ClassImmutabilityAnalysis.class) class TwoVirgin { - @DependentImmutableField(value="field has generic parameter", analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value="field has generic parameter", analyses = L3FieldImmutabilityAnalysis.class) @ImmutableFieldReference(value = "field is effective immutable", analyses = L0FieldReferenceImmutabilityAnalysis.class) private GenericBaseClass, B, C> gc1; 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 index 695e3ab6d7..ca72e60ae8 100644 --- 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 @@ -165,8 +165,8 @@ public synchronized Object getP(){ return p[2]; } - @DeepImmutableField("The elements of the array can escape, but have a deep immutable reference.") - @LazyInitializedThreadSafeFieldReference("The array is thread safe lazily intialized.") + //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) 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 index a7a7345b64..e57c6035f1 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala @@ -34,6 +34,8 @@ import org.opalj.tac.fpcf.analyses.LazyL2FieldImmutabilityAnalysis */ class ClassAndTypeImmutabilityTests extends PropertiesTest { + override def withRT = true + override def fixtureProjectPackage: List[String] = { List("org/opalj/fpcf/fixtures/immutability") } @@ -66,8 +68,17 @@ class ClassAndTypeImmutabilityTests extends PropertiesTest { Set("TypeImmutability", "ClassImmutability") ) } - /* + describe("the 1 class and type immutability analysis are executed") { + 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 val as = executeAnalyses(Set( LazyUnsoundPrematurelyReadFieldsAnalysis, @@ -90,5 +101,5 @@ class ClassAndTypeImmutabilityTests extends PropertiesTest { classFilesWithAnnotations(as.project).map(tp ⇒ (tp._1.thisType, tp._2, tp._3)), Set("TypeImmutability", "ClassImmutability") ) - }*/ + } } From 3d8dcce91a2d57b1b76b3802ec2efae195605609 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 22 Dec 2020 09:56:58 +0100 Subject: [PATCH 308/327] changed reimInfer comparison. Now filters out synthetic fields and enums --- .../org/opalj/support/info/Immutability.scala | 58 +++++++++---------- 1 file changed, 28 insertions(+), 30 deletions(-) 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 index dc62016f33..1682074e86 100644 --- 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 @@ -269,9 +269,7 @@ object Immutability { } } ) - println("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") time { project.get(callGraphKey) } { t ⇒ callGraphTime = t.toSeconds } - println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") var propertyStore: PropertyStore = null val analysesManager = project.get(FPCFAnalysesManagerKey) @@ -288,7 +286,7 @@ object Immutability { val allFieldsInProjectClassFiles = { if (reImInferComparison) { project.allProjectClassFiles.toIterator.flatMap { _.fields }. - filter(f ⇒ !f.isTransient && !f.isSynthetic).toSet + filter(f ⇒ !f.isSynthetic && !f.toString().contains("/*ENUM*/")).toSet } else project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet } @@ -331,40 +329,40 @@ object Immutability { stringBuilderResults.append( s""" | Mutable References: - | ${mutableFieldReferences.mkString(" | Mutable Reference \n")} + | ${mutableFieldReferences.map(_+" | Mutable Reference ").mkString("\n")} | | Lazy Initialized Not Thread Safe And Not Deterministic Field References: | ${ notThreadSafeLazyInitializedFieldReferences. - mkString(" | Lazy Initialized Not Thread Safe And Not Deterministic Field Reference\n") + map(_+" | Lazy Initialized Not Thread Safe And Not Deterministic Field Reference") + .mkString("\n") } | | Lazy Initialized Not Thread Safe But Deterministic Field References: | ${ - lazyInitializedReferencesNotThreadSafeButDeterministic. - mkString(" | Lazy Initialized Not Thread Safe But Deterministic Field Reference\n") + lazyInitializedReferencesNotThreadSafeButDeterministic + .map(_+" | Lazy Initialized Not Thread Safe But Deterministic Field Reference") + .mkString("\n") } | | Lazy Initialized Thread Safe References: | ${ - threadSafeLazyInitializedFieldReferences. - mkString(" | Lazy Initialized Thread Safe Field Reference\n") + threadSafeLazyInitializedFieldReferences + .map(_+" | Lazy Initialized Thread Safe Field Reference") + .mkString("\n") } | | Immutable References: - | ${immutableReferences.mkString(" | immutable field Reference\n")} + | ${ + immutableReferences + .map(_+" | immutable field Reference") + .mkString("\n") + } | |""".stripMargin ) } - for (eps ← propertyStore.entities(FieldImmutability.key)) { - if (eps.e.asInstanceOf[Field].name == "resolverFields") { - println("eps: "+eps) - // throw new Exception("...") - } - } - val fieldGroupedResults = propertyStore.entities(FieldImmutability.key). filter(eps ⇒ allFieldsInProjectClassFiles.contains(eps.e.asInstanceOf[Field])). toTraversable.groupBy(_.asFinal.p) @@ -385,16 +383,16 @@ object Immutability { stringBuilderResults.append( s""" | Mutable Fields: - | ${mutableFields.mkString(" | Mutable Field \n")} + | ${mutableFields.map(_+" | Mutable Field ").mkString("\n")} | | Shallow Immutable Fields: - | ${shallowImmutableFields.mkString(" | Shallow Immutable Field \n")} + | ${shallowImmutableFields.map(_+" | Shallow Immutable Field ").mkString("\n")} | | Dependent Immutable Fields: - | ${dependentImmutableFields.mkString(" | Dependent Immutable Field \n")} + | ${dependentImmutableFields.map(_+" | Dependent Immutable Field ").mkString("\n")} | | Deep Immutable Fields: - | ${deepImmutableFields.mkString(" | Deep Immutable Field \n")} + | ${deepImmutableFields.map(_+" | Deep Immutable Field ").mkString("\n")} | |""".stripMargin ) @@ -437,19 +435,19 @@ object Immutability { stringBuilderResults.append( s""" | Mutable Classes: - | ${mutableClasses.mkString(" | Mutable Class \n")} + | ${mutableClasses.map(_+" | Mutable Class ").mkString("\n")} | | Shallow Immutable Classes: - | ${shallowImmutableClasses.mkString(" | Shallow Immutable Class \n")} + | ${shallowImmutableClasses.map(_+" | Shallow Immutable Class ").mkString("\n")} | | Dependent Immutable Classes: - | ${dependentImmutableClasses.mkString(" | Dependent Immutable Class \n")} + | ${dependentImmutableClasses.map(_+" | Dependent Immutable Class ").mkString("\n")} | | Deep Immutable Classes: - | ${deepImmutableClasses.mkString(" | Deep Immutable Classes \n")} + | ${deepImmutableClasses.map(_+" | Deep Immutable Classes ").mkString("\n")} | | Deep Immutable Interfaces: - | ${deepImmutableClassesInterfaces.mkString(" | Deep Immutable Interfaces \n")} + | ${deepImmutableClassesInterfaces.map(_+" | Deep Immutable Interfaces ").mkString("\n")} |""".stripMargin ) } @@ -473,16 +471,16 @@ object Immutability { stringBuilderResults.append( s""" | Mutable Types: - | ${mutableTypes.mkString(" | Mutable Type \n")} + | ${mutableTypes.map(_+" | Mutable Type ").mkString("\n")} | | Shallow Immutable Types: - | ${shallowImmutableTypes.mkString(" | Shallow Immutable Types \n")} + | ${shallowImmutableTypes.map(_+" | Shallow Immutable Types ").mkString("\n")} | | Dependent Immutable Types: - | ${dependentImmutableTypes.mkString(" | Dependent Immutable Types \n")} + | ${dependentImmutableTypes.map(_+" | Dependent Immutable Types ").mkString("\n")} | | Deep Immutable Types: - | ${deepImmutableTypes.mkString(" | Deep Immutable Types \n")} + | ${deepImmutableTypes.map(_+" | Deep Immutable Types ").mkString("\n")} |""".stripMargin ) } From 0afd811e5efad03f5d947e46d554cd485fd75451 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 11 Feb 2021 12:07:22 +0100 Subject: [PATCH 309/327] removed reiminfer comparison --- .../org/opalj/support/info/Immutability.scala | 283 +++++++++++------- 1 file changed, 178 insertions(+), 105 deletions(-) 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 index 1682074e86..302aab4b13 100644 --- 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 @@ -73,7 +73,6 @@ 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.tac.fpcf.analyses.purity.EagerL2PurityAnalysis import org.opalj.ai.domain import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.fpcf.OrderedProperty @@ -105,7 +104,6 @@ object Immutability { closedWorldAssumption: Boolean, callGraphKey: AbstractCallGraphKey, level: Int, - reImInferComparison: Boolean, withoutConsiderEscape: Boolean, withoutConsiderGenericity: Boolean, withoutConsiderLazyInitialization: Boolean @@ -121,14 +119,16 @@ object Immutability { case None ⇒ Traversable.empty } - val JDKFiles = if (withoutJDK) Traversable.empty - else JavaClassFileReader().ClassFiles(JRELibraryFolder) + 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("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) { @@ -148,30 +148,30 @@ object Immutability { ConfigValueFactory.fromAnyRef(!withoutConsiderGenericity) ) - config = - config.withValue( - "org.opalj.fpcf.analyses.L0FieldReferenceImmutabilityAnalysis.considerLazyInitialization", - ConfigValueFactory.fromAnyRef(!withoutConsiderLazyInitialization) - ) + 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 purityAnalysis = - if (reImInferComparison) - EagerL2PurityAnalysis - else - LazyL2PurityAnalysis + Project( + classFiles, + libFiles ++ JDKFiles, + libraryClassFilesAreInterfacesOnly = false, + Traversable.empty + ) + } { t ⇒ + projectTime = t.toSeconds + } val fieldReferenceDependencies: List[FPCFAnalysisScheduler] = List( EagerL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - purityAnalysis, + LazyL2PurityAnalysis, LazyInterProceduralEscapeAnalysis, LazyStaticDataUsageAnalysis, LazyL0CompileTimeConstancyAnalysis, @@ -182,7 +182,7 @@ object Immutability { val fieldDependencies: List[FPCFAnalysisScheduler] = List( LazyL0FieldReferenceImmutabilityAnalysis, LazyUnsoundPrematurelyReadFieldsAnalysis, - purityAnalysis, + LazyL2PurityAnalysis, EagerL3FieldImmutabilityAnalysis, LazyL1ClassImmutabilityAnalysis, LazyL1TypeImmutabilityAnalysis, @@ -195,7 +195,7 @@ object Immutability { val classDependencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, - purityAnalysis, + LazyL2PurityAnalysis, LazyL0FieldReferenceImmutabilityAnalysis, LazyL3FieldImmutabilityAnalysis, LazyL1TypeImmutabilityAnalysis, @@ -209,7 +209,7 @@ object Immutability { val typeDependencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, - purityAnalysis, + LazyL2PurityAnalysis, LazyL0FieldReferenceImmutabilityAnalysis, LazyL3FieldImmutabilityAnalysis, LazyL1ClassImmutabilityAnalysis, @@ -224,7 +224,7 @@ object Immutability { val allImmAnalysisDependencies: List[FPCFAnalysisScheduler] = List( LazyUnsoundPrematurelyReadFieldsAnalysis, - purityAnalysis, + LazyL2PurityAnalysis, EagerL0FieldReferenceImmutabilityAnalysis, EagerL3FieldImmutabilityAnalysis, EagerL1ClassImmutabilityAnalysis, @@ -269,7 +269,9 @@ object Immutability { } } ) - time { project.get(callGraphKey) } { t ⇒ callGraphTime = t.toSeconds } + time { project.get(callGraphKey) } { t ⇒ + callGraphTime = t.toSeconds + } var propertyStore: PropertyStore = null val analysesManager = project.get(FPCFAnalysesManagerKey) @@ -277,23 +279,21 @@ object Immutability { time { propertyStore = analysesManager.runAll(dependencies)._1 propertyStore.waitOnPhaseCompletion() - } { t ⇒ analysisTime = t.toSeconds } + } { t ⇒ + analysisTime = t.toSeconds + } val stringBuilderResults: StringBuilder = new StringBuilder() val allProjectClassTypes = project.allProjectClassFiles.toIterator.map(_.thisType).toSet - val allFieldsInProjectClassFiles = { - if (reImInferComparison) { - project.allProjectClassFiles.toIterator.flatMap { _.fields }. - filter(f ⇒ !f.isSynthetic && !f.toString().contains("/*ENUM*/")).toSet - } else - project.allProjectClassFiles.toIterator.flatMap { _.fields }.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) + 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]) @@ -306,24 +306,38 @@ object Immutability { } val mutableFieldReferences = - fieldReferenceGroupedResults.getOrElse(MutableFieldReference, Iterator.empty).map(unpackFieldEPS). - toSeq.sortWith(_ < _) + fieldReferenceGroupedResults + .getOrElse(MutableFieldReference, Iterator.empty) + .map(unpackFieldEPS) + .toSeq + .sortWith(_ < _) val notThreadSafeLazyInitializedFieldReferences = - fieldReferenceGroupedResults.getOrElse(LazyInitializedNotThreadSafeFieldReference, Iterator.empty).toSeq. - map(unpackFieldEPS).sortWith(_ < _) + fieldReferenceGroupedResults + .getOrElse(LazyInitializedNotThreadSafeFieldReference, Iterator.empty) + .toSeq + .map(unpackFieldEPS) + .sortWith(_ < _) val lazyInitializedReferencesNotThreadSafeButDeterministic = - fieldReferenceGroupedResults. - getOrElse(LazyInitializedNotThreadSafeButDeterministicFieldReference, Iterator.empty). - toSeq.map(unpackFieldEPS).sortWith(_ < _) + 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(_ < _) + 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( @@ -333,8 +347,8 @@ object Immutability { | | Lazy Initialized Not Thread Safe And Not Deterministic Field References: | ${ - notThreadSafeLazyInitializedFieldReferences. - map(_+" | Lazy Initialized Not Thread Safe And Not Deterministic Field Reference") + notThreadSafeLazyInitializedFieldReferences + .map(_+" | Lazy Initialized Not Thread Safe And Not Deterministic Field Reference") .mkString("\n") } | @@ -363,21 +377,35 @@ object Immutability { ) } - 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(_ < _) + 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( @@ -389,7 +417,11 @@ object Immutability { | ${shallowImmutableFields.map(_+" | Shallow Immutable Field ").mkString("\n")} | | Dependent Immutable Fields: - | ${dependentImmutableFields.map(_+" | Dependent Immutable Field ").mkString("\n")} + | ${ + dependentImmutableFields + .map(_+" | Dependent Immutable Field ") + .mkString("\n") + } | | Deep Immutable Fields: | ${deepImmutableFields.map(_+" | Deep Immutable Field ").mkString("\n")} @@ -398,8 +430,11 @@ object Immutability { ) } - val classGroupedResults = propertyStore.entities(ClassImmutability.key). - filter(eps ⇒ allProjectClassTypes.contains(eps.e.asInstanceOf[ObjectType])).toTraversable.groupBy(_.asFinal.p) + 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] @@ -408,15 +443,25 @@ object Immutability { } val mutableClasses = - classGroupedResults.getOrElse(MutableClass, Iterator.empty).toSeq.map(unpackClass).sortWith(_ < _) + classGroupedResults + .getOrElse(MutableClass, Iterator.empty) + .toSeq + .map(unpackClass) + .sortWith(_ < _) val shallowImmutableClasses = - classGroupedResults.getOrElse(ShallowImmutableClass, Iterator.empty).toSeq. - map(unpackClass).sortWith(_ < _) + classGroupedResults + .getOrElse(ShallowImmutableClass, Iterator.empty) + .toSeq + .map(unpackClass) + .sortWith(_ < _) val dependentImmutableClasses = - classGroupedResults.getOrElse(DependentImmutableClass, Iterator.empty).toSeq. - map(unpackClass).sortWith(_ < _) + classGroupedResults + .getOrElse(DependentImmutableClass, Iterator.empty) + .toSeq + .map(unpackClass) + .sortWith(_ < _) val deepImmutables = classGroupedResults.getOrElse(DeepImmutableClass, Iterator.empty) @@ -424,12 +469,16 @@ object Immutability { project.allProjectClassFiles.filter(_.isInterfaceDeclaration).map(_.thisType).toSet val deepImmutableClassesInterfaces = deepImmutables - .filter(eps ⇒ allInterfaces.contains(eps.e.asInstanceOf[ObjectType])).toSeq. - map(unpackClass).sortWith(_ < _) + .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(_ < _) + .filter(eps ⇒ !allInterfaces.contains(eps.e.asInstanceOf[ObjectType])) + .toSeq + .map(unpackClass) + .sortWith(_ < _) if (analysis == All || analysis == Classes) { stringBuilderResults.append( @@ -441,31 +490,54 @@ object Immutability { | ${shallowImmutableClasses.map(_+" | Shallow Immutable Class ").mkString("\n")} | | Dependent Immutable Classes: - | ${dependentImmutableClasses.map(_+" | Dependent Immutable Class ").mkString("\n")} + | ${ + 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")} + | ${ + 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(_ < _) + 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( @@ -565,10 +637,14 @@ object Immutability { | level: ${project.getProjectInformationKeyInitializationData(AIDomainFactoryKey)} | | consider escape: ${ - project.config.atKey("org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape") + project.config.atKey( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape" + ) } | consider genericity: ${ - project.config.atKey("org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity") + project.config.atKey( + "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity" + ) } | consider lazy initialization: ${ project.config.atKey( @@ -674,6 +750,7 @@ object Immutability { | [-withoutConsiderGenericity] | [-withoutConsiderEscape] | [-withoutConsiderLazyInitialization] + | [-times <0...n>] (times of execution. n is a natural number) |""".stripMargin } @@ -690,10 +767,10 @@ object Immutability { var isLibrary = false var callGraphName: Option[String] = None var level = 2 - var reImInferComparison = false var withoutConsiderLazyInitialization = false var withoutConsiderGenericity = false var withoutConsiderEscape = false + var times = 1 def readNextArg(): String = { i = i + 1 @@ -739,7 +816,7 @@ object Immutability { case "-noJDK" ⇒ withoutJDK = true case "-callGraph" ⇒ callGraphName = Some(readNextArg()) case "-level" ⇒ level = Integer.parseInt(readNextArg()) - case "-ReImInferComparison" ⇒ reImInferComparison = true + case "-times" ⇒ times = Integer.parseInt(readNextArg()) case "-withoutConsiderGenericity" ⇒ withoutConsiderGenericity = true case "-withoutConsiderEscape" ⇒ withoutConsiderEscape = true case "-withoutConsiderLazyInitialization" ⇒ withoutConsiderLazyInitialization = true @@ -766,9 +843,8 @@ object Immutability { Console.println(usage) return ; } - var nIndex = 0 - val end = 0 - while (nIndex <= end) { + var nIndex = 1 + while (nIndex <= times) { println(s"start $nIndex") nIndex = nIndex + 1 evaluate( @@ -783,13 +859,10 @@ object Immutability { closedWorldAssumption, callGraphKey, level, - reImInferComparison, withoutConsiderEscape, withoutConsiderGenericity, withoutConsiderLazyInitialization ) } - } } - From 7f1866a64e2fb79de6133c7a499417124fb7f7a7 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Thu, 11 Feb 2021 13:34:27 +0100 Subject: [PATCH 310/327] removed escape setting possibility --- .../org/opalj/support/info/Immutability.scala | 15 --------------- 1 file changed, 15 deletions(-) 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 index 302aab4b13..1f8ccab2eb 100644 --- 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 @@ -104,7 +104,6 @@ object Immutability { closedWorldAssumption: Boolean, callGraphKey: AbstractCallGraphKey, level: Int, - withoutConsiderEscape: Boolean, withoutConsiderGenericity: Boolean, withoutConsiderLazyInitialization: Boolean ): BasicReport = { @@ -138,11 +137,6 @@ object Immutability { ) } - config = config.withValue( - "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape", - ConfigValueFactory.fromAnyRef(!withoutConsiderEscape) - ) - config = config.withValue( "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity", ConfigValueFactory.fromAnyRef(!withoutConsiderGenericity) @@ -659,11 +653,6 @@ object Immutability { val fileNameExtension = { { - if (withoutConsiderEscape) { - println("withoutConsiderEscape") - "_withoutConsiderEscape" - } else "" - } + { if (withoutConsiderGenericity) { println("withoutConsiderGenericity") "_withoutConsiderGenericity" @@ -748,7 +737,6 @@ object Immutability { | [-level] <0|1|2> (domain level Default: 2) | [-ReImInferComparison] (without transient fields and with eager L2 purity analysis) | [-withoutConsiderGenericity] - | [-withoutConsiderEscape] | [-withoutConsiderLazyInitialization] | [-times <0...n>] (times of execution. n is a natural number) |""".stripMargin @@ -769,7 +757,6 @@ object Immutability { var level = 2 var withoutConsiderLazyInitialization = false var withoutConsiderGenericity = false - var withoutConsiderEscape = false var times = 1 def readNextArg(): String = { @@ -818,7 +805,6 @@ object Immutability { case "-level" ⇒ level = Integer.parseInt(readNextArg()) case "-times" ⇒ times = Integer.parseInt(readNextArg()) case "-withoutConsiderGenericity" ⇒ withoutConsiderGenericity = true - case "-withoutConsiderEscape" ⇒ withoutConsiderEscape = true case "-withoutConsiderLazyInitialization" ⇒ withoutConsiderLazyInitialization = true case "-JDK" ⇒ @@ -859,7 +845,6 @@ object Immutability { closedWorldAssumption, callGraphKey, level, - withoutConsiderEscape, withoutConsiderGenericity, withoutConsiderLazyInitialization ) From f1ca91802456197e764d796b078ad438627a2f1b Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 15 Feb 2021 09:41:13 +0100 Subject: [PATCH 311/327] removed escape considering due to unsoundness --- .../L3FieldImmutabilityAnalysis.scala | 61 +++++++++++-------- 1 file changed, 35 insertions(+), 26 deletions(-) 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 index b19f0196f1..bd12f1801a 100755 --- 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 @@ -5,8 +5,11 @@ 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.ClassSignature */ import org.opalj.br.ClassTypeSignature import org.opalj.br.Field import org.opalj.br.ObjectType @@ -71,8 +74,6 @@ 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.ClassFile -import org.opalj.br.FormalTypeParameter import org.opalj.br.fpcf.properties.ClassImmutability import scala.collection.mutable import org.opalj.fpcf.PropertyBounds @@ -136,6 +137,15 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) final val definitionSites = project.get(DefinitionSitesKey) implicit final val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + var considerEscape = false /*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 _ ⇒ @@ -146,18 +156,11 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) field: Field ): ProperPropertyComputationResult = { - val considerEscape = project.config.getBoolean( - "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape" - ) - - val considerGenericity = - project.config.getBoolean( - "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerGenericity" - ) - + //TODO determine bounds /** * Returns the formal type parameters from the class' and outer classes' signature */ + /* def getFormalTypeParameters: Set[String] = { /** @@ -190,16 +193,16 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } } getAllFormalParameters(field.classFile).toSet - } + } */ - val classFormalTypeParameters: Set[String] = getFormalTypeParameters + //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) + // 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. @@ -243,8 +246,8 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) noRelevantAttributesFound = false onlyDeepImmutableTypesInGenericTypeFound = false state.fieldTypeIsDependentImmutable = true //generic type T is dependent immutable - if (!isInClassesGenericTypeParameters(t)) - noShallowOrMutableTypesInGenericTypeFound = false + // if (!isInClassesGenericTypeParameters(t)) + // noShallowOrMutableTypesInGenericTypeFound = false case ClassTypeSignature(_, SimpleClassTypeSignature(_, typeArguments), _) ⇒ noRelevantAttributesFound = false @@ -253,8 +256,8 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case ProperTypeArgument(_, TypeVariableSignature(identifier)) ⇒ onlyDeepImmutableTypesInGenericTypeFound = false - if (!isInClassesGenericTypeParameters(identifier)) - noShallowOrMutableTypesInGenericTypeFound = false + // if (!isInClassesGenericTypeParameters(identifier)) + // noShallowOrMutableTypesInGenericTypeFound = false case ProperTypeArgument( _, @@ -367,7 +370,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) */ def determineEscapeOfReferencedObjectOrValue()(implicit state: State): Unit = { state.escapesStillDetermined = true - + //val tmp = state.noEscapePossibilityViaReference if (state.noEscapePossibilityViaReference) { val writes = fieldAccessInformation.writeAccesses(state.field) @@ -380,7 +383,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) 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 @@ -438,7 +441,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } else if (innerArrayType.isObjectType) { //If a deep immutable object escapes, it can not be mutated val result = propertyStore(innerArrayType, TypeImmutability.key) - result match { + result match { //TODO unnecessary case LBP(DeepImmutableType) ⇒ false //nothing to do case UBP( DependentImmutableType | ShallowImmutableType | MutableType @@ -726,7 +729,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.concreteClassTypeIsKnown = true handleKnownClassType(newStmt.tpe.mostPreciseObjectType) } - if (!method.isConstructor) { + if (!method.isConstructor && !method.isStaticInitializer) { definitionSiteAssignment.targetVar.asVar.usedBy.exists { x ⇒ val tmpStmt = taCode.stmts(x) if (tmpStmt.isPutStatic || tmpStmt.isPutField) { @@ -741,7 +744,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } else false } else true } - } else { + } else { //TODO check definitionSiteAssignment.targetVar.usedBy.exists { useSite ⇒ val useSiteStmt = taCode.stmts(useSite) if (useSiteStmt.isNonVirtualMethodCall) { @@ -776,13 +779,16 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } 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) @@ -974,6 +980,9 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.noEscapePossibilityViaReference = 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 From 8b2339c7d4016d7ffd55050f5c421d90fff2447e Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 16 Feb 2021 09:57:54 +0100 Subject: [PATCH 312/327] adjust field reference imm analysis for evaluation. Removed: LazyInitializedNotThreadSafeButDeterministicFieldReference. And Handling open/closed world --- ...ctFieldReferenceImmutabilityAnalysis.scala | 20 ++++---- ...mutabilityAnalysisLazyInitialization.scala | 30 +++++------ ...L0FieldReferenceImmutabilityAnalysis.scala | 51 ++++++++++++------- 3 files changed, 59 insertions(+), 42 deletions(-) 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 index 92a6709b68..9e5ea7f95b 100644 --- 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 @@ -40,6 +40,7 @@ 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 /** * @@ -61,15 +62,16 @@ trait AbstractFieldReferenceImmutabilityAnalysis extends FPCFAnalysis { 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 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 || 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 index 41ff0f68b4..4b38eba1b8 100644 --- 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 @@ -11,13 +11,13 @@ 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.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.LazyInitializedNotThreadSafeButDeterministicFieldReference import org.opalj.br.fpcf.properties.LazyInitializedNotThreadSafeFieldReference import org.opalj.br.fpcf.properties.LazyInitializedThreadSafeFieldReference import org.opalj.br.fpcf.properties.MutableFieldReference @@ -26,7 +26,7 @@ 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.fpcf.properties.Purity import org.opalj.br.FieldType /** @@ -189,21 +189,21 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization else MutableFieldReference } else { - if (write.value.asVar.definedBy.forall { defSite ⇒ - defSite >= 0 && checkWriteIsDeterministic( + 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 + //val computationalFieldType = state.field.fieldType.computationalType + //if (computationalFieldType != ComputationalTypeInt && + // computationalFieldType != ComputationalTypeFloat) { + LazyInitializedNotThreadSafeFieldReference + //} /*else + //LazyInitializedNotThreadSafeButDeterministicFieldReference } else MutableFieldReference } } @@ -495,7 +495,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization * 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( + /* def checkWriteIsDeterministic( origin: Assignment[V], method: Method, code: Array[Stmt[V]], @@ -585,7 +585,7 @@ trait AbstractFieldReferenceImmutabilityAnalysisLazyInitialization } } value.isConst || isNonConstDeterministic(value, taCode) - } + } */ /** * Checks if an expression is a field read of the currently analyzed field. 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 index e6eed64531..5e904ec8a6 100644 --- 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 @@ -117,8 +117,8 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP if (field.isFinal) return Result(field, ImmutableFieldReference); - if (field.isPublic) - return Result(field, MutableFieldReference); + //if (field.isPublic) + // return Result(field, MutableFieldReference); implicit val state: State = State(field) @@ -126,7 +126,7 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP // Fields are not final if they are read prematurely! if (isPrematurelyRead(propertyStore(field, FieldPrematurelyRead.key))) - return Result(field, MutableFieldReference) + 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 @@ -138,7 +138,12 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP Set(field.classFile) } val classesHavingAccess: Iterator[ClassFile] = - if (field.isProtected) { + 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); } @@ -150,18 +155,19 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP } else { initialClasses.iterator } + // If there are native methods, we give up - if (classesHavingAccess.exists(_.methods.exists(_.isNative))) { + if (classesHavingAccess.exists(_.methods.exists(_.isNative))) { //TODO flag for comparison...reim if (!field.isFinal) - return Result(field, MutableFieldReference) + return Result(field, MutableFieldReference); } + for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) - taCode ← getTACAI(method, pcs) + taCode ← getTACAI(method, pcs) //TODO field accesses via this } { - if (methodUpdatesField(method, taCode, pcs)) { + if (methodUpdatesField(method, taCode, pcs)) return Result(field, MutableFieldReference); - } } if (state.lazyInitInvocation.isDefined) { val calleesEOP = propertyStore(state.lazyInitInvocation.get._1, Callees.key) @@ -182,9 +188,9 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP 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 IntegerType | ObjectType.Integer | ByteType | ObjectType.Byte | ShortType | + ObjectType.Short ⇒ + 0 case _: ReferenceType ⇒ null } } @@ -308,7 +314,9 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP } 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) @@ -362,7 +370,10 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP // 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)) + else + handleEscapeProperty( + propertyStore(definitionSites(method, definition.pc), EscapeProperty.key) + ) } } } @@ -379,14 +390,16 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP 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 _: 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 InterimUBP(AtMost(_)) ⇒ true + case _: InterimEP[DefinitionSite, EscapeProperty] ⇒ + true // Escape state is worse than via return case _ ⇒ state.escapeDependees += ep @@ -416,7 +429,8 @@ trait L0FieldReferenceImmutabilityAnalysisScheduler extends FPCFAnalysisSchedule /** * Executor for the eager field reference immutability analysis. */ -object EagerL0FieldReferenceImmutabilityAnalysis extends L0FieldReferenceImmutabilityAnalysisScheduler +object EagerL0FieldReferenceImmutabilityAnalysis + extends L0FieldReferenceImmutabilityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -434,7 +448,8 @@ object EagerL0FieldReferenceImmutabilityAnalysis extends L0FieldReferenceImmutab /** * Executor for the lazy field reference immutability analysis. */ -object LazyL0FieldReferenceImmutabilityAnalysis extends L0FieldReferenceImmutabilityAnalysisScheduler +object LazyL0FieldReferenceImmutabilityAnalysis + extends L0FieldReferenceImmutabilityAnalysisScheduler with BasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) From 14ff4358e9d0bc5c151aad15150f60302bda9964 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 16 Feb 2021 10:54:10 +0100 Subject: [PATCH 313/327] ommitted analyzing native methods --- .../L0FieldReferenceImmutabilityAnalysis.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 index 5e904ec8a6..612440038f 100644 --- 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 @@ -157,10 +157,10 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP } // 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); - } + //if (classesHavingAccess.exists(_.methods.exists(_.isNative))) { //TODO flag for comparison...reim + // if (!field.isFinal) + // return Result(field, MutableFieldReference); + // } for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) From 492e24abb46251f4d6a20d25fdf2c5eff97c4736 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 16 Feb 2021 11:05:24 +0100 Subject: [PATCH 314/327] outcommented unused values --- .../L0FieldReferenceImmutabilityAnalysis.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 index 612440038f..012da23280 100644 --- 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 @@ -137,24 +137,24 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP } else { Set(field.classFile) } - val classesHavingAccess: Iterator[ClassFile] = + //val classesHavingAccess: Iterator[ClassFile] = if (field.isPublic) { if (typeExtensibility(ObjectType.Object).isYesOrUnknown) { return Result(field, MutableFieldReference); } - project.allClassFiles.iterator + // project.allClassFiles.iterator } else if (field.isProtected) { if (typeExtensibility(thisType).isYesOrUnknown) { return Result(field, MutableFieldReference); } - val subclassesIterator: Iterator[ClassFile] = + /*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 - } + 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 From 7d30ca6e5c4b53937003a0a94109905e66bd51f7 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Tue, 16 Feb 2021 11:43:48 +0100 Subject: [PATCH 315/327] outcomment unused values --- ...L0FieldReferenceImmutabilityAnalysis.scala | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) 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 index 012da23280..ccde884c52 100644 --- 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 @@ -9,7 +9,7 @@ package fieldreference import scala.annotation.switch import org.opalj.br.BooleanType -import org.opalj.br.ClassFile +//import org.opalj.br.ClassFile import org.opalj.br.DeclaredMethod import org.opalj.br.Field import org.opalj.br.Method @@ -131,36 +131,36 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP // 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 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] = + 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 - //} + } //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) From 457f3ce29a361c58214667bf99bd769f1884510b Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 8 Mar 2021 18:53:34 +0100 Subject: [PATCH 316/327] first sketch of immutability benchmark; glacier annotation is commented out --- .../benchmark/arrays/ArrayClasses.java | 137 +++++ .../arrays/ArrayDeepImmutability.java | 76 +++ .../benchmark/arrays/ArrayInitialization.java | 45 ++ .../arrays/ArrayWithOneEscapingObject.java | 50 ++ ...rivateFinalArrayEscapesViaConstructor.java | 41 ++ .../assignability/CloneAssignable.java | 29 + .../assignability/CloneNonAssignable.java | 24 + ...litiesOfEffectivelyNonAssignableField.java | 31 ++ ...mentPossibilitiesOfNonAssignableField.java | 32 ++ .../assignability/NonAssignability.java | 48 ++ .../benchmark/assignability/Singleton.java | 46 ++ .../TriviallyEffectivelyNonAssignable.java | 36 ++ .../ConcreteAssignedInstanceTypeUnknown.java | 20 + .../ConcreteObjectInstanceAssigned.java | 25 + .../generals/ClassWithMutableField.java | 11 + .../ClassWithNotDeepImmutableFields.java | 38 ++ .../generals/ClassWithProtectedFields.java | 31 ++ .../generals/DeepImmutableFields.java | 40 ++ .../benchmark/generals/DifferentModifier.java | 106 ++++ .../benchmark/generals/EmptyClass.java | 10 + .../benchmark/generals/FinalEmptyClass.java | 12 + .../benchmark/generals/Interface.java | 16 + .../benchmark/generals/Mutability.java | 39 ++ .../benchmark/generals/StaticFields.java | 46 ++ .../fixtures/benchmark/generic/Generic.java | 17 + .../generic/GenericCounterExampleDeep.java | 11 + .../generic/GenericCounterExampleMutable.java | 9 + .../benchmark/generic/GenericFields.java | 54 ++ .../benchmark/generic/LowerUpperBounds.java | 19 + .../DoubleCheckedLocking.java | 511 ++++++++++++++++++ ...leCheckedLockingClassWithStaticFields.java | 21 + .../EffectivelyImmutableFields.java | 459 ++++++++++++++++ .../LazyInitialization.java | 345 ++++++++++++ .../lazy_initialization/MethodCalls.java | 94 ++++ .../SimpleLazyInitialization.java | 33 ++ .../SimpleStringModel.java | 38 ++ .../lazy_initialization/Template.java | 34 ++ 37 files changed, 2634 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayClasses.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayDeepImmutability.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayInitialization.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/ArrayWithOneEscapingObject.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/arrays/PrivateFinalArrayEscapesViaConstructor.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/CloneAssignable.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/CloneNonAssignable.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/DifferentAssignmentPossibilitiesOfEffectivelyNonAssignableField.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/DifferentAssignmentPossibilitiesOfNonAssignableField.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/NonAssignability.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/Singleton.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/TriviallyEffectivelyNonAssignable.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/concrete_class_type_is_known/ConcreteAssignedInstanceTypeUnknown.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/concrete_class_type_is_known/ConcreteObjectInstanceAssigned.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/ClassWithMutableField.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/ClassWithNotDeepImmutableFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/ClassWithProtectedFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/DeepImmutableFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/DifferentModifier.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/EmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/FinalEmptyClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/Interface.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/Mutability.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/StaticFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/Generic.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericCounterExampleDeep.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericCounterExampleMutable.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/LowerUpperBounds.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLocking.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/EffectivelyImmutableFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/LazyInitialization.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/MethodCalls.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/SimpleLazyInitialization.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/SimpleStringModel.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/Template.java 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..d3e4b2751b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/assignability/TriviallyEffectivelyNonAssignable.java @@ -0,0 +1,36 @@ +/* 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 TriviallyEffectivelyNonAssignable { + + + @DeepImmutableField("") + @ImmutableFieldReference("") + private int n = 5; + + @DeepImmutableField("") + @ImmutableFieldReference("") + private Object escapingViaConstructor = new Object(); + + @DeepImmutableField("") + @ImmutableFieldReference("") + private Object escapingViaGetter; + + + 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..6cb5031a31 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/DifferentModifier.java @@ -0,0 +1,106 @@ +/* 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.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; + +/** + * This testclass tests that different modifiers like transient, volatile or static + * does not have an impact of mutability. + * + * @author Tobias Roth + * + */ +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..94bcab5b4f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/EmptyClass.java @@ -0,0 +1,10 @@ +/* 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; + +//@Immutable +@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..85a6df3996 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/Interface.java @@ -0,0 +1,16 @@ +/* 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; + +//@Immutable +@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..f3427858c7 --- /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; + +@MutableType("") +@DeepImmutableClass("") +public class StaticFields { + + @MutableFieldReference("") + public static String name = "Class with static fields"; + + @ImmutableFieldReference("") + private static Object[] shallowArray = new Object[5]; + + //@Immutable + @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..ae54804444 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/Generic.java @@ -0,0 +1,17 @@ +/* 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; + +//@Immutable +@DependentImmutableClass("") +public final 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..b0cd4075ca --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericFields.java @@ -0,0 +1,54 @@ +/* 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.fixtures.benchmark.generals.ClassWithMutableField; +import org.opalj.fpcf.fixtures.benchmark.generals.FinalEmptyClass; +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.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.types.MutableType; + +@MutableType("") +@ShallowImmutableClass("") +class GenericFields { + + //@Immutable + @DeepImmutableField("") + private Generic deepImmutable = new Generic<>(new FinalEmptyClass()); + + @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())); + + //@Immutable + @DeepImmutableField("") + private Generic> nestedDeep = + new Generic<>(new Generic<>(new FinalEmptyClass())); + + //@Immutable + @DeepImmutableField("") + Generic fecG = + new Generic<>(new org.opalj.fpcf.fixtures.immutability.fields.FinalEmptyClass()); + + 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/LowerUpperBounds.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/LowerUpperBounds.java new file mode 100644 index 0000000000..cd1d15c339 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/LowerUpperBounds.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.fixtures.benchmark.generals.ClassWithMutableField; +import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; +import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; + +//@Immutable +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/DoubleCheckedLockingClassWithStaticFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java new file mode 100644 index 0000000000..30a7e58bda --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java @@ -0,0 +1,21 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; + +import org.opalj.fpcf.properties.immutability.references.LazyInitializedThreadSafeFieldReference; + +class DoubleCheckedLockingClassWithStaticFields { + + @LazyInitializedThreadSafeFieldReference("standard dcl pattern within a static method") + private static DoubleCheckedLockingClassWithStaticFields instance; + + public static DoubleCheckedLockingClassWithStaticFields getInstance() { + if (instance == null) { + synchronized (DoubleCheckedLockingClassWithStaticFields.class) { + if (instance == null) { + instance = new DoubleCheckedLockingClassWithStaticFields(); + } + } + } + return instance; + } +} 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..270cd329ce --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/EffectivelyImmutableFields.java @@ -0,0 +1,459 @@ +/* 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.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 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; + + + @MutableField(value = "due to mutable field reference") + @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(value = "field value has a primitive type and an immutable field reference") + @ImmutableFieldReference(value = "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(value = "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(value = "field value has a primitive type and an immutable field reference") + @ImmutableFieldReference(value = "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(value = "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(value = "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; + } + + + + @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/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..486a73df75 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/LazyInitialization.java @@ -0,0 +1,345 @@ +/* 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(value="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", + 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/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..6203f9e5d7 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/SimpleLazyInitialization.java @@ -0,0 +1,33 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; + +import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; + +/** + * This class represents lazy initialization in the simplest way + * with one guard and no synchronization + * + * @author Tobias Roth + * + */ +public class SimpleLazyInitialization { + + @LazyInitializedNotThreadSafeFieldReference("the write to the object reference simpleLazyInitialization is not atomic") + private static SimpleLazyInitialization simpleLazyInitialization; + + public static SimpleLazyInitialization init() { + if(simpleLazyInitialization ==null) + simpleLazyInitialization = new SimpleLazyInitialization(); + return simpleLazyInitialization; + } + + @LazyInitializedNotThreadSafeFieldReference("deterministic write due to guarded primitive type") + 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/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..03f5840863 --- /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(value = "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; + } + +} From 1f72ccefc94c922f4e7044aec24d3db00c47d24a Mon Sep 17 00:00:00 2001 From: tobias roth Date: Mon, 8 Mar 2021 18:54:58 +0100 Subject: [PATCH 317/327] first sketch of concrete "class type is known" recognition. Still wip; not yet optimized --- .../L3FieldImmutabilityAnalysis.scala | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 index bd12f1801a..1c5563fa69 100755 --- 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 @@ -54,7 +54,6 @@ 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.value.ASArrayValue import org.opalj.br.fpcf.properties.DeepImmutableClass import org.opalj.br.fpcf.properties.DependentImmutableClass import org.opalj.br.fpcf.properties.MutableClass @@ -137,7 +136,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) final val definitionSites = project.get(DefinitionSitesKey) implicit final val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) - var considerEscape = false /*project.config.getBoolean( //TODO due to unsoundness + var considerEscape = true /*project.config.getBoolean( //TODO due to unsoundness "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape" )*/ @@ -384,20 +383,21 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) determineEscapeViaFieldWritesWithKnownTAC(method, pcs, taCodeOption.get) } //TODO state.noEscapePossibilityViaReference = true - if (state.noEscapePossibilityViaReference) { + /*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, @@ -471,7 +471,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) }) { state.noEscapePossibilityViaReference = false } - } + } */ //// /** * Determine if the referenced object can escape via field writes @@ -804,8 +804,8 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) case Some(true) | None ⇒ if (state.tacDependees.isEmpty && !state.concreteClassTypeIsKnown) { - if (state.typeImmutability == DeepImmutable || - state.noEscapePossibilityViaReference) { + if (state.typeImmutability == DeepImmutable /*|| + state.noEscapePossibilityViaReference*/ ) { DeepImmutableField } else if (state.typeImmutability == Mutable && !state.fieldTypeIsDependentImmutable) { @@ -844,9 +844,9 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) state.concreteClassTypeIsKnown && state.classTypeImmutability == DeepImmutable) { Result(field, DeepImmutableField) } else { - if (state.noEscapePossibilityViaReference) { + /*if (state.noEscapePossibilityViaReference) { Result(field, DeepImmutableField) - } else { + } else */ { if (state.fieldTypeIsDependentImmutable && field.fieldType == ObjectType.Object || state.classTypeImmutability == DependentImmutable || state.typeImmutability == DependentImmutable) { @@ -920,7 +920,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) 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) + //determineEscapeViaFieldReadsWithKnownTAC(method, pcs._1, tac)(state) } else { state.tacDependees += method -> ((newEP, pcs)) } From 165e4a37090354dcdec7901e54a4290b56f15fdd Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 12 Mar 2021 16:02:31 +0100 Subject: [PATCH 318/327] current status of immutability benchmark --- .../TriviallyEffectivelyNonAssignable.java | 17 +- .../benchmark/generals/DifferentModifier.java | 5 +- .../benchmark/generals/EmptyClass.java | 2 + .../benchmark/generals/Interface.java | 2 + .../benchmark/generals/StaticFields.java | 2 +- .../fixtures/benchmark/generic/Generic.java | 4 +- .../benchmark/generic/GenericFields.java | 21 -- .../benchmark/generic/GenericFieldsDeep.java | 25 +++ .../benchmark/generic/LowerUpperBounds.java | 2 - ...ckedLockingClassWithStaticFieldsDeep.java} | 16 +- .../DoubleCheckedLockingDeep.java | 186 ++++++++++++++++++ .../EffectivelyImmutableFields.java | 46 ++--- .../LazyInitialization.java | 11 +- .../SimpleLazyInitialization.java | 29 +-- .../SimpleStringModel.java | 2 +- 15 files changed, 279 insertions(+), 91 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generic/GenericFieldsDeep.java rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/{DoubleCheckedLockingClassWithStaticFields.java => DoubleCheckedLockingClassWithStaticFieldsDeep.java} (50%) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingDeep.java 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 index d3e4b2751b..f90c8462a8 100644 --- 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 @@ -2,28 +2,29 @@ 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.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.fpcf.properties.immutability.types.ShallowImmutableType; //@Immutable -@MutableType("") -@DeepImmutableClass("") -public class TriviallyEffectivelyNonAssignable { +@ShallowImmutableType("") +@ShallowImmutableClass("") +public final class TriviallyEffectivelyNonAssignable { @DeepImmutableField("") @ImmutableFieldReference("") private int n = 5; - @DeepImmutableField("") + @ShallowImmutableField("") @ImmutableFieldReference("") - private Object escapingViaConstructor = new Object(); + private Object escapingViaConstructor; @DeepImmutableField("") @ImmutableFieldReference("") - private Object escapingViaGetter; + private Object escapingViaGetter = new Object(); public TriviallyEffectivelyNonAssignable(Object o) { 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 index 6cb5031a31..f81af27f2f 100644 --- 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 @@ -1,11 +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.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 @@ -14,6 +15,8 @@ * @author Tobias Roth * */ +@MutableType("") +@MutableClass("") public class DifferentModifier { @MutableField(value = "field has a mutable field reference") 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 index 94bcab5b4f..a2b23ffc49 100644 --- 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 @@ -3,8 +3,10 @@ //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/Interface.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/generals/Interface.java index 85a6df3996..9271d8bc80 100644 --- 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 @@ -3,8 +3,10 @@ //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 { 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 index f3427858c7..aec01ce5dc 100644 --- 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 @@ -8,6 +8,7 @@ import org.opalj.fpcf.properties.immutability.references.MutableFieldReference; import org.opalj.fpcf.properties.immutability.types.MutableType; +//@Immutable @MutableType("") @DeepImmutableClass("") public class StaticFields { @@ -18,7 +19,6 @@ public class StaticFields { @ImmutableFieldReference("") private static Object[] shallowArray = new Object[5]; - //@Immutable @DeepImmutableField("") @ImmutableFieldReference("") private static String deepImmutableString = "string"; 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 index ae54804444..90b3640884 100644 --- 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 @@ -5,10 +5,12 @@ 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 final class Generic { +public class Generic { @DependentImmutableField("") @ImmutableFieldReference("") 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 index b0cd4075ca..3df66280c8 100644 --- 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 @@ -1,11 +1,8 @@ /* 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.fixtures.benchmark.generals.ClassWithMutableField; -import org.opalj.fpcf.fixtures.benchmark.generals.FinalEmptyClass; 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.ShallowImmutableField; import org.opalj.fpcf.properties.immutability.types.MutableType; @@ -14,17 +11,12 @@ @ShallowImmutableClass("") class GenericFields { - //@Immutable - @DeepImmutableField("") - private Generic deepImmutable = new Generic<>(new FinalEmptyClass()); - @DependentImmutableField("") private Generic generic; @ShallowImmutableField("") private Generic mutable = new Generic(new ClassWithMutableField()); - @DependentImmutableField("") private Generic> nestedDependent; @@ -32,16 +24,6 @@ class GenericFields { private Generic> nestedShallow = new Generic<>(new Generic<>(new ClassWithMutableField())); - //@Immutable - @DeepImmutableField("") - private Generic> nestedDeep = - new Generic<>(new Generic<>(new FinalEmptyClass())); - - //@Immutable - @DeepImmutableField("") - Generic fecG = - new Generic<>(new org.opalj.fpcf.fixtures.immutability.fields.FinalEmptyClass()); - public GenericFields(T t){ this.generic = new Generic<>(t); this.nestedDependent = new Generic<>(new Generic<>(t)); @@ -49,6 +31,3 @@ public GenericFields(T 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 index cd1d15c339..2a958fe2c8 100644 --- 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 @@ -1,12 +1,10 @@ /* 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.fixtures.benchmark.generals.ClassWithMutableField; import org.opalj.fpcf.properties.immutability.fields.ShallowImmutableField; import org.opalj.fpcf.properties.immutability.references.ImmutableFieldReference; -//@Immutable class LowerUpperBounds { @ShallowImmutableField("") diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingClassWithStaticFieldsDeep.java similarity index 50% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingClassWithStaticFieldsDeep.java index 30a7e58bda..f001a6c5d9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingClassWithStaticFields.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/benchmark/lazy_initialization/DoubleCheckedLockingClassWithStaticFieldsDeep.java @@ -1,18 +1,24 @@ /* 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; -class DoubleCheckedLockingClassWithStaticFields { +//@Immutable +@MutableType("") +@DeepImmutableClass("") +public class DoubleCheckedLockingClassWithStaticFieldsDeep { @LazyInitializedThreadSafeFieldReference("standard dcl pattern within a static method") - private static DoubleCheckedLockingClassWithStaticFields instance; + private static Object instance; - public static DoubleCheckedLockingClassWithStaticFields getInstance() { + public static Object getInstance() { if (instance == null) { - synchronized (DoubleCheckedLockingClassWithStaticFields.class) { + synchronized (Object.class) { if (instance == null) { - instance = new DoubleCheckedLockingClassWithStaticFields(); + instance = new Object(); } } } 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 index 270cd329ce..fdd4330435 100644 --- 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 @@ -2,7 +2,6 @@ package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; 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; @@ -39,8 +38,6 @@ public synchronized int getInTheGetterLazyInitializedFieldWithPrimitiveType(){ @ImmutableFieldReference(value = "effective immutable field") private Integer effectiveImmutableIntegerField = 5; - - @MutableField(value = "due to mutable field reference") @LazyInitializedNotThreadSafeFieldReference(value = "write of reference objects is not atomic") private Integer simpleLazyInitializedIntegerField; @@ -68,8 +65,8 @@ public synchronized Integer getInGetterSynchronizedSimpleLazyInitializedIntegerF return inGetterSynchronizedSimpleLazyInitializedIntegerField; } - @DeepImmutableField(value = "field value has a primitive type and an immutable field reference") - @ImmutableFieldReference(value = "field is effective immutable") + @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") @@ -114,7 +111,7 @@ public synchronized Double getInAGetterLazyInitializedObjectDoubleField(){ return inAGetterLazyInitializedObjectDoubleField; } - @DeepImmutableField(value = "field value has a primitive type and an immutable field reference") + @DeepImmutableField("field value has a primitive type and an immutable field reference") @ImmutableFieldReference(value = "field is not written after initialization") private float effectiveImmutableFloatField = 5; @@ -160,8 +157,8 @@ public synchronized Float getInAGetterLazyInitializedFloatObjectField(){ return inAGetterLazyInitializedFloatObjectField; } - @DeepImmutableField(value = "field value has a primitive type and an immutable field reference") - @ImmutableFieldReference(value = "field is effective immutable") + @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") @@ -183,7 +180,7 @@ public synchronized byte getInGetterSynchronizedLazyInitializedByteField(){ return inGetterSynchronizedLazyInitializedByteField; } - @DeepImmutableField(value = "field value has a primitive type and an immutable field reference") + @DeepImmutableField("field value has a primitive type and an immutable field reference") @ImmutableFieldReference("field is effective immutable") private Byte effectiveImmutableByteObjectField = 5; @@ -229,7 +226,7 @@ public synchronized char c3(){ return inGetterSynchronizedLazyInitializedCharField; } - @DeepImmutableField(value = "field value has a primitive type and an immutable field reference") + @DeepImmutableField("field value has a primitive type and an immutable field reference") @ImmutableFieldReference(value = "field is not written after initialization") private long effectiveImmutableLongField = 5; @@ -323,11 +320,11 @@ public synchronized Object getInAGetterLazyInitializedObjectReference(){ - @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @ShallowImmutableField("") @ImmutableFieldReference("effective immutable reference") private List effectiveImmutableLinkedList = new LinkedList(); - @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @ShallowImmutableField("") @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") private List lazyInitializedLinkedList; @@ -346,22 +343,21 @@ public synchronized void initLinkedList3(){ lazyInitializedLinkedListWithManipulationAfterwards.add(new Object()); } - @DeepImmutableField("The concrete type of the object that is assigned is known " + - "and no manipulation after assignment") + @DeepImmutableField("") @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") - private Object inTheGetterLazyInitializedlinkedList; + private Object inTheGetterLazyInitializedObject; - public synchronized Object getInTheGetterLazyInitializedlinkedList(){ - if(inTheGetterLazyInitializedlinkedList == null) - inTheGetterLazyInitializedlinkedList = new Object(); - return inTheGetterLazyInitializedlinkedList; + public synchronized Object getInTheGetterLazyInitializedObject(){ + if(inTheGetterLazyInitializedObject == null) + inTheGetterLazyInitializedObject = new Object(); + return inTheGetterLazyInitializedObject; } - @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @ShallowImmutableField("") @ImmutableFieldReference("effective immutable reference") private List effectiveImmutableArrayList = new ArrayList(); - @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @ShallowImmutableField("") @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") private List lazyInitializedArrayList; @@ -390,11 +386,11 @@ public synchronized List getInTheGetterLazyInitializedArrayList(){ return inTheGetterLazyInitializedArrayList; } - @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @ShallowImmutableField("") @ImmutableFieldReference("effective immutable reference") private Set effectiveImmutableSet = new HashSet(); - @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @ShallowImmutableField("") @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") private Set lazyInitializedSet; @@ -423,11 +419,11 @@ public synchronized Set getInTheGetterLazyInitializedSet(){ return inTheGetterLazyInitializedSet; } - @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @ShallowImmutableField("") @ImmutableFieldReference("effective immutable reference") private HashMap effectiveImmutableHashMap = new HashMap(); - @DeepImmutableField("concrete object that is assigned is known and no other manipulation") + @ShallowImmutableField("") @LazyInitializedThreadSafeFieldReference("synchronized lazy initialization") private HashMap lazyInitializedHashMap; 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 index 486a73df75..4cca12fc13 100644 --- 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 @@ -177,7 +177,7 @@ public DeterministicCallOnFinalField(int v) { private final class Inner { - @DeepImmutableField(value="immutable reference with base type") + @DeepImmutableField("immutable reference with base type") final int val; public Inner(int v) { @@ -246,6 +246,7 @@ public int init() { } class DoubleLocalAssignment { + @LazyInitializedNotThreadSafeFieldReference(value = "Lazy initialization with a local that is updated twice") private int x; @@ -322,12 +323,10 @@ public int init() { 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) + @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) { 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 index 6203f9e5d7..f5e2aea1b0 100644 --- 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 @@ -1,26 +1,8 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.fixtures.benchmark.lazy_initialization; import org.opalj.fpcf.properties.immutability.references.LazyInitializedNotThreadSafeFieldReference; -/** - * This class represents lazy initialization in the simplest way - * with one guard and no synchronization - * - * @author Tobias Roth - * - */ -public class SimpleLazyInitialization { - - @LazyInitializedNotThreadSafeFieldReference("the write to the object reference simpleLazyInitialization is not atomic") - private static SimpleLazyInitialization simpleLazyInitialization; - - public static SimpleLazyInitialization init() { - if(simpleLazyInitialization ==null) - simpleLazyInitialization = new SimpleLazyInitialization(); - return simpleLazyInitialization; - } - +class SimpleLazyInitialization { @LazyInitializedNotThreadSafeFieldReference("deterministic write due to guarded primitive type") private int i = 0; public int hashcode() { @@ -28,6 +10,13 @@ public int hashcode() { 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 index 03f5840863..304947a657 100644 --- 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 @@ -9,7 +9,7 @@ */ public class SimpleStringModel { - @ImmutableFieldReference(value = "final field") + @ImmutableFieldReference("final field") private final char value[]; public char[] getValue(){ From fef638b0450258dc501786326420a02740e63882 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 12 Mar 2021 16:04:32 +0100 Subject: [PATCH 319/327] current status of immutability tests --- .../DependentClassWithGenericField.java | 14 ++-- .../GenericEscapesTransitive.java | 8 +- ...java => TrivialShallowImmutableClass.java} | 8 +- .../field_references/LazyInitialization.java | 76 +++++++++---------- .../field_references/Singleton.java | 4 +- .../DCL.java | 2 +- ...ion.java => SimpleLazyInitialization.java} | 17 ++--- .../SimpleStringModel.java | 3 +- .../immutability/fields/ArrayClasses.java | 4 +- .../fields/ArrayInitialization.java | 6 +- .../ClassWithEffectiveDeepImmutableField.java | 12 +-- .../fields/ClassWithProtectedFields.java | 4 +- .../ConstructorWithEscapingParameters.java | 2 +- .../fields/EffectivelyImmutableFields.java | 7 +- .../immutability/fields/Escapers.java | 10 +-- .../immutability/fields/MethodCalls.java | 2 +- .../WithMutableAndImmutableFieldType.java | 12 +-- .../fpcf/ClassAndTypeImmutabilityTests.scala | 20 ++--- .../opalj/fpcf/FieldImmutabilityTests.scala | 12 +-- .../org/opalj/fpcf/FieldLocalityTests.scala | 31 ++++++++ .../FieldReferenceImmutabilityTests.scala | 13 +++- .../scala/org/opalj/fpcf/PropertiesTest.scala | 41 +++++----- .../_AllFieldImmutabilityAnalysisTests.scala | 2 +- 23 files changed, 178 insertions(+), 132 deletions(-) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/{ShallowImmutableClass.java => TrivialShallowImmutableClass.java} (92%) rename DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/{simpleLazyInitialization.java => SimpleLazyInitialization.java} (75%) 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 index b3f08ed551..cc01cfcb87 100644 --- 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 @@ -339,18 +339,16 @@ final class MutableClassWithGenericField { 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) -@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) +@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="can not handle generic types", + @ShallowImmutableField(value="the generic type is concretised with mutable types", analyses = {L1FieldImmutabilityAnalysis.class, - L2FieldImmutabilityAnalysis.class}) - @DeepImmutableField(value = "the generic type is concretised with deep immutable types", - analyses = L3FieldImmutabilityAnalysis.class) + L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) @ImmutableFieldReference(value = "field is effectively final", analyses = L0FieldReferenceImmutabilityAnalysis.class) private SimpleGenericClass gc = 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 index 1754559937..279f64d171 100644 --- 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 @@ -7,6 +7,7 @@ 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; @@ -15,6 +16,7 @@ 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; @@ -22,12 +24,12 @@ import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; -@DeepImmutableType(value = "has only deep immutable fields and is not extensible", +@ShallowImmutableType(value = "has only deep immutable fields and is not extensible", analyses = L1TypeImmutabilityAnalysis.class) -@DeepImmutableClass(value = "has only deep immutable fields", analyses = L1ClassImmutabilityAnalysis.class) +@ShallowImmutableClass(value = "has only deep immutable fields", analyses = L1ClassImmutabilityAnalysis.class) final class ClassWithGenericField { - @DeepImmutableField("") + @ShallowImmutableField("") @ImmutableFieldReference("") private SimpleGenericClass gc = new SimpleGenericClass diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/TrivialShallowImmutableClass.java similarity index 92% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/TrivialShallowImmutableClass.java index 080a472102..b1cad1c4da 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/ShallowImmutableClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/classes/trivials/TrivialShallowImmutableClass.java @@ -7,9 +7,11 @@ 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; @@ -22,10 +24,10 @@ import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; @MutableType("Because of not final class") -@DeepImmutableClass("has shallow immutable field") -public class ShallowImmutableClass { +@ShallowImmutableClass("has shallow immutable field") +public class TrivialShallowImmutableClass { - @DeepImmutableField(value = "Because object can not escape", analyses = L3FieldImmutabilityAnalysis.class) + @ShallowImmutableField(value = "Because object can not escape", analyses = L3FieldImmutabilityAnalysis.class) @ImmutableFieldReference("Because it is private") private TrivialMutableClass mutableClass = new TrivialMutableClass(); } 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 index 70a2be308d..4b681e3340 100644 --- 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 @@ -7,6 +7,7 @@ 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; @@ -22,12 +23,11 @@ class Simple { - @DeepImmutableField(value="Simple Lazy Initialization and primitive field type", + /*@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", - analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) - @LazyInitializedNotThreadSafeButDeterministicReference("Simple lazy initialization") + @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() { @@ -40,11 +40,10 @@ public int init() { 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", - analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) - @LazyInitializedNotThreadSafeButDeterministicReference("Lazy initialization with 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() { @@ -75,12 +74,11 @@ public int init() { class LocalReversed { - @DeepImmutableField(value="Lazy initializatio with primitive type", analyses = L3FieldImmutabilityAnalysis.class) + /*@DeepImmutableField(value="Lazy initializatio with primitive type", analyses = L3FieldImmutabilityAnalysis.class) @ShallowImmutableField(value = "Lazy initialization with local (reversed)", - analyses = L2FieldImmutabilityAnalysis.class) - @MutableField(value = "Analysis doesn't recognize lazy initialization", - analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) - @LazyInitializedNotThreadSafeButDeterministicReference(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; @@ -95,13 +93,14 @@ public int init() { 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) + //@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 }) - @LazyInitializedNotThreadSafeButDeterministicReference( + 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; @@ -117,13 +116,12 @@ public int init() { class SimpleReversed { - @DeepImmutableField(value = "Lazy initialization with primitive type", + /* @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", - analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) - @LazyInitializedNotThreadSafeButDeterministicReference(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; @@ -192,7 +190,7 @@ 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 }) - @LazyInitializedNotThreadSafeButDeterministicReference("Lazy initialization with call to deterministic method") + @LazyInitializedNotThreadSafeFieldReference("Lazy initialization with call to deterministic method") private int x; public int init() { @@ -212,7 +210,7 @@ class DeterministicCallWithParam { @MutableField(value = "Lazy initialization is not the same for different invocations", analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) - @MutableFieldReference(value = "Lazy initialization is not the same for different invocations", + @LazyInitializedNotThreadSafeFieldReference(value = "Lazy initialization is not the same for different invocations", analyses = L0FieldReferenceImmutabilityAnalysis.class) private int x; @@ -230,12 +228,13 @@ private final int sum(int a, int b) { class DeterministicCallOnFinalField { - @DeepImmutableField(value = "Lazy initialization with call to deterministic " + + /*@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) - @LazyInitializedNotThreadSafeButDeterministicReference(value = "Lazy initialization with call to deterministic method " + + 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; @@ -277,7 +276,7 @@ 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}) - @MutableFieldReference(value = "Wrong lazy initialization with call to non-deterministic method on final field", + @LazyInitializedNotThreadSafeFieldReference(value = "Wrong lazy initialization with call to non-deterministic method on final field", analyses = L3FieldImmutabilityAnalysis.class) private int x; @@ -318,7 +317,7 @@ class NondeterministicCall { @MutableField(value = "Wrong lazy initialization with call to non-deterministic method", analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class, L3FieldImmutabilityAnalysis.class}) - @MutableFieldReference(value = "Wrong lazy initialization with call to non-deterministic method", + @LazyInitializedNotThreadSafeFieldReference(value = "Wrong lazy initialization with call to non-deterministic method", analyses = L0FieldReferenceImmutabilityAnalysis.class) private int x; @@ -333,13 +332,12 @@ public int init() { } class DoubleLocalAssignment { - @DeepImmutableField(value = "Lazy initialization with a local that is updated twice and primitive type", + /*@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", - analyses = { L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class }) - @LazyInitializedNotThreadSafeButDeterministicReference(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; @@ -401,7 +399,7 @@ class ExceptionInInitialization { // analyses = { L1FieldMutabilityAnalysis.class, L2FieldMutabilityAnalysis.class }) //FIXME: Issue with Java11 @NonFinal(value = "Instance field not considered by analysis", // analyses = L0FieldMutabilityAnalysis.class) - @ImmutableFieldReference(value = "Field is never initialized, so it stays on its default value", + @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; 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 index 10656a2236..37cb26eea1 100644 --- 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 @@ -19,10 +19,10 @@ public class Singleton { @MutableFieldReference("written by static initializer after the field becomes (indirectly) readable") private String name; - @DeepImmutableField(value ="referenced object can not escape", analyses = L3FieldImmutabilityAnalysis.class) @ShallowImmutableField( value = "only initialized once by the constructor", - analyses = { L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class } + analyses = { L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.class , + L3FieldImmutabilityAnalysis.class} ) @MutableField(value = "instance field not recognized by analysis", analyses = L0FieldImmutabilityAnalysis.class) 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 index f13afa950b..39ce10b75f 100644 --- 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 @@ -45,7 +45,7 @@ public int getInitializedWithDCLRandomWrite(){ return initializedWithDCLRandomWrite; } - @MutableFieldReference(value = "The field is not thread safe and not deterministic written", + @LazyInitializedNotThreadSafeFieldReference(value = "The field is not thread safe and not deterministic written", analyses = L0FieldReferenceImmutabilityAnalysis.class) private int notThreadSafeRandomWrite; 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 similarity index 75% rename from DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/simpleLazyInitialization.java rename to DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/lazy_initialized_field_references/SimpleLazyInitialization.java index cee7535279..963d8ffa5f 100644 --- 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 @@ -19,25 +19,24 @@ * @author Tobias Roth * */ -public class simpleLazyInitialization { +public class SimpleLazyInitialization { @LazyInitializedNotThreadSafeFieldReference(value = "the write to the object reference simpleLazyInitialization" + " is not atomic", analyses = L0FieldReferenceImmutabilityAnalysis.class) - private static simpleLazyInitialization simpleLazyInitialization; + private static SimpleLazyInitialization simpleLazyInitialization; - public static simpleLazyInitialization init() { + public static SimpleLazyInitialization init() { if(simpleLazyInitialization ==null) - simpleLazyInitialization = new simpleLazyInitialization(); + simpleLazyInitialization = new SimpleLazyInitialization(); return simpleLazyInitialization; } - @ShallowImmutableField(value = "can not handle transitive immutability", + /*@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", - analyses = {L0FieldImmutabilityAnalysis.class, L1FieldImmutabilityAnalysis.class}) - @LazyInitializedNotThreadSafeButDeterministicReference(value = "deterministic write due to guarded 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() { 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 index 1e050b0332..4408eb3993 100644 --- 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 @@ -7,6 +7,7 @@ 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; @@ -31,7 +32,7 @@ char[] getValue(){ //TODO @DeepImmutableField //TODO @LazyInitializedNotThreadSafeButDeterministicReference @MutableField("Mutable field reference") - @MutableFieldReference(value="The two def sites of h are conservatively handled as mutable") + @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 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 index ca72e60ae8..b9301c0b29 100644 --- 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 @@ -10,7 +10,7 @@ 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}; @@ -173,5 +173,5 @@ public synchronized Integer getQ(){ 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 index 306eb08d51..82ac43ff8e 100644 --- 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 @@ -48,8 +48,8 @@ public SimpleLazyObjectsInstantiation getInstance() { } class EscapingObjectDeep { - - @DeepImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) + //TODO + @ShallowImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) private Object o; public synchronized Object getO(){ @@ -80,7 +80,7 @@ public Object getO(){ class ClassUsingEmptyClass { - @DeepImmutableField(value = "concrete object is known", analyses = L3FieldImmutabilityAnalysis.class) + //TODO @DeepImmutableField(value = "concrete object is known", analyses = L3FieldImmutabilityAnalysis.class) private EmptyClass emptyClass = new EmptyClass(); public EmptyClass getEmptyClass() { 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 index 3ed155c32c..1d1bc50b4e 100644 --- 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 @@ -20,19 +20,19 @@ @MutableType(value = "class is extensible", analyses = {L0TypeImmutabilityAnalysis.class, L1TypeImmutabilityAnalysis.class}) -@DeepImmutableClass(value = "class has only the deep immutable field tmc", - analyses = L1ClassImmutabilityAnalysis.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) + 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) + // @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}) + 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/ClassWithProtectedFields.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/fields/ClassWithProtectedFields.java index 89da1defee..d3f1d90be3 100644 --- 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 @@ -6,6 +6,7 @@ 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; @@ -30,8 +31,7 @@ public class ClassWithProtectedFields { analyses = L0FieldReferenceImmutabilityAnalysis.class) protected ClassWithPublicFields cwpf1 = new ClassWithPublicFields(); - @DeepImmutableField(value = "field has an immutable reference and the concrete known " + - "referenced object can not escape", + @ShallowImmutableField(value = "field has an immutable reference and mutable type", analyses = L3FieldImmutabilityAnalysis.class) @ImmutableFieldReference(value = "Declared final Field", analyses = L0FieldReferenceImmutabilityAnalysis.class) 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 index 25ac828a8a..83f28ccf8f 100644 --- 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 @@ -11,7 +11,7 @@ public class ConstructorWithEscapingParameters { @ImmutableFieldReference("The field is only assigned in the constructor.") private TrivialClass tc1; - @DeepImmutableField("The construtor pararameter of the assigned object not escape") + //TODO @DeepImmutableField("The construtor pararameter of the assigned object not escape") @ImmutableFieldReference("The field is only assigned in the constructor.") private TrivialClass tc2; 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 index 1df52cd7ef..f14ae82653 100644 --- 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 @@ -13,6 +13,7 @@ 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; @@ -21,7 +22,7 @@ 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", @@ -59,7 +60,7 @@ public synchronized int getInTheGetterLazyInitializedFieldWithPrimitiveType(){ @MutableField(value = "due to mutable field reference", analyses = L3FieldImmutabilityAnalysis.class) - @MutableFieldReference(value = "write of reference objects is not atomic", + @LazyInitializedNotThreadSafeFieldReference(value = "write of reference objects is not atomic", analyses = L0FieldReferenceImmutabilityAnalysis.class) private Integer simpleLazyInitializedIntegerField; @@ -504,5 +505,5 @@ public synchronized HashMap getInTheGetterLazyInitializedHashMap 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 index 7f94cec401..97f5c708d7 100644 --- 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 @@ -52,7 +52,7 @@ public ClassWithPublicFields get(){ } } class OneThatNotEscapesAndOneWithDCL { - @DeepImmutableField(value = "immutable field reference and the assigned object is known", + @ShallowImmutableField(value = "immutable field reference and mutable type", analyses = L3FieldImmutabilityAnalysis.class) @ImmutableFieldReference(value = "field is only written once", analyses = L0FieldReferenceImmutabilityAnalysis.class) @@ -121,15 +121,15 @@ public GenericNotEscapesMutualEscapeDependencyNotAbleToResolve() { } } -class GenericNotEscapesMutualEscapeDependencyAbleToResolve{ +class GenericNotEscapesMutualEscapeDependencyAbleToResolve{ @DeepImmutableField("") @ImmutableFieldReference("") private FinalEmptyClass fec = new FinalEmptyClass(); - @DeepImmutableField("") + @DependentImmutableField(value = "", analyses = L3FieldImmutabilityAnalysis.class) @ImmutableFieldReference("") - private SimpleGenericClass sgc; + private SimpleGenericClass sgc; public GenericNotEscapesMutualEscapeDependencyAbleToResolve() { this.sgc = new SimpleGenericClass(this.fec); @@ -137,7 +137,7 @@ public GenericNotEscapesMutualEscapeDependencyAbleToResolve() { } @DependentImmutableClass("") -class SimpleGenericClass { +final class SimpleGenericClass { @DependentImmutableField(value = "") private T t; SimpleGenericClass(T t){ 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 index 01f26b79c3..f789708f99 100644 --- 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 @@ -70,7 +70,7 @@ public synchronized void getTm5() { tm5.nop(); } - @DeepImmutableField("") + @ShallowImmutableField("") @ImmutableFieldReference("") private TestMutable tm6 = new TestMutable(); 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 index cdf234708c..ac008d0566 100644 --- 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 @@ -6,6 +6,7 @@ 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; @@ -13,6 +14,7 @@ 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; @@ -20,8 +22,8 @@ import org.opalj.tac.fpcf.analyses.immutability.L3FieldImmutabilityAnalysis; import org.opalj.tac.fpcf.analyses.immutability.fieldreference.L0FieldReferenceImmutabilityAnalysis; -@DeepImmutableType("has only deep immutable fields and is final") -@DeepImmutableClass("has only deep immutable fields") +@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) @@ -32,10 +34,8 @@ public final class WithMutableAndImmutableFieldType { private FinalEmptyClass fec = new FinalEmptyClass(); @MutableField(value="can not handle effective immutability", analyses = L0FieldImmutabilityAnalysis.class) - @DeepImmutableField(value = "assigned object is known and can not escape", - analyses = L3FieldImmutabilityAnalysis.class) - @ShallowImmutableField(value = "has mutable type but is effective final", - analyses = {L1FieldImmutabilityAnalysis.class, L2FieldImmutabilityAnalysis.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(); 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 index e57c6035f1..af6fa64966 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ClassAndTypeImmutabilityTests.scala @@ -23,6 +23,15 @@ 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 @@ -37,7 +46,7 @@ class ClassAndTypeImmutabilityTests extends PropertiesTest { override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") + List("org/opalj/fpcf/fixtures/benchmark/h_classAndTypeImmutability") } override def init(p: Project[URL]): Unit = { @@ -70,15 +79,6 @@ class ClassAndTypeImmutabilityTests extends PropertiesTest { } describe("the 1 class and type immutability analysis are executed") { - 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 val as = executeAnalyses(Set( LazyUnsoundPrematurelyReadFieldsAnalysis, 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 index 2a449d6a8a..ef3d63afb3 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldImmutabilityTests.scala @@ -20,9 +20,9 @@ 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 +//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 @@ -34,7 +34,7 @@ class FieldImmutabilityTests extends PropertiesTest { override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") + List("org/opalj/fpcf/fixtures/benchmark/arrays") } override def init(p: Project[URL]): Unit = { @@ -54,7 +54,7 @@ class FieldImmutabilityTests extends PropertiesTest { validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) } - + /*SimpleSimple describe("the org.opalj.fpcf.analyses.L0FieldMutabilityAnalysis is executed") { val as = executeAnalyses( @@ -98,7 +98,7 @@ class FieldImmutabilityTests extends PropertiesTest { validateProperties(as, fieldsWithAnnotations(as.project), Set("FieldImmutability")) } - +*/ describe("the org.opalj.fpcf.analyses.L0FieldImmutabilityAnalysis is executed") { val as = executeAnalyses( Set( 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 index c87a6c504c..7921c86a87 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldReferenceImmutabilityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldReferenceImmutabilityTests.scala @@ -4,7 +4,6 @@ package fpcf 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 @@ -32,16 +31,24 @@ class FieldReferenceImmutabilityTests extends PropertiesTest { override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") + List("org/opalj/fpcf/fixtures/immutability/sandbox50") } override def init(p: Project[URL]): Unit = { p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ - Set[Class[_ <: AnyRef]](classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]]) + 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") { 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/_AllFieldImmutabilityAnalysisTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/_AllFieldImmutabilityAnalysisTests.scala index e8aa36cbe2..f1a5b16d28 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/_AllFieldImmutabilityAnalysisTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/_AllFieldImmutabilityAnalysisTests.scala @@ -39,7 +39,7 @@ class _AllFieldImmutabilityAnalysisTests extends PropertiesTest { override def withRT = true override def fixtureProjectPackage: List[String] = { - List("org/opalj/fpcf/fixtures/immutability") + List("org/opalj/fpcf/fixtures/immutability/xxx") } override def init(p: Project[URL]): Unit = { From 501ac1ddf450ccddbafa605af1c4cc12717fb06b Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 12 Mar 2021 16:06:56 +0100 Subject: [PATCH 320/327] temp tests --- .../field_references/NativeFunctionCalls.java | 18 ++++ .../immutability/field_references/Test.java | 15 ++++ .../TypeCastsInLazyInitialization.java | 34 ++++++++ .../NonVirtualMethodCallFalsePositive.java | 36 ++++++++ .../StaticEffectiveDeepImmutablFields.java | 56 ++++++++++++ .../fixtures/immutability/sandbox21/Test.java | 32 +++++++ .../sandbox22/EscapeFailureTest.java | 18 ++++ .../immutability/sandbox25/TestClass5.java | 16 ++++ ...nericFieldWithOneLeftGenericParameter.java | 86 +++++++++++++++++++ .../immutability/sandbox30/Generic.java | 4 + .../fixtures/immutability/sandbox30/Test.java | 32 +++++++ .../immutability/sandbox30/Test2.java | 28 ++++++ .../immutability/sandbox31/TwoVirgin.java | 44 ++++++++++ .../immutability/sandbox32/DeepGeneric.java | 75 ++++++++++++++++ .../immutability/sandbox35/MethodTest.java | 36 ++++++++ .../immutability/sandbox36/Test2.java | 74 ++++++++++++++++ .../fixtures/immutability/sandbox40/C.java | 16 ++++ .../fixtures/immutability/sandbox40/GC.java | 17 ++++ .../sandbox40/NativeFunctionCalls.java | 13 +++ .../immutability/sandbox41/GenericTest.java | 59 +++++++++++++ .../immutability/sandbox42/Clone.java | 21 +++++ .../sandbox42/CloneNotImmutable.java | 20 +++++ .../fixtures/immutability/sandbox50/Test.java | 10 +++ .../immutability/sandbox60/Generic.java | 19 ++++ .../immutability/sandbox60/GenericFields.java | 30 +++++++ .../sandbox60/GenericFieldsDeep.java | 25 ++++++ 26 files changed, 834 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/NativeFunctionCalls.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/Test.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/field_references/TypeCastsInLazyInitialization.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox15/NonVirtualMethodCallFalsePositive.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox20/StaticEffectiveDeepImmutablFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox21/Test.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox22/EscapeFailureTest.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox25/TestClass5.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox26/DependentClassWithGenericFieldWithOneLeftGenericParameter.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox30/Generic.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox30/Test.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox30/Test2.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox31/TwoVirgin.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox32/DeepGeneric.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox35/MethodTest.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox36/Test2.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox40/C.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox40/GC.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox40/NativeFunctionCalls.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox41/GenericTest.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox42/Clone.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox42/CloneNotImmutable.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox50/Test.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox60/Generic.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox60/GenericFields.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/immutability/sandbox60/GenericFieldsDeep.java 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/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/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 {} + + + From e0f2638175014da847c62922fc49cfcdba6a7eec Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 12 Mar 2021 16:08:05 +0100 Subject: [PATCH 321/327] new application config file --- .../main/resources/ApplicationProject.conf | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 OPAL/framework/src/main/resources/ApplicationProject.conf 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 From bd023fda01d3f17284c8901af1e56e6db917f580 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 12 Mar 2021 16:08:40 +0100 Subject: [PATCH 322/327] new immutability benchmark test runner --- .../fpcf/ImmutabilityBenchmarkTests.scala | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ImmutabilityBenchmarkTests.scala 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") + ) + } +} From b8c2f09680094c3fecfe487cefb805bcc54cd545 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 12 Mar 2021 16:16:09 +0100 Subject: [PATCH 323/327] purity runner customized for comparison --- .../scala/org/opalj/support/info/Purity.scala | 124 ++++++++++++------ 1 file changed, 81 insertions(+), 43 deletions(-) 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 a9fb29f1d5..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 @@ -148,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)) @@ -161,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) { @@ -188,7 +190,9 @@ object Purity { libraryClassFilesAreInterfacesOnly = false, Traversable.empty ) - } { t ⇒ projectTime = t.toSeconds } + } { t ⇒ + projectTime = t.toSeconds + } project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { case None ⇒ Set(domain) @@ -209,7 +213,9 @@ object Purity { ) 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 ⇒ @@ -227,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) @@ -240,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 { @@ -263,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) { @@ -325,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 { @@ -341,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) { @@ -388,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") @@ -550,7 +589,6 @@ object Purity { support ::= LazyInterProceduralEscapeAnalysis case Some("none") ⇒ - case Some(a) ⇒ Console.println(s"unknown escape analysis: $a") Console.println(usage) @@ -587,12 +625,12 @@ object Purity { } case Some("none") ⇒ - - case None ⇒ analysis match { - case LazyL0PurityAnalysis ⇒ LazyL0FieldImmutabilityAnalysis - case LazyL1PurityAnalysis ⇒ LazyL1FieldImmutabilityAnalysis - case LazyL2PurityAnalysis ⇒ LazyL1FieldImmutabilityAnalysis - } + case None ⇒ + analysis match { + case LazyL0PurityAnalysis ⇒ LazyL0FieldImmutabilityAnalysis + case LazyL1PurityAnalysis ⇒ LazyL1FieldImmutabilityAnalysis + case LazyL2PurityAnalysis ⇒ LazyL1FieldImmutabilityAnalysis + } case Some(a) ⇒ Console.println(s"unknown field mutability analysis: $a") From 8b44a0f51decec9dca101962f9e57e08d139d267 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 12 Mar 2021 16:17:48 +0100 Subject: [PATCH 324/327] added recognition of known class type --- OPAL/si/src/main/resources/reference.conf | 1 + .../L3FieldImmutabilityAnalysis.scala | 24 ++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) 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/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 index 1c5563fa69..cd0fed5c3a 100755 --- 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 @@ -136,8 +136,8 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) final val definitionSites = project.get(DefinitionSitesKey) implicit final val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) - var considerEscape = true /*project.config.getBoolean( //TODO due to unsoundness - "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape" + var considerEscape = true; //TODO true /*project.config.getBoolean( //TODO due to unsoundness + /* "org.opalj.fpcf.analyses.L3FieldImmutabilityAnalysis.considerEscape" )*/ val considerGenericity = @@ -567,12 +567,12 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } else if (assignmentExpression.isNew) { val newStmt = assignmentExpression.asNew if (field.fieldType.isObjectType && - newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && - state.totalNumberOfFieldWrites == 1) { + 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) { @@ -581,7 +581,8 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) doesNonVirtualMethodCallEnablesEscape(stmt.asNonVirtualMethodCall) } else false } else true - } + } */ + true } else assignmentExpression.isConst } else { @@ -632,6 +633,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val arrayStmt = taCode.stmts(usedByIndex) if (arrayStmt != putStmt) { if (arrayStmt.isArrayStore) { + val arrayStore = arrayStmt.asArrayStore val arrayStoreIndex = arrayStore.index val isArrayIndexConst = @@ -679,7 +681,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) handleKnownClassType(newStmt.tpe.mostPreciseObjectType) } - valueAssignment.targetVar.asVar.usedBy.exists { index ⇒ + /*valueAssignment.targetVar.asVar.usedBy.exists { index ⇒ val tmpStmt = taCode.stmts(index) if (tmpStmt.isArrayStore) { false // can be ingored @@ -691,7 +693,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) } false //nothing to do in the else case. Stmt has still been handled } else true - } + } */ true } else true } else true } else true @@ -724,8 +726,8 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) val newStmt = definitionSiteAssignment.expr.asNew if (field.fieldType.isObjectType && - newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType && - state.totalNumberOfFieldWrites == 1) { + newStmt.tpe.mostPreciseObjectType == field.fieldType.asObjectType /*&& + state.totalNumberOfFieldWrites == 1*/ ) { state.concreteClassTypeIsKnown = true handleKnownClassType(newStmt.tpe.mostPreciseObjectType) } @@ -977,7 +979,7 @@ class L3FieldImmutabilityAnalysis private[analyses] (val project: SomeProject) * Determines whether the reference object escapes or can be mutated if the flag [[considerEscape]] is set */ if (considerEscape) { - state.noEscapePossibilityViaReference = state.field.isPrivate + state.noEscapePossibilityViaReference = true //state.field.isPrivate if (state.referenceIsImmutable.isEmpty || state.referenceIsImmutable.get) determineEscapeOfReferencedObjectOrValue() if (state.escapesStillDetermined && !state.field.isPrivate) From f53e3758f01f7c0815d325d3fdc6960551aae99d Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 12 Mar 2021 16:18:25 +0100 Subject: [PATCH 325/327] current status of immutability demos --- .../ClassImmutabilityAnalysisDemo.scala | 2 +- .../FieldImmutabilityAnalysisDemo.scala | 56 ++++++++++++++----- ...eldReferenceImmutabilityAnalysisDemo.scala | 2 +- .../TypeImmutabilityAnalysisDemo.scala | 2 +- 4 files changed, 44 insertions(+), 18 deletions(-) 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 index 7b00949af4..af81ba8091 100644 --- 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 @@ -60,7 +60,7 @@ object ClassImmutabilityAnalysisDemo extends ProjectAnalysisApplication { var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + project.get(RTACallGraphKey) time { propertyStore = analysesManager 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 index daffbef98c..74e675e596 100644 --- 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 @@ -34,6 +34,8 @@ 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. @@ -61,7 +63,12 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + project.get(RTACallGraphKey) + + project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ ⇒ + Set[Class[_ <: AnyRef]](classOf[domain.l2.DefaultPerformInvocationsDomainWithCFG[URL]]) + } + time { propertyStore = analysesManager .runAll( @@ -85,19 +92,24 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val allFieldsInProjectClassFiles = project.allProjectClassFiles.toIterator.flatMap { _.fields }.toSet - val groupedResults = propertyStore.entities(FieldImmutability.key). - filter(field ⇒ allFieldsInProjectClassFiles.contains(field.asInstanceOf[Field])). - toTraversable.groupBy(_.toFinalEP.p) + 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(MutableField).toSeq.sortWith(order) - val shallowImmutableFields = groupedResults(ShallowImmutableField).toSeq.sortWith(order) - val dependentImmutableFields = groupedResults(DependentImmutableField).toSeq.sortWith(order) - val deepImmutableFields = groupedResults(DeepImmutableField).toSeq.sortWith(order) + 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 = - s""" + /* | Mutable Fields: | ${mutableFields.mkString(" | Mutable Field \n")} | @@ -109,8 +121,8 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { | | Deep Immutable Fields: | ${deepImmutableFields.mkString(" | Deep Immutable Field\n")} - | - | + |*/ + s""" | | Mutable Fields: ${mutableFields.size} | Shallow Immutable Fields: ${shallowImmutableFields.size} | Dependent Immutable Fields: ${dependentImmutableFields.size} @@ -122,21 +134,35 @@ object FieldImmutabilityAnalysisDemo extends ProjectAnalysisApplication { } | | took : $analysisTime seconds + | + | level: ${project.getProjectInformationKeyInitializationData(AIDomainFactoryKey)} + |propertyStore: ${propertyStore.getClass} |""".stripMargin - val file = new File(s"${Calendar.getInstance().formatted("dd_MM_yyyy_hh_mm_ss")}.txt") + 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} + case e: IOException ⇒ + println( + s""" Could not write file: ${file.getName} | ${e.getMessage} |""".stripMargin - ) + ) } finally { bw.close() } 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 index d783bed48f..3b1dc50b15 100644 --- 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 @@ -61,7 +61,7 @@ object FieldReferenceImmutabilityAnalysisDemo extends ProjectAnalysisApplication var propertyStore: PropertyStore = null var analysisTime: Seconds = Seconds.None val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + project.get(RTACallGraphKey) time { propertyStore = analysesManager 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 index 190cd4e325..bafa739dfc 100644 --- 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 @@ -65,7 +65,7 @@ object TypeImmutabilityAnalysisDemo extends ProjectAnalysisApplication { val analysesManager = project.get(FPCFAnalysesManagerKey) - analysesManager.project.get(RTACallGraphKey) + project.get(RTACallGraphKey) time { propertyStore = analysesManager From ee8722157fc66acbd96df7e6495160c297b54e48 Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 12 Mar 2021 16:20:56 +0100 Subject: [PATCH 326/327] current status of field reference immutability analysis; still wip; todo: remove handling native functions --- ...L0FieldReferenceImmutabilityAnalysis.scala | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) 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 index ccde884c52..2f23d8f6e2 100644 --- 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 @@ -113,6 +113,7 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP private[analyses] def determineFieldReferenceImmutability( field: Field ): ProperPropertyComputationResult = { + import org.opalj.br.ClassFile if (field.isFinal) return Result(field, ImmutableFieldReference); @@ -131,36 +132,36 @@ class L0FieldReferenceImmutabilityAnalysis private[analyses] (val project: SomeP // 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 initialClasses = + if (field.isProtected || field.isPackagePrivate) { + project.classesPerPackage(thisType.packageName) + } else { + Set(field.classFile) } - /*val subclassesIterator: Iterator[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 - //} + 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); - // } + if (classesHavingAccess.exists(_.methods.exists(_.isNative))) { //TODO flag for comparison...reim + if (!field.isFinal) + return Result(field, MutableFieldReference); + } for { (method, pcs) ← fieldAccessInformation.writeAccesses(field) From defa4bbdaa968afdd8f20ff53a8c2893e10f2e8a Mon Sep 17 00:00:00 2001 From: tobias roth Date: Fri, 12 Mar 2021 16:21:18 +0100 Subject: [PATCH 327/327] temporary config file --- OPAL/br/src/main/resources/reference.conf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OPAL/br/src/main/resources/reference.conf b/OPAL/br/src/main/resources/reference.conf index 98d78e0640..983debed82 100644 --- a/OPAL/br/src/main/resources/reference.conf +++ b/OPAL/br/src/main/resources/reference.conf @@ -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 = [