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 {
class WholeProgram : TraverserFactory {
///
/// Table to be filled by the metadata traverser when it first gets to an assembly.
/// [TODO: It should be full set of assemblies that are being translated (CUA).]
///
/// The table lists the direct supertypes of all type definitions that it encounters during the
/// traversal. (But the table is organized so that subTypes[T] is the list of type definitions
/// that are direct subtypes of T.)
///
readonly public Dictionary> subTypes = new Dictionary>();
public override MetadataTraverser MakeMetadataTraverser(Sink sink, IContractProvider contractProvider, PdbReader pdbReader) {
return new WholeProgramMetadataSemantics(this, sink, pdbReader);
}
public class WholeProgramMetadataSemantics : MetadataTraverser {
readonly WholeProgram parent;
readonly Sink sink;
///
/// TODO: Need to have this populated before any of the assemblies in the CUA are traversed.
///
readonly Dictionary codeUnderAnalysis = new Dictionary();
public WholeProgramMetadataSemantics(WholeProgram parent, Sink sink, PdbReader/*?*/ pdbReader)
: base(sink, pdbReader) {
this.parent = parent;
this.sink = sink;
}
public override void Visit(IAssembly assembly) {
#region When doing whole-program analysis, traverse the assembly gathering type information
this.codeUnderAnalysis.Add(assembly, true);
var typeRecorder = new RecordSubtypes(this.parent.subTypes);
typeRecorder.Visit(assembly);
#endregion
base.Visit(assembly);
}
class RecordSubtypes : BaseMetadataTraverser {
Dictionary> subTypes;
public RecordSubtypes(Dictionary> subTypes) {
this.subTypes = subTypes;
}
public override void Visit(ITypeDefinition typeDefinition) {
foreach (var baseClass in typeDefinition.BaseClasses) {
if (!this.subTypes.ContainsKey(baseClass)) {
this.subTypes[baseClass] = new List();
}
this.subTypes[baseClass].Add(typeDefinition);
}
base.Visit(typeDefinition);
}
}
}
public override ExpressionTraverser MakeExpressionTraverser(Sink sink, StatementTraverser/*?*/ statementTraverser, bool contractContext) {
return new WholeProgramExpressionSemantics(this, sink, statementTraverser, contractContext);
}
///
/// implement virtual method calls to methods defined in the CUA (code under analysis, i.e.,
/// the set of assemblies being translated) by a "switch statement" that dispatches to the
/// most derived type's method. I.e., make explicit the dynamic dispatch mechanism.
///
public class WholeProgramExpressionSemantics : CLRSemantics.CLRExpressionSemantics {
readonly WholeProgram parent;
readonly public Dictionary> subTypes;
public WholeProgramExpressionSemantics(WholeProgram parent, Sink sink, StatementTraverser/*?*/ statementTraverser, bool contractContext)
: base(sink, statementTraverser, contractContext) {
this.parent = parent;
this.subTypes = parent.subTypes;
}
public override void Visit(IMethodCall methodCall) {
if (!methodCall.IsVirtualCall) {
base.Visit(methodCall);
return;
}
var containingType = methodCall.MethodToCall.ContainingType;
List subTypesOfContainingType;
if (!this.subTypes.TryGetValue(containingType, out subTypesOfContainingType)) {
base.Visit(methodCall);
return;
}
Contract.Assert(0 < subTypesOfContainingType.Count);
Contract.Assert(!methodCall.IsStaticCall);
var resolvedMethod = methodCall.MethodToCall.ResolvedMethod;
Contract.Assert(!resolvedMethod.IsConstructor);
var overrides = FindOverrides(containingType, resolvedMethod);
if (0 == overrides.Count) {
base.Visit(methodCall);
return;
}
#region Translate In Parameters
var inexpr = new List();
#region Create the 'this' argument for the function call
this.Visit(methodCall.ThisArgument);
inexpr.Add(this.TranslatedExpressions.Pop());
#endregion
Dictionary p2eMap = new Dictionary();
IEnumerator penum = resolvedMethod.Parameters.GetEnumerator();
penum.MoveNext();
foreach (IExpression exp in methodCall.Arguments) {
if (penum.Current == null) {
throw new TranslationException("More Arguments than Parameters in functioncall");
}
this.Visit(exp);
Bpl.Expr e = this.TranslatedExpressions.Pop();
p2eMap.Add(penum.Current, e);
if (!penum.Current.IsOut) {
inexpr.Add(e);
}
penum.MoveNext();
}
#endregion
Bpl.IToken token = methodCall.Token();
// TODO: if there is no stmttraverser we are visiting a contract and should use a boogie function instead of procedure!
#region Translate Out vars
var outvars = new List();
foreach (KeyValuePair kvp in p2eMap) {
if (kvp.Key.IsOut || kvp.Key.IsByReference) {
Bpl.IdentifierExpr iexp = kvp.Value as Bpl.IdentifierExpr;
if (iexp == null) {
throw new TranslationException("Trying to pass complex expression as out in functioncall");
}
outvars.Add(iexp);
}
}
#endregion
if (methodCall.Type.ResolvedType.TypeCode != PrimitiveTypeCode.Void) {
Bpl.Variable v = this.sink.CreateFreshLocal(methodCall.Type.ResolvedType);
outvars.Add(new Bpl.IdentifierExpr(token, v));
TranslatedExpressions.Push(new Bpl.IdentifierExpr(token, v));
}
Bpl.QKeyValue attrib = null;
foreach (var a in resolvedMethod.Attributes) {
if (TypeHelper.GetTypeName(a.Type).EndsWith("AsyncAttribute")) {
attrib = new Bpl.QKeyValue(token, "async", new List