Prerequisites:
  1. Program Points
  2. Lattices and Domain Lattices
  3. Symbolic Expressions

Semantic Domains

Semantic domains implement transfer functions over lattice structures. Contrarty to Lattices, a unique instance of a semantic domain is used for the whole analysis. Such instance is the one passed as part of the Configuration, and is thus user-defined. This page presents the SemanticDomain interface, its prerequisites, its main implementation and usages.

 Note:
This page contains class diagrams. Interfaces are represented with yellow rectangles, abstract classes with blue rectangles, and concrete classes with green rectangles. After type names, type parameters are reported, but their bounds are omitted for clarity. Only public members are listed in each type: the + symbol marks instance members, the * symbol marks static members, and a ! in front of the name denotes a member with a default implementation. Method-specific type parameters are written before the method name, wrapped in <>. When a class or interface has already been introduced in an earlier diagram, its inner members are omitted.

The Semantic Domain Interface

The SemanticDomain interface defines the available transfer functions that anyone interacting with a domain implementation can perform over a state (i.e., a DomainLattice). SemanticDomain has four type parameters:

SemanticDomain Interface Diagram

There are three main transformers defined in the SemanticDomain interface:

The assign and smallStepSemantics methods are the core of any semantic domain, and are called several times during the analysis of a program in different places. For instance, Statements use them to evaluate expressions and assignments, the Interprocedural Analysis uses assign to model parameter passing and return value assignments, and so on. Instead, assume is mainly called when traversing conditional Edges in a CFG.

 Warning:
Identifiers can be strong or weak. A strong identifier represent exactly one variable or memory location: when assigning a value to it, it is safe to forget any previous value (similarly to what an assignment to a variable does in any programming language). A weak identifier may instead represent multiple variables or memory locations (i.e., it might be a summary of several memory locations): when assigning a value to it, it is not safe to forget previous values, as they might still be relevant. When semantic domains assign values to weak identifiers, they must lub the new value and the previous one together, and store the result as the new value for that identifier.
 Important:
Regardless of the domain that is being implemented, all instances of SemanticDomain must return the bottom element when an assignment is performed on a bottom element. This ensures that unreachable states stay unreachable after assignments, and that errors originating in an evaluation preceeding an assignment are propagated.

SemanticDomain further defines four methods:

The satisfies method returns an instance of Satisfiability, a Lattice instance that models sets of boolean values (i.e., true, false, both, or none).

 Tip:
Implementing assume might be tricky. A simple and sound starting point is to implement it by returning the input state unchanged. If you implement satisfies, you can have assume return bottom when the input state does not satisfy the expression, and return the input state unchanged otherwise.
 Warning:
If no event listensers are registered for the analysis, the setEventQueue method will not be called. If your domain issues events, make sure to null-check the event queue before using it.

The Abstract Domain Interface

The AbstractDomain interface is a specialization of SemanticDomain that defines what a user of LiSA has to provide to implement an analysis. It is parametric on the type L, which extends AbstractLattice<L>, that is the concrete type of Abstract Lattices managed by the domain. The interface extends SemanticDomain<L, L, SymbolicExpression, Identifier>, meaning that the transformers return instances of the same type as the input state, and that they operate on generic symbolic expressions and identifiers.

AbstractDomain Interface Diagram

AbstractDomain adds one additional method to those already defined in SemanticDomain: makeOracle. This method returns a SemanticOracle, that roughly corresponds to a pair of the domain and a specific abstract state at a given program point. The oracle can be used to query the domain about properties that hold in that specific state:

Note that the oracle must be sound, but it is not required to be as precise as possible: for instance, the oracle might return all possible types when asked for the runtime types of an expression. Such information will be used throughout the analysis, e.g. when resolving call targets or when computing the semantics of a statement. The more precise the oracle is, the more precise the analysis will be, but also the more expensive it will be to compute.

For a list of abstract domains already implemented in LiSA, see the Configuration page.

The Analysis Class

The Analysis class is the central component that LiSA uses to evolve Analysis States. Since the AbstractDomain used for an analysis is user-provided, this allows analysis-independent implementations of other compoments (like statement semantics or interprocedural analyses): there is no compile-time binding between these entities and the concrete instances of AbstractDomain that will be executed, thus all operations that are performed by such entities have to be agnostic of the actual domain in place. This ensures a high level of modularity: all internal analysis components can be changed or replaced without requiring any modification of external ones.

The Analysis class is parametric on the type D, that extends AbstractDomain<A>, of the abstract domain to use during the analysis, and on the type A, that extends AbstractLattice<A> of the Abstract Lattices managed by D. Similarly to AbstractDomain, the Analysis class implements SemanticDomain<AnalysisState<A>, AnalysisState<A>, SymbolicExpression, Identifier>, meaning that the transformers return instances of the same type as the input state (that is, of the Analysis State), and that they operate on generic symbolic expressions and identifiers.

Analysis Class Diagram

The Analysis class contains the domain to execute and the shouldSmashErrors predicate as fields. The latter is a predicate passed to LiSA as part of the Configuration that decides which error types are not interesting for the user, and can thus be “smashed” inside the analysis state. Transformers defined by SemanticDomain are implemented by invoking the corresponding operation on the domain, using the normal execution continuation as source. The expression that has been evaluated (or, in the case of assignments, the Identifier that has been assigned) is stored in the ProgramState’s computed expressions.

Analysis also provides several additional methods: