it.unive.lisa.analysis.string.SubstringDomain 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.string;
import it.unive.lisa.analysis.ScopeToken;
import it.unive.lisa.analysis.SemanticException;
import it.unive.lisa.analysis.SemanticOracle;
import it.unive.lisa.analysis.lattices.ExpressionInverseSet;
import it.unive.lisa.analysis.lattices.FunctionalLattice;
import it.unive.lisa.analysis.lattices.Satisfiability;
import it.unive.lisa.analysis.value.ValueDomain;
import it.unive.lisa.program.SyntheticLocation;
import it.unive.lisa.program.cfg.ProgramPoint;
import it.unive.lisa.symbolic.SymbolicExpression;
import it.unive.lisa.symbolic.value.BinaryExpression;
import it.unive.lisa.symbolic.value.Constant;
import it.unive.lisa.symbolic.value.Identifier;
import it.unive.lisa.symbolic.value.TernaryExpression;
import it.unive.lisa.symbolic.value.ValueExpression;
import it.unive.lisa.symbolic.value.Variable;
import it.unive.lisa.symbolic.value.operator.binary.BinaryOperator;
import it.unive.lisa.symbolic.value.operator.binary.LogicalAnd;
import it.unive.lisa.symbolic.value.operator.binary.LogicalOr;
import it.unive.lisa.symbolic.value.operator.binary.StringConcat;
import it.unive.lisa.symbolic.value.operator.binary.StringContains;
import it.unive.lisa.symbolic.value.operator.binary.StringEndsWith;
import it.unive.lisa.symbolic.value.operator.binary.StringEquals;
import it.unive.lisa.symbolic.value.operator.binary.StringStartsWith;
import it.unive.lisa.symbolic.value.operator.ternary.StringReplace;
import it.unive.lisa.symbolic.value.operator.ternary.StringSubstring;
import it.unive.lisa.type.Type;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* The substring relational abstract domain, tracking relation between string
* expressions. The domain is implemented as a {@link FunctionalLattice},
* mapping identifiers to string expressions, tracking which string expressions
* are definitely substring of an identifier. This domain follows the one
* defined
* in
* this paper.
*
* @author Michele
* Martelli
* @author Vincenzo Arceri
*/
public class SubstringDomain extends FunctionalLattice
implements
ValueDomain {
private static final SubstringDomain TOP = new SubstringDomain(new ExpressionInverseSet().top());
private static final SubstringDomain BOTTOM = new SubstringDomain(new ExpressionInverseSet().bottom());
/**
* Builds the top abstract value.
*/
public SubstringDomain() {
this(new ExpressionInverseSet());
}
/**
* Builds the abstract value by cloning the given function.
*
* @param lattice the underlying lattice
* @param function the function to clone
*/
protected SubstringDomain(
ExpressionInverseSet lattice,
Map function) {
super(lattice, function);
}
private SubstringDomain(
ExpressionInverseSet lattice) {
super(lattice);
}
@Override
public SubstringDomain lubAux(
SubstringDomain other)
throws SemanticException {
return functionalLift(other, lattice.top(), this::glbKeys, (
o1,
o2) -> o1.lub(o2)).clear();
}
@Override
public SubstringDomain glbAux(
SubstringDomain other)
throws SemanticException {
return functionalLift(other, lattice.top(), this::lubKeys, (
o1,
o2) -> o1.glb(o2)).closure();
}
@Override
public SubstringDomain top() {
return isTop() ? this : TOP;
}
@Override
public SubstringDomain bottom() {
return isBottom() ? this : BOTTOM;
}
@Override
public ExpressionInverseSet stateOfUnknown(
Identifier key) {
return lattice.top();
}
@Override
public SubstringDomain mk(
ExpressionInverseSet lattice,
Map function) {
return new SubstringDomain(lattice.isBottom() ? lattice.bottom() : lattice.top(), function);
}
@Override
public SubstringDomain assign(
Identifier id,
ValueExpression expression,
ProgramPoint pp,
SemanticOracle oracle)
throws SemanticException {
/*
* If the assigned expression is not dynamically typed as a string (or
* untyped) return this.
*/
if (oracle != null && pp != null && oracle.getRuntimeTypesOf(expression, pp, oracle).stream()
.allMatch(t -> !t.isStringType() && !t.isUntyped()))
return this;
/*
* The string type is unique and can be retrieved from the type system.
*/
Type strType;
if (pp != null) // Correct: get the string type from the program point
strType = pp.getProgram().getTypes().getStringType();
else // Used in tests where pp is null, get the string type from the
// expression
strType = expression.getStaticType();
Set expressions = extrPlus(expression, pp, oracle, strType);
SubstringDomain result = mk(lattice, mkNewFunction(function, false));
result = result.remove(expressions, id);
result = result.add(expressions, id);
result = result.interasg(id, expression);
result = result.closure(id);
return result.clear();
}
@Override
public SubstringDomain smallStepSemantics(
ValueExpression expression,
ProgramPoint pp,
SemanticOracle oracle)
throws SemanticException {
return this;
}
@Override
public SubstringDomain assume(
ValueExpression expression,
ProgramPoint src,
ProgramPoint dest,
SemanticOracle oracle)
throws SemanticException {
/*
* Assume only binary expressions
*/
if (expression instanceof BinaryExpression) {
BinaryExpression binaryExpression = (BinaryExpression) expression;
BinaryOperator binaryOperator = binaryExpression.getOperator();
SymbolicExpression left = binaryExpression.getLeft();
SymbolicExpression right = binaryExpression.getRight();
/*
* The string type is unique and can be retrieved from the type
* system.
*/
Type strType;
if (src != null) // Correct: get the string type from the program
// point
strType = src.getProgram().getTypes().getStringType();
else // Used in tests where src is null, get the string type from
// the expression
strType = left.getStaticType();
if (binaryOperator instanceof StringContains
|| binaryOperator instanceof StringStartsWith
|| binaryOperator instanceof StringEndsWith) {
/*
* Evaluate only if the left operand is an identidier
*/
if (!(left instanceof Identifier))
return this;
if (!(right instanceof ValueExpression))
throw new SemanticException("instanceof right");
Set extracted = extrPlus((ValueExpression) right, src, oracle, strType);
SubstringDomain result = mk(lattice, mkNewFunction(function, false));
result = result.add(extracted, (Identifier) left);
result = result.closure();
return result.clear();
} else if (binaryOperator instanceof StringEquals) {
// case both operands are identifiers
if ((left instanceof Identifier) && (right instanceof Identifier)) {
SubstringDomain result = mk(lattice, mkNewFunction(function, false));
result = result.add(left, (Identifier) right);
result = result.add(right, (Identifier) left);
result = result.closure();
return result.clear();
}
// case where only one is an identifier
else if ((left instanceof Identifier) || (right instanceof Identifier)) {
if (right instanceof Identifier) {
// make left the identifier
SymbolicExpression temp = left;
left = right;
right = temp;
}
if (!(right instanceof ValueExpression))
throw new SemanticException("instanceof right != ValueExpression.class");
Set add = extrPlus((ValueExpression) right, src, oracle, strType);
SubstringDomain result = mk(lattice, mkNewFunction(function, false));
result = result.add(add, (Identifier) left);
result = result.closure();
return result.clear();
}
} else if (binaryOperator instanceof LogicalOr || binaryOperator instanceof LogicalAnd) {
if (!(left instanceof ValueExpression) || !(right instanceof ValueExpression))
throw new SemanticException(
"!(left instanceof ValueExpression) || !(right instanceof ValueExpression)");
ValueExpression rightValueExpression = (ValueExpression) right;
ValueExpression leftValueExpression = (ValueExpression) left;
SubstringDomain leftDomain = assume(leftValueExpression, src, dest, oracle);
SubstringDomain rightDomain = assume(rightValueExpression, src, dest, oracle);
if (binaryOperator instanceof LogicalOr) {
return leftDomain.lub(rightDomain).clear();
} else {
return leftDomain.glb(rightDomain).clear();
}
}
}
return this;
}
@Override
public boolean knowsIdentifier(
Identifier id) {
if (id == null || function == null)
return false;
if (function.containsKey(id))
return true;
for (Map.Entry entry : function.entrySet()) {
for (SymbolicExpression expr : entry.getValue().elements) {
if (appears(id, expr))
return true;
}
}
return false;
}
@Override
public SubstringDomain forgetIdentifier(
Identifier id)
throws SemanticException {
if (!knowsIdentifier(id))
return this;
Map newFunction = mkNewFunction(function, false); // function
// !=
// null
newFunction.remove(id);
newFunction.replaceAll((
key,
value) -> removeFromSet(value, id));
return mk(lattice, newFunction);
}
@Override
public SubstringDomain forgetIdentifiersIf(
Predicate test)
throws SemanticException {
if (function == null || function.keySet().isEmpty())
return this;
// Get all identifiers
Set ids = new HashSet<>();
for (Map.Entry entry : function.entrySet()) {
ids.add(entry.getKey());
for (SymbolicExpression s : entry.getValue().elements()) {
if (s instanceof Identifier) {
Identifier id = (Identifier) s;
ids.add(id);
}
}
}
SubstringDomain result = mk(lattice, mkNewFunction(function, false));
for (Identifier id : ids) {
// Test each identifier
if (test.test(id)) {
result = result.forgetIdentifier(id);
}
}
return result;
}
@Override
public Satisfiability satisfies(
ValueExpression expression,
ProgramPoint pp,
SemanticOracle oracle)
throws SemanticException {
if (isBottom() || !(expression instanceof BinaryExpression))
return Satisfiability.UNKNOWN;
BinaryExpression binaryExpression = (BinaryExpression) expression;
BinaryOperator binaryOperator = binaryExpression.getOperator();
SymbolicExpression left = binaryExpression.getLeft();
SymbolicExpression right = binaryExpression.getRight();
if (binaryOperator instanceof StringContains) {
if (!(left instanceof Variable))
return Satisfiability.UNKNOWN;
return getState((Identifier) left).contains(right) ? Satisfiability.SATISFIED : Satisfiability.UNKNOWN;
} else if (binaryOperator instanceof StringEquals || binaryOperator instanceof StringEndsWith
|| binaryOperator instanceof StringStartsWith) {
if (!(left instanceof Variable) || !(right instanceof Variable))
return Satisfiability.UNKNOWN;
return (getState((Identifier) left).contains(right)) && (getState((Identifier) right).contains(left))
? Satisfiability.SATISFIED
: Satisfiability.UNKNOWN;
} else if (binaryOperator instanceof LogicalOr) {
if (!(left instanceof ValueExpression) || !(right instanceof ValueExpression))
throw new SemanticException(
"!(left instanceof ValueExpression) || !(right instanceof ValueExpression)");
Satisfiability leftSatisfiability = satisfies((ValueExpression) left, pp, oracle);
if (leftSatisfiability.equals(Satisfiability.SATISFIED))
return Satisfiability.SATISFIED;
Satisfiability rightSatisfiability = satisfies((ValueExpression) right, pp, oracle);
return rightSatisfiability;
} else if (binaryOperator instanceof LogicalAnd) {
if (!(left instanceof ValueExpression) || !(right instanceof ValueExpression))
throw new SemanticException(
"!(left instanceof ValueExpression) || !(right instanceof ValueExpression)");
Satisfiability leftSatisfiability = satisfies((ValueExpression) left, pp, oracle);
Satisfiability rightSatisfiability = satisfies((ValueExpression) right, pp, oracle);
if (leftSatisfiability.equals(Satisfiability.SATISFIED)
&& rightSatisfiability.equals(Satisfiability.SATISFIED))
return Satisfiability.SATISFIED;
else
return Satisfiability.UNKNOWN;
}
return Satisfiability.UNKNOWN;
}
@Override
public SubstringDomain pushScope(
ScopeToken token)
throws SemanticException {
return new SubstringDomain(lattice.pushScope(token), mkNewFunction(function, true));
}
@Override
public SubstringDomain popScope(
ScopeToken token)
throws SemanticException {
return new SubstringDomain(lattice.popScope(token), mkNewFunction(function, true));
}
/*
* Extract the ordered expressions of a concatenation. If the expression is
* not a concatenation returns an empty list. Example: {@code x + y + "ab"}
* returns {@code x, y, "ab"} as List
* @param expression to extract
* @return List containing the sub-expressions
*/
private static List extr(
ValueExpression expression) {
List result = new ArrayList<>();
if (expression instanceof BinaryExpression
&& ((BinaryExpression) expression).getOperator() instanceof StringConcat) {
BinaryExpression binaryExpression = (BinaryExpression) expression;
ValueExpression left = (ValueExpression) binaryExpression.getLeft();
ValueExpression right = (ValueExpression) binaryExpression.getRight();
result.addAll(extr(left));
result.addAll(extr(right));
} else
result.add(expression);
return result;
}
/*
* Returns all the possible substring of a given expression.
* @param expression
* @param strType
* @return
*/
private Set extrPlus(
ValueExpression expression,
ProgramPoint pp,
SemanticOracle oracle,
Type strType)
throws SemanticException {
List extracted = extr(expression);
/*
* If there are more than 2 consecutive constants, merge them into a
* single constant
*/
List> sub_expressions = split(extracted, pp, oracle, strType);
for (int i = 0; i < sub_expressions.size(); i++) {
List list = sub_expressions.get(i);
sub_expressions.set(i, mergeStringLiterals(list, strType));
}
Set result = new HashSet<>();
// Iterate sublists splitted from StringReplace expressions
for (List list : sub_expressions) {
// Iterate over the length of the expression
for (int l = 1; l <= list.size(); l++) {
// Iterate from the start to the end according to the size of
// the
// expression to create
for (int i = 0; i <= (list.size() - l); i++) {
List subList = list.subList(i, i + l);
result.add(composeExpression(subList));
if (subList.size() == 1 && subList.get(0) instanceof Constant) {
/*
* Case the expression to ass is a single constant ->
* add its substrings
*/
Set substrings = getSubstrings((Constant) subList.get(0));
for (Constant substring : substrings) {
List newList = new ArrayList<>();
newList.add(substring);
result.add(composeExpression(newList));
}
} else if (subList.get(0) instanceof Constant) {
/*
* Case the first expression of the expression to add is
* a constant -> add an expression for each suffix of
* the constant
*/
Set suffixes = getSuffix((Constant) subList.get(0));
for (Constant suffix : suffixes) {
List newList = new ArrayList<>(subList);
newList.set(0, suffix);
if (subList.get(subList.size() - 1) instanceof Constant) {
/*
* Case the last expression of the expression to
* add is a constant -> add an expression for
* each prefix of the constant
*/
Set prefixes = getPrefix((Constant) subList.get(subList.size() - 1));
for (Constant prefix : prefixes) {
newList.set(newList.size() - 1, prefix);
result.add(composeExpression(newList));
}
} else
result.add(composeExpression(newList));
}
} else if (subList.get(subList.size() - 1) instanceof Constant) {
/*
* Case the last expression of the expression to add is
* a constant -> add an expression for each prefix of
* the constant
*/
Set prefixes = getPrefix((Constant) subList.get(subList.size() - 1));
for (Constant prefix : prefixes) {
List newList = new ArrayList<>(subList);
newList.set(newList.size() - 1, prefix);
result.add(composeExpression(newList));
}
}
}
}
}
return new HashSet<>(result);
}
private List> split(
List extracted,
ProgramPoint pp,
SemanticOracle oracle,
Type strType)
throws SemanticException {
List> result = new ArrayList<>();
List temp = new ArrayList<>();
for (ValueExpression s : extracted) {
if (!(s instanceof Identifier || s instanceof Constant)) {
if (s instanceof TernaryExpression) {
TernaryExpression ternaryExpression = (TernaryExpression) s;
// strrep(c1, c2, c3) where c1 or c2 or c3 can be a
// concatenation of constants
if (ternaryExpression.getOperator() instanceof StringReplace
&& ternaryExpression.getLeft() instanceof ValueExpression
&& ternaryExpression.getMiddle() instanceof ValueExpression
&& ternaryExpression.getRight() instanceof ValueExpression
&& hasOnlyConstants((ValueExpression) ternaryExpression.getLeft())
&& hasOnlyConstants((ValueExpression) ternaryExpression.getMiddle())
&& hasOnlyConstants((ValueExpression) ternaryExpression.getRight())) {
// left, middle, right are only constants or
// concatenation of constants
List left = extr((ValueExpression) ternaryExpression.getLeft());
left = mergeStringLiterals(left, strType);
if (left.size() != 1 && !(left.get(0) instanceof Constant))
throw new SemanticException("unexpected");
Constant leftConstant = (Constant) left.get(0);
List middle = extr((ValueExpression) ternaryExpression.getMiddle());
middle = mergeStringLiterals(middle, strType);
if (middle.size() != 1 && !(middle.get(0) instanceof Constant))
throw new SemanticException("unexpected");
Constant middleConstant = (Constant) middle.get(0);
List right = extr((ValueExpression) ternaryExpression.getRight());
right = mergeStringLiterals(right, strType);
if (right.size() != 1 && !(right.get(0) instanceof Constant))
throw new SemanticException("unexpected");
Constant rightConstant = (Constant) right.get(0);
String s1 = leftConstant.getValue() instanceof String ? (String) leftConstant.getValue()
: leftConstant.getValue().toString();
String s2 = middleConstant.getValue() instanceof String ? (String) middleConstant.getValue()
: middleConstant.getValue().toString();
String s3 = rightConstant.getValue() instanceof String ? (String) rightConstant.getValue()
: rightConstant.getValue().toString();
temp.add(new Constant(strType, s1.replace(s2, s3), SyntheticLocation.INSTANCE));
continue;
}
// strsub(s1, i1, i2) where s1 can be a concatenation of
// constants
if (ternaryExpression.getOperator() instanceof StringSubstring
&& ternaryExpression.getLeft() instanceof ValueExpression
&& ternaryExpression.getMiddle() instanceof Constant
&& ternaryExpression.getRight() instanceof Constant
&& hasOnlyConstants((ValueExpression) ternaryExpression.getLeft())) {
List left = extr((ValueExpression) ternaryExpression.getLeft());
left = mergeStringLiterals(left, strType);
if (left.size() != 1 && !(left.get(0) instanceof Constant))
throw new SemanticException("unexpected");
Constant leftConstant = (Constant) left.get(0);
String s1 = leftConstant.getValue() instanceof String ? (String) leftConstant.getValue()
: leftConstant.getValue().toString();
Integer i1 = (Integer) ((Constant) ternaryExpression.getMiddle()).getValue();
Integer i2 = (Integer) ((Constant) ternaryExpression.getRight()).getValue();
temp.add(new Constant(strType, s1.substring(i1, i2), SyntheticLocation.INSTANCE));
continue;
}
}
// Split list
result.add(new ArrayList<>(temp));
temp.clear();
} else {
// s instance of Identifier || s instance of Constant
temp.add(s);
}
}
result.add(temp);
return result;
}
/*
* Returns true iff the parameter is a Constant or a concatenation of
* Constant values
*/
private boolean hasOnlyConstants(
ValueExpression expr) {
if (expr instanceof Constant)
return true;
if (expr instanceof BinaryExpression && ((BinaryExpression) expr).getOperator() instanceof StringConcat) {
BinaryExpression be = (BinaryExpression) expr;
if (!(be.getLeft() instanceof ValueExpression && be.getRight() instanceof ValueExpression))
return false;
return hasOnlyConstants((ValueExpression) be.getLeft())
&& hasOnlyConstants((ValueExpression) be.getRight());
}
return false;
}
/*
* Returns a list with no more than one consecutive constant where if {@code
* extracted} has consecutive constants, the returned value merges them
* @param extracted List to analyze
* @param strType
* @return The list without consecutive constants.
*/
private static List mergeStringLiterals(
List extracted,
Type strType) {
List result = new ArrayList<>();
StringBuilder recent = new StringBuilder();
for (ValueExpression expr : extracted) {
if (expr instanceof Constant) {
// Update the string builder value adding the value of the
// constant
Constant c = (Constant) expr;
recent.append(c.getValue() instanceof String ? (String) c.getValue() : c.getValue().toString());
} else {
/*
* Iterating a Variable
*/
if (recent.length() > 0) {
// The previous value / values are constants; add to the
// list the constant with value the built string
result.add(new Constant(strType, recent.toString(), SyntheticLocation.INSTANCE));
// Clear the string builder
recent.delete(0, recent.length());
}
// Add the variable
result.add(expr);
}
}
// If the last element is a constant add the last built string
if (recent.length() > 0)
result.add(new Constant(strType, recent.toString(), SyntheticLocation.INSTANCE));
return result;
}
/*
* Returns the set containing the substrings of a Constant
* @param c Constant to analyze
* @return The set containing the substrings of a Constant
*/
private static Set getSubstrings(
Constant c) {
Set result = new HashSet<>();
String str = c.getValue() instanceof String ? (String) c.getValue() : c.getValue().toString();
// Iterate over the length of the resulting string
for (int l = 1; l < str.length(); l++) {
// Iterate over the starting char
for (int i = 0; i <= str.length() - l; i++) {
Constant substring = new Constant(c.getStaticType(), str.substring(i, i + l),
SyntheticLocation.INSTANCE);
result.add(substring);
}
}
return result;
}
/*
* Returns the set containing the prefixes of a Constant
* @param c Constant to analyze
* @return The set containing the prefixes of a Constant
*/
private static Set getPrefix(
Constant c) {
Set result = new HashSet<>();
String str = c.getValue() instanceof String ? (String) c.getValue() : c.getValue().toString();
// Iterate over the length
for (int i = 1; i <= str.length(); i++) {
Constant prefix = new Constant(c.getStaticType(), str.substring(0, i), SyntheticLocation.INSTANCE);
result.add(prefix);
}
return result;
}
/*
* Returns the set containing the suffixes of a Constant
* @param c Constant to analyze
* @return The set containing the suffixes of a Constant
*/
private static Set getSuffix(
Constant c) {
Set result = new HashSet<>();
String str = c.getValue() instanceof String ? (String) c.getValue() : c.getValue().toString();
int length = str.length();
// Iterate over the length
for (int i = 1; i <= length; i++) {
Constant suffix = new Constant(c.getStaticType(), str.substring(length - i, length),
SyntheticLocation.INSTANCE);
result.add(suffix);
}
return result;
}
/*
* Creates am expression given a list. The returned expression is the
* ordered concatenation of the expression in the list.
* @param expressions
* @return The expression representing concatenation of the expressions in
* the list.
*/
private static SymbolicExpression composeExpression(
List expressions) {
if (expressions.size() == 1)
return expressions.get(0);
return new BinaryExpression(expressions.get(0).getStaticType(), expressions.get(0),
composeExpression(expressions.subList(1, expressions.size())), StringConcat.INSTANCE,
SyntheticLocation.INSTANCE);
}
/**
* Adds a set of expressions ({@code exprs}) for {@code id} to the this
* abstract value.
*
* @param exprs the set of symbolic expressions
* @param id the identifier
*
* @return a new abstract value with the added expressions
*
* @throws SemanticException if an error occurs during the computation
*/
protected SubstringDomain add(
Set exprs,
Identifier id)
throws SemanticException {
Map newFunction = mkNewFunction(function, false);
// Don't add the expressions that contain the key variable (ex: x -> x,
// x -> x + y will not be added)
Set expressionsToAdd = new HashSet<>();
for (SymbolicExpression se : exprs) {
if (!appears(id, se))
expressionsToAdd.add(se);
}
if (expressionsToAdd.isEmpty())
return this;
ExpressionInverseSet newSet = new ExpressionInverseSet(expressionsToAdd);
if (!(newFunction.get(id) == null))
newSet = newSet.glb(newFunction.get(id));
newFunction.put(id, newSet);
return mk(lattice, newFunction);
}
/**
* Adds a single expression ({@code expr}) for {@code id} to the this
* abstract value.
*
* @param expr the symbolic expression
* @param id the identifier
*
* @return a new abstract value with the added expression
*
* @throws SemanticException if an error occurs during the computation
*/
protected SubstringDomain add(
SymbolicExpression expr,
Identifier id)
throws SemanticException {
Set expression = new HashSet<>();
expression.add(expr);
return add(expression, id);
}
/*
* First step of assignment, removing obsolete relations.
* @param extracted Expression assigned
* @param id Expression getting assigned
* @return Copy of the domain, with the holding relations after the
* assignment.
* @throws SemanticException if an error occurs during the computation
*/
private SubstringDomain remove(
Set extracted,
Identifier id) {
Map newFunction = mkNewFunction(function, false);
// If assignment is similar to x = x + ..., then we keep current
// relations to x, otherwise we remove them.
if (!extracted.contains(id)) {
newFunction.remove(id);
}
// Remove relations containing id from the other entries
for (Map.Entry entry : newFunction.entrySet()) {
ExpressionInverseSet newSet = removeFromSet(entry.getValue(), id);
entry.setValue(newSet);
}
return mk(lattice, newFunction);
}
/*
* Returns a set without expressions containing id starting from source
*/
private static ExpressionInverseSet removeFromSet(
ExpressionInverseSet source,
Identifier id) {
Set newSet = source.elements.stream()
.filter(element -> !appears(id, element))
.collect(Collectors.toSet());
return newSet.isEmpty() ? new ExpressionInverseSet().top() : new ExpressionInverseSet(newSet);
}
/**
* Performs the inter-assignment phase
*
* @param assignedId Variable getting assigned
* @param assignedExpression Expression assigned
*
* @return Copy of the domain with new relations following the
* inter-assignment phase
*
* @throws SemanticException if an error occurs during the computation
*/
private SubstringDomain interasg(
Identifier assignedId,
SymbolicExpression assignedExpression)
throws SemanticException {
Map newFunction = mkNewFunction(function, false);
if (!knowsIdentifier(assignedId))
return this;
for (Map.Entry entry : function.entrySet()) {
// skip same entry
if (entry.getKey().equals(assignedId))
continue;
if (entry.getValue().contains(assignedExpression)) {
Set newRelation = new HashSet<>();
newRelation.add(assignedId);
ExpressionInverseSet newSet = newFunction.get(entry.getKey())
.glb(new ExpressionInverseSet(newRelation));
newFunction.put(entry.getKey(), newSet);
}
}
return mk(lattice, newFunction);
}
/**
* Performs the closure over an identifier. The method adds to {@code id}
* the expressions found in the variables mapped to {@code id}
*
* @return A copy of the domain with the added relations
*
* @throws SemanticException if an error occurs during the computation
*/
private SubstringDomain closure(
Identifier id)
throws SemanticException {
if (isTop() || isBottom())
return this;
SubstringDomain result = mk(lattice, mkNewFunction(function, false));
ExpressionInverseSet set = result.getState(id);
for (SymbolicExpression se : set) {
if (se instanceof Variable) {
Variable variable = (Variable) se;
// Variable found --> add the relations of variable to id
if (result.knowsIdentifier(variable)) {
Set add = new HashSet<>(result.getState(variable).elements);
result = result.add(add, id);
}
}
}
return result;
}
/**
* Performs the closure over the domain.
*
* @return A copy of the domain with the added relations
*
* @throws SemanticException if an error occurs during the computation
*/
protected SubstringDomain closure() throws SemanticException {
if (isTop() || isBottom())
return this;
SubstringDomain prev;
SubstringDomain result = mk(lattice, mkNewFunction(function, false));
do {
prev = mk(lattice, mkNewFunction(result.function, false));
Set set = prev.function.keySet();
for (Identifier id : set) {
// Perform the closure on every identifier of the domain
result = result.closure(id);
}
result = result.clear();
// Some relations may be added; check that close is applied
// correctly to the added relations
} while (!prev.equals(result));
return result;
}
private SubstringDomain clear() throws SemanticException {
Map newMap = mkNewFunction(function, false);
newMap.entrySet().removeIf(entry -> entry.getValue().isTop());
return new SubstringDomain(lattice, newMap);
}
private static boolean appears(
Identifier id,
SymbolicExpression expr) {
if (expr instanceof Identifier)
return id.equals(expr);
if (expr instanceof Constant)
return false;
if (expr instanceof BinaryExpression) {
BinaryExpression expression = (BinaryExpression) expr;
SymbolicExpression left = expression.getLeft();
SymbolicExpression right = expression.getRight();
return appears(id, left) || appears(id, right);
}
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy