package janala.solvers; import janala.config.Config; import janala.interpreters.*; import janala.interpreters.StringValue; import janala.utils.MyLogger; import janala.utils.FileUtil; import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.TreeMap; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; public class CVC4Solver implements Solver { public static enum RESULT_TYPE { TRUE, FALSE, UNKNOWN }; public static enum CONSTRAINT_TYPE { INT, STR }; public static enum SOLVER_STATUS { SAT, UNSAT, FAIL } private List<InputElement> inputs; public List<InputElement> getInputs() { return inputs; } private final Config config; private final FileUtil fileUtil; private List<String> solution; public List<String> getSolution() { return solution; } public CVC4Solver(Config config, FileUtil fileUtil) { this.config = config; this.fileUtil = fileUtil; this.solution = null; } public CVC4Solver() { config = Config.instance; fileUtil = new FileUtil(); this.solution = null; } List<Constraint> constraints; int pathConstraintIndex; private final static Logger logger = MyLogger.getLogger(CVC4Solver.class.getName()); private final static Logger tester = MyLogger.getTestLogger(Config.mainClass + "." + Config.iteration); public void setInputs(List<InputElement> inputs) { this.inputs = inputs; } public void setPathConstraint(List<Constraint> pathConstraint) { this.constraints = pathConstraint; } public void setPathConstraintIndex(int pathConstraintIndex) { this.pathConstraintIndex = pathConstraintIndex; } public static class Printer { private final Set<String> freeVars; private final Map<String, Long> soln; private final CONSTRAINT_TYPE type; private final PrintStream out; public Printer(Set<String> freeVars, Map<String, Long> sol, CONSTRAINT_TYPE type, PrintStream out) { this.freeVars = freeVars; this.soln = sol; this.type = type; this.out = out; } public void printSymInt(SymbolicInt c) { boolean first = true; for (Map.Entry<Integer, Long> it : c.getLinear().entrySet()) { if (first) { first = false; } else { out.print(" + "); } out.printf("%s*(%d)", "x" + String.valueOf(it.getKey()), it.getValue()); } if (c.getConstant() != 0) { out.printf(" + (%d)", c.getConstant()); } out.print(" " + c.getOp() + " "); out.print("0"); for (Map.Entry<Integer, Long> it : c.getLinear().entrySet()){ int integer = it.getKey(); freeVars.add("x" + integer); } } //Visible for testing public void printIntCompare(SymbolicIntCompareConstraint c) { out.printf("(%s) - (%s) %s 0", c.left, c.right, c.op); if (c.left.isSym) { freeVars.add(c.left.getSym()); } if (c.right.isSym) { freeVars.add(c.right.getSym()); } } public void printOr(SymbolicOrConstraint or){ if (or.constraints.isEmpty()) { out.print(" TRUE "); } else { boolean first = true; for (Constraint c : or.constraints) { if (first) { first = false; } else { out.print(" OR "); } out.print("("); print(c); // Recursion out.print(")"); } } } public void printAnd(SymbolicAndConstraint and) { if (and.constraints.isEmpty()) { out.print(" FALSE "); return; } boolean first = true; for (Constraint c : and.constraints) { if (first) { first = false; } else { out.print(" AND "); } out.print("("); print(c); out.print(")"); } } public void printNot(SymbolicNotConstraint not) { out.print(" NOT "); out.print("("); print(not.getConstraint()); out.print(")"); } public void print(Constraint con) { if (con instanceof SymbolicInt) { printSymInt((SymbolicInt)con); } else if (con instanceof SymbolicIntCompareConstraint) { printIntCompare((SymbolicIntCompareConstraint)con); } else if (con instanceof SymbolicOrConstraint) { printOr((SymbolicOrConstraint) con); } else if (con instanceof SymbolicAndConstraint) { printAnd((SymbolicAndConstraint) con); } else if (con instanceof SymbolicNotConstraint) { printNot((SymbolicNotConstraint) con); } else if (con instanceof SymbolicTrueConstraint) { out.print(" TRUE "); } else if (con instanceof SymbolicFalseConstraint) { out.print(" FALSE "); } else if (con instanceof SymbolicStringPredicate) { SymbolicStringPredicate str = (SymbolicStringPredicate) con; Constraint intConstraint = str.getFormula(freeVars, type, soln); print(intConstraint); } else { throw new RuntimeException("Unimplemented constraint type " + con); } } } private void print(Constraint con, PrintStream out, Set<String> freeVars, CONSTRAINT_TYPE type, Map<String, Long> soln) { new Printer(freeVars, soln, type, out).print(con); } public void visitSymbolicInt(SymbolicInt c) {} public void visitSymbolicOr(SymbolicOrConstraint c) {} public void visitSymbolicStringPredicate(SymbolicStringPredicate c) {} public void visitSymbolicAnd(SymbolicAndConstraint c) {} public void visitSymbolicNot(SymbolicNotConstraint c) {} public void visitSymbolicTrue(SymbolicTrueConstraint c) {} public void visitSymbolicFalse(SymbolicFalseConstraint c) {} public void visitSymbolicIntCompare(SymbolicIntCompareConstraint c) {} private boolean quickUnsatCheck(CONSTRAINT_TYPE type) { if (type == CONSTRAINT_TYPE.INT) { Constraint last = constraints.get(pathConstraintIndex); if (last instanceof SymbolicStringPredicate) { for (int i = 0; i < pathConstraintIndex; i++) { Constraint tmp = constraints.get(i); if (tmp.equals(last)) { return true; } } } } return false; } public boolean printFormula(PrintStream out, Map<String, Long> soln, Set<String> freeVars, String extra, CONSTRAINT_TYPE type) { boolean allTrue = true; for (int i = 0; i < pathConstraintIndex; i++) { out.print("ASSERT "); Constraint tmp = constraints.get(i).substitute(soln); if (tmp != SymbolicTrueConstraint.instance) { allTrue = false; } print(tmp, out, freeVars, type, soln); out.println(";"); } if (extra != null) { out.print("ASSERT "); out.print(extra); out.println(";"); } out.print("CHECKSAT "); //System.out.println("Constraint "+pathConstraintIndex+": !" + constraints.get(pathConstraintIndex)); Constraint notCon = constraints.get(pathConstraintIndex).not().substitute(soln); if (notCon != SymbolicTrueConstraint.instance) { allTrue = false; } print(notCon, out, freeVars, type, soln); out.println(";"); out.println("COUNTERMODEL;"); return allTrue; } private RESULT_TYPE writeFormula( String extra, CONSTRAINT_TYPE type, TreeMap<String, Long> soln) { try { PrintStream out = new PrintStream( new BufferedOutputStream(new FileOutputStream(config.formulaFile + ".tmp"))); if (quickUnsatCheck(type)) { return RESULT_TYPE.FALSE; } LinkedHashSet<String> freeVars = new LinkedHashSet<String>(); boolean allTrue = printFormula(out, soln, freeVars, extra, type); out.close(); concatFile( freeVars, config.formulaFile + ".tmp", config.formulaFile, true); return allTrue ? RESULT_TYPE.TRUE : RESULT_TYPE.UNKNOWN; } catch (IOException ioe) { ioe.printStackTrace(); logger.log(Level.SEVERE, "{0}", ioe); Runtime.getRuntime().halt(1); return RESULT_TYPE.UNKNOWN; } } public List<String> getSolution(Map<String, Long> soln) { List<String> result = new ArrayList<String>(); for (InputElement ielem : inputs) { Integer sym = ielem.symbol; Value val = ielem.value; if (sym.intValue() == config.scopeBeginSymbol) { result.add(config.scopeBeginMarker); } else if (sym.intValue() == config.scopeEndSymbol) { result.add(config.scopeEndMarker); } else { //System.out.println("sym "+sym); Long l = soln.get("x" + sym); if (l != null) { result.add(l.toString()); //System.out.println("l = " + l); } else { if (val instanceof StringValue) { StringValue sval = (StringValue) val; String old = sval.getConcrete(); assert sval.getSymbolicExp() != null; IntValue tmp = sval.getSymbolicExp().getField("length"); int len = (int) (long) tmp.substituteInLinear(soln); StringBuilder ret = new StringBuilder(); for (int i = 0; i < len; i++) { Long v = soln.get("x" + sym + "__" + i); char c; if (v != null) { c = (char) (long) v; } else if (i < old.length()) { c = old.charAt(i); } else { c = 'a'; } ret.append(c); } result.add(ret.toString()); } else { result.add(val.getConcrete().toString()); } } } } return result; } private void processResults(TreeMap<String, Long> soln) { solution = getSolution(soln); } public String processInputs(BufferedReader br, Map<String, Long> soln) { String line = null; String negatedSolution = null; try { if (config.printFormulaAndSolutions) { System.out.println("-----------Solution-------------"); } line = br.readLine(); if (config.printFormulaAndSolutions) { System.out.println(line); } if (!line.startsWith("sat")) { if (!line.contains("unsat")) { logger.log(Level.SEVERE, line); logger.log( Level.SEVERE, "Call to CVC4 failed (concolic.cvc4 = " + config.cvc4Command + ")"); Runtime.getRuntime().halt(1); } logger.log(Level.INFO, "-- Infeasible"); while ((line = br.readLine()) != null) { if (config.printFormulaAndSolutions) { System.out.println(line); } } br.close(); return null; } else { while ((line = br.readLine()) != null) { if (config.printFormulaAndSolutions) { System.out.println(line); } String[] tokens = line.split(" "); if (tokens.length == 5) { String tmp = tokens[4].trim(); tmp = tmp.substring(0, tmp.indexOf(";")); if (negatedSolution != null) { negatedSolution += " AND (" + tokens[0] + " = " + tmp + " )"; } else { negatedSolution = "(" + tokens[0] + " = " + tmp + " )"; } long val = Long.parseLong(tmp); soln.put(tokens[0], val); } } br.close(); negatedSolution = "(NOT (" + negatedSolution + "))"; return negatedSolution; } } catch (IOException ioe) { ioe.printStackTrace(); logger.log(Level.SEVERE, "{0}", ioe); Runtime.getRuntime().halt(1); return null; } } public boolean solve() { int count = 0, MAX_COUNT = 100; String extra = null; while (count < MAX_COUNT) { TreeMap<String, Long> soln = new TreeMap<String, Long>(); String negatedSolution = solve(extra, CONSTRAINT_TYPE.INT, soln); if (negatedSolution != null) { String negatedSolution2 = solve(null, CONSTRAINT_TYPE.STR, soln); if (negatedSolution2 != null) { processResults(soln); tester.log(Level.INFO, "Feasible = true at " + pathConstraintIndex); return true; } else { if (extra != null) { extra = extra + " AND " + negatedSolution; } else { extra = negatedSolution; } } } else { tester.log(Level.INFO, "Feasible = false at " + pathConstraintIndex); return false; } count++; } tester.log(Level.INFO, "Feasible = false at " + pathConstraintIndex); return false; } private String solve(String extra, CONSTRAINT_TYPE type, TreeMap<String, Long> soln) { try { RESULT_TYPE res; String negatedSolution; if ((res = writeFormula(extra, type, soln)) == RESULT_TYPE.TRUE) { return ""; } else if (res == RESULT_TYPE.FALSE) { return null; } ProcessBuilder builder = new ProcessBuilder( new String[] { config.cvc4Command, "--lang", "cvc4", config.formulaFile }); builder.redirectErrorStream(true); Process process = builder.start(); (new StreamGobbler(process.getErrorStream(), "ERROR")).start(); InputStream is = process.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); negatedSolution = processInputs(br, soln); process.waitFor(); return negatedSolution; } catch (IOException ioe) { ioe.printStackTrace(); logger.log(Level.SEVERE, "{0}", ioe); Runtime.getRuntime().halt(1); return null; } catch (InterruptedException ie) { ie.printStackTrace(); logger.log(Level.SEVERE, "{0}", ie); Runtime.getRuntime().halt(1); return null; } } //VisbleForTest public void concatStreams(List<PrintStream> ps, Set<String> freeVars, String from, boolean cvc4) throws java.io.IOException { for (PrintStream pw : ps) { if (cvc4) { pw.println("OPTION \"produce-models\";"); } for (String var : freeVars) { pw.print(var); pw.println(" :INT;"); } fileUtil.copyContent(from, pw); } } public void concatFile( LinkedHashSet<String> freeVars, String from, String to, boolean cvc4) throws java.io.IOException { List<PrintStream> ps = new ArrayList<PrintStream>(); PrintStream toStream = new PrintStream(new BufferedOutputStream(new FileOutputStream(to))); ps.add(toStream); if (config.printFormulaAndSolutions) { System.out.println("-----------Formula-------------"); ps.add(System.out); } concatStreams(ps, freeVars, from, cvc4); toStream.close(); } }