summaryrefslogtreecommitdiff
path: root/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs')
-rw-r--r--ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs1220
1 files changed, 1220 insertions, 0 deletions
diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
new file mode 100644
index 00000000..a4d80e59
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
@@ -0,0 +1,1220 @@
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+
+using ICSharpCode.Decompiler.Ast.Transforms;
+using ICSharpCode.Decompiler.ILAst;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+using ICSharpCode.NRefactory.Utils;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.Ast
+{
+ using Ast = NRefactory.CSharp;
+ using Cecil = Mono.Cecil;
+
+ public class AstMethodBodyBuilder
+ {
+ MethodDefinition methodDef;
+ TypeSystem typeSystem;
+ DecompilerContext context;
+ HashSet<ILVariable> localVariablesToDefine = new HashSet<ILVariable>(); // local variables that are missing a definition
+
+ /// <summary>
+ /// Creates the body for the method definition.
+ /// </summary>
+ /// <param name="methodDef">Method definition to decompile.</param>
+ /// <param name="context">Decompilation context.</param>
+ /// <param name="parameters">Parameter declarations of the method being decompiled.
+ /// These are used to update the parameter names when the decompiler generates names for the parameters.</param>
+ /// <returns>Block for the method body</returns>
+ public static BlockStatement CreateMethodBody(MethodDefinition methodDef,
+ DecompilerContext context,
+ IEnumerable<ParameterDeclaration> parameters = null)
+ {
+ MethodDefinition oldCurrentMethod = context.CurrentMethod;
+ Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef);
+ context.CurrentMethod = methodDef;
+ context.CurrentMethodIsAsync = false;
+ try {
+ AstMethodBodyBuilder builder = new AstMethodBodyBuilder();
+ builder.methodDef = methodDef;
+ builder.context = context;
+ builder.typeSystem = methodDef.Module.TypeSystem;
+ if (Debugger.IsAttached) {
+ return builder.CreateMethodBody(parameters);
+ } else {
+ try {
+ return builder.CreateMethodBody(parameters);
+ } catch (OperationCanceledException) {
+ throw;
+ } catch (Exception ex) {
+ throw new DecompilerException(methodDef, ex);
+ }
+ }
+ } finally {
+ context.CurrentMethod = oldCurrentMethod;
+ }
+ }
+
+ public BlockStatement CreateMethodBody(IEnumerable<ParameterDeclaration> parameters)
+ {
+ if (methodDef.Body == null) {
+ return null;
+ }
+
+ context.CancellationToken.ThrowIfCancellationRequested();
+ ILBlock ilMethod = new ILBlock();
+ ILAstBuilder astBuilder = new ILAstBuilder();
+ ilMethod.Body = astBuilder.Build(methodDef, true, context);
+
+ context.CancellationToken.ThrowIfCancellationRequested();
+ ILAstOptimizer bodyGraph = new ILAstOptimizer();
+ bodyGraph.Optimize(context, ilMethod);
+ context.CancellationToken.ThrowIfCancellationRequested();
+
+ var localVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable)
+ .Where(v => v != null && !v.IsParameter).Distinct();
+ Debug.Assert(context.CurrentMethod == methodDef);
+ NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, localVariables, ilMethod);
+
+ if (parameters != null) {
+ foreach (var pair in (from p in parameters
+ join v in astBuilder.Parameters on p.Annotation<ParameterDefinition>() equals v.OriginalParameter
+ select new { p, v.Name }))
+ {
+ pair.p.Name = pair.Name;
+ }
+ }
+
+ context.CancellationToken.ThrowIfCancellationRequested();
+ BlockStatement astBlock = TransformBlock(ilMethod);
+ CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments
+
+ Statement insertionPoint = astBlock.Statements.FirstOrDefault();
+ foreach (ILVariable v in localVariablesToDefine) {
+ AstType type;
+ if (v.Type.ContainsAnonymousType())
+ type = new SimpleType("var");
+ else
+ type = AstBuilder.ConvertType(v.Type);
+ var newVarDecl = new VariableDeclarationStatement(type, v.Name);
+ newVarDecl.Variables.Single().AddAnnotation(v);
+ astBlock.Statements.InsertBefore(insertionPoint, newVarDecl);
+ }
+
+ astBlock.AddAnnotation(new MethodDebugSymbols(methodDef) { LocalVariables = localVariables.ToList() });
+
+ return astBlock;
+ }
+
+ BlockStatement TransformBlock(ILBlock block)
+ {
+ BlockStatement astBlock = new BlockStatement();
+ if (block != null) {
+ foreach(ILNode node in block.GetChildren()) {
+ astBlock.Statements.AddRange(TransformNode(node));
+ }
+ }
+ return astBlock;
+ }
+
+ IEnumerable<Statement> TransformNode(ILNode node)
+ {
+ if (node is ILLabel) {
+ yield return new LabelStatement { Label = ((ILLabel)node).Name };
+ } else if (node is ILExpression) {
+ List<ILRange> ilRanges = ILRange.OrderAndJoin(node.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.ILRanges));
+ AstNode codeExpr = TransformExpression((ILExpression)node);
+ if (codeExpr != null) {
+ codeExpr = codeExpr.WithAnnotation(ilRanges);
+ if (codeExpr is Expression) {
+ yield return new ExpressionStatement { Expression = (Expression)codeExpr };
+ } else if (codeExpr is Statement) {
+ yield return (Statement)codeExpr;
+ } else {
+ throw new Exception();
+ }
+ }
+ } else if (node is ILWhileLoop) {
+ ILWhileLoop ilLoop = (ILWhileLoop)node;
+ WhileStatement whileStmt = new WhileStatement() {
+ Condition = ilLoop.Condition != null ? (Expression)TransformExpression(ilLoop.Condition) : new PrimitiveExpression(true),
+ EmbeddedStatement = TransformBlock(ilLoop.BodyBlock)
+ };
+ yield return whileStmt;
+ } else if (node is ILCondition) {
+ ILCondition conditionalNode = (ILCondition)node;
+ bool hasFalseBlock = conditionalNode.FalseBlock.EntryGoto != null || conditionalNode.FalseBlock.Body.Count > 0;
+ yield return new IfElseStatement {
+ Condition = (Expression)TransformExpression(conditionalNode.Condition),
+ TrueStatement = TransformBlock(conditionalNode.TrueBlock),
+ FalseStatement = hasFalseBlock ? TransformBlock(conditionalNode.FalseBlock) : null
+ };
+ } else if (node is ILSwitch) {
+ ILSwitch ilSwitch = (ILSwitch)node;
+ if (TypeAnalysis.IsBoolean(ilSwitch.Condition.InferredType) && (
+ from cb in ilSwitch.CaseBlocks
+ where cb.Values != null
+ from val in cb.Values
+ select val
+ ).Any(val => val != 0 && val != 1))
+ {
+ // If switch cases contain values other then 0 and 1, force the condition to be non-boolean
+ ilSwitch.Condition.ExpectedType = typeSystem.Int32;
+ }
+ SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) };
+ foreach (var caseBlock in ilSwitch.CaseBlocks) {
+ SwitchSection section = new SwitchSection();
+ if (caseBlock.Values != null) {
+ section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.ExpectedType ?? ilSwitch.Condition.InferredType) }));
+ } else {
+ section.CaseLabels.Add(new CaseLabel());
+ }
+ section.Statements.Add(TransformBlock(caseBlock));
+ switchStmt.SwitchSections.Add(section);
+ }
+ yield return switchStmt;
+ } else if (node is ILTryCatchBlock) {
+ ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node);
+ var tryCatchStmt = new TryCatchStatement();
+ tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock);
+ foreach (var catchClause in tryCatchNode.CatchBlocks) {
+ if (catchClause.ExceptionVariable == null
+ && (catchClause.ExceptionType == null || catchClause.ExceptionType.MetadataType == MetadataType.Object))
+ {
+ tryCatchStmt.CatchClauses.Add(new CatchClause { Body = TransformBlock(catchClause) });
+ } else {
+ tryCatchStmt.CatchClauses.Add(
+ new CatchClause {
+ Type = AstBuilder.ConvertType(catchClause.ExceptionType),
+ VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name,
+ Body = TransformBlock(catchClause)
+ }.WithAnnotation(catchClause.ExceptionVariable));
+ }
+ }
+ if (tryCatchNode.FinallyBlock != null)
+ tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock);
+ if (tryCatchNode.FaultBlock != null) {
+ CatchClause cc = new CatchClause();
+ cc.Body = TransformBlock(tryCatchNode.FaultBlock);
+ cc.Body.Add(new ThrowStatement()); // rethrow
+ tryCatchStmt.CatchClauses.Add(cc);
+ }
+ yield return tryCatchStmt;
+ } else if (node is ILFixedStatement) {
+ ILFixedStatement fixedNode = (ILFixedStatement)node;
+ FixedStatement fixedStatement = new FixedStatement();
+ foreach (ILExpression initializer in fixedNode.Initializers) {
+ Debug.Assert(initializer.Code == ILCode.Stloc);
+ ILVariable v = (ILVariable)initializer.Operand;
+ fixedStatement.Variables.Add(
+ new VariableInitializer {
+ Name = v.Name,
+ Initializer = (Expression)TransformExpression(initializer.Arguments[0])
+ }.WithAnnotation(v));
+ }
+ fixedStatement.Type = AstBuilder.ConvertType(((ILVariable)fixedNode.Initializers[0].Operand).Type);
+ fixedStatement.EmbeddedStatement = TransformBlock(fixedNode.BodyBlock);
+ yield return fixedStatement;
+ } else if (node is ILBlock) {
+ yield return TransformBlock((ILBlock)node);
+ } else {
+ throw new Exception("Unknown node type");
+ }
+ }
+
+ AstNode TransformExpression(ILExpression expr)
+ {
+ AstNode node = TransformByteCode(expr);
+ Expression astExpr = node as Expression;
+
+ // get IL ranges - used in debugger
+ List<ILRange> ilRanges = ILRange.OrderAndJoin(expr.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.ILRanges));
+ AstNode result;
+
+ if (astExpr != null)
+ result = Convert(astExpr, expr.InferredType, expr.ExpectedType);
+ else
+ result = node;
+
+ if (result != null)
+ result = result.WithAnnotation(new TypeInformation(expr.InferredType, expr.ExpectedType));
+
+ if (result != null)
+ return result.WithAnnotation(ilRanges);
+
+ return result;
+ }
+
+ AstNode TransformByteCode(ILExpression byteCode)
+ {
+ object operand = byteCode.Operand;
+ AstType operandAsTypeRef = AstBuilder.ConvertType(operand as TypeReference);
+
+ List<Expression> args = new List<Expression>();
+ foreach(ILExpression arg in byteCode.Arguments) {
+ args.Add((Expression)TransformExpression(arg));
+ }
+ Expression arg1 = args.Count >= 1 ? args[0] : null;
+ Expression arg2 = args.Count >= 2 ? args[1] : null;
+ Expression arg3 = args.Count >= 3 ? args[2] : null;
+
+ switch (byteCode.Code) {
+ #region Arithmetic
+ case ILCode.Add:
+ case ILCode.Add_Ovf:
+ case ILCode.Add_Ovf_Un:
+ {
+ BinaryOperatorExpression boe;
+ if (byteCode.InferredType is PointerType) {
+ boe = new BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
+ if (byteCode.Arguments[0].ExpectedType is PointerType ||
+ byteCode.Arguments[1].ExpectedType is PointerType) {
+ boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
+ }
+ } else {
+ boe = new BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
+ }
+ boe.AddAnnotation(byteCode.Code == ILCode.Add ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation);
+ return boe;
+ }
+ case ILCode.Sub:
+ case ILCode.Sub_Ovf:
+ case ILCode.Sub_Ovf_Un:
+ {
+ BinaryOperatorExpression boe;
+ if (byteCode.InferredType is PointerType) {
+ boe = new BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
+ if (byteCode.Arguments[0].ExpectedType is PointerType) {
+ boe.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
+ }
+ } else {
+ boe = new BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
+ }
+ boe.AddAnnotation(byteCode.Code == ILCode.Sub ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation);
+ return boe;
+ }
+ case ILCode.Div: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2);
+ case ILCode.Div_Un: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2);
+ case ILCode.Mul: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation);
+ case ILCode.Mul_Ovf: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation);
+ case ILCode.Mul_Ovf_Un: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation);
+ case ILCode.Rem: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2);
+ case ILCode.Rem_Un: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2);
+ case ILCode.And: return new BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2);
+ case ILCode.Or: return new BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2);
+ case ILCode.Xor: return new BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2);
+ case ILCode.Shl: return new BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2);
+ case ILCode.Shr: return new BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
+ case ILCode.Shr_Un: return new BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
+ case ILCode.Neg: return new UnaryOperatorExpression(UnaryOperatorType.Minus, arg1).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation);
+ case ILCode.Not: return new UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1);
+ case ILCode.PostIncrement:
+ case ILCode.PostIncrement_Ovf:
+ case ILCode.PostIncrement_Ovf_Un:
+ {
+ if (arg1 is DirectionExpression)
+ arg1 = ((DirectionExpression)arg1).Expression.Detach();
+ var uoe = new UnaryOperatorExpression(
+ (int)byteCode.Operand > 0 ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement, arg1);
+ uoe.AddAnnotation((byteCode.Code == ILCode.PostIncrement) ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation);
+ return uoe;
+ }
+ #endregion
+ #region Arrays
+ case ILCode.Newarr: {
+ var ace = new ArrayCreateExpression();
+ ace.Type = operandAsTypeRef;
+ ComposedType ct = operandAsTypeRef as ComposedType;
+ if (ct != null) {
+ // change "new (int[,])[10] to new int[10][,]"
+ ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
+ }
+ if (byteCode.Code == ILCode.InitArray) {
+ ace.Initializer = new ArrayInitializerExpression();
+ ace.Initializer.Elements.AddRange(args);
+ } else {
+ ace.Arguments.Add(arg1);
+ }
+ return ace;
+ }
+ case ILCode.InitArray: {
+ var ace = new ArrayCreateExpression();
+ ace.Type = operandAsTypeRef;
+ ComposedType ct = operandAsTypeRef as ComposedType;
+ var arrayType = (ArrayType) operand;
+ if (ct != null)
+ {
+ // change "new (int[,])[10] to new int[10][,]"
+ ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
+ ace.Initializer = new ArrayInitializerExpression();
+ }
+ var newArgs = new List<Expression>();
+ foreach (var arrayDimension in arrayType.Dimensions.Skip(1).Reverse())
+ {
+ int length = (int)arrayDimension.UpperBound - (int)arrayDimension.LowerBound;
+ for (int j = 0; j < args.Count; j += length)
+ {
+ var child = new ArrayInitializerExpression();
+ child.Elements.AddRange(args.GetRange(j, length));
+ newArgs.Add(child);
+ }
+ var temp = args;
+ args = newArgs;
+ newArgs = temp;
+ newArgs.Clear();
+ }
+ ace.Initializer.Elements.AddRange(args);
+ return ace;
+ }
+ case ILCode.Ldlen: return arg1.Member("Length");
+ case ILCode.Ldelem_I:
+ case ILCode.Ldelem_I1:
+ case ILCode.Ldelem_I2:
+ case ILCode.Ldelem_I4:
+ case ILCode.Ldelem_I8:
+ case ILCode.Ldelem_U1:
+ case ILCode.Ldelem_U2:
+ case ILCode.Ldelem_U4:
+ case ILCode.Ldelem_R4:
+ case ILCode.Ldelem_R8:
+ case ILCode.Ldelem_Ref:
+ case ILCode.Ldelem_Any:
+ return arg1.Indexer(arg2);
+ case ILCode.Ldelema:
+ return MakeRef(arg1.Indexer(arg2));
+ case ILCode.Stelem_I:
+ case ILCode.Stelem_I1:
+ case ILCode.Stelem_I2:
+ case ILCode.Stelem_I4:
+ case ILCode.Stelem_I8:
+ case ILCode.Stelem_R4:
+ case ILCode.Stelem_R8:
+ case ILCode.Stelem_Ref:
+ case ILCode.Stelem_Any:
+ return new AssignmentExpression(arg1.Indexer(arg2), arg3);
+ case ILCode.CompoundAssignment:
+ {
+ CastExpression cast = arg1 as CastExpression;
+ var boe = cast != null ? (BinaryOperatorExpression)cast.Expression : arg1 as BinaryOperatorExpression;
+ // AssignmentExpression doesn't support overloaded operators so they have to be processed to BinaryOperatorExpression
+ if (boe == null) {
+ var tmp = new ParenthesizedExpression(arg1);
+ ReplaceMethodCallsWithOperators.ProcessInvocationExpression((InvocationExpression)arg1);
+ boe = (BinaryOperatorExpression)tmp.Expression;
+ }
+ var assignment = new AssignmentExpression {
+ Left = boe.Left.Detach(),
+ Operator = ReplaceMethodCallsWithOperators.GetAssignmentOperatorForBinaryOperator(boe.Operator),
+ Right = boe.Right.Detach()
+ }.CopyAnnotationsFrom(boe);
+ // We do not mark the resulting assignment as RestoreOriginalAssignOperatorAnnotation, because
+ // the operator cannot be translated back to the expanded form (as the left-hand expression
+ // would be evaluated twice, and might have side-effects)
+ if (cast != null) {
+ cast.Expression = assignment;
+ return cast;
+ } else {
+ return assignment;
+ }
+ }
+ #endregion
+ #region Comparison
+ case ILCode.Ceq: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2);
+ case ILCode.Cne: return new BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2);
+ case ILCode.Cgt: return new BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2);
+ case ILCode.Cgt_Un: {
+ // can also mean Inequality, when used with object references
+ TypeReference arg1Type = byteCode.Arguments[0].InferredType;
+ if (arg1Type != null && !arg1Type.IsValueType) goto case ILCode.Cne;
+
+ // when comparing signed integral values using Cgt_Un with 0
+ // the Ast should actually contain InEquality since "(uint)a > 0u" is identical to "a != 0"
+ if (arg1Type.IsSignedIntegralType())
+ {
+ var p = arg2 as PrimitiveExpression;
+ if (p != null && p.Value.IsZero()) goto case ILCode.Cne;
+ }
+
+ goto case ILCode.Cgt;
+ }
+ case ILCode.Cle_Un: {
+ // can also mean Equality, when used with object references
+ TypeReference arg1Type = byteCode.Arguments[0].InferredType;
+ if (arg1Type != null && !arg1Type.IsValueType) goto case ILCode.Ceq;
+
+ // when comparing signed integral values using Cle_Un with 0
+ // the Ast should actually contain Equality since "(uint)a <= 0u" is identical to "a == 0"
+ if (arg1Type.IsSignedIntegralType())
+ {
+ var p = arg2 as PrimitiveExpression;
+ if (p != null && p.Value.IsZero()) goto case ILCode.Ceq;
+ }
+
+ goto case ILCode.Cle;
+ }
+ case ILCode.Cle: return new BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2);
+ case ILCode.Cge_Un:
+ case ILCode.Cge: return new BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2);
+ case ILCode.Clt_Un:
+ case ILCode.Clt: return new BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
+ #endregion
+ #region Logical
+ case ILCode.LogicNot: return new UnaryOperatorExpression(UnaryOperatorType.Not, arg1);
+ case ILCode.LogicAnd: return new BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalAnd, arg2);
+ case ILCode.LogicOr: return new BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalOr, arg2);
+ case ILCode.TernaryOp: return new ConditionalExpression() { Condition = arg1, TrueExpression = arg2, FalseExpression = arg3 };
+ case ILCode.NullCoalescing: return new BinaryOperatorExpression(arg1, BinaryOperatorType.NullCoalescing, arg2);
+ #endregion
+ #region Branch
+ case ILCode.Br: return new GotoStatement(((ILLabel)byteCode.Operand).Name);
+ case ILCode.Brtrue:
+ return new IfElseStatement() {
+ Condition = arg1,
+ TrueStatement = new BlockStatement() {
+ new GotoStatement(((ILLabel)byteCode.Operand).Name)
+ }
+ };
+ case ILCode.LoopOrSwitchBreak: return new BreakStatement();
+ case ILCode.LoopContinue: return new ContinueStatement();
+ #endregion
+ #region Conversions
+ case ILCode.Conv_I1:
+ case ILCode.Conv_I2:
+ case ILCode.Conv_I4:
+ case ILCode.Conv_I8:
+ case ILCode.Conv_U1:
+ case ILCode.Conv_U2:
+ case ILCode.Conv_U4:
+ case ILCode.Conv_U8:
+ case ILCode.Conv_I:
+ case ILCode.Conv_U:
+ {
+ // conversion was handled by Convert() function using the info from type analysis
+ CastExpression cast = arg1 as CastExpression;
+ if (cast != null) {
+ cast.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation);
+ }
+ return arg1;
+ }
+ case ILCode.Conv_R4:
+ case ILCode.Conv_R8:
+ case ILCode.Conv_R_Un: // TODO
+ return arg1;
+ case ILCode.Conv_Ovf_I1:
+ case ILCode.Conv_Ovf_I2:
+ case ILCode.Conv_Ovf_I4:
+ case ILCode.Conv_Ovf_I8:
+ case ILCode.Conv_Ovf_U1:
+ case ILCode.Conv_Ovf_U2:
+ case ILCode.Conv_Ovf_U4:
+ case ILCode.Conv_Ovf_U8:
+ case ILCode.Conv_Ovf_I1_Un:
+ case ILCode.Conv_Ovf_I2_Un:
+ case ILCode.Conv_Ovf_I4_Un:
+ case ILCode.Conv_Ovf_I8_Un:
+ case ILCode.Conv_Ovf_U1_Un:
+ case ILCode.Conv_Ovf_U2_Un:
+ case ILCode.Conv_Ovf_U4_Un:
+ case ILCode.Conv_Ovf_U8_Un:
+ case ILCode.Conv_Ovf_I:
+ case ILCode.Conv_Ovf_U:
+ case ILCode.Conv_Ovf_I_Un:
+ case ILCode.Conv_Ovf_U_Un:
+ {
+ // conversion was handled by Convert() function using the info from type analysis
+ CastExpression cast = arg1 as CastExpression;
+ if (cast != null) {
+ cast.AddAnnotation(AddCheckedBlocks.CheckedAnnotation);
+ }
+ return arg1;
+ }
+ case ILCode.Unbox_Any:
+ // unboxing does not require a cast if the argument was an isinst instruction
+ if (arg1 is AsExpression && byteCode.Arguments[0].Code == ILCode.Isinst && TypeAnalysis.IsSameType(operand as TypeReference, byteCode.Arguments[0].Operand as TypeReference))
+ return arg1;
+ else
+ goto case ILCode.Castclass;
+ case ILCode.Castclass:
+ if ((byteCode.Arguments[0].InferredType != null && byteCode.Arguments[0].InferredType.IsGenericParameter) || ((TypeReference)operand).IsGenericParameter)
+ return arg1.CastTo(new PrimitiveType("object")).CastTo(operandAsTypeRef);
+ else
+ return arg1.CastTo(operandAsTypeRef);
+ case ILCode.Isinst:
+ return arg1.CastAs(operandAsTypeRef);
+ case ILCode.Box:
+ return arg1;
+ case ILCode.Unbox:
+ return MakeRef(arg1.CastTo(operandAsTypeRef));
+ #endregion
+ #region Indirect
+ case ILCode.Ldind_Ref:
+ case ILCode.Ldobj:
+ if (arg1 is DirectionExpression)
+ return ((DirectionExpression)arg1).Expression.Detach();
+ else
+ return new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1);
+ case ILCode.Stind_Ref:
+ case ILCode.Stobj:
+ if (arg1 is DirectionExpression)
+ return new AssignmentExpression(((DirectionExpression)arg1).Expression.Detach(), arg2);
+ else
+ return new AssignmentExpression(new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1), arg2);
+ #endregion
+ case ILCode.Arglist:
+ return new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess };
+ case ILCode.Break: return InlineAssembly(byteCode, args);
+ case ILCode.Call:
+ case ILCode.CallGetter:
+ case ILCode.CallSetter:
+ return TransformCall(false, byteCode, args);
+ case ILCode.Callvirt:
+ case ILCode.CallvirtGetter:
+ case ILCode.CallvirtSetter:
+ return TransformCall(true, byteCode, args);
+ case ILCode.Ldftn: {
+ MethodReference cecilMethod = ((MethodReference)operand);
+ var expr = new IdentifierExpression(cecilMethod.Name);
+ expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod));
+ expr.AddAnnotation(cecilMethod);
+ return new IdentifierExpression("ldftn").Invoke(expr)
+ .WithAnnotation(new DelegateConstruction.Annotation(false));
+ }
+ case ILCode.Ldvirtftn: {
+ MethodReference cecilMethod = ((MethodReference)operand);
+ var expr = new IdentifierExpression(cecilMethod.Name);
+ expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod));
+ expr.AddAnnotation(cecilMethod);
+ return new IdentifierExpression("ldvirtftn").Invoke(expr)
+ .WithAnnotation(new DelegateConstruction.Annotation(true));
+ }
+ case ILCode.Calli: return InlineAssembly(byteCode, args);
+ case ILCode.Ckfinite: return InlineAssembly(byteCode, args);
+ case ILCode.Constrained: return InlineAssembly(byteCode, args);
+ case ILCode.Cpblk: return InlineAssembly(byteCode, args);
+ case ILCode.Cpobj: return InlineAssembly(byteCode, args);
+ case ILCode.Dup: return arg1;
+ case ILCode.Endfilter: return InlineAssembly(byteCode, args);
+ case ILCode.Endfinally: return null;
+ case ILCode.Initblk: return InlineAssembly(byteCode, args);
+ case ILCode.Initobj: return InlineAssembly(byteCode, args);
+ case ILCode.DefaultValue:
+ return MakeDefaultValue((TypeReference)operand);
+ case ILCode.Jmp: return InlineAssembly(byteCode, args);
+ case ILCode.Ldc_I4:
+ return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType);
+ case ILCode.Ldc_I8:
+ return AstBuilder.MakePrimitive((long)operand, byteCode.InferredType);
+ case ILCode.Ldc_R4:
+ case ILCode.Ldc_R8:
+ case ILCode.Ldc_Decimal:
+ return new PrimitiveExpression(operand);
+ case ILCode.Ldfld:
+ if (arg1 is DirectionExpression)
+ arg1 = ((DirectionExpression)arg1).Expression.Detach();
+ return arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand);
+ case ILCode.Ldsfld:
+ return AstBuilder.ConvertType(((FieldReference)operand).DeclaringType)
+ .Member(((FieldReference)operand).Name).WithAnnotation(operand);
+ case ILCode.Stfld:
+ if (arg1 is DirectionExpression)
+ arg1 = ((DirectionExpression)arg1).Expression.Detach();
+ return new AssignmentExpression(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand), arg2);
+ case ILCode.Stsfld:
+ return new AssignmentExpression(
+ AstBuilder.ConvertType(((FieldReference)operand).DeclaringType)
+ .Member(((FieldReference)operand).Name).WithAnnotation(operand),
+ arg1);
+ case ILCode.Ldflda:
+ if (arg1 is DirectionExpression)
+ arg1 = ((DirectionExpression)arg1).Expression.Detach();
+ return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand));
+ case ILCode.Ldsflda:
+ return MakeRef(
+ AstBuilder.ConvertType(((FieldReference)operand).DeclaringType)
+ .Member(((FieldReference)operand).Name).WithAnnotation(operand));
+ case ILCode.Ldloc: {
+ ILVariable v = (ILVariable)operand;
+ if (!v.IsParameter)
+ localVariablesToDefine.Add((ILVariable)operand);
+ Expression expr;
+ if (v.IsParameter && v.OriginalParameter.Index < 0)
+ expr = new ThisReferenceExpression();
+ else
+ expr = new IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand);
+ return v.IsParameter && v.Type is ByReferenceType ? MakeRef(expr) : expr;
+ }
+ case ILCode.Ldloca: {
+ ILVariable v = (ILVariable)operand;
+ if (v.IsParameter && v.OriginalParameter.Index < 0)
+ return MakeRef(new ThisReferenceExpression());
+ if (!v.IsParameter)
+ localVariablesToDefine.Add((ILVariable)operand);
+ return MakeRef(new IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand));
+ }
+ case ILCode.Ldnull: return new NullReferenceExpression();
+ case ILCode.Ldstr: return new PrimitiveExpression(operand);
+ case ILCode.Ldtoken:
+ if (operand is TypeReference) {
+ return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle");
+ } else {
+ Expression referencedEntity;
+ string loadName;
+ string handleName;
+ if (operand is FieldReference) {
+ loadName = "fieldof";
+ handleName = "FieldHandle";
+ FieldReference fr = (FieldReference)operand;
+ referencedEntity = AstBuilder.ConvertType(fr.DeclaringType).Member(fr.Name).WithAnnotation(fr);
+ } else if (operand is MethodReference) {
+ loadName = "methodof";
+ handleName = "MethodHandle";
+ MethodReference mr = (MethodReference)operand;
+ var methodParameters = mr.Parameters.Select(p => new TypeReferenceExpression(AstBuilder.ConvertType(p.ParameterType)));
+ referencedEntity = AstBuilder.ConvertType(mr.DeclaringType).Invoke(mr.Name, methodParameters).WithAnnotation(mr);
+ } else {
+ loadName = "ldtoken";
+ handleName = "Handle";
+ referencedEntity = new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand));
+ }
+ return new IdentifierExpression(loadName).Invoke(referencedEntity).WithAnnotation(new LdTokenAnnotation()).Member(handleName);
+ }
+ case ILCode.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name };
+ case ILCode.Localloc:
+ {
+ PointerType ptrType = byteCode.InferredType as PointerType;
+ TypeReference type;
+ if (ptrType != null) {
+ type = ptrType.ElementType;
+ } else {
+ type = typeSystem.Byte;
+ }
+ return new StackAllocExpression {
+ Type = AstBuilder.ConvertType(type),
+ CountExpression = arg1
+ };
+ }
+ case ILCode.Mkrefany:
+ {
+ DirectionExpression dir = arg1 as DirectionExpression;
+ if (dir != null) {
+ return new UndocumentedExpression {
+ UndocumentedExpressionType = UndocumentedExpressionType.MakeRef,
+ Arguments = { dir.Expression.Detach() }
+ };
+ } else {
+ return InlineAssembly(byteCode, args);
+ }
+ }
+ case ILCode.Refanytype:
+ return new UndocumentedExpression {
+ UndocumentedExpressionType = UndocumentedExpressionType.RefType,
+ Arguments = { arg1 }
+ }.Member("TypeHandle");
+ case ILCode.Refanyval:
+ return MakeRef(
+ new UndocumentedExpression {
+ UndocumentedExpressionType = UndocumentedExpressionType.RefValue,
+ Arguments = { arg1, new TypeReferenceExpression(operandAsTypeRef) }
+ });
+ case ILCode.Newobj: {
+ TypeReference declaringType = ((MethodReference)operand).DeclaringType;
+ if (declaringType is ArrayType) {
+ ComposedType ct = AstBuilder.ConvertType((ArrayType)declaringType) as ComposedType;
+ if (ct != null && ct.ArraySpecifiers.Count >= 1) {
+ var ace = new ArrayCreateExpression();
+ ct.ArraySpecifiers.First().Remove();
+ ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
+ ace.Type = ct;
+ ace.Arguments.AddRange(args);
+ return ace;
+ }
+ }
+ if (declaringType.IsAnonymousType()) {
+ MethodDefinition ctor = ((MethodReference)operand).Resolve();
+ if (methodDef != null) {
+ AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression();
+ if (CanInferAnonymousTypePropertyNamesFromArguments(args, ctor.Parameters)) {
+ atce.Initializers.AddRange(args);
+ } else {
+ for (int i = 0; i < args.Count; i++) {
+ atce.Initializers.Add(
+ new NamedExpression {
+ Name = ctor.Parameters[i].Name,
+ Expression = args[i]
+ });
+ }
+ }
+ return atce;
+ }
+ }
+ var oce = new ObjectCreateExpression();
+ oce.Type = AstBuilder.ConvertType(declaringType);
+ oce.Arguments.AddRange(args);
+ return oce.WithAnnotation(operand);
+ }
+ case ILCode.No: return InlineAssembly(byteCode, args);
+ case ILCode.Nop: return null;
+ case ILCode.Pop: return arg1;
+ case ILCode.Readonly: return InlineAssembly(byteCode, args);
+ case ILCode.Ret:
+ if (methodDef.ReturnType.FullName != "System.Void") {
+ return new ReturnStatement { Expression = arg1 };
+ } else {
+ return new ReturnStatement();
+ }
+ case ILCode.Rethrow: return new ThrowStatement();
+ case ILCode.Sizeof: return new SizeOfExpression { Type = operandAsTypeRef };
+ case ILCode.Stloc: {
+ ILVariable locVar = (ILVariable)operand;
+ if (!locVar.IsParameter)
+ localVariablesToDefine.Add(locVar);
+ return new AssignmentExpression(new IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1);
+ }
+ case ILCode.Switch: return InlineAssembly(byteCode, args);
+ case ILCode.Tail: return InlineAssembly(byteCode, args);
+ case ILCode.Throw: return new ThrowStatement { Expression = arg1 };
+ case ILCode.Unaligned: return InlineAssembly(byteCode, args);
+ case ILCode.Volatile: return InlineAssembly(byteCode, args);
+ case ILCode.YieldBreak:
+ return new YieldBreakStatement();
+ case ILCode.YieldReturn:
+ return new YieldReturnStatement { Expression = arg1 };
+ case ILCode.InitObject:
+ case ILCode.InitCollection:
+ {
+ ArrayInitializerExpression initializer = new ArrayInitializerExpression();
+ for (int i = 1; i < args.Count; i++) {
+ Match m = objectInitializerPattern.Match(args[i]);
+ if (m.Success) {
+ MemberReferenceExpression mre = m.Get<MemberReferenceExpression>("left").Single();
+ initializer.Elements.Add(
+ new NamedExpression {
+ Name = mre.MemberName,
+ Expression = m.Get<Expression>("right").Single().Detach()
+ }.CopyAnnotationsFrom(mre));
+ } else {
+ m = collectionInitializerPattern.Match(args[i]);
+ if (m.Success) {
+ if (m.Get("arg").Count() == 1) {
+ initializer.Elements.Add(m.Get<Expression>("arg").Single().Detach());
+ } else {
+ ArrayInitializerExpression argList = new ArrayInitializerExpression();
+ foreach (var expr in m.Get<Expression>("arg")) {
+ argList.Elements.Add(expr.Detach());
+ }
+ initializer.Elements.Add(argList);
+ }
+ } else {
+ initializer.Elements.Add(args[i]);
+ }
+ }
+ }
+ ObjectCreateExpression oce = arg1 as ObjectCreateExpression;
+ DefaultValueExpression dve = arg1 as DefaultValueExpression;
+ if (oce != null) {
+ oce.Initializer = initializer;
+ return oce;
+ } else if (dve != null) {
+ oce = new ObjectCreateExpression(dve.Type.Detach());
+ oce.CopyAnnotationsFrom(dve);
+ oce.Initializer = initializer;
+ return oce;
+ } else {
+ return new AssignmentExpression(arg1, initializer);
+ }
+ }
+ case ILCode.InitializedObject:
+ return new InitializedObjectExpression();
+ case ILCode.Wrap:
+ return arg1.WithAnnotation(PushNegation.LiftedOperatorAnnotation);
+ case ILCode.AddressOf:
+ return MakeRef(arg1);
+ case ILCode.ExpressionTreeParameterDeclarations:
+ args[args.Count - 1].AddAnnotation(new ParameterDeclarationAnnotation(byteCode));
+ return args[args.Count - 1];
+ case ILCode.Await:
+ return new UnaryOperatorExpression(UnaryOperatorType.Await, UnpackDirectionExpression(arg1));
+ case ILCode.NullableOf:
+ case ILCode.ValueOf:
+ return arg1;
+ default:
+ throw new Exception("Unknown OpCode: " + byteCode.Code);
+ }
+ }
+
+ internal static bool CanInferAnonymousTypePropertyNamesFromArguments(IList<Expression> args, IList<ParameterDefinition> parameters)
+ {
+ for (int i = 0; i < args.Count; i++) {
+ string inferredName;
+ if (args[i] is IdentifierExpression)
+ inferredName = ((IdentifierExpression)args[i]).Identifier;
+ else if (args[i] is MemberReferenceExpression)
+ inferredName = ((MemberReferenceExpression)args[i]).MemberName;
+ else
+ inferredName = null;
+
+ if (inferredName != parameters[i].Name) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static readonly AstNode objectInitializerPattern = new AssignmentExpression(
+ new MemberReferenceExpression {
+ Target = new InitializedObjectExpression(),
+ MemberName = Pattern.AnyString
+ }.WithName("left"),
+ new AnyNode("right")
+ );
+
+ static readonly AstNode collectionInitializerPattern = new InvocationExpression {
+ Target = new MemberReferenceExpression {
+ Target = new InitializedObjectExpression(),
+ MemberName = "Add"
+ },
+ Arguments = { new Repeat(new AnyNode("arg")) }
+ };
+
+ sealed class InitializedObjectExpression : IdentifierExpression
+ {
+ public InitializedObjectExpression() : base("__initialized_object__") {}
+
+ protected override bool DoMatch(AstNode other, Match match)
+ {
+ return other is InitializedObjectExpression;
+ }
+ }
+
+ Expression MakeDefaultValue(TypeReference type)
+ {
+ TypeDefinition typeDef = type.Resolve();
+ if (typeDef != null) {
+ if (TypeAnalysis.IsIntegerOrEnum(typeDef))
+ return AstBuilder.MakePrimitive(0, typeDef);
+ else if (!typeDef.IsValueType)
+ return new NullReferenceExpression();
+ switch (typeDef.FullName) {
+ case "System.Nullable`1":
+ return new NullReferenceExpression();
+ case "System.Single":
+ return new PrimitiveExpression(0f);
+ case "System.Double":
+ return new PrimitiveExpression(0.0);
+ case "System.Decimal":
+ return new PrimitiveExpression(0m);
+ }
+ }
+ return new DefaultValueExpression { Type = AstBuilder.ConvertType(type) };
+ }
+
+ AstNode TransformCall(bool isVirtual, ILExpression byteCode, List<Expression> args)
+ {
+ MethodReference cecilMethod = (MethodReference)byteCode.Operand;
+ MethodDefinition cecilMethodDef = cecilMethod.Resolve();
+ Expression target;
+ List<Expression> methodArgs = new List<Expression>(args);
+ if (cecilMethod.HasThis) {
+ target = methodArgs[0];
+ methodArgs.RemoveAt(0);
+
+ // Unpack any DirectionExpression that is used as target for the call
+ // (calling methods on value types implicitly passes the first argument by reference)
+ target = UnpackDirectionExpression(target);
+
+ if (cecilMethodDef != null) {
+ // convert null.ToLower() to ((string)null).ToLower()
+ if (target is NullReferenceExpression)
+ target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType));
+
+ if (cecilMethodDef.DeclaringType.IsInterface) {
+ TypeReference tr = byteCode.Arguments[0].InferredType;
+ if (tr != null) {
+ TypeDefinition td = tr.Resolve();
+ if (td != null && !td.IsInterface) {
+ // Calling an interface method on a non-interface object:
+ // we need to introduce an explicit cast
+ target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType));
+ }
+ }
+ }
+ }
+ } else {
+ target = new TypeReferenceExpression { Type = AstBuilder.ConvertType(cecilMethod.DeclaringType) };
+ }
+ if (target is ThisReferenceExpression && !isVirtual) {
+ // a non-virtual call on "this" might be a "base"-call.
+ if (cecilMethod.DeclaringType.GetElementType() != methodDef.DeclaringType) {
+ // If we're not calling a method in the current class; we must be calling one in the base class.
+ target = new BaseReferenceExpression();
+ }
+ }
+
+ if (cecilMethod.Name == ".ctor" && cecilMethod.DeclaringType.IsValueType) {
+ // On value types, the constructor can be called.
+ // This is equivalent to 'target = new ValueType(args);'.
+ ObjectCreateExpression oce = new ObjectCreateExpression();
+ oce.Type = AstBuilder.ConvertType(cecilMethod.DeclaringType);
+ oce.AddAnnotation(cecilMethod);
+ AdjustArgumentsForMethodCall(cecilMethod, methodArgs);
+ oce.Arguments.AddRange(methodArgs);
+ return new AssignmentExpression(target, oce);
+ }
+
+ if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
+ return target.Indexer(methodArgs);
+ } else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) {
+ return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last());
+ }
+
+ // Test whether the method is an accessor:
+ if (cecilMethodDef != null) {
+ if (cecilMethodDef.IsGetter && methodArgs.Count == 0) {
+ foreach (var prop in cecilMethodDef.DeclaringType.Properties) {
+ if (prop.GetMethod == cecilMethodDef)
+ return target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod);
+ }
+ } else if (cecilMethodDef.IsGetter) { // with parameters
+ PropertyDefinition indexer = GetIndexer(cecilMethodDef);
+ if (indexer != null)
+ return target.Indexer(methodArgs).WithAnnotation(indexer).WithAnnotation(cecilMethod);
+ } else if (cecilMethodDef.IsSetter && methodArgs.Count == 1) {
+ foreach (var prop in cecilMethodDef.DeclaringType.Properties) {
+ if (prop.SetMethod == cecilMethodDef)
+ return new AssignmentExpression(target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod), methodArgs[0]);
+ }
+ } else if (cecilMethodDef.IsSetter && methodArgs.Count > 1) {
+ PropertyDefinition indexer = GetIndexer(cecilMethodDef);
+ if (indexer != null)
+ return new AssignmentExpression(
+ target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)).WithAnnotation(indexer).WithAnnotation(cecilMethod),
+ methodArgs[methodArgs.Count - 1]
+ );
+ } else if (cecilMethodDef.IsAddOn && methodArgs.Count == 1) {
+ foreach (var ev in cecilMethodDef.DeclaringType.Events) {
+ if (ev.AddMethod == cecilMethodDef) {
+ return new AssignmentExpression {
+ Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod),
+ Operator = AssignmentOperatorType.Add,
+ Right = methodArgs[0]
+ };
+ }
+ }
+ } else if (cecilMethodDef.IsRemoveOn && methodArgs.Count == 1) {
+ foreach (var ev in cecilMethodDef.DeclaringType.Events) {
+ if (ev.RemoveMethod == cecilMethodDef) {
+ return new AssignmentExpression {
+ Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod),
+ Operator = AssignmentOperatorType.Subtract,
+ Right = methodArgs[0]
+ };
+ }
+ }
+ } else if (cecilMethodDef.Name == "Invoke" && cecilMethodDef.DeclaringType.BaseType != null && cecilMethodDef.DeclaringType.BaseType.FullName == "System.MulticastDelegate") {
+ AdjustArgumentsForMethodCall(cecilMethod, methodArgs);
+ return target.Invoke(methodArgs).WithAnnotation(cecilMethod);
+ }
+ }
+ // Default invocation
+ AdjustArgumentsForMethodCall(cecilMethodDef ?? cecilMethod, methodArgs);
+ return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod);
+ }
+
+ static Expression UnpackDirectionExpression(Expression target)
+ {
+ if (target is DirectionExpression) {
+ return ((DirectionExpression)target).Expression.Detach();
+ } else {
+ return target;
+ }
+ }
+
+ static void AdjustArgumentsForMethodCall(MethodReference cecilMethod, List<Expression> methodArgs)
+ {
+ // Convert 'ref' into 'out' where necessary
+ for (int i = 0; i < methodArgs.Count && i < cecilMethod.Parameters.Count; i++) {
+ DirectionExpression dir = methodArgs[i] as DirectionExpression;
+ ParameterDefinition p = cecilMethod.Parameters[i];
+ if (dir != null && p.IsOut && !p.IsIn)
+ dir.FieldDirection = FieldDirection.Out;
+ }
+ }
+
+ internal static PropertyDefinition GetIndexer(MethodDefinition cecilMethodDef)
+ {
+ TypeDefinition typeDef = cecilMethodDef.DeclaringType;
+ string indexerName = null;
+ foreach (CustomAttribute ca in typeDef.CustomAttributes) {
+ if (ca.Constructor.FullName == "System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") {
+ indexerName = ca.ConstructorArguments.Single().Value as string;
+ break;
+ }
+ }
+ if (indexerName == null)
+ return null;
+ foreach (PropertyDefinition prop in typeDef.Properties) {
+ if (prop.Name == indexerName) {
+ if (prop.GetMethod == cecilMethodDef || prop.SetMethod == cecilMethodDef)
+ return prop;
+ }
+ }
+ return null;
+ }
+
+ #if DEBUG
+ static readonly ConcurrentDictionary<ILCode, int> unhandledOpcodes = new ConcurrentDictionary<ILCode, int>();
+ #endif
+
+ [Conditional("DEBUG")]
+ public static void ClearUnhandledOpcodes()
+ {
+ #if DEBUG
+ unhandledOpcodes.Clear();
+ #endif
+ }
+
+ [Conditional("DEBUG")]
+ public static void PrintNumberOfUnhandledOpcodes()
+ {
+ #if DEBUG
+ foreach (var pair in unhandledOpcodes) {
+ Debug.WriteLine("AddMethodBodyBuilder unhandled opcode: {1}x {0}", pair.Key, pair.Value);
+ }
+ #endif
+ }
+
+ static Expression InlineAssembly(ILExpression byteCode, List<Expression> args)
+ {
+ #if DEBUG
+ unhandledOpcodes.AddOrUpdate(byteCode.Code, c => 1, (c, n) => n+1);
+ #endif
+ // Output the operand of the unknown IL code as well
+ if (byteCode.Operand != null) {
+ args.Insert(0, new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand)));
+ }
+ return new IdentifierExpression(byteCode.Code.GetName()).Invoke(args);
+ }
+
+ static string FormatByteCodeOperand(object operand)
+ {
+ if (operand == null) {
+ return string.Empty;
+ //} else if (operand is ILExpression) {
+ // return string.Format("IL_{0:X2}", ((ILExpression)operand).Offset);
+ } else if (operand is MethodReference) {
+ return ((MethodReference)operand).Name + "()";
+ } else if (operand is TypeReference) {
+ return ((TypeReference)operand).FullName;
+ } else if (operand is VariableDefinition) {
+ return ((VariableDefinition)operand).Name;
+ } else if (operand is ParameterDefinition) {
+ return ((ParameterDefinition)operand).Name;
+ } else if (operand is FieldReference) {
+ return ((FieldReference)operand).Name;
+ } else if (operand is string) {
+ return "\"" + operand + "\"";
+ } else if (operand is int) {
+ return operand.ToString();
+ } else {
+ return operand.ToString();
+ }
+ }
+
+ static IEnumerable<AstType> ConvertTypeArguments(MethodReference cecilMethod)
+ {
+ GenericInstanceMethod g = cecilMethod as GenericInstanceMethod;
+ if (g == null)
+ return null;
+ if (g.GenericArguments.Any(ta => ta.ContainsAnonymousType()))
+ return null;
+ return g.GenericArguments.Select(t => AstBuilder.ConvertType(t));
+ }
+
+ static DirectionExpression MakeRef(Expression expr)
+ {
+ return new DirectionExpression { Expression = expr, FieldDirection = FieldDirection.Ref };
+ }
+
+ Expression Convert(Expression expr, TypeReference actualType, TypeReference reqType)
+ {
+ if (actualType == null || reqType == null || TypeAnalysis.IsSameType(actualType, reqType)) {
+ return expr;
+ } else if (actualType is ByReferenceType && reqType is PointerType && expr is DirectionExpression) {
+ return Convert(
+ new UnaryOperatorExpression(UnaryOperatorType.AddressOf, ((DirectionExpression)expr).Expression.Detach()),
+ new PointerType(((ByReferenceType)actualType).ElementType),
+ reqType);
+ } else if (actualType is PointerType && reqType is ByReferenceType) {
+ expr = Convert(expr, actualType, new PointerType(((ByReferenceType)reqType).ElementType));
+ return new DirectionExpression {
+ FieldDirection = FieldDirection.Ref,
+ Expression = new UnaryOperatorExpression(UnaryOperatorType.Dereference, expr)
+ };
+ } else if (actualType is PointerType && reqType is PointerType) {
+ if (actualType.FullName != reqType.FullName)
+ return expr.CastTo(AstBuilder.ConvertType(reqType));
+ else
+ return expr;
+ } else {
+ bool actualIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(actualType);
+ bool requiredIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(reqType);
+
+ if (TypeAnalysis.IsBoolean(reqType)) {
+ if (TypeAnalysis.IsBoolean(actualType))
+ return expr;
+ if (actualIsIntegerOrEnum) {
+ return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, AstBuilder.MakePrimitive(0, actualType));
+ } else {
+ return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, new NullReferenceExpression());
+ }
+ }
+ if (TypeAnalysis.IsBoolean(actualType) && requiredIsIntegerOrEnum) {
+ return new ConditionalExpression {
+ Condition = expr,
+ TrueExpression = AstBuilder.MakePrimitive(1, reqType),
+ FalseExpression = AstBuilder.MakePrimitive(0, reqType)
+ };
+ }
+
+ if (expr is PrimitiveExpression && !requiredIsIntegerOrEnum && TypeAnalysis.IsEnum(actualType))
+ {
+ return expr.CastTo(AstBuilder.ConvertType(actualType));
+ }
+
+ bool actualIsPrimitiveType = actualIsIntegerOrEnum
+ || actualType.MetadataType == MetadataType.Single || actualType.MetadataType == MetadataType.Double;
+ bool requiredIsPrimitiveType = requiredIsIntegerOrEnum
+ || reqType.MetadataType == MetadataType.Single || reqType.MetadataType == MetadataType.Double;
+ if (actualIsPrimitiveType && requiredIsPrimitiveType) {
+ return expr.CastTo(AstBuilder.ConvertType(reqType));
+ }
+ return expr;
+ }
+ }
+ }
+}