//-----------------------------------------------------------------------------
//
// 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 ArrayContentsVariable
{
get { return this.arrayContentsVariable; }
}
readonly Bpl.Variable arrayContentsVariable = TranslationHelper.TempArrayContentsVar();
public Bpl.Variable ArrayLengthVariable
{
get { return this.arrayLengthVariable; }
}
readonly Bpl.Variable arrayLengthVariable = TranslationHelper.TempArrayLengthVar();
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;
///
/// 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 = TranslationHelper.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;
}
private Dictionary localVarMap = null;
public Dictionary LocalVarMap {
get { return this.localVarMap; }
}
///
///
///
///
///
public Bpl.Variable FindOrCreateLocalVariable(ILocalDefinition local) {
Bpl.LocalVariable v;
Bpl.IToken tok = local.Token();
Bpl.Type t = TranslationHelper.CciTypeToBoogie(local.Type.ResolvedType);
if (!localVarMap.TryGetValue(local, out v)) {
v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, local.Name.Value, 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 = this.Heap.CreateEventVariable(e);
this.declaredEvents.Add(e, v);
this.TranslatedProgram.TopLevelDeclarations.Add(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 = Bpl.Type.Int;
var name = "$string_literal_" + TranslationHelper.TurnStringIntoValidIdentifier(str);
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();
private Dictionary declaredProperties = new Dictionary();
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.Function f;
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);
if (mp.inParameterCopy != null) in_count++;
if (mp.outParameterCopy != null && (formal.IsByReference || formal.IsOut))
out_count++;
formalMap.Add(formal, mp);
}
#region Look for Returnvalue
Bpl.Variable savedRetVariable = this.RetVariable;
if (method.Type.TypeCode != PrimitiveTypeCode.Void) {
Bpl.Type rettype = TranslationHelper.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;
#region Create 'this' parameter
if (!method.IsStatic) {
in_count++;
Bpl.Type selftype = Bpl.Type.Int;
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
if (!method.IsStatic)
invars[i++] = self;
#endregion
foreach (MethodParameter mparam in formalMap.Values) {
if (mparam.inParameterCopy != null) {
invars[i++] = mparam.inParameterCopy;
}
if (mparam.outParameterCopy != null) {
if (mparam.underlyingParameter.IsByReference || mparam.underlyingParameter.IsOut)
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
Bpl.DeclWithFormals decl;
if (IsPure(method)) {
var func = new Bpl.Function(method.Token(),
MethodName,
new Bpl.VariableSeq(invars),
this.RetVariable);
decl = func;
} else {
var proc = new Bpl.Procedure(method.Token(),
MethodName,
new Bpl.TypeVariableSeq(),
new Bpl.VariableSeq(invars),
new Bpl.VariableSeq(outvars),
boogiePrecondition,
boogieModifies,
boogiePostcondition);
decl = proc;
}
string newName = null;
if (IsStubMethod(method, out newName)) {
if (newName != null) {
decl.Name = newName;
}
} else {
this.TranslatedProgram.TopLevelDeclarations.Add(decl);
}
procAndFormalMap = new ProcedureInfo(decl, formalMap, this.RetVariable);
this.declaredMethods.Add(key, procAndFormalMap);
this.RetVariable = savedRetVariable;
}
return procAndFormalMap.Decl;
}
// TODO: Fix test to return true iff method is marked with the "real" [Pure] attribute
// also, should it return true for properties and all of the other things the tools
// consider pure?
private bool IsPure(IMethodDefinition method) {
foreach (var a in method.Attributes) {
if (TypeHelper.GetTypeName(a.Type).EndsWith("PureAttribute")) {
return true;
}
}
return false;
}
// TODO: check method's containing type in case the entire type is a stub type.
// TODO: do a type test, not a string test for the attribute
private bool IsStubMethod(IMethodReference method, out string/*?*/ newName) {
newName = null;
var methodDefinition = method.ResolvedMethod;
foreach (var a in methodDefinition.Attributes) {
if (TypeHelper.GetTypeName(a.Type).EndsWith("StubAttribute")) {
foreach (var c in a.Arguments) {
var mdc = c as IMetadataConstant;
if (mdc != null && mdc.Type.TypeCode == PrimitiveTypeCode.String) {
newName = (string) (mdc.Value);
break;
}
}
return true;
}
}
return false;
}
public ProcedureInfo FindOrCreateProcedureAndReturnProcAndFormalMap(IMethodDefinition method) {
this.FindOrCreateProcedure(method);
return this.declaredMethods[method.InternedKey];
}
private static IMethodReference Unspecialize(IMethodReference method) {
IMethodReference result = method;
var gmir = result as IGenericMethodInstanceReference;
if (gmir != null) {
result = gmir.GenericMethod;
}
var smr = result as ISpecializedMethodReference;
if (smr != null) {
result = smr.UnspecializedVersion;
}
// Temporary hack until ISpecializedMethodDefinition implements ISpecializedMethodReference
var smd = result as ISpecializedMethodDefinition;
if (smd != null) {
result = smd.UnspecializedVersion;
}
return result;
}
///
/// Creates a fresh variable that represents the type of
/// in the Bpl program. I.e., its
/// value represents the expression "typeof(type)".
///
public Bpl.Variable FindOrCreateType(ITypeReference type) {
// 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 t;
var key = type.InternedKey;
if (!this.declaredTypes.TryGetValue(key, out t)) {
t = this.Heap.CreateTypeVariable(type);
this.declaredTypes.Add(key, t);
this.TranslatedProgram.TopLevelDeclarations.Add(t);
}
return t;
}
///
/// The keys to the table are the interned key of the type.
///
private Dictionary declaredTypes = new Dictionary();
///
/// The keys to the table are the interned keys of the methods.
/// The values are pairs: first element is the procedure,
/// second element is the formal map for the procedure
///
private Dictionary declaredMethods = new Dictionary();
///
/// The values in this table are the procedures
/// defined in the program created by the heap in the Sink's ctor.
///
private Dictionary initiallyDeclaredProcedures = new Dictionary();
public void BeginMethod() {
this.localVarMap = new Dictionary();
}
public Dictionary> delegateTypeToDelegates = new Dictionary>();
public void AddDelegate(ITypeDefinition type, IMethodDefinition defn)
{
if (!delegateTypeToDelegates.ContainsKey(type))
delegateTypeToDelegates[type] = new HashSet();
delegateTypeToDelegates[type].Add(defn);
}
public void AddDelegateType(ITypeDefinition type) {
if (!delegateTypeToDelegates.ContainsKey(type))
delegateTypeToDelegates[type] = new HashSet();
}
private Dictionary delegateMethods = new Dictionary();
private IContractAwareHost host;
public Bpl.Constant FindOrAddDelegateMethodConstant(IMethodDefinition defn)
{
if (delegateMethods.ContainsKey(defn))
return delegateMethods[defn];
string methodName = TranslationHelper.CreateUniqueMethodName(defn);
var typedIdent = new Bpl.TypedIdent(Bpl.Token.NoToken, methodName, Bpl.Type.Int);
var constant = new Bpl.Constant(Bpl.Token.NoToken, typedIdent, true);
this.TranslatedProgram.TopLevelDeclarations.Add(constant);
delegateMethods[defn] = constant;
return constant;
}
}
}