
it.unive.lisa.analysis.nonRedundantSet.NonRedundantPowersetOfBaseNonRelationalValueDomain Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lisa-analyses Show documentation
Show all versions of lisa-analyses Show documentation
A library for static analysis
The newest version!
package it.unive.lisa.analysis.nonRedundantSet;
import it.unive.lisa.analysis.BaseLattice;
import it.unive.lisa.analysis.Lattice;
import it.unive.lisa.analysis.SemanticException;
import it.unive.lisa.analysis.SemanticOracle;
import it.unive.lisa.analysis.lattices.Satisfiability;
import it.unive.lisa.analysis.nonrelational.value.BaseNonRelationalValueDomain;
import it.unive.lisa.analysis.nonrelational.value.NonRelationalValueDomain;
import it.unive.lisa.program.cfg.ProgramPoint;
import it.unive.lisa.symbolic.value.Constant;
import it.unive.lisa.symbolic.value.operator.binary.BinaryOperator;
import it.unive.lisa.symbolic.value.operator.unary.UnaryOperator;
import it.unive.lisa.util.representation.SetRepresentation;
import it.unive.lisa.util.representation.StructuredRepresentation;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* This abstract class generalize the concept of an abstract domain whose domain
* is the finite non redundant subset of the domain of another lattice (in this
* case the other lattice is of type E). This implementation follows the
* guidelines of this
* paper. It is
* implemented as a {@link BaseNonRelationalValueDomain}, handling top and
* bottom values for the expression evaluation and bottom values for the
* expression satisfiability. Top and bottom cases for least upper bounds,
* widening and less or equals operations are handled by {@link BaseLattice} in
* {@link BaseLattice#lub}, {@link BaseLattice#widening} and
* {@link BaseLattice#lessOrEqual} methods, respectively.
*
* @param the concrete {@link NonRedundantPowerset} instance
* @param the type of the elements contained in this set
*/
public abstract class NonRedundantPowersetOfBaseNonRelationalValueDomain<
C extends NonRedundantPowersetOfBaseNonRelationalValueDomain,
E extends BaseNonRelationalValueDomain>
implements
BaseNonRelationalValueDomain {
/**
* The set that containing the elements.
*/
protected final SortedSet elementsSet;
/**
* The underlying {@link BaseNonRelationalValueDomain} by which it can be
* possible to retrieve top and bottom elements.
*/
protected final E valueDomain;
/**
* Creates an instance with elementsSet as elements and valueDomain as
* element.
*
* @param elements the set of elements in the set
* @param element the underlying {@link BaseNonRelationalValueDomain}
*/
protected NonRedundantPowersetOfBaseNonRelationalValueDomain(
SortedSet elements,
E element) {
elementsSet = new TreeSet<>(elements);
valueDomain = element.bottom();
}
/**
* Utility for creating a concrete instance of
* {@link NonRedundantPowersetOfBaseNonRelationalValueDomain} given a set.
*
* @param elements the set containing the elements that must be included in
* the lattice instance
*
* @return a new concrete instance of {@link NonRedundantPowerset}
* containing the elements of the given set
*/
protected abstract C mk(
SortedSet elements);
/**
* Yields a new concrete set of elements equivalent to this but that is not
* redundant. An element x of a subset S of the domain of a lattice is
* redundant iff exists another element y in S such that x ≤ y. Given a
* redundant set is always possible to construct an equivalent set that is
* not redundant. This operator is usually called omega reduction
* (represented as Ω). Given a subset S of a domain of a lattice:
* Ω(S) = S \ {s ∋ S | ( s = bottom ) OR ( ∃ s' ∋ S. s ≤
* s' )}
*
* @return an equivalent element that is not redundant.
*
* @throws SemanticException if an error occurs during the computation
*/
protected C removeRedundancy() throws SemanticException {
SortedSet newElementsSet = new TreeSet<>();
for (E element : elementsSet)
if (!element.isBottom()) {
boolean toRemove = false;
for (E otherElement : elementsSet)
if (element.lessOrEqual(otherElement) && !otherElement.lessOrEqual(element)) {
toRemove = true;
break;
}
if (!toRemove)
newElementsSet.add(element);
}
return mk(newElementsSet);
}
/**
* Yields a new concrete set less or equal to this that has not overlapping
* elements inside. Two elements of a set are overlapping iff their glb is
* not bottom. It uses an auxiliary method
* removeOverlappingBetweenElelements that knows how to remove the
* overlapping between two elements of type E.
*
* @return an equivalent set of elements that doesn't have overlapping
* elements.
*
* @throws SemanticException if an error occurs during the computation
*/
protected C removeOverlapping() throws SemanticException {
SortedSet newSet;
SortedSet tmpSet = this.elementsSet;
do {
newSet = tmpSet;
tmpSet = new TreeSet<>();
for (E e1 : newSet) {
boolean intersectionFound = false;
for (E e2 : newSet)
if (!e1.glb(e2).isBottom()) {
E notOverlappingElement = removeOverlappingBetweenElements(e1, e2);
if (!tmpSet.contains(notOverlappingElement))
tmpSet.add(notOverlappingElement);
intersectionFound = true;
}
if (!intersectionFound)
tmpSet.add(e1);
}
} while (tmpSet.size() != newSet.size());
return mk(tmpSet).removeRedundancy();
}
/**
* Yields a new element equivalent to the two overlapping elements e1 and
* e2. By default it calculates the lub between the two elements. But if
* exist a better method that looses less information it can be implemented
* in the concrete class.
*
* @param e1 the first element
* @param e2 the second element
*
* @return an element equivalent to the two overlapping elements e1 and e2
*
* @throws SemanticException if an error occurs during the computation
*/
protected E removeOverlappingBetweenElements(
E e1,
E e2)
throws SemanticException {
return e1.lub(e2);
}
/**
* An Egli-Milner connector is an upper bound operator for the
* {@link #lessOrEqualEgliMilner(NonRedundantPowersetOfBaseNonRelationalValueDomain)
* Egli-Milner relation ≤EM}. An Egli-Milner connector is
* represented as +EM. Given two subsets S1 and
* S2 of a domain of a lattice S1 +EM
* S2 = S3 such that ( S1 ≤EM
* S3 ) AND ( S1 ≤EM S3 ).
* The default implementation just performs the lub on the union of the two
* sets.
*
* @param other the other concrete element
*
* @return a new set that is ≤EM than both this and other
*
* @throws SemanticException if an error occurs during the computation
*/
protected C EgliMilnerConnector(
C other)
throws SemanticException {
SortedSet newSet = new TreeSet<>();
if (elementsSet.isEmpty() && other.elementsSet.isEmpty())
return mk(newSet);
E completeLub = valueDomain.bottom();
for (E element : elementsSet)
completeLub = completeLub.lub(element);
for (E element : other.elementsSet)
completeLub = completeLub.lub(element);
newSet.add(completeLub);
return mk(newSet);
}
@Override
public StructuredRepresentation representation() {
if (isBottom())
return Lattice.bottomRepresentation();
return new SetRepresentation(elementsSet, NonRelationalValueDomain::representation);
}
@Override
public String toString() {
return representation().toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((elementsSet == null) ? 0 : elementsSet.hashCode());
result = prime * result + ((valueDomain == null) ? 0 : valueDomain.hashCode());
return result;
}
@Override
public boolean equals(
Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NonRedundantPowersetOfInterval other = (NonRedundantPowersetOfInterval) obj;
if (elementsSet == null) {
if (other.elementsSet != null)
return false;
} else if (!elementsSet.equals(other.elementsSet))
return false;
if (valueDomain == null) {
if (other.valueDomain != null)
return false;
} else if (!valueDomain.equals(other.valueDomain))
return false;
return true;
}
/**
* Performs the least upper bound between this non redundant set and the
* given one. The lub between two non redundant sets is the union of the two
* removed of redundancy.
*/
@Override
public C lubAux(
C other)
throws SemanticException {
SortedSet lubSet = new TreeSet<>(elementsSet);
lubSet.addAll(other.elementsSet);
return mk(lubSet).removeRedundancy().removeOverlapping();
}
@Override
public C glbAux(
C other)
throws SemanticException {
SortedSet glbSet = new TreeSet<>();
for (E s1 : elementsSet)
for (E s2 : other.elementsSet)
glbSet.add(s1.glb(s2));
return mk(glbSet).removeRedundancy().removeOverlapping();
}
/**
* This method implements the widening-connected extrapolation heuristic
* proposed in the
* paper
* (represented as h∇). Given two subsets S1 and
* S2 of a domain of a lattice:
*
* h∇( S1, S2) = LUB (S2,
* Ω({ s1 ∇ s2 | s1 ∋
* S1, s2 ∋ S2, s1 <
* s2}))
*
* where
*
* - Ω is the {@link #removeRedundancy() omega reduction} operator
* that removes redundancy from a set,
* - ∇ is the widening operator of the underlying lattice,
* - < is the strict partial order relation of the underlying
* lattice,
* - LUB is the
* {@link #lubAux(NonRedundantPowersetOfBaseNonRelationalValueDomain) least
* upper bound} operator between non redundant subsets of the domain of the
* underlying lattice.
*
*
* @param other the other set to perform the extrapolation with
*
* @return the new extrapolated set
*
* @throws SemanticException if an error occurs during the computation
*/
protected C extrapolationHeuristic(
C other)
throws SemanticException {
SortedSet extrapolatedSet = new TreeSet<>();
for (E s1 : elementsSet)
for (E s2 : other.elementsSet)
if (s1.lessOrEqual(s2) && !s2.lessOrEqual(s1))
extrapolatedSet.add(s1.widening(s2));
return mk(extrapolatedSet).removeRedundancy().lub(other);
}
/**
* Perform the wideninig operation between two finite non redundant subsets
* of the domain of a lattice following the Egli-Milner widening
* implementation shown in this
* paper.
* Given two subset S1 and S2 of the domain of a
* lattice widening(S1, S2) =
* h∇(S1, T2), where
* h∇ is a widenining-connected extrapolation heuristic and
* T2 is equal to:
*
* - S2 if
* S1 ≤EM S2
* - S1 +EM S2 otherwise
*
* where ≤EM is the
* {@link #lessOrEqualEgliMilner(NonRedundantPowersetOfBaseNonRelationalValueDomain)
* Egli-Milner relation} and +EM is an Egli-Milner connector.
*/
@Override
public C wideningAux(
C other)
throws SemanticException {
C arg = lessOrEqualEgliMilner(other) ? other : EgliMilnerConnector(other);
return extrapolationHeuristic(arg).removeRedundancy().removeOverlapping();
}
/**
* Yields {@code true} if and only if this element is in Egli-Milner
* relation with the given one (represented as ≤EM). For two
* subset S1 and S2 of the domain of a lattice
* S1 ≤EM S2 iff: ( S1
* ≤S S2 ) AND ( ∀ s2 ∋
* S2, ∃ s1 ∋ S1 : s1
* ≤ s2 ). Where
* {@link #lessOrEqualAux(NonRedundantPowersetOfBaseNonRelationalValueDomain)
* ≤S} is the less or equal relation between sets. This
* operation is not commutative.
*
* @param other the other concrete element
*
* @return {@code true} if and only if that condition holds
*
* @throws SemanticException if an error occurs during the computation
*/
public boolean lessOrEqualEgliMilner(
C other)
throws SemanticException {
if (!lessOrEqual(other))
return false;
if (isBottom())
return true;
for (E s2 : other.elementsSet) {
boolean existsLowerElement = false;
for (E s1 : elementsSet)
if (s1.lessOrEqual(s2)) {
existsLowerElement = true;
break;
}
if (!existsLowerElement)
return false;
}
return true;
}
/**
* For two subset S1 and S2 of the domain of a lattice
* S1 ≤S S2 iff: ∀ s1
* ∋ S1, ∃ s2 ∋ S2 :
* s1 ≤ s2.
*/
@Override
public boolean lessOrEqualAux(
C other)
throws SemanticException {
for (E s1 : elementsSet) {
boolean existsGreaterElement = false;
for (E s2 : other.elementsSet)
if (s1.lessOrEqual(s2)) {
existsGreaterElement = true;
break;
}
if (!existsGreaterElement)
return false;
}
return true;
}
@Override
public C evalNonNullConstant(
Constant constant,
ProgramPoint pp,
SemanticOracle oracle)
throws SemanticException {
SortedSet newSet = new TreeSet<>();
newSet.add(valueDomain.evalNonNullConstant(constant, pp, oracle));
return mk(newSet).removeRedundancy().removeOverlapping();
}
@Override
public C evalUnaryExpression(
UnaryOperator operator,
C arg,
ProgramPoint pp,
SemanticOracle oracle)
throws SemanticException {
SortedSet newSet = new TreeSet<>();
for (E s : arg.elementsSet)
newSet.add(valueDomain.evalUnaryExpression(operator, s, pp, oracle));
return mk(newSet).removeRedundancy().removeOverlapping();
}
@Override
public C evalBinaryExpression(
BinaryOperator operator,
C left,
C right,
ProgramPoint pp,
SemanticOracle oracle)
throws SemanticException {
SortedSet newSet = new TreeSet<>();
for (E sLeft : left.elementsSet)
for (E sRight : right.elementsSet)
newSet.add(valueDomain.evalBinaryExpression(operator, sLeft, sRight, pp, oracle));
return mk(newSet).removeRedundancy().removeOverlapping();
}
@Override
public Satisfiability satisfiesBinaryExpression(
BinaryOperator operator,
C left,
C right,
ProgramPoint pp,
SemanticOracle oracle)
throws SemanticException {
if (left.isTop() || right.isTop())
return Satisfiability.UNKNOWN;
Satisfiability sat = Satisfiability.BOTTOM;
for (E sLeft : left.elementsSet)
for (E sRight : right.elementsSet)
sat = sat.lub(valueDomain.satisfiesBinaryExpression(operator, sLeft, sRight, pp, oracle));
return sat;
}
@Override
public C top() {
SortedSet topSet = new TreeSet<>();
topSet.add(valueDomain.top());
return mk(topSet);
}
@Override
public C bottom() {
return mk(Collections.emptySortedSet());
}
@Override
public boolean isBottom() {
return elementsSet.isEmpty();
}
@Override
public boolean isTop() {
for (E element : elementsSet)
if (element.isTop())
return true;
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy