From cd2c70a6b44071c4f31edb805570934bda359831 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 14:12:38 +0100 Subject: [PATCH] First implementation of type inference. --- .../Ast/AstMethodBodyBuilder.cs | 90 +++---- .../ICSharpCode.Decompiler.csproj | 1 + .../ILAst/ILAstOptimizer.cs | 5 +- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 41 +++- ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 221 ++++++++++++++++++ ILSpy/ILAstLanguage.cs | 18 +- 6 files changed, 317 insertions(+), 59 deletions(-) create mode 100644 ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 6edf089b1..45460e928 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -16,6 +16,7 @@ namespace Decompiler public class AstMethodBodyBuilder { MethodDefinition methodDef; + TypeSystem typeSystem; CancellationToken cancellationToken; HashSet definedLocalVars = new HashSet(); @@ -24,6 +25,7 @@ namespace Decompiler AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); builder.cancellationToken = cancellationToken; builder.methodDef = methodDef; + builder.typeSystem = methodDef.Module.TypeSystem; if (Debugger.IsAttached) { return builder.CreateMethodBody(); } else { @@ -65,7 +67,7 @@ namespace Decompiler cancellationToken.ThrowIfCancellationRequested(); ILAstOptimizer bodyGraph = new ILAstOptimizer(); - bodyGraph.Optimize(ilMethod); + bodyGraph.Optimize(methodDef, ilMethod); cancellationToken.ThrowIfCancellationRequested(); List intNames = new List(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); @@ -146,7 +148,7 @@ namespace Decompiler yield return new Ast.ForStatement { EmbeddedStatement = TransformBlock(((ILLoop)node).ContentBlock) }; - /* + /* } else if (node is Branch) { yield return new Ast.LabelStatement { Label = ((Branch)node).FirstBasicBlock.Label }; @@ -163,7 +165,7 @@ namespace Decompiler }; yield return ifElseStmt; - */ + */ } else if (node is ILCondition) { ILCondition conditionalNode = (ILCondition)node; if (conditionalNode.FalseBlock.Body.Any()) { @@ -185,9 +187,9 @@ namespace Decompiler SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition.Arguments[0]) }; for (int i = 0; i < ilSwitch.CaseBlocks.Count; i++) { switchStmt.AddChild(new SwitchSection() { - CaseLabels = new[] { new CaseLabel() { Expression = new PrimitiveExpression(i) } }, - Statements = new[] { TransformBlock(ilSwitch.CaseBlocks[i]) } - }, SwitchStatement.SwitchSectionRole); + CaseLabels = new[] { new CaseLabel() { Expression = new PrimitiveExpression(i) } }, + Statements = new[] { TransformBlock(ilSwitch.CaseBlocks[i]) } + }, SwitchStatement.SwitchSectionRole); } yield return switchStmt; } else if (node is ILTryCatchBlock) { @@ -195,10 +197,10 @@ namespace Decompiler List catchClauses = new List(); foreach (var catchClause in tryCatchNode.CatchBlocks) { catchClauses.Add(new Ast.CatchClause { - Type = AstBuilder.ConvertType(catchClause.ExceptionType), - VariableName = "exception", - Body = TransformBlock(catchClause) - }); + Type = AstBuilder.ConvertType(catchClause.ExceptionType), + VariableName = "exception", + Body = TransformBlock(catchClause) + }); } yield return new Ast.TryCatchStatement { TryBlock = TransformBlock(tryCatchNode.TryBlock), @@ -225,7 +227,7 @@ namespace Decompiler AstNode TransformExpression(ILExpression expr) { List args = TransformExpressionArguments(expr); - return TransformByteCode(methodDef, expr, args); + return TransformByteCode(expr, args); } Ast.Expression MakeBranchCondition(ILExpression expr) @@ -234,19 +236,19 @@ namespace Decompiler Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; switch(expr.OpCode.Code) { - case Code.Brfalse: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); - case Code.Brtrue: return arg1; - case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); - case Code.Bge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); - case Code.Bge_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); - case Code.Bgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case Code.Bgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case Code.Ble: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); - case Code.Ble_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); - case Code.Blt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - case Code.Blt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - case Code.Bne_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); - default: throw new Exception("Bad opcode"); + case Code.Brfalse: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); + case Code.Brtrue: return arg1; + case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); + case Code.Bge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); + case Code.Bge_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); + case Code.Bgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + case Code.Bgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + case Code.Ble: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); + case Code.Ble_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); + case Code.Blt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case Code.Blt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case Code.Bne_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); + default: throw new Exception("Bad opcode"); } /* } else if (branch is ShortCircuitBranch) { @@ -282,13 +284,13 @@ namespace Decompiler } else { throw new Exception("Bad type"); } - */ + */ } - AstNode TransformByteCode(MethodDefinition methodDef, ILExpression byteCode, List args) + AstNode TransformByteCode(ILExpression byteCode, List args) { try { - AstNode ret = TransformByteCode_Internal(methodDef, byteCode, args); + AstNode ret = TransformByteCode_Internal(byteCode, args); // ret.UserData["Type"] = byteCode.Type; return ret; } catch (NotImplementedException) { @@ -325,7 +327,7 @@ namespace Decompiler } } - AstNode TransformByteCode_Internal(MethodDefinition methodDef, ILExpression byteCode, List args) + AstNode TransformByteCode_Internal(ILExpression byteCode, List args) { // throw new NotImplementedException(); @@ -541,6 +543,20 @@ namespace Decompiler return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name)); } case Code.Ldc_I4: + if (byteCode.InferredType == typeSystem.Boolean && (int)operand == 0) + return new Ast.PrimitiveExpression(false); + else if (byteCode.InferredType == typeSystem.Boolean && (int)operand == 1) + return new Ast.PrimitiveExpression(true); + if (byteCode.InferredType != null && byteCode.InferredType.IsValueType) { + TypeDefinition enumDefinition = byteCode.InferredType.Resolve(); + if (enumDefinition != null && enumDefinition.IsEnum) { + foreach (FieldDefinition field in enumDefinition.Fields) { + if (field.IsStatic && object.Equals(field.Constant, operand)) + return AstBuilder.ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field); + } + } + } + return new Ast.PrimitiveExpression(operand); case Code.Ldc_I8: case Code.Ldc_R4: case Code.Ldc_R8: @@ -724,26 +740,10 @@ namespace Decompiler if (reqType == null) { return expr; } else { - return Convert(expr, reqType.FullName); + return expr; } } - static Ast.Expression Convert(Ast.Expression expr, string reqType) - { -// if (expr.UserData.ContainsKey("Type")) { -// Cecil.TypeReference exprType = (Cecil.TypeReference)expr.UserData["Type"]; -// if (exprType == ByteCode.TypeZero && -// reqType == ByteCode.TypeBool.FullName) { -// return new PrimitiveExpression(false, "false"); -// } -// if (exprType == ByteCode.TypeOne && -// reqType == ByteCode.TypeBool.FullName) { -// return new PrimitiveExpression(true, "true"); -// } -// } - return expr; - } - static Ast.Expression ConvertIntToBool(Ast.Expression astInt) { return astInt; diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index fdda5b4ab..c2df0e929 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -87,6 +87,7 @@ + diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 233e00d84..d7eba8e58 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -16,6 +16,7 @@ namespace Decompiler.ControlFlow FlattenNestedMovableBlocks, SimpleGotoRemoval, RemoveDeadLabels, + TypeInference, None } @@ -23,7 +24,7 @@ namespace Decompiler.ControlFlow { Dictionary labelToCfNode = new Dictionary(); - public void Optimize(ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) + public void Optimize(MethodDefinition cecilMethod, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) { if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { @@ -55,6 +56,8 @@ namespace Decompiler.ControlFlow SimpleGotoRemoval(method); if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return; RemoveDeadLabels(method); + if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; + TypeAnalysis.Run(cecilMethod.Module.TypeSystem, method); } class ILMoveableBlock: ILBlock diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index ff79656b8..89b3b9667 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -55,7 +55,8 @@ namespace Decompiler public override IEnumerable GetChildren() { - yield return EntryPoint; + if (EntryPoint != null) + yield return EntryPoint; foreach(ILNode child in this.Body) { yield return child; } @@ -157,7 +158,7 @@ namespace Decompiler } } - public class ILExpression: ILNode + public class ILExpression : ILNode { public OpCode OpCode { get; set; } public object Operand { get; set; } @@ -165,6 +166,8 @@ namespace Decompiler // Mapping to the original instructions (useful for debugging) public List ILRanges { get; set; } + public TypeReference InferredType { get; set; } + public ILExpression(OpCode opCode, object operand, params ILExpression[] args) { this.OpCode = opCode; @@ -213,24 +216,39 @@ namespace Decompiler public override void WriteTo(ITextOutput output) { if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) { - if (OpCode.Name == "stloc") { - output.Write(((ILVariable)Operand).Name + " = "); + if (OpCode == OpCodes.Stloc && this.InferredType == null) { + output.Write(((ILVariable)Operand).Name); + output.Write(" = "); Arguments.First().WriteTo(output); return; - } else if (OpCode.Name == "ldloc") { + } else if (OpCode == OpCodes.Ldloc) { output.Write(((ILVariable)Operand).Name); + if (this.InferredType != null) { + output.Write(':'); + this.InferredType.WriteTo(output, true, true); + } return; } } output.Write(OpCode.Name); + if (this.InferredType != null) { + output.Write(':'); + this.InferredType.WriteTo(output, true, true); + } output.Write('('); bool first = true; if (Operand != null) { - if (Operand is ILLabel) - output.Write(((ILLabel)Operand).Name); - else + if (Operand is ILLabel) { + output.WriteReference(((ILLabel)Operand).Name, Operand); + } else if (Operand is MethodReference) { + MethodReference method = (MethodReference)Operand; + method.DeclaringType.WriteTo(output, true, true); + output.Write("::"); + output.WriteReference(method.Name, method); + } else { DisassemblerHelpers.WriteOperand(output, Operand); + } first = false; } foreach (ILExpression arg in this.Arguments) { @@ -242,7 +260,7 @@ namespace Decompiler } } - public class ILLoop: ILNode + public class ILLoop : ILNode { public ILBlock ContentBlock; @@ -261,7 +279,7 @@ namespace Decompiler } } - public class ILCondition: ILNode + public class ILCondition : ILNode { public ILExpression Condition; public ILBlock TrueBlock; // Branch was taken @@ -271,7 +289,8 @@ namespace Decompiler { yield return Condition; yield return TrueBlock; - yield return FalseBlock; + if (FalseBlock != null) + yield return FalseBlock; } public override void WriteTo(ITextOutput output) diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs new file mode 100644 index 000000000..01c17f9a1 --- /dev/null +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -0,0 +1,221 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Decompiler; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Decompiler +{ + /// + /// Assigns C# types to IL expressions. + /// + /// + /// Types are inferred in a bidirectional manner: + /// The expected type flows from the outside to the inside, the actual inferred type flows from the inside to the outside. + /// + public class TypeAnalysis + { + public static void Run(TypeSystem typeSystem, ILNode node) + { + TypeAnalysis ta = new TypeAnalysis(); + ta.typeSystem = typeSystem; + ta.InferTypes(node); + } + + TypeSystem typeSystem; + List storedToGeneratedVariables = new List(); + + void InferTypes(ILNode node) + { + foreach (ILNode child in node.GetChildren()) { + ILExpression expr = child as ILExpression; + if (expr != null) { + ILVariable v = expr.Operand as ILVariable; + if (v != null && v.IsGenerated && v.Type == null && expr.OpCode == OpCodes.Stloc) { + // don't deal with this node or its children yet, + // wait for the expected type to be inferred first + storedToGeneratedVariables.Add(expr); + continue; + } + bool anyArgumentIsMissingType = expr.Arguments.Any(a => a.InferredType == null); + if (expr.InferredType == null || anyArgumentIsMissingType) + expr.InferredType = InferTypeForExpression(expr, null, forceInferChildren: anyArgumentIsMissingType); + } + InferTypes(child); + } + } + + /// + /// Infers the C# type of . + /// + /// The expression + /// The expected type of the expression + /// Whether direct children should be inferred even if its not necessary. (does not apply to nested children!) + /// The inferred type + TypeReference InferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) + { + if (forceInferChildren || expr.InferredType == null) + expr.InferredType = DoInferTypeForExpression(expr, expectedType, forceInferChildren); + return expr.InferredType; + } + + TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) + { + switch (expr.OpCode.Code) { + case Code.Stloc: + if (forceInferChildren) + InferTypeForExpression(expr.Arguments.Single(), ((ILVariable)expr.Operand).Type); + return null; + case Code.Ldloc: + return ((ILVariable)expr.Operand).Type; + case Code.Ldarg: + return ((ParameterDefinition)expr.Operand).ParameterType; + case Code.Call: + case Code.Callvirt: + { + MethodReference method = (MethodReference)expr.Operand; + if (forceInferChildren) { + for (int i = 0; i < expr.Arguments.Count; i++) { + if (i == 0 && method.HasThis) + InferTypeForExpression(expr.Arguments[i], method.DeclaringType); + else + InferTypeForExpression(expr.Arguments[i], method.Parameters[method.HasThis ? i - 1: i].ParameterType); + } + } + return method.ReturnType; + } + case Code.Newobj: + { + MethodReference ctor = (MethodReference)expr.Operand; + if (forceInferChildren) { + for (int i = 0; i < ctor.Parameters.Count; i++) { + InferTypeForExpression(expr.Arguments[i], ctor.Parameters[i].ParameterType); + } + } + return ctor.DeclaringType; + } + case Code.Or: + return InferArgumentsInBinaryOperator(expr); + case Code.Shl: + case Code.Shr: + if (forceInferChildren) + InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); + return InferTypeForExpression(expr.Arguments[0], expectedType); + case Code.Ldc_I4: + return (IsSigned(expectedType) != null || expectedType == typeSystem.Boolean) ? expectedType : typeSystem.Int32; + case Code.Ldc_I8: + return (IsSigned(expectedType) != null) ? expectedType : typeSystem.Int64; + case Code.Conv_I8: + return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int64; + case Code.Dup: + return InferTypeForExpression(expr.Arguments.Single(), expectedType); + case Code.Ceq: + case Code.Clt: + if (forceInferChildren) + InferArgumentsInBinaryOperator(expr); + return typeSystem.Boolean; + case Code.Beq: + case Code.Blt: + if (forceInferChildren) + InferArgumentsInBinaryOperator(expr); + return null; + case Code.Brtrue: + case Code.Brfalse: + if (forceInferChildren) + InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean); + return null; + default: + //throw new NotImplementedException("Can't handle " + expr.OpCode.Name); + return null; + } + } + + TypeReference InferArgumentsInBinaryOperator(ILExpression expr) + { + ILExpression left = expr.Arguments[0]; + ILExpression right = expr.Arguments[1]; + TypeReference leftPreferred = DoInferTypeForExpression(left, null); + TypeReference rightPreferred = DoInferTypeForExpression(right, null); + if (leftPreferred == rightPreferred) { + return left.InferredType = right.InferredType = leftPreferred; + } else if (rightPreferred == DoInferTypeForExpression(left, rightPreferred)) { + return left.InferredType = right.InferredType = rightPreferred; + } else if (leftPreferred == DoInferTypeForExpression(right, leftPreferred)) { + return left.InferredType = right.InferredType = leftPreferred; + } else { + return left.InferredType = right.InferredType = TypeWithMoreInformation(leftPreferred, rightPreferred); + } + } + + TypeReference TypeWithMoreInformation(TypeReference leftPreferred, TypeReference rightPreferred) + { + int left = GetInformationAmount(typeSystem, leftPreferred); + int right = GetInformationAmount(typeSystem, rightPreferred); + if (left < right) + return rightPreferred; + else + return leftPreferred; + } + + int GetInformationAmount(TypeReference type) + { + return GetInformationAmount(typeSystem, type); + } + + static int GetInformationAmount(TypeSystem typeSystem, TypeReference type) + { + if (type == null) + return 0; + if (type.IsValueType) { + // value type might be an enum + TypeDefinition typeDef = type.Resolve() as TypeDefinition; + if (typeDef != null && typeDef.IsEnum) { + TypeReference underlyingType = typeDef.Fields.Single(f => f.IsRuntimeSpecialName && !f.IsStatic).FieldType; + return GetInformationAmount(typeDef.Module.TypeSystem, underlyingType); + } + } + if (type == typeSystem.Boolean) + return 1; + else if (type == typeSystem.Byte || type == typeSystem.SByte) + return 8; + else if (type == typeSystem.Int16 || type == typeSystem.UInt16) + return 16; + else if (type == typeSystem.Int32 || type == typeSystem.UInt32) + return 32; + else if (type == typeSystem.IntPtr || type == typeSystem.UIntPtr) + return 33; // treat native int as between int32 and int64 + else if (type == typeSystem.Int64 || type == typeSystem.UInt64) + return 64; + return 100; // we consider structs/objects to have more information than any primitives + } + + bool? IsSigned(TypeReference type) + { + return IsSigned(typeSystem, type); + } + + static bool? IsSigned(TypeSystem typeSystem, TypeReference type) + { + if (type == null) + return null; + if (type.IsValueType) { + // value type might be an enum + TypeDefinition typeDef = type.Resolve() as TypeDefinition; + if (typeDef != null && typeDef.IsEnum) { + TypeReference underlyingType = typeDef.Fields.Single(f => f.IsRuntimeSpecialName && !f.IsStatic).FieldType; + return IsSigned(typeDef.Module.TypeSystem, underlyingType); + } + } + if (type == typeSystem.Byte || type == typeSystem.UInt16 || type == typeSystem.UInt32 || type == typeSystem.UInt64 || type == typeSystem.UIntPtr) + return false; + if (type == typeSystem.SByte || type == typeSystem.Int16 || type == typeSystem.Int32 || type == typeSystem.Int64 || type == typeSystem.IntPtr) + return true; + return null; + } + } +} diff --git a/ILSpy/ILAstLanguage.cs b/ILSpy/ILAstLanguage.cs index 5478c24a8..7b19986ad 100644 --- a/ILSpy/ILAstLanguage.cs +++ b/ILSpy/ILAstLanguage.cs @@ -24,6 +24,7 @@ using Decompiler; using Decompiler.ControlFlow; using Decompiler.Transforms; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; @@ -46,13 +47,26 @@ namespace ICSharpCode.ILSpy public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { + ILAstBuilder astBuilder = new ILAstBuilder(); ILBlock ilMethod = new ILBlock(); - ilMethod.Body = new ILAstBuilder().Build(method, inlineVariables); + ilMethod.Body = astBuilder.Build(method, inlineVariables); if (abortBeforeStep != null) { - new ILAstOptimizer().Optimize(ilMethod, abortBeforeStep.Value); + new ILAstOptimizer().Optimize(method, ilMethod, abortBeforeStep.Value); } + var allVariables = astBuilder.Variables + .Concat(ilMethod.GetSelfAndChildrenRecursive().Select(e => e.Operand as ILVariable).Where(v => v != null)).Distinct(); + foreach (ILVariable v in allVariables) { + output.Write(v.Name); + if (v.Type != null) { + output.Write(" : "); + v.Type.WriteTo(output, true, true); + } + output.WriteLine(); + } + output.WriteLine(); + foreach (ILNode node in ilMethod.Body) { node.WriteTo(output); output.WriteLine();