//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Cci;
using Microsoft.Cci.MetadataReader;
using Microsoft.Cci.MutableCodeModel;
using Microsoft.Cci.Contracts;
using Microsoft.Cci.ILToCodeModel;
using System.Diagnostics.Contracts;
using Bpl = Microsoft.Boogie;
namespace BytecodeTranslator {
public class Sink {
public TraverserFactory Factory {
get { return this.factory; }
}
readonly TraverserFactory factory;
public Sink(IContractAwareHost host, TraverserFactory factory, HeapFactory heapFactory) {
Contract.Requires(host != null);
Contract.Requires(factory != null);
Contract.Requires(heapFactory != null);
this.host = host;
this.factory = factory;
var b = heapFactory.MakeHeap(this, out this.heap, out this.TranslatedProgram); // TODO: what if it returns false?
if (this.TranslatedProgram == null) {
this.TranslatedProgram = new Bpl.Program();
} else {
foreach (var d in this.TranslatedProgram.TopLevelDeclarations) {
var p = d as Bpl.Procedure;
if (p != null) {
this.initiallyDeclaredProcedures.Add(p.Name, p);
}
}
}
}
public Heap Heap {
get { return this.heap; }
}
readonly Heap heap;
public Bpl.Variable ThisVariable = TranslationHelper.TempThisVar();
public Bpl.Variable RetVariable;
public readonly string AllocationMethodName = "Alloc";
public readonly string StaticFieldFunction = "ClassRepr";
public readonly string ReferenceTypeName = "Ref";
public readonly string DelegateAddHelperName = "DelegateAddHelper";
public readonly string DelegateAddName = "DelegateAdd";
public readonly string DelegateRemoveName = "DelegateRemove";
public Bpl.Expr ReadHead(Bpl.Expr delegateReference)
{
return Bpl.Expr.Select(new Bpl.IdentifierExpr(delegateReference.tok, this.heap.DelegateHead), delegateReference);
}
public Bpl.Expr ReadNext(Bpl.Expr delegateReference, Bpl.Expr listNodeReference)
{
return Bpl.Expr.Select(Bpl.Expr.Select(new Bpl.IdentifierExpr(delegateReference.tok, this.heap.DelegateNext), delegateReference), listNodeReference);
}
public Bpl.Expr ReadMethod(Bpl.Expr delegateReference, Bpl.Expr listNodeReference)
{
return Bpl.Expr.Select(Bpl.Expr.Select(new Bpl.IdentifierExpr(delegateReference.tok, this.heap.DelegateMethod), delegateReference), listNodeReference);
}
public Bpl.Expr ReadReceiver(Bpl.Expr delegateReference, Bpl.Expr listNodeReference)
{
return Bpl.Expr.Select(Bpl.Expr.Select(new Bpl.IdentifierExpr(delegateReference.tok, this.heap.DelegateReceiver), delegateReference), listNodeReference);
}
public readonly Bpl.Program TranslatedProgram;
public Bpl.Type CciTypeToBoogie(ITypeReference type) {
if (TypeHelper.TypesAreEquivalent(type, type.PlatformType.SystemBoolean))
return Bpl.Type.Bool;
else if (TypeHelper.IsPrimitiveInteger(type))
return Bpl.Type.Int;
else if (type.TypeCode == PrimitiveTypeCode.Float32 || type.TypeCode == PrimitiveTypeCode.Float64)
return heap.RealType;
else if (type.ResolvedType.IsStruct)
return heap.RefType; // structs are kept on the heap with special rules about assignment
else if (type.IsEnum)
return Bpl.Type.Int; // The underlying type of an enum is always some kind of integer
else if (type is IGenericTypeParameter)
return heap.BoxType;
else
return heap.RefType;
}
///
/// Creates a fresh local var of the given Type and adds it to the
/// Bpl Implementation
///
/// The type of the new variable
/// A fresh Variable with automatic generated name and location
public Bpl.Variable CreateFreshLocal(ITypeReference typeReference) {
Bpl.IToken loc = Bpl.Token.NoToken; // Helper Variables do not have a location
Bpl.Type t = CciTypeToBoogie(typeReference);
Bpl.LocalVariable v = new Bpl.LocalVariable(loc, new Bpl.TypedIdent(loc, TranslationHelper.GenerateTempVarName(), t));
ILocalDefinition dummy = new LocalDefinition(); // Creates a dummy entry for the Dict, since only locals in the dict are translated to boogie
localVarMap.Add(dummy, v);
return v;
}
public Bpl.Variable CreateFreshLocal(Bpl.Type t) {
Bpl.IToken loc = Bpl.Token.NoToken; // Helper Variables do not have a location
Bpl.LocalVariable v = new Bpl.LocalVariable(loc, new Bpl.TypedIdent(loc, TranslationHelper.GenerateTempVarName(), t));
ILocalDefinition dummy = new LocalDefinition(); // Creates a dummy entry for the Dict, since only locals in the dict are translated to boogie
localVarMap.Add(dummy, v);
return v;
}
///
/// State that gets re-initialized per method
///
private Dictionary localVarMap = null;
public Dictionary LocalVarMap {
get { return this.localVarMap; }
}
private int localCounter;
public int LocalCounter { get { return this.localCounter++; } }
///
///
///
///
///
public Bpl.Variable FindOrCreateLocalVariable(ILocalDefinition local) {
Bpl.LocalVariable v;
Bpl.IToken tok = local.Token();
Bpl.Type t = CciTypeToBoogie(local.Type.ResolvedType);
if (!localVarMap.TryGetValue(local, out v)) {
var name = local.Name.Value;
name = TranslationHelper.TurnStringIntoValidIdentifier(name);
v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, t));
localVarMap.Add(local, v);
}
return v;
}
///
///
///
///
/// STUB
///
public Bpl.Variable FindParameterVariable(IParameterDefinition param, bool contractContext) {
MethodParameter mp;
ProcedureInfo procAndFormalMap;
var sig = param.ContainingSignature;
// BUGBUG: If param's signature is not a method reference, then it doesn't have an interned
// key. The declaredMethods table needs to use ISignature for its keys.
var key = ((IMethodReference)sig).InternedKey;
this.declaredMethods.TryGetValue(key, out procAndFormalMap);
var formalMap = procAndFormalMap.FormalMap;
formalMap.TryGetValue(param, out mp);
return contractContext ? mp.inParameterCopy : mp.outParameterCopy;
}
public Bpl.Variable FindOrCreateFieldVariable(IFieldReference field) {
// The Heap has to decide how to represent the field (i.e., its type),
// all the Sink cares about is adding a declaration for it.
Bpl.Variable v;
var key = field.InternedKey;
if (!this.declaredFields.TryGetValue(key, out v)) {
v = this.Heap.CreateFieldVariable(field);
this.declaredFields.Add(key, v);
this.TranslatedProgram.TopLevelDeclarations.Add(v);
}
return v;
}
///
/// The keys to the table are the interned key of the field.
///
private Dictionary declaredFields = new Dictionary();
public Bpl.Variable FindOrCreateEventVariable(IEventDefinition e)
{
Bpl.Variable v;
if (!this.declaredEvents.TryGetValue(e, out v))
{
v = null;
// First, see if the compiler generated a field (which happens when the event did not explicitly
// define an adder and remover. If so, then just use the variable that corresponds to that field.
foreach (var f in e.ContainingTypeDefinition.Fields) {
if (e.Name == f.Name) {
v = this.FindOrCreateFieldVariable(f);
break;
}
}
if (v == null) {
v = this.Heap.CreateEventVariable(e);
this.TranslatedProgram.TopLevelDeclarations.Add(v);
}
this.declaredEvents.Add(e, v);
}
return v;
}
private Dictionary declaredEvents = new Dictionary();
public Bpl.Variable FindOrCreatePropertyVariable(IPropertyDefinition p)
{
return null;
}
public Bpl.Constant FindOrCreateConstant(string str) {
Bpl.Constant c;
if (!this.declaredStringConstants.TryGetValue(str, out c)) {
var tok = Bpl.Token.NoToken;
var t = Heap.RefType;
var name = "$string_literal_" + TranslationHelper.TurnStringIntoValidIdentifier(str) + "_" + declaredStringConstants.Count;
var tident = new Bpl.TypedIdent(tok, name, t);
c = new Bpl.Constant(tok, tident, true);
this.declaredStringConstants.Add(str, c);
this.TranslatedProgram.TopLevelDeclarations.Add(c);
}
return c;
}
private Dictionary declaredStringConstants = new Dictionary();
public Bpl.Constant FindOrCreateConstant(double d) {
Bpl.Constant c;
var str = d.ToString();
if (!this.declaredRealConstants.TryGetValue(str, out c)) {
var tok = Bpl.Token.NoToken;
var t = Heap.RealType;
var name = "$real_literal_" + TranslationHelper.TurnStringIntoValidIdentifier(str) + "_" + declaredStringConstants.Count;
var tident = new Bpl.TypedIdent(tok, name, t);
c = new Bpl.Constant(tok, tident, true);
this.declaredRealConstants.Add(str, c);
this.TranslatedProgram.TopLevelDeclarations.Add(c);
}
return c;
}
private Dictionary declaredRealConstants = new Dictionary();
private Dictionary declaredProperties = new Dictionary();
private List projectionFunctions = new List();
private Dictionary arityToNaryIntFunctions = new Dictionary();
public Bpl.Function FindOrCreateNaryIntFunction(int arity) {
Bpl.Function f;
if (!this.arityToNaryIntFunctions.TryGetValue(arity, out f)) {
Bpl.VariableSeq vseq = new Bpl.VariableSeq();
for (int i = 0; i < arity; i++) {
vseq.Add(new Bpl.Formal(Bpl.Token.NoToken, new Bpl.TypedIdent(Bpl.Token.NoToken, "arg" + i, Bpl.Type.Int), true));
}
f = new Bpl.Function(Bpl.Token.NoToken, "Int" + arity, vseq, new Bpl.Formal(Bpl.Token.NoToken, new Bpl.TypedIdent(Bpl.Token.NoToken, "result", Bpl.Type.Int), false));
this.arityToNaryIntFunctions.Add(arity, f);
TranslatedProgram.TopLevelDeclarations.Add(f);
if (arity > projectionFunctions.Count) {
for (int i = projectionFunctions.Count; i < arity; i++) {
Bpl.Variable input = new Bpl.Formal(Bpl.Token.NoToken, new Bpl.TypedIdent(Bpl.Token.NoToken, "in", Bpl.Type.Int), true);
Bpl.Variable output = new Bpl.Formal(Bpl.Token.NoToken, new Bpl.TypedIdent(Bpl.Token.NoToken, "out", Bpl.Type.Int), false);
Bpl.Function g = new Bpl.Function(Bpl.Token.NoToken, "Proj" + i, new Bpl.VariableSeq(input), output);
TranslatedProgram.TopLevelDeclarations.Add(g);
projectionFunctions.Add(g);
}
}
Bpl.VariableSeq qvars = new Bpl.VariableSeq();
Bpl.ExprSeq exprs = new Bpl.ExprSeq();
for (int i = 0; i < arity; i++) {
Bpl.Variable v = new Bpl.Constant(Bpl.Token.NoToken, new Bpl.TypedIdent(Bpl.Token.NoToken, "arg" + i, Bpl.Type.Int));
qvars.Add(v);
exprs.Add(Bpl.Expr.Ident(v));
}
Bpl.Expr e = new Bpl.NAryExpr(Bpl.Token.NoToken, new Bpl.FunctionCall(f), exprs);
for (int i = 0; i < arity; i++) {
Bpl.Expr appl = new Bpl.NAryExpr(Bpl.Token.NoToken, new Bpl.FunctionCall(projectionFunctions[i]), new Bpl.ExprSeq(e));
Bpl.Expr qexpr = new Bpl.ForallExpr(Bpl.Token.NoToken, new Bpl.TypeVariableSeq(), qvars, null, null, Bpl.Expr.Eq(appl, Bpl.Expr.Ident(qvars[i])));
TranslatedProgram.TopLevelDeclarations.Add(new Bpl.Axiom(Bpl.Token.NoToken, qexpr));
}
}
return f;
}
public struct ProcedureInfo {
private Bpl.DeclWithFormals decl;
private Dictionary formalMap;
private Bpl.Variable returnVariable;
public ProcedureInfo(Bpl.DeclWithFormals decl, Dictionary formalMap, Bpl.Variable returnVariable) {
this.decl = decl;
this.formalMap = formalMap;
this.returnVariable = returnVariable;
}
public Bpl.DeclWithFormals Decl { get { return decl; } }
public Dictionary FormalMap { get { return formalMap; } }
public Bpl.Variable ReturnVariable { get { return returnVariable; } }
}
public Bpl.DeclWithFormals FindOrCreateProcedure(IMethodDefinition method) {
ProcedureInfo procAndFormalMap;
var key = method.InternedKey;
if (!this.declaredMethods.TryGetValue(key, out procAndFormalMap)) {
string MethodName = TranslationHelper.CreateUniqueMethodName(method);
Bpl.Procedure p;
if (this.initiallyDeclaredProcedures.TryGetValue(MethodName, out p)) return p;
#region Create in- and out-parameters
int in_count = 0;
int out_count = 0;
MethodParameter mp;
var formalMap = new Dictionary();
foreach (IParameterDefinition formal in method.Parameters) {
mp = new MethodParameter(formal, this.CciTypeToBoogie(formal.Type));
if (mp.inParameterCopy != null) in_count++;
if (mp.outParameterCopy != null && formal.IsByReference)
out_count++;
formalMap.Add(formal, mp);
}
#region Look for Returnvalue
Bpl.Variable savedRetVariable = this.RetVariable;
if (method.Type.TypeCode != PrimitiveTypeCode.Void) {
Bpl.Type rettype = CciTypeToBoogie(method.Type);
out_count++;
this.RetVariable = new Bpl.Formal(method.Token(),
new Bpl.TypedIdent(method.Type.Token(),
"$result", rettype), false);
} else {
this.RetVariable = null;
}
#endregion
Bpl.Formal/*?*/ self = null;
Bpl.Formal selfOut = null;
#region Create 'this' parameter
if (!method.IsStatic) {
var selfType = CciTypeToBoogie(method.ContainingType);
if (method.ContainingType.ResolvedType.IsStruct) {
//selfType = Heap.StructType;
in_count++;
self = new Bpl.Formal(method.Token(), new Bpl.TypedIdent(method.Type.Token(), "thisIn", selfType), true);
out_count++;
selfOut = new Bpl.Formal(method.Token(), new Bpl.TypedIdent(method.Type.Token(), "this", selfType), false);
}
else {
in_count++;
//selfType = Heap.RefType;
self = new Bpl.Formal(method.Token(), new Bpl.TypedIdent(method.Type.Token(), "this", selfType), true);
}
}
#endregion
Bpl.Variable[] invars = new Bpl.Formal[in_count];
Bpl.Variable[] outvars = new Bpl.Formal[out_count];
int i = 0;
int j = 0;
#region Add 'this' parameter as first in parameter and 'thisOut' parameter as first out parameter
if (self != null)
invars[i++] = self;
if (selfOut != null)
outvars[j++] = selfOut;
#endregion
foreach (MethodParameter mparam in formalMap.Values) {
if (mparam.inParameterCopy != null) {
invars[i++] = mparam.inParameterCopy;
}
if (mparam.outParameterCopy != null) {
if (mparam.underlyingParameter.IsByReference)
outvars[j++] = mparam.outParameterCopy;
}
}
#region add the returnvalue to out if there is one
if (this.RetVariable != null) outvars[j] = this.RetVariable;
#endregion
#endregion
#region Check The Method Contracts
Bpl.RequiresSeq boogiePrecondition = new Bpl.RequiresSeq();
Bpl.EnsuresSeq boogiePostcondition = new Bpl.EnsuresSeq();
Bpl.IdentifierExprSeq boogieModifies = new Bpl.IdentifierExprSeq();
var possiblyUnspecializedMethod = Unspecialize(method);
var contract = Microsoft.Cci.MutableContracts.ContractHelper.GetMethodContractFor(this.host, possiblyUnspecializedMethod.ResolvedMethod);
if (contract != null) {
try {
foreach (IPrecondition pre in contract.Preconditions) {
var stmtTraverser = this.factory.MakeStatementTraverser(this, null, true);
ExpressionTraverser exptravers = this.factory.MakeExpressionTraverser(this, stmtTraverser, true);
exptravers.Visit(pre.Condition); // TODO
// Todo: Deal with Descriptions
var req = new Bpl.Requires(pre.Token(), false, exptravers.TranslatedExpressions.Pop(), "");
boogiePrecondition.Add(req);
}
foreach (IPostcondition post in contract.Postconditions) {
var stmtTraverser = this.factory.MakeStatementTraverser(this, null, true);
ExpressionTraverser exptravers = this.factory.MakeExpressionTraverser(this, stmtTraverser, true);
exptravers.Visit(post.Condition);
// Todo: Deal with Descriptions
var ens = new Bpl.Ensures(post.Token(), false, exptravers.TranslatedExpressions.Pop(), "");
boogiePostcondition.Add(ens);
}
foreach (IAddressableExpression mod in contract.ModifiedVariables) {
ExpressionTraverser exptravers = this.factory.MakeExpressionTraverser(this, null, true);
exptravers.Visit(mod);
Bpl.IdentifierExpr idexp = exptravers.TranslatedExpressions.Pop() as Bpl.IdentifierExpr;
if (idexp == null) {
throw new TranslationException(String.Format("Cannot create IdentifierExpr for Modifyed Variable {0}", mod.ToString()));
}
boogieModifies.Add(idexp);
}
} catch (TranslationException te) {
throw new NotImplementedException("Cannot Handle Errors in Method Contract: " + te.ToString());
} catch {
throw;
}
}
#endregion
#region Add disjointness contracts for any struct values passed by value
var paramList = new List(method.Parameters);
for (int p1index = 0; p1index < method.ParameterCount; p1index++) {
var p1 = paramList[p1index];
if (p1.IsByReference) continue;
if (!TranslationHelper.IsStruct(p1.Type)) continue;
for (int p2index = p1index + 1; p2index < method.ParameterCount; p2index++) {
var p2 = paramList[p2index];
if (p2.IsByReference) continue;
if (!TranslationHelper.IsStruct(p2.Type)) continue;
if (!TypeHelper.TypesAreEquivalent(p1.Type, p2.Type)) continue;
var req = new Bpl.Requires(true, Bpl.Expr.Binary(Bpl.BinaryOperator.Opcode.Neq,
Bpl.Expr.Ident(formalMap[p1].inParameterCopy), Bpl.Expr.Ident(formalMap[p2].inParameterCopy)));
boogiePrecondition.Add(req);
}
}
#endregion
var tok = method.Token();
Bpl.DeclWithFormals decl;
if (IsPure(method)) {
var func = new Bpl.Function(tok,
MethodName,
new Bpl.VariableSeq(invars),
this.RetVariable);
decl = func;
} else {
var proc = new Bpl.Procedure(tok,
MethodName,
new Bpl.TypeVariableSeq(),
new Bpl.VariableSeq(invars),
new Bpl.VariableSeq(outvars),
boogiePrecondition,
boogieModifies,
boogiePostcondition);
decl = proc;
}
if (this.assemblyBeingTranslated != null && !TypeHelper.GetDefiningUnitReference(method.ContainingType).UnitIdentity.Equals(this.assemblyBeingTranslated.UnitIdentity)) {
var attrib = new Bpl.QKeyValue(tok, "extern", new List