diff options
Diffstat (limited to 'ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs')
-rw-r--r-- | ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs | 1220 |
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; + } + } + } +} |