it.unive.lisa.analysis.nonRedundantSet.NonRedundantPowerset 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.Lattice;
import it.unive.lisa.analysis.ScopeToken;
import it.unive.lisa.analysis.SemanticDomain;
import it.unive.lisa.analysis.SemanticException;
import it.unive.lisa.analysis.SemanticOracle;
import it.unive.lisa.analysis.lattices.Satisfiability;
import it.unive.lisa.analysis.lattices.SetLattice;
import it.unive.lisa.program.cfg.ProgramPoint;
import it.unive.lisa.symbolic.SymbolicExpression;
import it.unive.lisa.symbolic.value.Identifier;
import it.unive.lisa.util.representation.StringRepresentation;
import it.unive.lisa.util.representation.StructuredRepresentation;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Predicate;
/**
* This abstract class generalize the abstract domain lattice whose domain is
* the set of all the non redundant set in the power set of the domain of
* another lattice (in this case the other lattice is {@code }). It also
* defines the basic lattice operation for this domain (such as lub, glb,
* widening, lessOrEqual and other operations needed for the calculations of the
* previous ones). This implementations follows the guidelines of this
* paper.
*
* @param the concrete type of NonRedundantPowerset
* @param the concrete type of the elements in the sets
* @param the type of {@link SymbolicExpression} that {@code } and in
* turn this domain, can process
* @param the type of {@link Identifier} that {@code } and in turn this
* domain, handle
*/
public abstract class NonRedundantPowerset,
T extends SemanticDomain & Lattice,
E extends SymbolicExpression,
I extends Identifier> extends SetLattice implements SemanticDomain {
/**
* An instance of the underlying lattice from which top and bottom can be
* retrieved. It is necessary in certain basic lattice operation.
*/
public final T valueDomain;
/**
* Create an instance of non redundant set of elements of the type of
* valueDomain with the elements contained in elements.
*
* @param elements the elements to include in the lattice element
* @param isTop whether or not this element should be the top element
* or not
* @param valueDomain an instance of the underlying lattice
*/
public NonRedundantPowerset(
SortedSet elements,
boolean isTop,
T valueDomain) {
super(elements, isTop);
this.valueDomain = valueDomain;
}
/**
* 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 {
Set newElementsSet = new TreeSet<>();
for (T element : this.elements) {
if (!element.isBottom()) {
boolean toRemove = false;
for (T otherElement : this.elements)
if (element.lessOrEqual(otherElement) && !otherElement.lessOrEqual(element))
toRemove = true;
if (!toRemove)
newElementsSet.add(element);
}
}
return mk(newElementsSet);
}
@Override
public C top() {
return mk(new TreeSet<>(), true, this.valueDomain);
}
@Override
public C bottom() {
return mk(new TreeSet<>(), false, this.valueDomain);
}
@Override
public boolean isBottom() {
return !this.isTop && this.elements.isEmpty();
}
@Override
public boolean isTop() {
return this.isTop && this.elements.isEmpty();
}
/**
* 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 {
Set lubSet = new HashSet(this.elements);
lubSet.addAll(other.elements);
return mk(lubSet).removeRedundancy();
}
@Override
public C glbAux(
C other)
throws SemanticException {
Set glbSet = new TreeSet<>();
for (T s1 : this.elements)
for (T s2 : other.elements)
glbSet.add(s1.glb(s2));
return mk(glbSet).removeRedundancy();
}
/**
* An Egli-Milner connector is an upper bound operator for the
* {@link #lessOrEqualEgliMilner(NonRedundantPowerset) 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 {
Set unionSet = new HashSet(this.elements);
unionSet.addAll(other.elements);
T completeLub = valueDomain.bottom();
if (!unionSet.isEmpty()) {
for (T element : unionSet) {
completeLub = completeLub.lub(element);
}
}
Set newSet = new TreeSet<>();
if (completeLub != null)
newSet.add(completeLub);
return mk(newSet);
}
/**
* This method implements the widening-connected hextrapolation 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(NonRedundantPowerset) 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 {
Set extrapolatedSet = new TreeSet<>();
for (T s1 : this.elements) {
for (T s2 : other.elements) {
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(NonRedundantPowerset) Egli-Milner relation}
* and +EM is an Egli-Milner connector.
*/
@Override
public C wideningAux(
C other)
throws SemanticException {
if (lessOrEqualEgliMilner(other)) {
return extrapolationHeuristic(other).removeRedundancy();
} else {
return extrapolationHeuristic(EgliMilnerConnector(other)).removeRedundancy();
}
}
/**
* 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 (T s1 : this.elements) {
boolean existsGreaterElement = false;
for (T s2 : other.elements) {
if (s1.lessOrEqual(s2)) {
existsGreaterElement = true;
break;
}
}
if (!existsGreaterElement)
return false;
}
return true;
}
/**
* 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(NonRedundantPowerset)
* ≤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)) {
if (!isBottom()) {
for (T s2 : other.elements) {
boolean existsLowerElement = false;
for (T s1 : this.elements) {
if (s1.lessOrEqual(s2)) {
existsLowerElement = true;
break;
}
}
if (!existsLowerElement)
return false;
}
}
} else
return false;
return true;
}
@Override
public C assign(
I id,
E expression,
ProgramPoint pp,
SemanticOracle oracle)
throws SemanticException {
Set newElements = new TreeSet<>();
for (T elem : this.elements) {
newElements.add(elem.assign(id, expression, pp, oracle));
}
return mk(newElements).removeRedundancy();
}
@Override
public C smallStepSemantics(
E expression,
ProgramPoint pp,
SemanticOracle oracle)
throws SemanticException {
Set newElements = new TreeSet<>();
for (T elem : this.elements) {
newElements.add(elem.smallStepSemantics(expression, pp, oracle));
}
return mk(newElements).removeRedundancy();
}
@Override
public C assume(
E expression,
ProgramPoint src,
ProgramPoint dest,
SemanticOracle oracle)
throws SemanticException {
Set newElements = new TreeSet<>();
for (T elem : this.elements) {
newElements.add(elem.assume(expression, src, dest, oracle));
}
return mk(newElements).removeRedundancy();
}
@Override
public C forgetIdentifier(
Identifier id)
throws SemanticException {
Set newElements = new TreeSet<>();
for (T elem : this.elements) {
newElements.add(elem.forgetIdentifier(id));
}
return mk(newElements).removeRedundancy();
}
@Override
public C forgetIdentifiersIf(
Predicate test)
throws SemanticException {
Set newElements = new TreeSet<>();
for (T elem : this.elements) {
newElements.add(elem.forgetIdentifiersIf(test));
}
return mk(newElements).removeRedundancy();
}
@Override
public C pushScope(
ScopeToken token)
throws SemanticException {
Set newElements = new TreeSet<>();
for (T elem : this.elements) {
newElements.add(elem.pushScope(token));
}
return mk(newElements).removeRedundancy();
}
@Override
public C popScope(
ScopeToken token)
throws SemanticException {
Set newElements = new TreeSet<>();
for (T elem : this.elements) {
newElements.add(elem.popScope(token));
}
return mk(newElements).removeRedundancy();
}
@Override
public StructuredRepresentation representation() {
if (isBottom())
return Lattice.bottomRepresentation();
String representation = "[";
boolean first = true;
for (T element : this.elements) {
if (!first)
representation += ", ";
else
first = false;
representation += element.representation();
}
representation += "]";
return new StringRepresentation(representation);
}
@Override
public C mk(
Set set) {
SortedSet sorted = set instanceof SortedSet ? (SortedSet) set : new TreeSet<>(set);
if (set.isEmpty())
return mk(sorted, false, this.valueDomain);
for (T elem : set) {
if (elem.isTop())
return mk(new TreeSet<>(), true, this.valueDomain);
}
return mk(sorted, false, this.valueDomain);
}
/**
* Utility used for creating a concrete instance of
* {@link NonRedundantPowerset} given a set, whether or not the element is
* the top element and an instance of the the underlying lattice.
*
* @param set the set containing the elements that must be included
* in the lattice instance
* @param isTop wheter or not the element is top
* @param valueDomain an instance of the underlying lattice
*
* @return a new concrete instance of {@link NonRedundantPowerset} with the
* given configuration passed
*/
public abstract C mk(
SortedSet set,
boolean isTop,
T valueDomain);
@Override
public Satisfiability satisfies(
E expression,
ProgramPoint pp,
SemanticOracle oracle)
throws SemanticException {
Set setSatisf = new HashSet();
for (T element : this.elements)
setSatisf.add(element.satisfies(expression, pp, oracle));
if ((setSatisf.contains(Satisfiability.SATISFIED) && setSatisf.contains(Satisfiability.NOT_SATISFIED)) ||
setSatisf.contains(Satisfiability.UNKNOWN))
return Satisfiability.UNKNOWN;
else if (setSatisf.contains(Satisfiability.SATISFIED))
return Satisfiability.SATISFIED;
else if (setSatisf.contains(Satisfiability.NOT_SATISFIED))
return Satisfiability.NOT_SATISFIED;
return Satisfiability.UNKNOWN;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.elements == null) ? 0 : this.elements.hashCode());
result = prime * result + ((valueDomain == null) ? 0 : valueDomain.hashCode());
result = prime * result + (isTop ? 1231 : 1237);
return result;
}
@Override
public boolean equals(
Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NonRedundantPowerset, ?, ?, ?> other = (NonRedundantPowerset, ?, ?, ?>) obj;
if (this.elements == null) {
if (other.elements != null)
return false;
} else if (!this.elements.equals(other.elements))
return false;
if (valueDomain == null) {
if (other.valueDomain != null)
return false;
} else if (!valueDomain.equals(other.valueDomain))
return false;
if (isTop != other.isTop)
return false;
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy