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

org.evosuite.symbolic.solver.cvc4.CVC4Solver Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
 * contributors
 *
 * This file is part of EvoSuite.
 *
 * EvoSuite is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3.0 of the License, or
 * (at your option) any later version.
 *
 * EvoSuite is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with EvoSuite. If not, see .
 */
package org.evosuite.symbolic.solver.cvc4;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.evosuite.Properties;
import org.evosuite.symbolic.expr.Constraint;
import org.evosuite.symbolic.expr.Variable;
import org.evosuite.symbolic.solver.SmtExprBuilder;
import org.evosuite.symbolic.solver.SolverEmptyQueryException;
import org.evosuite.symbolic.solver.SolverErrorException;
import org.evosuite.symbolic.solver.SolverParseException;
import org.evosuite.symbolic.solver.SolverResult;
import org.evosuite.symbolic.solver.SolverTimeoutException;
import org.evosuite.symbolic.solver.SubProcessSolver;
import org.evosuite.symbolic.solver.smt.SmtAssertion;
import org.evosuite.symbolic.solver.smt.SmtCheckSatQuery;
import org.evosuite.symbolic.solver.smt.SmtConstantDeclaration;
import org.evosuite.symbolic.solver.smt.SmtExpr;
import org.evosuite.symbolic.solver.smt.SmtFunctionDeclaration;
import org.evosuite.symbolic.solver.smt.SmtFunctionDefinition;
import org.evosuite.symbolic.solver.smt.SmtIntVariable;
import org.evosuite.symbolic.solver.smt.SmtOperation;
import org.evosuite.symbolic.solver.smt.SmtOperation.Operator;
import org.evosuite.symbolic.solver.smt.SmtOperatorCollector;
import org.evosuite.symbolic.solver.smt.SmtRealVariable;
import org.evosuite.symbolic.solver.smt.SmtStringVariable;
import org.evosuite.symbolic.solver.smt.SmtVariable;
import org.evosuite.symbolic.solver.smt.SmtVariableCollector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class CVC4Solver extends SubProcessSolver {

	private boolean reWriteNonLinearConstraints = false;

	/**
	 * If enabled the translation will approximate non-linear constraints with
	 * concrete values
	 * 
	 * @param rewrite
	 */
	public void setRewriteNonLinearConstraints(boolean rewrite) {
		reWriteNonLinearConstraints = rewrite;
	}

	static Logger logger = LoggerFactory.getLogger(CVC4Solver.class);

	public CVC4Solver(boolean addMissingValues) {
		super(addMissingValues);
	}

	public CVC4Solver() {
		super();
	}

	@Override
	public SolverResult solve(Collection> constraints) throws SolverTimeoutException,
			SolverEmptyQueryException, SolverErrorException, SolverParseException, IOException {

		if (Properties.CVC4_PATH == null) {
			String errMsg = "Property CVC4_PATH should be setted in order to use the CVC4 Solver!";
			logger.error(errMsg);
			throw new IllegalStateException(errMsg);
		}

		// CVC4 has very little support for non-linear arithemtics
		// In fact, it cannot even produce models for non-linear theories
		if (!reWriteNonLinearConstraints && hasNonLinearConstraints(constraints)) {
			logger.debug("Skipping query due to (unsupported) non-linear constraints");
			throw new SolverEmptyQueryException("Skipping query due to (unsupported) non-linear constraints");
		}

		long cvcTimeout = Properties.DSE_CONSTRAINT_SOLVER_TIMEOUT_MILLIS;

		Set> variables = new HashSet>();
		for (Constraint c : constraints) {
			Set> c_variables = c.getVariables();
			variables.addAll(c_variables);
		}

		SmtCheckSatQuery smtQuery = buildSmtCheckSatQuery(constraints);

		if (smtQuery == null) {
			logger.debug("No variables found during the creation of the SMT query.");
			throw new SolverEmptyQueryException("No variables found during the creation of the SMT query.");
		}

		if (smtQuery.getAssertions().isEmpty()) {
			Map emptySolution = new HashMap();
			SolverResult emptySAT = SolverResult.newSAT(emptySolution);
			return emptySAT;
		}

		CVC4QueryPrinter printer = new CVC4QueryPrinter();
		String smtQueryStr = printer.print(smtQuery);

		if (smtQueryStr == null) {
			logger.debug("No variables found during constraint solving.");
			throw new SolverEmptyQueryException("No variables found during constraint solving.");
		}

		logger.debug("CVC4 Query:");
		logger.debug(smtQueryStr);

		String cvc4Cmd = buildCVC4cmd(cvcTimeout);

		ByteArrayOutputStream stdout = new ByteArrayOutputStream();

		try {
			launchNewProcess(cvc4Cmd, smtQueryStr, (int) cvcTimeout, stdout);

			String cvc4ResultStr = stdout.toString("UTF-8");

			if (cvc4ResultStr.startsWith("unsat") && cvc4ResultStr.contains(
					"(error \"Cannot get the current model unless immediately preceded by SAT/INVALID or UNKNOWN response.\")")) {
				// UNSAT
				SolverResult unsatResult = SolverResult.newUNSAT();
				return unsatResult;
			}

			if (cvc4ResultStr.contains("error")) {
				String errMsg = "An error occurred while executing CVC4!";
				logger.error(errMsg);
				throw new SolverErrorException(errMsg);
			}

			// parse solution
			Map initialValues = getConcreteValues(variables);
			CVC4ResultParser resultParser;
			if (addMissingVariables()) {
				resultParser = new CVC4ResultParser(initialValues);
			} else {
				resultParser = new CVC4ResultParser();
			}
			SolverResult solverResult = resultParser.parse(cvc4ResultStr);

			if (solverResult.isSAT()) {
				// check if the found solution is useful
				boolean check = checkSAT(constraints, solverResult);
				if (!check) {
					logger.debug("CVC4 solution does not solve the original constraint system. ");
					SolverResult unsatResult = SolverResult.newUNSAT();
					return unsatResult;
				}
			}

			return solverResult;

		} catch (IOException e) {
			if (e.getMessage().contains("Permission denied")) {
				logger.error("No permissions for running CVC4 binary");
			} else {
				logger.error("IO Exception during launching of CVC4 command");
			}
			throw e;

		}

	}

	private static SmtCheckSatQuery buildSmtCheckSatQuery(Collection> constraints) {

		ConstraintToCVC4Visitor v = new ConstraintToCVC4Visitor(true);
		SmtVariableCollector varCollector = new SmtVariableCollector();
		SmtOperatorCollector funCollector = new SmtOperatorCollector();

		List smtAssertions = new LinkedList();
		for (Constraint c : constraints) {
			SmtExpr smtExpr = c.accept(v, null);
			if (smtExpr != null) {
				SmtAssertion smtAssertion = new SmtAssertion(smtExpr);
				smtAssertions.add(smtAssertion);
				smtExpr.accept(varCollector, null);
				smtExpr.accept(funCollector, null);
			}
		}

		Set variables = varCollector.getSmtVariables();

		if (variables.isEmpty()) {
			return null; // no variables, constraint system is trivial
		}

		List functionDefinitions = new LinkedList();

		final boolean addCharToInt = funCollector.getOperators().contains(Operator.CHAR_TO_INT);
		if (addCharToInt) {
			String charToIntFunction = buildCharToIntFunction();
			SmtFunctionDefinition funcDefinition = new SmtFunctionDefinition(charToIntFunction);
			functionDefinitions.add(funcDefinition);
		}

		final boolean addIntToChar = funCollector.getOperators().contains(Operator.INT_TO_CHAR);
		if (addIntToChar) {
			String intToCharFunction = buildIntToCharFunction();
			SmtFunctionDefinition funcDefinition = new SmtFunctionDefinition(intToCharFunction);
			functionDefinitions.add(funcDefinition);
		}

		List functionDeclarations = new LinkedList();
		for (SmtVariable var : variables) {
			String varName = var.getName();
			if (var instanceof SmtIntVariable) {
				SmtFunctionDeclaration intVar = SmtExprBuilder.mkIntFunctionDeclaration(varName);
				functionDeclarations.add(intVar);

			} else if (var instanceof SmtRealVariable) {
				SmtFunctionDeclaration realVar = SmtExprBuilder.mkRealFunctionDeclaration(varName);
				functionDeclarations.add(realVar);

			} else if (var instanceof SmtStringVariable) {
				SmtFunctionDeclaration stringVar = SmtExprBuilder.mkStringFunctionDeclaration(varName);
				functionDeclarations.add(stringVar);
			} else {
				throw new RuntimeException("Unknown variable type " + var.getClass().getCanonicalName());
			}
		}

		SmtCheckSatQuery smtQuery = new SmtCheckSatQuery(new LinkedList(), functionDeclarations,
				functionDefinitions, smtAssertions);

		return smtQuery;

	}

	private static String buildCVC4cmd(long cvcTimeout) {
		String cmd = Properties.CVC4_PATH;
		cmd += "  --rewrite-divk"; // rewrite-divk rewrites division (or
									// modulus) by a constant value
		cmd += " --lang smt"; // query language is SMT-LIB
		cmd += " --finite-model-find";
		/**
		 * Option --finite-model-find has two effects:
		 * 
		 * 1. It extends the ground UF solver to
		 * "UF + finite cardinality constraints", as described in this paper:
		 * http://homepage.cs.uiowa.edu/~ajreynol/cav13.pdf This is used to
		 * minimize the size of the interpretation of uninterpreted sorts.
		 * 
		 * 2. It modifies the quantifier instantiation heuristics, as described
		 * in this paper: http://homepage.cs.uiowa.edu/~ajreynol/cade24.pdf In
		 * particular, it disables E-matching and enables
		 * "model-based quantifier instantiation", which is the strategy that
		 * enables CVC4 to answer "SAT" in the presence of universally
		 * quantified formulas.
		 * 
		 * More details on both of these points can be found in Sections 5.2 -
		 * 5.4 of http://homepage.cs.uiowa.edu/~ajreynol/thesis.pdf.
		 */
		cmd += " --tlimit=" + cvcTimeout; // set timeout to cvcTimeout
		return cmd;
	}

	private static boolean hasNonLinearConstraints(Collection> constraints) {
		NonLinearConstraintVisitor v = new NonLinearConstraintVisitor();
		for (Constraint constraint : constraints) {
			Boolean ret_val = constraint.accept(v, null);
			if (ret_val) {
				return true;
			}
		}
		return false;
	}

	// private final static int ASCII_TABLE_LENGTH = 256;
	private final static int ASCII_TABLE_LENGTH = 256;

	private static String buildIntToCharFunction() {
		StringBuffer buff = new StringBuffer();
		buff.append(SmtOperation.Operator.INT_TO_CHAR + "((!x Int)) String");
		buff.append("\n");
		for (int i = 0; i < ASCII_TABLE_LENGTH; i++) {
			String hexStr;
			if (i < 16) {
				hexStr = "0" + Integer.toHexString(i);
			} else {
				hexStr = Integer.toHexString(i);
			}
			String escapedHexStr = "\\x" + hexStr;
			if (i < ASCII_TABLE_LENGTH - 1) {
				String iteStr = String.format("(ite (= !x %s) \"%s\"", i, escapedHexStr);
				buff.append(iteStr);
				buff.append("\n");
			} else {
				buff.append(String.format("\"%s\"", escapedHexStr));
			}
		}
		for (int i = 0; i < ASCII_TABLE_LENGTH - 1; i++) {
			buff.append(")");
		}
		return buff.toString();
	}

	private static String buildCharToIntFunction() {
		StringBuffer buff = new StringBuffer();
		buff.append(SmtOperation.Operator.CHAR_TO_INT + "((!x String)) Int");
		buff.append("\n");
		for (int i = 0; i < ASCII_TABLE_LENGTH; i++) {
			String hexStr;
			if (i < 16) {
				hexStr = "0" + Integer.toHexString(i);
			} else {
				hexStr = Integer.toHexString(i);
			}
			String escapedHexStr = "\\x" + hexStr;
			if (i < ASCII_TABLE_LENGTH - 1) {
				String iteStr = String.format("(ite (= !x \"%s\") %s", escapedHexStr, i);
				buff.append(iteStr);
				buff.append("\n");
			} else {
				buff.append(i);
			}
		}
		for (int i = 0; i < ASCII_TABLE_LENGTH - 1; i++) {
			buff.append(")");
		}
		return buff.toString();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy