package jayhorn.checker;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.google.common.base.Verify;
import jayhorn.Log;
import jayhorn.Options;
import jayhorn.hornify.HornEncoderContext;
import jayhorn.hornify.HornPredicate;
import jayhorn.hornify.Hornify;
import jayhorn.solver.Prover;
import jayhorn.solver.ProverExpr;
import jayhorn.solver.ProverFactory;
import jayhorn.solver.ProverHornClause;
import jayhorn.solver.ProverResult;
import jayhorn.utils.GhostRegister;
import jayhorn.utils.HeapCounterTransformer;
import jayhorn.utils.Stats;
import soottocfg.cfg.Program;
import soottocfg.cfg.method.Method;
import soottocfg.cfg.type.IntType;
import soottocfg.cfg.variable.ClassVariable;
import soottocfg.cfg.variable.Variable;
/**
* @author teme
*/
public class EldaricaChecker extends Checker{
private ProverFactory factory;
private Prover prover;
public EldaricaChecker(ProverFactory factory) {
this.factory = factory;
}
private List<ProverHornClause> allClauses = new LinkedList<ProverHornClause>();
public boolean checkProgram(Program program) {
Preconditions.checkNotNull(program.getEntryPoint(),
"The program has no entry points and thus is trivially verified.");
GhostRegister.reset();
if (soottocfg.Options.v().memPrecision() >= 2) {
GhostRegister.v().ghostVariableMap.put("pushID", IntType.instance());
}
if (Options.v().useCallIDs) {
Log.info("Inserting call IDs ... ");
Verify.verify(false, "Don't run this for now!");
// CallingContextTransformer cct = new CallingContextTransformer();
// cct.transform(program);
}
HeapCounterTransformer hct = new HeapCounterTransformer();
hct.transform(program);
if (Options.v().printCFG) {
System.out.println(program);
}
Log.info("Hornify ... ");
Hornify hf = new Hornify(factory);
Stopwatch toHornTimer = Stopwatch.createStarted();
HornEncoderContext hornContext = hf.toHorn(program);
Stats.stats().add("ToHorn", String.valueOf(toHornTimer.stop()));
prover = hf.getProver();
allClauses.addAll(hf.clauses);
if (Options.v().getPrintHorn()) {
System.out.println(hf.writeHorn());
}
ProverResult result = ProverResult.Unknown;
try {
final Method entryPoint = program.getEntryPoint();
Log.info("Running from entry point: " + entryPoint.getMethodName());
prover.push();
// add an entry clause from the preconditions
final HornPredicate entryPred = hornContext.getMethodContract(entryPoint).precondition;
Map<Variable, ProverExpr> initialState = new HashMap<Variable, ProverExpr>();
//Set the heap counter initially to one (because 0 is reserved for null)
final ProverExpr entryAtom = entryPred.instPredicate(initialState);
final ProverHornClause entryClause = prover.mkHornClause(entryAtom, new ProverExpr[0],
prover.mkLiteral(true));
allClauses.add(entryClause);
Hornify.hornToSMTLIBFile(allClauses, 0, prover);
Hornify.hornToFile(allClauses, 0);
for (ProverHornClause clause : allClauses)
prover.addAssertion(clause);
Stopwatch satTimer = Stopwatch.createStarted();
if (jayhorn.Options.v().getTimeout() > 0) {
int timeoutInMsec = (int) TimeUnit.SECONDS.toMillis(jayhorn.Options.v().getTimeout());
prover.checkSat(false);
result = prover.getResult(timeoutInMsec);
} else {
result = prover.checkSat(true);
}
printHeapInvariants(hornContext);
Stats.stats().add("CheckSatTime", String.valueOf(satTimer.stop()));
// for (Entry<ClassVariable, Map<Long, HornPredicate>> entry : hornContext.getInvariantPredicates().entrySet()) {
//
// for (Entry<Long, HornPredicate> entry2 : entry.getValue().entrySet()) {
// ProverExpr pe = prover.evaluate(entry2.getValue().instPredicate(new HashMap<Variable, ProverExpr>()));
// System.err.println(pe);
// }
//
//
// }
allClauses.remove(allClauses.size() - 1);
prover.pop();
} catch (Throwable t) {
t.printStackTrace();
throw new RuntimeException(t);
} finally {
prover.shutdown();
}
if (result == ProverResult.Sat) {
return true;
} else if (result == ProverResult.Unsat) {
return false;
}
throw new RuntimeException("Verification failed with prover code " + result);
}
private void printHeapInvariants(HornEncoderContext hornContext) {
if (prover.getLastSolution()!=null) {
StringBuilder sb = new StringBuilder();
sb.append("No assertion can fail using the following heap invarians:\n");
Map<ClassVariable, TreeMap<Long,String>> heapInvariants = new LinkedHashMap<ClassVariable, TreeMap<Long,String>>();
for (Entry<String, String> entry : prover.getLastSolution().entrySet()) {
boolean found = false;
for (Entry<ClassVariable, Map<Long, HornPredicate>> pentry : hornContext.getInvariantPredicates().entrySet()) {
for (Entry<Long, HornPredicate> predEntry : pentry.getValue().entrySet()) {
HornPredicate hp = predEntry.getValue();
if (hp.predicate.toString().contains(entry.getKey())) {
//we found one.
if (!heapInvariants.containsKey(pentry.getKey())) {
heapInvariants.put(pentry.getKey(), new TreeMap<Long,String>());
}
String readable = entry.getValue();
for (int i = 0; i<hp.variables.size(); i++) {
readable = readable.replace("_"+i, hp.variables.get(i).getName());
}
heapInvariants.get(pentry.getKey()).put(predEntry.getKey(), readable);
//
// sb.append(pentry.getKey().getName());
// sb.append(":\n\t");
// int i=0;
// String readable = entry.getValue();
// for (Variable v : hp.variables) {
// readable = readable.replace("_"+(i++), v.getName());
// }
// sb.append(readable);
// sb.append("\n");
// found = true;
// break;
}
}
if (found) {
break;
}
}
}
for (Entry<ClassVariable, TreeMap<Long,String>> entry : heapInvariants.entrySet()) {
sb.append(entry.getKey());
sb.append("\n ");
for (Variable v : entry.getKey().getAssociatedFields()) {
sb.append(", ");
sb.append(v.getName());
}
sb.append(":\n");
for (Entry<Long,String> e2 : entry.getValue().entrySet()) {
sb.append("\t");
sb.append(e2.getKey());
sb.append(": ");
sb.append(e2.getValue());
sb.append("\n");
}
sb.append("--\n");
}
sb.append("----\n");
System.err.println(sb.toString());
}
}
}