All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.unive.lisa.analysis.string.CharInclusion Maven / Gradle / Ivy

The newest version!
package it.unive.lisa.analysis.string;

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.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.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.StringIndexOf;
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.TernaryOperator;
import it.unive.lisa.util.numeric.IntInterval;
import it.unive.lisa.util.numeric.MathNumber;
import it.unive.lisa.util.representation.StringRepresentation;
import it.unive.lisa.util.representation.StructuredRepresentation;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;

/**
 * The suffix string abstract domain.
 *
 * @author Vincenzo Arceri
 * @author Sergio
 *             Salvatore Evola
 *
 * @see 
 *          https://link.springer.com/chapter/10.1007/978-3-642-24559-6_34
 */
public class CharInclusion implements BaseNonRelationalValueDomain, ContainsCharProvider {

	private final Set certainlyContained;

	private final Set maybeContained;

	private static final CharInclusion TOP = new CharInclusion();
	private static final CharInclusion BOTTOM = new CharInclusion(null, null);

	/**
	 * Builds the top char inclusion abstract element.
	 */
	public CharInclusion() {
		this(new TreeSet<>(), null);
	}

	/**
	 * Builds a char inclusion abstract element.
	 *
	 * @param certainlyContained the set of certainly contained characters
	 * @param maybeContained     the set of maybe contained characters
	 */
	public CharInclusion(
			Set certainlyContained,
			Set maybeContained) {
		this.certainlyContained = certainlyContained;
		this.maybeContained = maybeContained;
	}

	@Override
	public CharInclusion lubAux(
			CharInclusion other)
			throws SemanticException {
		Set lubAuxCertainly = new TreeSet<>();

		Set lubAuxMaybe;
		if (maybeContained == null || other.maybeContained == null)
			lubAuxMaybe = null;
		else {
			lubAuxMaybe = new TreeSet<>();
			lubAuxMaybe.addAll(maybeContained);
			lubAuxMaybe.addAll(other.maybeContained);
		}

		for (Character certainlyContainedChar : this.certainlyContained)
			if (other.certainlyContained.contains(certainlyContainedChar))
				lubAuxCertainly.add(certainlyContainedChar);

		return new CharInclusion(lubAuxCertainly, lubAuxMaybe);
	}

	@Override
	public boolean lessOrEqualAux(
			CharInclusion other)
			throws SemanticException {
		if (this.certainlyContained.size() > other.certainlyContained.size())
			return false;
		if (!other.certainlyContained.containsAll(certainlyContained))
			return false;
		if (other.maybeContained == null)
			return true;
		if (maybeContained == null)
			return false;
		if (this.maybeContained.size() > other.maybeContained.size())
			return false;
		return other.maybeContained.containsAll(this.maybeContained);
	}

	@Override
	public boolean equals(
			Object o) {
		if (this == o)
			return true;
		if (o == null || getClass() != o.getClass())
			return false;
		CharInclusion that = (CharInclusion) o;
		return Objects.equals(certainlyContained, that.certainlyContained)
				&& Objects.equals(maybeContained, that.maybeContained);
	}

	@Override
	public int hashCode() {
		return Objects.hash(certainlyContained, maybeContained);
	}

	@Override
	public CharInclusion top() {
		return TOP;
	}

	@Override
	public CharInclusion bottom() {
		return BOTTOM;
	}

	@Override
	public StructuredRepresentation representation() {
		if (isBottom())
			return Lattice.bottomRepresentation();
		if (isTop())
			return Lattice.topRepresentation();

		return new StringRepresentation(formatRepresentation());
	}

	/**
	 * Yields the set of certainly contained characters of this abstract value.
	 *
	 * @return the set of certainly contained characters of this abstract value.
	 */
	public Set getCertainlyContained() {
		return this.certainlyContained;
	}

	/**
	 * Yields the set of maybe contained characters of this abstract value.
	 *
	 * @return the set of maybe contained characters of this abstract value, or
	 *             {@code null} if the whole alphabet might be part of the
	 *             string
	 */
	public Set getMaybeContained() {
		return this.maybeContained;
	}

	private String formatRepresentation() {
		return "CertainlyContained: {" + StringUtils.join(this.certainlyContained, ", ") +
				"}, MaybeContained: {" + (maybeContained == null ? "Σ" : StringUtils.join(this.maybeContained, ", ")) +
				"}";
	}

	@Override
	public CharInclusion evalNonNullConstant(
			Constant constant,
			ProgramPoint pp,
			SemanticOracle oracle) {
		if (constant.getValue() instanceof String) {
			Set charsSet = ((String) constant.getValue()).chars()
					.mapToObj(e -> (char) e).collect(Collectors.toCollection(TreeSet::new));

			return new CharInclusion(charsSet, charsSet);
		}

		return TOP;
	}

