package fpcf
The fixpoint computations framework (fpcf) is a general framework to perform fixpoint
computations of properties ordered by a lattice. The framework in particular supports the
development of static analyses.
In this case, the fixpoint computations/static analyses are generally operating on the code and need to be executed until the computations have reached their (implicit) fixpoint. The fixpoint framework explicitly supports resolving cyclic dependencies/computations. A prime use case of the fixpoint framework are all those analyses that may interact with the results of other analyses.
For example, an analysis that analyzes all field write accesses to determine if we can refine a field's type (for the purpose of the analysis) can (reuse) the information about the return types of methods, which however may depend on the refined field types.
The framework is generic enough to facilitate the implementation of anytime algorithms.
- Note
This framework assumes that all data-structures (e.g., dependee lists and properties) that are passed to the framework are effectively immutable! (Effectively immutable means that the datastructure is never updated after it was passed to the framework.)
,The dependency relation is as follows: “A depends on B”
,===“A is the depender, B is the dependee”.===“B is depended on by A”The very core of the framework is described in: Lattice Based Modularization of Static Analyses
- Alphabetic
- By Inheritance
- fpcf
- AnyRef
- Any
- Hide All
- Show All
- Public
- All
Type Members
-
class
AnalysisScenario extends AnyRef
Provides functionality to compute an optimal schedule to execute a set of analyses.
Provides functionality to compute an optimal schedule to execute a set of analyses. Here, optimal means that the schedule will try to minimize the number of notifications due to updated properties. It will run analyses that just use information provided by earlier analyses, but which do not provide information required by the earlier ones, in a later batch/phase.
-
final
type
ComputationResults = TraversableOnce[SomeFinalEP]
The result of a computation if the computation derives multiple properties at the same time.
-
trait
ComputationSpecification extends AnyRef
Specification of the properties and the life-cycle methods of a fixpoint computation (FPC) that are relevant when computing the correct scheduling order and actually executing the fixpoint computations.
Specification of the properties and the life-cycle methods of a fixpoint computation (FPC) that are relevant when computing the correct scheduling order and actually executing the fixpoint computations.
- Note
The PropertyStore can be used without using ComputationSpecifications and AnalysisScenarios; both latter classes just provide convenience functionality that ensures that common issues are identified early on/are avoided.
-
case class
ContextNotAvailableException(context: AnyRef, completeContext: Map[Class[_], AnyRef]) extends RuntimeException with Product with Serializable
Thrown if a context object is requested, but could not be found.
Thrown if a context object is requested, but could not be found.
Context objects are generally unrelated to entities and properties. They just store information that may be required by fixpoint computations executed using the property store.
- Note
If the
org.opalj.br.ProjectInformationKeyis used to get the property store, theProjectis stored in the context.
-
final
type
Continuation[P <: Property] = (Entity, P) ⇒ PropertyComputationResult
A function that continues the computation of a property.
A function that continues the computation of a property. It takes the entity and property of the entity on which the computation depends.
-
sealed
trait
EOptionP[+E <: Entity, +P <: Property] extends AnyRef
An entity associated with the current extension of a property or
Noneif no (preliminary) property is already computed. -
final
class
EPK[+E <: Entity, +P <: Property] extends EOptionP[E, P]
A simple pair consisting of an Entity and a PropertyKey.
-
sealed
trait
EPS[+E <: Entity, +P <: Property] extends EOptionP[E, P]
A pairing of an Entity and an associated Property along with its state.
-
final
type
Entity = AnyRef
The type of the values stored in a property store.
The type of the values stored in a property store.
Basically, a simple type alias to facilitate comprehension of the code.
-
trait
ExplicitlyNamedProperty extends Property
A property which has an explicit name.
A property which has an explicit name. This is particularly useful when we want to refer to a property in the context of some test cases. In general, it should be tried that the name is reasonably unique w.r.t. its usage scenario.
-
final
type
FallbackPropertyComputation[E <: Entity, P <: Property] = (PropertyStore, FallbackReason, E) ⇒ P
The FallbackReason specifies the reason why a fallback property is required.
The FallbackReason specifies the reason why a fallback property is required. This information can be used to handle situations where the fallback should be different based on the information whether a corresponding analysis was executed or not.
-
sealed
trait
FallbackReason extends AnyRef
Specifies the reason why a fallback is used.
-
final
class
FinalEP[+E <: Entity, +P <: Property] extends EPS[E, P]
Encapsulate the final property
Pfor the entityE.Encapsulate the final property
Pfor the entityE.For a detailed discussion of the semantics of
lbandubsee EOptionP.ub. -
sealed abstract
class
FinalPropertyComputationResult extends PropertyComputationResult
Encapsulates the final result of the computation of a property.
Encapsulates the final result of the computation of a property. I.e., the analysis determined that the computed property will not be updated in the future.
A final result is only to be used if no further refinement is possible or may happen and if the bounds are correct/sound abstractions.
- Note
The framework will invoke and deregister all dependent computations (observers). If – after having a result - another result w.r.t. the given entity and property is given to the property store the behavior is undefined and may/will result in immediate and/or deferred failures!
-
case class
IncrementalResult[E <: Entity](result: PropertyComputationResult, nextComputations: Iterator[(PropertyComputation[E], E)], propertyComputationsHint: PropertyComputationHint = DefaultPropertyComputation) extends PropertyComputationResult with Product with Serializable
Encapsulates some result and also some computations that should be computed next.
Encapsulates some result and also some computations that should be computed next. In this case the property store DOES NOT guarantee that the result is processed before the next computations are triggered. Hence,
nextComputationscan query the e/pk related to the previous result, but should not expect to already see the value of the given result(s).Incremental results are particularly useful to process tree structures such as the class hierarchy.
- Note
All computations must compute different e/pk pairs which are not yet computed/scheduled or for which lazy computations are scheduled.
,To ensure correctness it is absolutely essential that all entities - for which some property could eventually be computed - has a property before the property store reaches quiescence. Hence, it is generally not possible that a lazy computation returns
IncrementalResultobjects.
-
final
class
IntermediateEP[+E <: Entity, +P <: Property] extends EPS[E, P]
Encapsulates the intermediate lower- and upper bound related to the computation of the respective property kind for the entity
E.Encapsulates the intermediate lower- and upper bound related to the computation of the respective property kind for the entity
E.For a detailed discussion of the semantics of
lbandubsee EOptionP.lb. -
final
class
IntermediateESimpleP[+E <: Entity, +P <: Property] extends EPS[E, P]
Encapsulates the intermediate simple property of the respective property kind for the entity
E.Encapsulates the intermediate simple property of the respective property kind for the entity
E.For a more detailed discussion see EOptionP.ub.
-
case class
IntermediateResult[P <: Property](e: Entity, lb: P, ub: P, dependees: Traversable[SomeEOptionP], c: OnUpdateContinuation, hint: PropertyComputationHint = DefaultPropertyComputation) extends PropertyComputationResult with Product with Serializable
Encapsulates an intermediate result of the computation of a property.
Encapsulates an intermediate result of the computation of a property.
Intermediate results are to be used if further refinements are possible. Hence, if a property of any of the dependees changes (outgoing dependencies), the given continuation
cis invoked.All current computations that depend on the property of the entity will be invoked.
- dependees
The entity/property (kind) pairs the analysis depends on. Each
entity/property kindpair must occur at most once in the list, the current entity/property kind (ep) must not occur; i.e., self-reference are forbidden! A dependee must have been queried usingPropertyStore.apply(...); directly returning a dependee without a prior querying of the property store can lead to unexpected results. A dependee must NEVER be less precise than the value returned by the query. In general, the set of dependees is expected to shrink over time and the result should capture the effect of all properties. However, it is possible to first wait on specific properties of specific entities, if these properties ultimately determine the overall result. Hence, it is possible to partition the set of entity / properties and to query each group one after another. AnIntermediateResultreturned by anOnUpdateContinuationmust contain the EPS given to the continuation function or a newer EPS (i.e., an onUpdateContinuation is allowed to query the store again). The given set of dependees must not be mutated; it is used internally and if the set is mutated, propagation of changes no longer works reliably.- c
The function which is called if a property of any of the dependees is updated.
cdoes not have to be thread safe unless the same instance ofcis returned multiple times for different entities (e) which should be avoided and is generally not necessary. I.e., it is recommended to think aboutcas the function that completes the computation of the propertypfor the entityeidentified byep. In general,ccan have (mutual) state encapsulates (temporary) information required to compute the final property.
- Note
All elements on which the result declares to be dependent on must have been queried before (using one of the
applyfunctions of the property store.)
-
case class
MultiResult(properties: ComputationResults) extends FinalPropertyComputationResult with Product with Serializable
Encapsulates the final results of the computation of a set of properties.
Encapsulates the final results of the computation of a set of properties. Hence, all results have to be w.r.t. different e/pk pairs.
The encapsulated results are not atomically set; they are set one after another.
- See also
FinalPropertyComputationResult for further information.
- final type OnUpdateContinuation = (SomeEPS) ⇒ PropertyComputationResult
-
trait
OrderedProperty extends Property
Ordered properties make the order between all properties of a specific kind explicit; all properties that are of the same kind have to inherit from ordered property or none.
Ordered properties make the order between all properties of a specific kind explicit; all properties that are of the same kind have to inherit from ordered property or none.
This information is used by the property store when assertions/debugging is turned on to test if an analysis, which derives a new property, always derives a more precise property.
-
case class
PartialResult[E >: Null <: Entity, P >: Null <: Property](e: E, pk: PropertyKey[P], u: (EOptionP[E, P]) ⇒ Option[EPS[E, P]]) extends PropertyComputationResult with Product with Serializable
PartialResults are used for properties of entities which are computed collaboratively/in a piecewise fashion.PartialResults are used for properties of entities which are computed collaboratively/in a piecewise fashion.For example, let's assume that we have an entity
Projectwhich has the property to store the types which are instantiated and which is updated whenever an analysis of a method detects the instantiation of a type. In this case, the analysis of the method could return a Results object which contains the(Intermediate)Resultfor the analysis of the method as such and aPartialResultwhich will update the information about the overall set of instantiated types.- P
The type of the property.
- e
The entity for which we have a partial result.
- pk
The kind of the property for which we have a partial result.
- u
The function which is given the current property (if any) and which computes the new property.
uhas to returnNoneif the update does not change the property andSome(NewProperty)otherwise.
-
trait
Property extends PropertyMetaInformation
An immutable information associated with an entity.
An immutable information associated with an entity. Each property belongs to exactly one property kind specified by the PropertyKey. For details regarding the semantics of a property see EOptionP#ub.
Implementation Requirements
Properties have to be (effectively) immutable when passed to the framework. If a property is mutable and (by some analysis) mutated the overall results will most likely no longer be deterministic!
Structural Equality
Each implementation of the property trait has to implement an
equalsmethod that determines if two properties are equal. -
case class
PropertyBounds[P <: Property](lb: P, ub: P) extends Product with Serializable
Encapsulates the bounds related to a specific entity/property kind.
-
final
type
PropertyComputation[E <: Entity] = (E) ⇒ PropertyComputationResult
A function that takes an entity and returns a result.
A function that takes an entity and returns a result. The result maybe:
- the final derived property,
- a function that will continue computing the result once the information about some other entity is available or,
- an intermediate result which may be refined later on, but not by the current running analysis.
- Note
In some cases it makes sense that an analysis processes entities of kind A, but derives properties related to entities with kind B. E.g., it is possible to have an analysis that processes entire classes to compute the properties of some fields. This scenario is, however, only supported by eager analyses.
-
sealed
trait
PropertyComputationHint extends AnyRef
Hints about the nature of the property computations, which can/are used by the property store to implement different scheduling schemes.
-
sealed abstract
class
PropertyComputationResult extends AnyRef
Encapsulates the (intermediate) result of the computation of a property.
-
final
class
PropertyKey[+P] extends AnyVal with PropertyKind
A value object that identifies a specific kind of properties.
A value object that identifies a specific kind of properties. Every entity in the PropertyStore must be associated with at most one property per property kind/key.
To create a property key use one of the companion object's PropertyKey$.
createmethod. -
trait
PropertyKind extends Any
Identifies the kind of a property.
Identifies the kind of a property.
Generally, we distinguish between regular properties and simple properties. The latter are generally only to be used if lower bounds cannot be computed or a very extensive and are never of interest to any potential client. E.g., in case of an IFDS analysis, computing the lower bound is not meaningful; in case of a call graph analysis, the lower bound is usually either prohibitively expensive or is not usefull to any analysis.
-
trait
PropertyMetaInformation extends PropertyKind
The meta information shared by properties and their respective kinds.
-
abstract
class
PropertyStore extends AnyRef
A property store manages the execution of computations of properties related to specific concrete as well as artificial entities (e.g., methods, fields and classes of a program, but also the call graph as such etc.).
A property store manages the execution of computations of properties related to specific concrete as well as artificial entities (e.g., methods, fields and classes of a program, but also the call graph as such etc.). These computations may require and provide information about other entities of the store and the property store implements the logic to handle the computations related to the dependencies between the entities. Furthermore, the property store may parallelize the computation of the properties as far as possible without requiring users to take care of it; users are also generally not required to think about the concurrency when implementing an analysis as long as only immutable data-structures are used. The concepts are also described in the SOAP paper: "Lattice Based Modularization of Static Analyses" (https://conf.researchr.org/event/issta-2018/soap-2018-papers-lattice-based-modularization-of-static-analyses)
Usage
The correct strategy, when using the PropertyStore, is to always continue computing the property of an entity and to collect the dependencies on those elements that are (still) relevant. I.e., if some information is not or just not completely available, the analysis should still continue using the provided information and (internally) record the dependency. Later on, when the analysis has computed its result, it reports the same and informs the framework about its dependencies. Based on the later the framework will call back the analysis when a dependency is updated. In general, an analysis should always try to minimize the number of dependencies to the minimum set to enable the property store to suspend computations that are no longer required.
Core Requirements on Property Computation Functions (Modular Static Analyses)
The following requirements ensure correctness and determinism of the result.
- At Most One Lazy Function per Property Kind A specific kind of property is (in each
phase) always computed by only one registered lazy
PropertyComputationfunction. No other analysis is (conceptually) allowed to derive a value for an E/PK pairing for which a lazy function is registered. It is also not allowed to schedule a computation eagerly if a lazy computation is also registered. - Thread-Safe PropertyComputation functions If a single instance of a property computation function (which is the standard case) is scheduled for computing the properties of multiple entities, that function has to be thread safe. I.e., the function may be executed concurrently for different entities. The OnUpdateContinuation functions are, however, executed sequentially w.r.t. one E/PK pair.
- Non-Overlapping Results PropertyComputation functions that are invoked on different entities have to compute result sets that are disjoint unless a PartialResult is used. For example, an analysis that performs a computation on class files and that derives properties of a specific kind related to a class file's methods must ensure that two concurrent calls of the same analysis - running concurrently on two different class files - does not derive information about the same method. If results for a specific entity are collaboratively computed, then a PartialResult has to be used.
- Monoton If a PropertyComputation
function calculates (refines) a (new) property for a specific element, then the result must be equal or more specific.
Closed-strongly Connected Component Dependencies
In general, it may happen that some analyses cannot make any progress, because they are mutually dependent. In this case the computation of a property
pof an entitye1depends on the propertyp'of an entitye2that requires the propertypof the entitye1. In this case a registered strategy is used to resolve the cyclic dependency. If no strategy is available all current values will be committed, if no "current" value is available the fallback value will be committed.Thread Safety
The sequential property stores are not thread-safe; the parallelized implementation(s) are thread-safe in the following manner:
- a client has to use the SAME thread (the driver thread) to call the setupPhase,
registerLazyPropertyComputation, scheduleEagerComputationForEntity /
scheduleEagerComputationsForEntities, force and (finally)
PropertyStore#waitOnPhaseCompletion methods. Hence, the previously mentioned methods MUST
NO be called by PropertyComputation/OnUpdateComputation functions.
The methods to query the store (
apply) are thread-safe and can be called at any time.
Common Abbreviations
- e = Entity
- p = Property
- pk = Property Key
- pc = Property Computation
- lpc = Lazy Property Computation
- c = Continuation (The part of the analysis that factors in all properties of dependees)
- EPK = Entity and a PropertyKey
- EPS = Entity and an intermediate Property
- EP = Entity and some (final or intermediate) Property
- EOptionP = Entity and either a PropertyKey or (if available) a Property
Exceptions
In general, exceptions are only thrown if debugging is turned on due to the costs of checking for the respective violations. That is, if debugging is turned off, many potential errors leading to "incomprehensible" results will not be reported. Hence, after debugging an analysis turn debugging (and assertions!) off to get the best performance.
We will throw
IllegalArgumentException's iff a parameter is in itself invalid. E.g., the lower and upper bound do not have the same PropertyKind. In all other casesIllegalStateExceptions are thrown. All exceptions are either thrown immediately or eventually, when PropertyStore#waitOnPhaseCompletion is called. In the latter case, the exceptions are accumulated in the first thrown exception using suppressed exceptions. - At Most One Lazy Function per Property Kind A specific kind of property is (in each
phase) always computed by only one registered lazy
- class PropertyStoreContext[+T <: AnyRef] extends AnyRef
-
trait
PropertyStoreFactory extends AnyRef
Common interface implemented by all PropertyStore factories.
-
case class
Result(e: Entity, p: Property) extends FinalPropertyComputationResult with Product with Serializable
Encapsulates the final result of the computation of the property
pfor the given entitye.Encapsulates the final result of the computation of the property
pfor the given entitye. See EOptionP#ub for a discussion related to properties.- See also
FinalPropertyComputationResult for further information.
-
case class
Results(results: TraversableOnce[PropertyComputationResult]) extends PropertyComputationResult with Product with Serializable
Just a collection of multiple results.
Just a collection of multiple results. The results have to be disjoint w.r.t. the underlying e/pk pairs for which it contains results.
-
case class
Schedule(batches: Chain[Chain[ComputationSpecification]]) extends (PropertyStore) ⇒ Unit with Product with Serializable
Encapsulates a computed schedule and enables the execution of it.
Encapsulates a computed schedule and enables the execution of it. Use an AnalysisScenario to compute a schedule.
- batches
The representation of the computed schedule.
- case class SimplePIntermediateResult[P <: Property](e: Entity, ub: P, dependees: Traversable[SomeEOptionP], c: OnUpdateContinuation, hint: PropertyComputationHint = DefaultPropertyComputation) extends PropertyComputationResult with Product with Serializable
- final type SomeContinuation = Continuation[_ <: Property]
- final type SomeEOptionP = EOptionP[_ <: Entity, _ <: Property]
- final type SomeEPK = EPK[_ <: Entity, _ <: Property]
- final type SomeEPS = EPS[_ <: Entity, _ <: Property]
- final type SomeFallbackPropertyComputation = FallbackPropertyComputation[_ <: Entity, _ <: Property]
- final type SomeFinalEP = FinalEP[_ <: Entity, _ <: Property]
- final type SomePartialResult = PartialResult[_ >: Null <: Entity, _ >: Null <: Property]
- final type SomePropertyComputation = PropertyComputation[_ <: Entity]
- final type SomePropertyKey = PropertyKey[_ <: Property]
- case class SpecificationViolation(message: String) extends Exception with Product with Serializable
Value Members
- final val FrameworkName: String("OPAL Static Analyses Infrastructure")
-
object
AnalysisScenario
Factory to create an AnalysisScenario.
-
object
CheapPropertyComputation extends PropertyComputationHint with Product with Serializable
The property computation is extremely cheap.
The property computation is extremely cheap. Therefore, the computation can/should be processed in the current thread, it is extremely unlikely that we will gain anything from parallelization.
-
object
DefaultPropertyComputation extends PropertyComputationHint with Product with Serializable
A standard property computation.
-
object
EOptionP
Factory and extractor for EOptionP objects.
-
object
EPK
Factory and extractor for EPK objects.
-
object
EPS
Provides a factory and an extractor for EPS objects.
- object ESimplePS
-
object
ExplicitlyNamedProperty
Defines an extractor for an ExplicitlyNamedProperty.
- object FinalEP
- object IncrementalResult extends Serializable
- object IntermediateEP
- object IntermediateESimpleP
- object IntermediateResult extends Serializable
- object MultiResult extends Serializable
- object NoProperty
-
object
NoResult extends PropertyComputationResult
Used if the analysis found no entities for which a property could be computed.
Used if the analysis found no entities for which a property could be computed.
- Note
A
NoResultcan only be used as the result of an initial computation. Hence, anOnUpdateContinuationmust never returnNoResult.
- object PartialResult extends Serializable
-
object
PropertyIsNotComputedByAnyAnalysis extends FallbackReason with Product with Serializable
The fallback is used, because the property was queried, but was not explicitly computed in the past, is not computed now and will also not be computed in the future.
-
object
PropertyIsNotDerivedByPreviouslyExecutedAnalysis extends FallbackReason with Product with Serializable
The fallback is used, because the property was queried/is required, but the property was not computed for the specific entity though an analysis is scheduled/executed.
The fallback is used, because the property was queried/is required, but the property was not computed for the specific entity though an analysis is scheduled/executed.
- Note
This may happen for properties associated with dead code/code that is no used by the current project. E.g., the callers property of an unused library method is most likely not computed. If it is queried, then this is the Property that should be returned.
-
object
PropertyKey
Factory and registry for PropertyKey objects.
- object PropertyKind
-
object
PropertyStore
Manages general configuration options.
Manages general configuration options. Please note, that changes of these options can be done at any time.
- object PropertyStoreContext
- object Result extends Serializable
- object Results extends Serializable
- object SimplePIntermediateResult extends Serializable