//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using Microsoft.Boogie.AbstractInterpretation;
using Microsoft.Boogie.VCExprAST;
using Microsoft.Basetypes;
namespace Microsoft.Boogie {
///
/// Interface to the theorem prover specialized to Boogie.
///
/// This class creates the appropriate background axioms. There
/// should be one instance per BoogiePL program.
///
public sealed class Checker {
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(gen != null);
Contract.Invariant(thmProver != null);
Contract.Invariant(ProverDone != null);
}
private readonly VCExpressionGenerator gen;
private ProverInterface thmProver;
private int timeout;
// state for the async interface
private volatile ProverInterface.Outcome outcome;
private volatile bool busy;
private volatile bool hasOutput;
private volatile UnexpectedProverOutputException outputExn;
private DateTime proverStart;
private TimeSpan proverRunTime;
private volatile ProverInterface.ErrorHandler handler;
private volatile bool closed;
public readonly AutoResetEvent ProverDone = new AutoResetEvent(false);
public bool WillingToHandle(Implementation impl, int timeout) {
return !closed && timeout == this.timeout;
}
public VCExpressionGenerator VCExprGen {
get {
Contract.Ensures(Contract.Result() != null);
return this.gen;
}
}
public ProverInterface TheoremProver {
get {
Contract.Ensures(Contract.Result() != null);
return this.thmProver;
}
}
/////////////////////////////////////////////////////////////////////////////////
// We share context information for the same program between different Checkers
private struct ContextCacheKey {
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(program != null);
}
public readonly Program program;
public ContextCacheKey(Program prog) {
Contract.Requires(prog != null);
this.program = prog;
}
[Pure]
[Reads(ReadsAttribute.Reads.Nothing)]
public override bool Equals(object that) {
if (that is ContextCacheKey) {
ContextCacheKey thatKey = (ContextCacheKey)that;
return this.program.Equals(thatKey.program);
}
return false;
}
[Pure]
public override int GetHashCode() {
return this.program.GetHashCode();
}
}
/////////////////////////////////////////////////////////////////////////////////
///
/// Constructor. Initialize a checker with the program and log file.
///
public Checker(VC.ConditionGeneration vcgen, Program prog, string/*?*/ logFilePath, bool appendLogFile, int timeout) {
Contract.Requires(vcgen != null);
Contract.Requires(prog != null);
this.timeout = timeout;
ProverOptions options = cce.NonNull(CommandLineOptions.Clo.TheProverFactory).BlankProverOptions();
if (logFilePath != null) {
options.LogFilename = logFilePath;
if (appendLogFile)
options.AppendLogFile = appendLogFile;
}
if (timeout > 0) {
options.TimeLimit = timeout * 1000;
}
options.Parse(CommandLineOptions.Clo.ProverOptions);
ContextCacheKey key = new ContextCacheKey(prog);
ProverContext ctx;
ProverInterface prover;
if (vcgen.CheckerCommonState == null) {
vcgen.CheckerCommonState = new Dictionary();
}
IDictionary/*!>!*/ cachedContexts = (IDictionary)vcgen.CheckerCommonState;
if (cachedContexts.TryGetValue(key, out ctx)) {
ctx = (ProverContext)cce.NonNull(ctx).Clone();
prover = (ProverInterface)
CommandLineOptions.Clo.TheProverFactory.SpawnProver(options, ctx);
} else {
ctx = (ProverContext)CommandLineOptions.Clo.TheProverFactory.NewProverContext(options);
// set up the context
foreach (Declaration decl in prog.TopLevelDeclarations) {
Contract.Assert(decl != null);
TypeCtorDecl t = decl as TypeCtorDecl;
if (t != null) {
ctx.DeclareType(t, null);
}
}
foreach (Declaration decl in prog.TopLevelDeclarations) {
Contract.Assert(decl != null);
Constant c = decl as Constant;
if (c != null) {
ctx.DeclareConstant(c, c.Unique, null);
} else {
Function f = decl as Function;
if (f != null) {
ctx.DeclareFunction(f, null);
}
}
}
foreach (Declaration decl in prog.TopLevelDeclarations) {
Contract.Assert(decl != null);
Axiom ax = decl as Axiom;
if (ax != null) {
ctx.AddAxiom(ax, null);
}
}
foreach (Declaration decl in prog.TopLevelDeclarations) {
Contract.Assert(decl != null);
GlobalVariable v = decl as GlobalVariable;
if (v != null) {
ctx.DeclareGlobalVariable(v, null);
}
}
// we first generate the prover and then store a clone of the
// context in the cache, so that the prover can setup stuff in
// the context to be cached
prover = (ProverInterface)
CommandLineOptions.Clo.TheProverFactory.SpawnProver(options, ctx);
cachedContexts.Add(key, cce.NonNull((ProverContext)ctx.Clone()));
}
this.thmProver = prover;
this.gen = prover.VCExprGen;
// base();
}
///
/// Clean-up.
///
public void Close() {
this.closed = true;
thmProver.Close();
}
///
/// Push a Verification Condition as an Axiom
/// (Required for Doomed Program Point detection)
///
public void PushVCExpr(VCExpr vc) {
Contract.Requires(vc != null);
//thmProver.Context.AddAxiom(vc);
thmProver.PushVCExpression(vc);
}
public bool IsBusy {
get {
return busy;
}
}
public bool Closed {
get {
return closed;
}
}
public bool HasOutput {
get {
return hasOutput;
}
}
public TimeSpan ProverRunTime {
get {
return proverRunTime;
}
}
private void WaitForOutput(object dummy) {
try {
outcome = thmProver.CheckOutcome(cce.NonNull(handler));
} catch (UnexpectedProverOutputException e) {
outputExn = e;
}
switch (outcome) {
case ProverInterface.Outcome.Valid:
thmProver.LogComment("Valid");
break;
case ProverInterface.Outcome.Invalid:
thmProver.LogComment("Invalid");
break;
case ProverInterface.Outcome.TimeOut:
thmProver.LogComment("Timed out");
break;
case ProverInterface.Outcome.OutOfMemory:
thmProver.LogComment("Out of memory");
break;
case ProverInterface.Outcome.Undetermined:
thmProver.LogComment("Undetermined");
break;
}
Contract.Assert(busy);
hasOutput = true;
proverRunTime = DateTime.UtcNow - proverStart;
ProverDone.Set();
}
public void BeginCheck(string descriptiveName, VCExpr vc, ProverInterface.ErrorHandler handler) {
Contract.Requires(descriptiveName != null);
Contract.Requires(vc != null);
Contract.Requires(handler != null);
Contract.Requires(!IsBusy);
Contract.Assert(!busy);
busy = true;
hasOutput = false;
outputExn = null;
this.handler = handler;
proverStart = DateTime.UtcNow;
thmProver.BeginCheck(descriptiveName, vc, handler);
// gen.ClearSharedFormulas(); PR: don't know yet what to do with this guy
ThreadPool.QueueUserWorkItem(WaitForOutput);
}
public ProverInterface.Outcome ReadOutcome() {
Contract.Requires(IsBusy);
Contract.Requires(HasOutput);
Contract.EnsuresOnThrow(true);
hasOutput = false;
busy = false;
if (outputExn != null) {
throw outputExn;
}
return outcome;
}
}
// -----------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------
// This class will go away soon. Use Model class instead.
public class ErrorModel {
public Dictionary/*!*/ identifierToPartition;
public List>!*/>> partitionToIdentifiers;
public List