	@Override
	public CharInclusion evalBinaryExpression(
			BinaryOperator operator,
			CharInclusion left,
			CharInclusion right,
			ProgramPoint pp,
			SemanticOracle oracle) {
		if (operator == StringConcat.INSTANCE) {
			Set resultCertainlyContained = new TreeSet<>();
			resultCertainlyContained.addAll(left.certainlyContained);
			resultCertainlyContained.addAll(right.certainlyContained);

			Set resultMaybeContained;
			if (left.maybeContained == null || right.maybeContained == null)
				resultMaybeContained = null;
			else {
				resultMaybeContained = new TreeSet<>();
				resultMaybeContained.addAll(left.maybeContained);
				resultMaybeContained.addAll(right.maybeContained);
			}

			return new CharInclusion(resultCertainlyContained, resultMaybeContained);
		}

		else if (operator == StringContains.INSTANCE ||
				operator == StringEndsWith.INSTANCE ||
				operator == StringEquals.INSTANCE ||
				operator == StringIndexOf.INSTANCE ||
				operator == StringStartsWith.INSTANCE) {
			return TOP;
		}

		return TOP;
	}

	@Override
	public CharInclusion evalTernaryExpression(
			TernaryOperator operator,
			CharInclusion left,
			CharInclusion middle,
			CharInclusion right,
			ProgramPoint pp,
			SemanticOracle oracle)
			throws SemanticException {
		if (operator == StringReplace.INSTANCE) {

			if (!left.certainlyContained.containsAll(middle.certainlyContained))
				// no replace for sure
				return this;

			Set included = new TreeSet<>(left.certainlyContained);
			Set possibly = new TreeSet<>(left.maybeContained);
			// since we do not know if the replace will happen, we move
			// everything to the
			// possibly included characters
			included.removeAll(middle.certainlyContained);
			possibly.addAll(middle.certainlyContained);

			included.removeAll(middle.maybeContained);
			Set tmp = new TreeSet<>(middle.maybeContained);
			tmp.retainAll(left.certainlyContained); // just the ones that
													// we removed before
			possibly.addAll(tmp);

			// add the second string
			possibly.addAll(right.certainlyContained);
			possibly.addAll(right.maybeContained);

			return new CharInclusion(included, possibly);
		}

		return TOP;
	}

	@Override
	public Satisfiability satisfiesBinaryExpression(
			BinaryOperator operator,
			CharInclusion left,
			CharInclusion right,
			ProgramPoint pp,
			SemanticOracle oracle) {
		if (left.isTop() || right.isBottom())
			return Satisfiability.UNKNOWN;

		if (operator == StringContains.INSTANCE)
			if (right.isEmptyString())
				return Satisfiability.SATISFIED;

		return Satisfiability.UNKNOWN;
	}

	/**
	 * Checks whether this char inclusion abstract value models the empty
	 * string, i.e., the sets of the maybe and certainly contained are both
	 * empty.
	 *
	 * @return whether this char inclusion abstract value models the empty
	 *             string
	 */
	private boolean isEmptyString() {
		return (maybeContained != null && maybeContained.isEmpty()) && certainlyContained.isEmpty();
	}

	/**
	 * Yields the char inclusion abstract value corresponding to the substring
	 * of this char inclusion abstract value between two indexes.
	 * 
	 * @param begin where the substring starts
	 * @param end   where the substring ends
	 * 
	 * @return the char inclusion abstract value corresponding to the substring
	 *             of this char inclusion abstract value between two indexes
	 */
	public CharInclusion substring(
			long begin,
			long end) {
		if (isTop() || isBottom())
			return this;
		return new CharInclusion(new TreeSet<>(), maybeContained);
	}

	/**
	 * Yields the {@link IntInterval} containing the minimum and maximum length
	 * of this abstract value.
	 * 
	 * @return the minimum and maximum length of this abstract value
	 */
	public IntInterval length() {
		return new IntInterval(new MathNumber(certainlyContained.size()), MathNumber.PLUS_INFINITY);
	}

	/**
	 * Yields the {@link IntInterval} containing the minimum and maximum index
	 * of {@code s} in {@code this}.
	 *
	 * @param s the string to be searched
	 * 
	 * @return the minimum and maximum index of {@code s} in {@code this}
	 */
	public IntInterval indexOf(
			CharInclusion s) {
		return new IntInterval(MathNumber.MINUS_ONE, MathNumber.PLUS_INFINITY);
	}

	@Override
	public Satisfiability containsChar(
			char c) {
		if (isTop())
			return Satisfiability.UNKNOWN;
		if (isBottom())
			return Satisfiability.BOTTOM;
		if (this.certainlyContained.contains(c))
			return Satisfiability.SATISFIED;
		else if (this.maybeContained == null || this.maybeContained.contains(c))
			return Satisfiability.UNKNOWN;
		else
			return Satisfiability.NOT_SATISFIED;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy