//----------------------------------------------------------------------------- // // 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 Bpl = Microsoft.Boogie; using System.Diagnostics.Contracts; namespace BytecodeTranslator { /// /// Responsible for traversing all metadata elements (i.e., everything exclusive /// of method bodies). /// public class MetadataTraverser : BaseMetadataTraverser { readonly Sink sink; public readonly TraverserFactory factory; public readonly PdbReader/*?*/ PdbReader; public MetadataTraverser(Sink sink, PdbReader/*?*/ pdbReader) : base() { this.sink = sink; this.factory = sink.Factory; this.PdbReader = pdbReader; } public Bpl.Program TranslatedProgram { get { return this.sink.TranslatedProgram; } } #region Overrides public override void Visit(IModule module) { base.Visit(module); } public override void Visit(IAssembly assembly) { base.Visit(assembly); foreach (ITypeDefinition type in sink.delegateTypeToDelegates.Keys) { CreateDispatchMethod(type); } } private Bpl.IfCmd BuildReturnCmd(Bpl.Expr b) { Bpl.StmtListBuilder ifStmtBuilder = new Bpl.StmtListBuilder(); ifStmtBuilder.Add(new Bpl.ReturnCmd(b.tok)); return new Bpl.IfCmd(b.tok, b, ifStmtBuilder.Collect(b.tok), null, null); } private Bpl.IfCmd BuildIfCmd(Bpl.Expr b, Bpl.Cmd cmd, Bpl.IfCmd ifCmd) { Bpl.StmtListBuilder ifStmtBuilder; ifStmtBuilder = new Bpl.StmtListBuilder(); ifStmtBuilder.Add(cmd); return new Bpl.IfCmd(b.tok, b, ifStmtBuilder.Collect(b.tok), ifCmd, null); } private void CreateDispatchMethod(ITypeDefinition type) { Contract.Assert(type.IsDelegate); IMethodDefinition invokeMethod = null; foreach (IMethodDefinition m in type.Methods) { if (m.Name.Value == "Invoke") { invokeMethod = m; break; } } var procAndFormalMap = this.sink.FindOrCreateProcedureAndReturnProcAndFormalMap(invokeMethod, invokeMethod.IsStatic); var proc = procAndFormalMap.Procedure; var invars = proc.InParams; var outvars = proc.OutParams; Bpl.IToken token = invokeMethod.Token(); this.sink.BeginMethod(); try { Bpl.LocalVariable method = new Bpl.LocalVariable(token, new Bpl.TypedIdent(token, "method", Bpl.Type.Int)); Bpl.LocalVariable receiver = new Bpl.LocalVariable(token, new Bpl.TypedIdent(token, "receiver", Bpl.Type.Int)); Bpl.LocalVariable iter = new Bpl.LocalVariable(token, new Bpl.TypedIdent(token, "iter", Bpl.Type.Int)); Bpl.LocalVariable niter = new Bpl.LocalVariable(token, new Bpl.TypedIdent(token, "niter", Bpl.Type.Int)); Bpl.StmtListBuilder implStmtBuilder = new Bpl.StmtListBuilder(); implStmtBuilder.Add(TranslationHelper.BuildAssignCmd(Bpl.Expr.Ident(iter), this.sink.ReadHead(Bpl.Expr.Ident(invars[0])))); Bpl.StmtListBuilder whileStmtBuilder = new Bpl.StmtListBuilder(); whileStmtBuilder.Add(TranslationHelper.BuildAssignCmd(Bpl.Expr.Ident(niter), this.sink.ReadNext(Bpl.Expr.Ident(invars[0]), Bpl.Expr.Ident(iter)))); whileStmtBuilder.Add(BuildReturnCmd(Bpl.Expr.Eq(Bpl.Expr.Ident(niter), this.sink.ReadHead(Bpl.Expr.Ident(invars[0]))))); whileStmtBuilder.Add(TranslationHelper.BuildAssignCmd(Bpl.Expr.Ident(method), this.sink.ReadMethod(Bpl.Expr.Ident(invars[0]), Bpl.Expr.Ident(niter)))); whileStmtBuilder.Add(TranslationHelper.BuildAssignCmd(Bpl.Expr.Ident(receiver), this.sink.ReadReceiver(Bpl.Expr.Ident(invars[0]), Bpl.Expr.Ident(niter)))); Bpl.IfCmd ifCmd = BuildIfCmd(Bpl.Expr.True, new Bpl.AssumeCmd(token, Bpl.Expr.False), null); foreach (IMethodDefinition defn in sink.delegateTypeToDelegates[type]) { Bpl.ExprSeq ins = new Bpl.ExprSeq(); Bpl.IdentifierExprSeq outs = new Bpl.IdentifierExprSeq(); if (!defn.IsStatic) ins.Add(Bpl.Expr.Ident(receiver)); int index; for (index = 1; index < invars.Length; index++) { ins.Add(Bpl.Expr.Ident(invars[index])); } for (index = 0; index < outvars.Length; index++) { outs.Add(Bpl.Expr.Ident(outvars[index])); } Bpl.Constant c = sink.FindOrAddDelegateMethodConstant(defn); Bpl.Expr bexpr = Bpl.Expr.Binary(Bpl.BinaryOperator.Opcode.Eq, Bpl.Expr.Ident(method), Bpl.Expr.Ident(c)); Bpl.CallCmd callCmd = new Bpl.CallCmd(token, c.Name, ins, outs); ifCmd = BuildIfCmd(bexpr, callCmd, ifCmd); } whileStmtBuilder.Add(ifCmd); whileStmtBuilder.Add(TranslationHelper.BuildAssignCmd(Bpl.Expr.Ident(iter), Bpl.Expr.Ident(niter))); Bpl.WhileCmd whileCmd = new Bpl.WhileCmd(token, Bpl.Expr.True, new List(), whileStmtBuilder.Collect(token)); implStmtBuilder.Add(whileCmd); Bpl.Implementation impl = new Bpl.Implementation(token, proc.Name, new Bpl.TypeVariableSeq(), proc.InParams, proc.OutParams, new Bpl.VariableSeq(iter, niter, method, receiver), implStmtBuilder.Collect(token) ); impl.Proc = proc; this.sink.TranslatedProgram.TopLevelDeclarations.Add(impl); } catch (TranslationException te) { throw new NotImplementedException(te.ToString()); } catch { throw; } finally { // Maybe this is a good place to add the procedure to the toplevel declarations } } /// /// Translate the type definition. /// /// public override void Visit(ITypeDefinition typeDefinition) { if (typeDefinition.IsClass) { bool savedSawCctor = this.sawCctor; this.sawCctor = false; sink.FindOrCreateType(typeDefinition); base.Visit(typeDefinition); if (!this.sawCctor) { CreateStaticConstructor(typeDefinition); } this.sawCctor = savedSawCctor; } else if (typeDefinition.IsDelegate) { sink.AddDelegateType(typeDefinition); } else if (typeDefinition.IsInterface) { sink.FindOrCreateType(typeDefinition); base.Visit(typeDefinition); } else if (typeDefinition.IsEnum) { return; // enums just are translated as ints } else if (typeDefinition.IsStruct) { Console.WriteLine("Skipping definition of '" + TypeHelper.GetTypeName(typeDefinition) + "' because it is a struct!"); } else { Console.WriteLine("Unknown kind of type definition '{0}' was found", TypeHelper.GetTypeName(typeDefinition)); throw new NotImplementedException(String.Format("Unknown kind of type definition '{0}'.", TypeHelper.GetTypeName(typeDefinition))); } } private bool sawCctor = false; private void CreateStaticConstructor(ITypeDefinition typeDefinition) { var typename = TypeHelper.GetTypeName(typeDefinition); typename = TranslationHelper.TurnStringIntoValidIdentifier(typename); var proc = new Bpl.Procedure(Bpl.Token.NoToken, typename + ".#cctor", new Bpl.TypeVariableSeq(), new Bpl.VariableSeq(), // in new Bpl.VariableSeq(), // out new Bpl.RequiresSeq(), new Bpl.IdentifierExprSeq(), // modifies new Bpl.EnsuresSeq() ); this.sink.TranslatedProgram.TopLevelDeclarations.Add(proc); var stmtBuilder = new Bpl.StmtListBuilder(); foreach (var f in typeDefinition.Fields) { if (f.IsStatic) { Bpl.Expr e; var bplType = TranslationHelper.CciTypeToBoogie(f.Type); if (bplType == Bpl.Type.Int) { e = Bpl.Expr.Literal(0); e.Type = Bpl.Type.Int; } else if (bplType == Bpl.Type.Bool) { e = Bpl.Expr.False; e.Type = Bpl.Type.Bool; } else { throw new NotImplementedException("Don't know how to translate type"); } stmtBuilder.Add( TranslationHelper.BuildAssignCmd( Bpl.Expr.Ident(this.sink.FindOrCreateFieldVariable(f)), e )); } } Bpl.Implementation impl = new Bpl.Implementation(Bpl.Token.NoToken, proc.Name, new Bpl.TypeVariableSeq(), proc.InParams, proc.OutParams, new Bpl.VariableSeq(), stmtBuilder.Collect(Bpl.Token.NoToken) ); impl.Proc = proc; this.sink.TranslatedProgram.TopLevelDeclarations.Add(impl); } /// /// /// public override void Visit(IMethodDefinition method) { if (method.IsStaticConstructor) this.sawCctor = true; bool isEventAddOrRemove = method.IsSpecialName && (method.Name.Value.StartsWith("add_") || method.Name.Value.StartsWith("remove_")); if (isEventAddOrRemove) return; this.sink.BeginMethod(); var procAndFormalMap = this.sink.FindOrCreateProcedureAndReturnProcAndFormalMap(method, method.IsStatic); if (method.IsAbstract) { // we're done, just define the procedure return; } var proc = procAndFormalMap.Procedure; var formalMap = procAndFormalMap.FormalMap; this.sink.RetVariable = procAndFormalMap.ReturnVariable; try { StatementTraverser stmtTraverser = this.factory.MakeStatementTraverser(this.sink, this.PdbReader); #region Add assignments from In-Params to local-Params foreach (MethodParameter mparam in formalMap.Values) { if (mparam.inParameterCopy != null) { Bpl.IToken tok = method.Token(); stmtTraverser.StmtBuilder.Add(Bpl.Cmd.SimpleAssign(tok, new Bpl.IdentifierExpr(tok, mparam.outParameterCopy), new Bpl.IdentifierExpr(tok, mparam.inParameterCopy))); } } #endregion #region For non-deferring ctors and all cctors, initialize all fields to null-equivalent values var inits = InitializeFieldsInConstructor(method); if (0 < inits.Count) { new BlockStatement() { Statements = inits, }.Dispatch(stmtTraverser); } #endregion try { method.Body.Dispatch(stmtTraverser); } catch (TranslationException te) { throw new NotImplementedException("No Errorhandling in Methodvisitor / " + te.ToString()); } catch { throw; } #region Create Local Vars For Implementation List vars = new List(); foreach (MethodParameter mparam in formalMap.Values) { if (!(mparam.underlyingParameter.IsByReference || mparam.underlyingParameter.IsOut)) vars.Add(mparam.outParameterCopy); } foreach (Bpl.Variable v in this.sink.LocalVarMap.Values) { vars.Add(v); } Bpl.VariableSeq vseq = new Bpl.VariableSeq(vars.ToArray()); #endregion Bpl.Implementation impl = new Bpl.Implementation(method.Token(), proc.Name, new Microsoft.Boogie.TypeVariableSeq(), proc.InParams, proc.OutParams, vseq, stmtTraverser.StmtBuilder.Collect(Bpl.Token.NoToken)); impl.Proc = proc; #region Translate method attributes // Don't need an expression translator because there is a limited set of things // that can appear as arguments to custom attributes foreach (var a in method.Attributes) { var attrName = TypeHelper.GetTypeName(a.Type); if (attrName.EndsWith("Attribute")) attrName = attrName.Substring(0, attrName.Length - 9); var args = new object[IteratorHelper.EnumerableCount(a.Arguments)]; int argIndex = 0; foreach (var c in a.Arguments) { var mdc = c as IMetadataConstant; if (mdc != null) { object o; switch (mdc.Type.TypeCode) { case PrimitiveTypeCode.Boolean: o = (bool)mdc.Value ? Bpl.Expr.True : Bpl.Expr.False; break; case PrimitiveTypeCode.Int32: var lit = Bpl.Expr.Literal((int)mdc.Value); lit.Type = Bpl.Type.Int; o = lit; break; case PrimitiveTypeCode.String: o = mdc.Value; break; default: throw new InvalidCastException("Invalid metadata constant type"); } args[argIndex++] = o; } } impl.AddAttribute(attrName, args); } #endregion this.sink.TranslatedProgram.TopLevelDeclarations.Add(impl); } catch (TranslationException te) { throw new NotImplementedException(te.ToString()); } catch { throw; } finally { // Maybe this is a good place to add the procedure to the toplevel declarations } } private static List InitializeFieldsInConstructor(IMethodDefinition method) { Contract.Ensures(Contract.Result>() != null); var inits = new List(); if (method.IsConstructor || method.IsStaticConstructor) { var smb = method.Body as ISourceMethodBody; if (method.IsStaticConstructor || (smb != null && !FindCtorCall.IsDeferringCtor(method, smb.Block))) { var thisExp = new ThisReference() { Type = method.ContainingTypeDefinition, }; foreach (var f in method.ContainingTypeDefinition.Fields) { if (f.IsStatic == method.IsStatic) { var a = new Assignment() { Source = new DefaultValue() { Type = f.Type, }, Target = new TargetExpression() { Definition = f, Instance = method.IsConstructor ? thisExp : null, Type = f.Type }, Type = f.Type, }; inits.Add(new ExpressionStatement() { Expression = a, }); } } } } return inits; } public override void Visit(IFieldDefinition fieldDefinition) { this.sink.FindOrCreateFieldVariable(fieldDefinition); } #endregion private class FindCtorCall : BaseCodeTraverser { private bool isDeferringCtor = false; public ITypeReference containingType; public static bool IsDeferringCtor(IMethodDefinition method, IBlockStatement body) { var fcc = new FindCtorCall(method.ContainingType); fcc.Visit(body); return fcc.isDeferringCtor; } private FindCtorCall(ITypeReference containingType) { this.containingType = containingType; } public override void Visit(IMethodCall methodCall) { var md = methodCall.MethodToCall.ResolvedMethod; if (md != null && md.IsConstructor && methodCall.ThisArgument is IThisReference) { this.isDeferringCtor = TypeHelper.TypesAreEquivalent(md.ContainingType, containingType); this.stopTraversal = true; return; } base.Visit(methodCall); } } } }