/// <summary> /// Inlines 'expr' into 'next', if possible. /// </summary> public static bool InlineIfPossible(ILExpression expr, ILNode next, ILBlock method) { if (expr.Code != ILCode.Stloc) throw new ArgumentException("expr must be stloc"); // ensure the variable is accessed only a single time if (method.GetSelfAndChildrenRecursive<ILExpression>().Count(e => e != expr && e.Operand == expr.Operand) != 1) return false; ILExpression parent; int pos; if (FindLoadInNext(next as ILExpression, (ILVariable)expr.Operand, out parent, out pos) == true) { parent.Arguments[pos] = expr.Arguments[0]; return true; } return false; }
/// <summary> /// Finds the position to inline to. /// </summary> /// <returns>true = found; false = cannot continue search; null = not found</returns> static bool? FindLoadInNext(ILExpression expr, ILVariable v, out ILExpression parent, out int pos) { parent = null; pos = 0; if (expr == null) return false; for (int i = 0; i < expr.Arguments.Count; i++) { ILExpression arg = expr.Arguments[i]; if (arg.Code == ILCode.Ldloc && arg.Operand == v) { parent = expr; pos = i; return true; } bool? r = FindLoadInNext(arg, v, out parent, out pos); if (r != null) return r; } return IsWithoutSideEffects(expr.Code) ? (bool?)null : false; }
static string GetNameFromExpression(ILExpression expr) { switch (expr.Code) { case ILCode.Ldfld: // Use the field name only if it's not a field on this (avoid confusion between local variables and fields) if (!(expr.Arguments[0].Code == ILCode.Ldarg && ((ParameterDefinition)expr.Arguments[0].Operand).Index < 0)) return ((FieldReference)expr.Operand).Name; break; case ILCode.Ldsfld: return ((FieldReference)expr.Operand).Name; case ILCode.Call: case ILCode.Callvirt: MethodReference mr = (MethodReference)expr.Operand; if (mr.Name.StartsWith("get_", StringComparison.Ordinal)) return CleanUpVariableName(mr.Name.Substring(4)); else if (mr.Name.StartsWith("Get", StringComparison.Ordinal) && mr.Name.Length >= 4 && char.IsUpper(mr.Name[3])) return CleanUpVariableName(mr.Name.Substring(3)); break; } return null; }
static bool HandleStaticallyInitializedArray(StoreToVariable arg2, ILBlock block, int i, ILExpression newArrInst, int arrayLength) { FieldDefinition field = ((ILExpression)block.Body[i + 3]).Arguments[1].Operand as FieldDefinition; if (field == null || field.InitialValue == null) return false; switch (TypeAnalysis.GetTypeCode(newArrInst.Operand as TypeReference)) { case TypeCode.Int32: case TypeCode.UInt32: if (field.InitialValue.Length == arrayLength * 4) { ILExpression[] newArr = new ILExpression[arrayLength]; for (int j = 0; j < newArr.Length; j++) { newArr[j] = new ILExpression(ILCode.Ldc_I4, BitConverter.ToInt32(field.InitialValue, j * 4)); } block.Body[i] = new ILExpression(ILCode.Stloc, arg2.LastVariable, new ILExpression(ILCode.InitArray, newArrInst.Operand, newArr)); block.Body.RemoveRange(i + 1, 3); return true; } break; } return false; }
bool TrySimplifyGoto(ILExpression gotoExpr) { Debug.Assert(gotoExpr.Code == ILCode.Br || gotoExpr.Code == ILCode.Leave); Debug.Assert(gotoExpr.Prefixes == null); Debug.Assert(gotoExpr.Operand != null); ILExpression target = Enter(gotoExpr, new HashSet<ILNode>()); if (target == null) return false; if (target == Exit(gotoExpr, new HashSet<ILNode>())) { gotoExpr.Code = ILCode.Nop; gotoExpr.Operand = null; target.ILRanges.AddRange(gotoExpr.ILRanges); gotoExpr.ILRanges.Clear(); return true; } ILWhileLoop loop = null; ILNode current = gotoExpr; while(loop == null && current != null) { current = parent[current]; loop = current as ILWhileLoop; } if (loop != null && target == Exit(loop, new HashSet<ILNode>())) { gotoExpr.Code = ILCode.LoopBreak; gotoExpr.Operand = null; return true; } if (loop != null && target == Enter(loop, new HashSet<ILNode>())) { gotoExpr.Code = ILCode.LoopContinue; gotoExpr.Operand = null; return true; } return false; }
static Ast.INode TransformByteCode_Internal(MethodDefinition methodDef, ILExpression byteCode, List<Ast.Expression> args) { // throw new NotImplementedException(); OpCode opCode = byteCode.OpCode; object operand = byteCode.Operand; Ast.TypeReference operandAsTypeRef = operand is Cecil.TypeReference ? new Ast.TypeReference(((Cecil.TypeReference)operand).FullName) : null; ILExpression operandAsByteCode = operand as ILExpression; Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; Ast.Expression arg3 = args.Count >= 3 ? args[2] : null; Ast.Statement branchCommand = null; if (byteCode.Operand is ILLabel) { branchCommand = new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); } switch(opCode.Code) { #region Arithmetic case Code.Add: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Add_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Add_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); case Code.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); case Code.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); case Code.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); case Code.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); case Code.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); case Code.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); case Code.Sub: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); case Code.Sub_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); case Code.Sub_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); case Code.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2); case Code.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2); case Code.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2); case Code.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); case Code.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); case Code.Neg: return new Ast.UnaryOperatorExpression(arg1, UnaryOperatorType.Minus); case Code.Not: return new Ast.UnaryOperatorExpression(arg1, UnaryOperatorType.BitNot); #endregion #region Arrays case Code.Newarr: operandAsTypeRef.RankSpecifier = new int[] {0}; return new Ast.ArrayCreateExpression(operandAsTypeRef, new List<Expression>(new Expression[] {arg1})); case Code.Ldlen: return new Ast.MemberReferenceExpression(arg1, "Length"); case Code.Ldelem_I: case Code.Ldelem_I1: case Code.Ldelem_I2: case Code.Ldelem_I4: case Code.Ldelem_I8: case Code.Ldelem_U1: case Code.Ldelem_U2: case Code.Ldelem_U4: case Code.Ldelem_R4: case Code.Ldelem_R8: case Code.Ldelem_Ref: return new Ast.IndexerExpression(arg1, new List<Expression>(new Expression[] {arg2})); case Code.Ldelem_Any: throw new NotImplementedException(); case Code.Ldelema: return new Ast.IndexerExpression(arg1, new List<Expression>(new Expression[] {arg2})); case Code.Stelem_I: case Code.Stelem_I1: case Code.Stelem_I2: case Code.Stelem_I4: case Code.Stelem_I8: case Code.Stelem_R4: case Code.Stelem_R8: case Code.Stelem_Ref: return new Ast.AssignmentExpression(new Ast.IndexerExpression(arg1, new List<Expression>(new Expression[] {arg2})), AssignmentOperatorType.Assign, arg3); case Code.Stelem_Any: throw new NotImplementedException(); #endregion #region Branching case Code.Br: return branchCommand; case Code.Brfalse: return new Ast.IfElseStatement(new Ast.UnaryOperatorExpression(arg1, UnaryOperatorType.Not), branchCommand); case Code.Brtrue: return new Ast.IfElseStatement(arg1, branchCommand); case Code.Beq: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2), branchCommand); case Code.Bge: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2), branchCommand); case Code.Bge_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2), branchCommand); case Code.Bgt: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2), branchCommand); case Code.Bgt_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2), branchCommand); case Code.Ble: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2), branchCommand); case Code.Ble_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2), branchCommand); case Code.Blt: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2), branchCommand); case Code.Blt_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2), branchCommand); case Code.Bne_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2), branchCommand); #endregion #region Comparison case Code.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, ConvertIntToBool(arg2)); case Code.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case Code.Cgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case Code.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); case Code.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); #endregion #region Conversions case Code.Conv_I: return new Ast.CastExpression(new Ast.TypeReference(typeof(int).FullName), arg1, CastType.Cast); // TODO case Code.Conv_I1: return new Ast.CastExpression(new Ast.TypeReference(typeof(SByte).FullName), arg1, CastType.Cast); case Code.Conv_I2: return new Ast.CastExpression(new Ast.TypeReference(typeof(Int16).FullName), arg1, CastType.Cast); case Code.Conv_I4: return new Ast.CastExpression(new Ast.TypeReference(typeof(Int32).FullName), arg1, CastType.Cast); case Code.Conv_I8: return new Ast.CastExpression(new Ast.TypeReference(typeof(Int64).FullName), arg1, CastType.Cast); case Code.Conv_U: return new Ast.CastExpression(new Ast.TypeReference(typeof(uint).FullName), arg1, CastType.Cast); // TODO case Code.Conv_U1: return new Ast.CastExpression(new Ast.TypeReference(typeof(Byte).FullName), arg1, CastType.Cast); case Code.Conv_U2: return new Ast.CastExpression(new Ast.TypeReference(typeof(UInt16).FullName), arg1, CastType.Cast); case Code.Conv_U4: return new Ast.CastExpression(new Ast.TypeReference(typeof(UInt32).FullName), arg1, CastType.Cast); case Code.Conv_U8: return new Ast.CastExpression(new Ast.TypeReference(typeof(UInt64).FullName), arg1, CastType.Cast); case Code.Conv_R4: return new Ast.CastExpression(new Ast.TypeReference(typeof(float).FullName), arg1, CastType.Cast); case Code.Conv_R8: return new Ast.CastExpression(new Ast.TypeReference(typeof(double).FullName), arg1, CastType.Cast); case Code.Conv_R_Un: return new Ast.CastExpression(new Ast.TypeReference(typeof(double).FullName), arg1, CastType.Cast); // TODO case Code.Conv_Ovf_I: return new Ast.CastExpression(new Ast.TypeReference(typeof(int).FullName), arg1, CastType.Cast); // TODO case Code.Conv_Ovf_I1: return new Ast.CastExpression(new Ast.TypeReference(typeof(SByte).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_I2: return new Ast.CastExpression(new Ast.TypeReference(typeof(Int16).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_I4: return new Ast.CastExpression(new Ast.TypeReference(typeof(Int32).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_I8: return new Ast.CastExpression(new Ast.TypeReference(typeof(Int64).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_U: return new Ast.CastExpression(new Ast.TypeReference(typeof(uint).FullName), arg1, CastType.Cast); // TODO case Code.Conv_Ovf_U1: return new Ast.CastExpression(new Ast.TypeReference(typeof(Byte).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_U2: return new Ast.CastExpression(new Ast.TypeReference(typeof(UInt16).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_U4: return new Ast.CastExpression(new Ast.TypeReference(typeof(UInt32).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_U8: return new Ast.CastExpression(new Ast.TypeReference(typeof(UInt64).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_I_Un: return new Ast.CastExpression(new Ast.TypeReference(typeof(int).FullName), arg1, CastType.Cast); // TODO case Code.Conv_Ovf_I1_Un: return new Ast.CastExpression(new Ast.TypeReference(typeof(SByte).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_I2_Un: return new Ast.CastExpression(new Ast.TypeReference(typeof(Int16).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_I4_Un: return new Ast.CastExpression(new Ast.TypeReference(typeof(Int32).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_I8_Un: return new Ast.CastExpression(new Ast.TypeReference(typeof(Int64).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_U_Un: return new Ast.CastExpression(new Ast.TypeReference(typeof(uint).FullName), arg1, CastType.Cast); // TODO case Code.Conv_Ovf_U1_Un: return new Ast.CastExpression(new Ast.TypeReference(typeof(Byte).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_U2_Un: return new Ast.CastExpression(new Ast.TypeReference(typeof(UInt16).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_U4_Un: return new Ast.CastExpression(new Ast.TypeReference(typeof(UInt32).FullName), arg1, CastType.Cast); case Code.Conv_Ovf_U8_Un: return new Ast.CastExpression(new Ast.TypeReference(typeof(UInt64).FullName), arg1, CastType.Cast); #endregion #region Indirect case Code.Ldind_I: throw new NotImplementedException(); case Code.Ldind_I1: throw new NotImplementedException(); case Code.Ldind_I2: throw new NotImplementedException(); case Code.Ldind_I4: throw new NotImplementedException(); case Code.Ldind_I8: throw new NotImplementedException(); case Code.Ldind_U1: throw new NotImplementedException(); case Code.Ldind_U2: throw new NotImplementedException(); case Code.Ldind_U4: throw new NotImplementedException(); case Code.Ldind_R4: throw new NotImplementedException(); case Code.Ldind_R8: throw new NotImplementedException(); case Code.Ldind_Ref: throw new NotImplementedException(); case Code.Stind_I: throw new NotImplementedException(); case Code.Stind_I1: throw new NotImplementedException(); case Code.Stind_I2: throw new NotImplementedException(); case Code.Stind_I4: throw new NotImplementedException(); case Code.Stind_I8: throw new NotImplementedException(); case Code.Stind_R4: throw new NotImplementedException(); case Code.Stind_R8: throw new NotImplementedException(); case Code.Stind_Ref: throw new NotImplementedException(); #endregion case Code.Arglist: throw new NotImplementedException(); case Code.Box: throw new NotImplementedException(); case Code.Break: throw new NotImplementedException(); case Code.Call: case Code.Callvirt: // TODO: Diferentiate vitual and non-vitual dispach Cecil.MethodReference cecilMethod = ((MethodReference)operand); Ast.Expression target; List<Ast.Expression> methodArgs = new List<Ast.Expression>(args); if (cecilMethod.HasThis) { target = methodArgs[0]; methodArgs.RemoveAt(0); } else { target = new Ast.IdentifierExpression(cecilMethod.DeclaringType.FullName); } // TODO: Constructors are ignored if (cecilMethod.Name == ".ctor") { return MakeComment("// Constructor"); } // TODO: Hack, detect properties properly if (cecilMethod.Name.StartsWith("get_")) { return new Ast.MemberReferenceExpression(target, cecilMethod.Name.Remove(0, 4)); } else if (cecilMethod.Name.StartsWith("set_")) { return new Ast.AssignmentExpression( new Ast.MemberReferenceExpression(target, cecilMethod.Name.Remove(0, 4)), AssignmentOperatorType.Assign, methodArgs[0] ); } // Multi-dimensional array acces // TODO: do properly if (cecilMethod.Name == "Get") { return new Ast.IndexerExpression(target, methodArgs); } else if (cecilMethod.Name == "Set") { Expression val = methodArgs[methodArgs.Count - 1]; methodArgs.RemoveAt(methodArgs.Count - 1); return new Ast.AssignmentExpression( new Ast.IndexerExpression(target, methodArgs), AssignmentOperatorType.Assign, Convert(val, ((Cecil.ArrayType)target.UserData["Type"]).ElementType) ); } // Default invocation return new Ast.InvocationExpression( new Ast.MemberReferenceExpression(target, cecilMethod.Name), methodArgs ); case Code.Calli: throw new NotImplementedException(); case Code.Castclass: return new Ast.CastExpression(operandAsTypeRef, arg1, CastType.Cast); case Code.Ckfinite: throw new NotImplementedException(); case Code.Constrained: throw new NotImplementedException(); case Code.Cpblk: throw new NotImplementedException(); case Code.Cpobj: throw new NotImplementedException(); case Code.Dup: return arg1; case Code.Endfilter: throw new NotImplementedException(); case Code.Endfinally: return null; case Code.Initblk: throw new NotImplementedException(); case Code.Initobj: throw new NotImplementedException(); case Code.Isinst: return new Ast.TypeOfIsExpression(arg1, new Ast.TypeReference(((Cecil.TypeReference)operand).FullName)); case Code.Jmp: throw new NotImplementedException(); case Code.Ldarg: if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { return new Ast.ThisReferenceExpression(); } else { return new Ast.IdentifierExpression(((ParameterDefinition)operand).Name); } case Code.Ldarga: throw new NotImplementedException(); case Code.Ldc_I4: case Code.Ldc_I8: case Code.Ldc_R4: case Code.Ldc_R8: return new Ast.PrimitiveExpression(operand, null); case Code.Ldfld: case Code.Ldsfld: { if (operand is FieldDefinition) { FieldDefinition field = (FieldDefinition) operand; if (field.IsStatic) { return new Ast.MemberReferenceExpression( new Ast.IdentifierExpression(field.DeclaringType.FullName), field.Name ); } else { return new Ast.MemberReferenceExpression(arg1, field.Name); } } else { // TODO: Static accesses return new Ast.MemberReferenceExpression(arg1, ((FieldReference)operand).Name); } } case Code.Stfld: case Code.Stsfld: { FieldDefinition field = (FieldDefinition) operand; if (field.IsStatic) { return new AssignmentExpression( new Ast.MemberReferenceExpression( new Ast.IdentifierExpression(field.DeclaringType.FullName), field.Name ), AssignmentOperatorType.Assign, arg1 ); } else { return new AssignmentExpression( new Ast.MemberReferenceExpression(arg1, field.Name), AssignmentOperatorType.Assign, arg2 ); } } case Code.Ldflda: case Code.Ldsflda: throw new NotImplementedException(); case Code.Ldftn: throw new NotImplementedException(); case Code.Ldloc: if (operand is ILStackVariable) { return new Ast.IdentifierExpression(((ILStackVariable)operand).Name); } else { return new Ast.IdentifierExpression(((VariableDefinition)operand).Name); } case Code.Ldloca: throw new NotImplementedException(); case Code.Ldnull: return new Ast.PrimitiveExpression(null, null); case Code.Ldobj: throw new NotImplementedException(); case Code.Ldstr: return new Ast.PrimitiveExpression(operand, null); case Code.Ldtoken: if (operand is Cecil.TypeReference) { return new Ast.MemberReferenceExpression( new Ast.TypeOfExpression(operandAsTypeRef), "TypeHandle" ); } else { throw new NotImplementedException(); } case Code.Ldvirtftn: throw new NotImplementedException(); case Code.Leave: return null; case Code.Localloc: throw new NotImplementedException(); case Code.Mkrefany: throw new NotImplementedException(); case Code.Newobj: Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; // TODO: Ensure that the corrent overloaded constructor is called if (declaringType is ArrayType) { return new Ast.ArrayCreateExpression( new Ast.TypeReference(((ArrayType)declaringType).ElementType.FullName, new int[] {}), new List<Expression>(args) ); } return new Ast.ObjectCreateExpression( new Ast.TypeReference(declaringType.FullName), new List<Expression>(args) ); case Code.No: throw new NotImplementedException(); case Code.Nop: return null; case Code.Or: throw new NotImplementedException(); case Code.Pop: return arg1; case Code.Readonly: throw new NotImplementedException(); case Code.Refanytype: throw new NotImplementedException(); case Code.Refanyval: throw new NotImplementedException(); case Code.Ret: { if (methodDef.ReturnType.FullName != Constants.Void) { arg1 = Convert(arg1, methodDef.ReturnType); return new Ast.ReturnStatement(arg1); } else { return new Ast.ReturnStatement(null); } } case Code.Rethrow: return new Ast.ThrowStatement(new IdentifierExpression("exception")); case Code.Sizeof: throw new NotImplementedException(); case Code.Starg: throw new NotImplementedException(); case Code.Stloc: { if (operand is ILStackVariable) { Ast.LocalVariableDeclaration astLocalVar = new Ast.LocalVariableDeclaration(new Ast.VariableDeclaration(((ILStackVariable)operand).Name, arg1)); astLocalVar.TypeReference = new Ast.TypeReference("var"); return astLocalVar; } VariableDefinition locVar = (VariableDefinition)operand; string name = locVar.Name; arg1 = Convert(arg1, locVar.VariableType); if (localVarDefined.ContainsKey(name)) { if (localVarDefined[name]) { return new Ast.AssignmentExpression(new Ast.IdentifierExpression(name), AssignmentOperatorType.Assign, arg1); } else { Ast.LocalVariableDeclaration astLocalVar = new Ast.LocalVariableDeclaration(new Ast.VariableDeclaration(name, arg1)); astLocalVar.TypeReference = new Ast.TypeReference(localVarTypes[name].FullName); localVarDefined[name] = true; return astLocalVar; } } else { return new Ast.AssignmentExpression(new Ast.IdentifierExpression(name), AssignmentOperatorType.Assign, arg1); } } case Code.Stobj: throw new NotImplementedException(); case Code.Switch: throw new NotImplementedException(); case Code.Tail: throw new NotImplementedException(); case Code.Throw: return new Ast.ThrowStatement(arg1); case Code.Unaligned: throw new NotImplementedException(); case Code.Unbox: throw new NotImplementedException(); case Code.Unbox_Any: throw new NotImplementedException(); case Code.Volatile: throw new NotImplementedException(); default: throw new Exception("Unknown OpCode: " + opCode); } }
static object TransformByteCode(MethodDefinition methodDef, ILExpression byteCode, List<Ast.Expression> args) { try { Ast.INode ret = TransformByteCode_Internal(methodDef, byteCode, args); if (ret is Ast.Expression) { ret = new ParenthesizedExpression((Ast.Expression)ret); } // ret.UserData["Type"] = byteCode.Type; return ret; } catch (NotImplementedException) { // Output the operand of the unknown IL code as well if (byteCode.Operand != null) { args.Insert(0, new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand))); } return new Ast.InvocationExpression(new IdentifierExpression(byteCode.OpCode.Name), args); } }
public List<ILNode> ConvertToAst(List<Instruction> body) { List<ILNode> ast = new List<ILNode>(); // Convert stack-based IL code to ILAst tree foreach(Instruction inst in body) { OpCode opCode = inst.OpCode; object operand = inst.Operand; MethodBodyRocks.ExpandMacro(ref opCode, ref operand, methodDef.Body); ILExpression expr = new ILExpression(opCode, operand); // Label for this instruction ILLabel label; if (labels.TryGetValue(inst, out label)) { ast.Add(label); } // Branch using labels if (inst.Operand is Instruction[]) { List<ILLabel> newOperand = new List<ILLabel>(); foreach(Instruction target in (Instruction[])inst.Operand) { newOperand.Add(labels[target]); } expr.Operand = newOperand.ToArray(); } else if (inst.Operand is Instruction) { expr.Operand = labels[(Instruction)inst.Operand]; } // Reference arguments using temporary variables ILStack stack = stackBefore[inst]; int popCount = inst.GetPopCount(); if (popCount == int.MaxValue) popCount = stackBefore[inst].Items.Count; // Pop all for (int i = stack.Items.Count - popCount; i < stack.Items.Count; i++) { Instruction pushedBy = stack.Items[i].PushedBy; if (pushedBy != null) { ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new ILStackVariable() { Name = "expr" + pushedBy.Offset.ToString("X2") }); expr.Arguments.Add(ldExpr); } else { ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new ILStackVariable() { Name = "exception" }); expr.Arguments.Add(ldExpr); } } // If the bytecode pushes anything store the result in temporary variable int pushCount = inst.GetPushCount(); if (pushCount > 0) { ILExpression stExpr = new ILExpression(OpCodes.Stloc, new ILStackVariable() { Name = "expr" + inst.Offset.ToString("X2"), RefCount = pushCount }); stExpr.Arguments.Add(expr); expr = stExpr; } ast.Add(expr); } // Try to in-line stloc / ldloc pairs for(int i = 0; i < ast.Count - 1; i++) { if (i < 0) continue; ILExpression expr = ast[i] as ILExpression; ILExpression nextExpr = ast[i + 1] as ILExpression; if (expr != null && nextExpr != null && expr.OpCode.Code == Code.Stloc && expr.Operand is ILStackVariable) { // If the next expression is stloc, look inside if (nextExpr.OpCode.Code == Code.Stloc && nextExpr.Operand is ILStackVariable) { nextExpr = nextExpr.Arguments[0]; } // Find the use of the 'expr' for(int j = 0; j < nextExpr.Arguments.Count; j++) { ILExpression arg = nextExpr.Arguments[j]; // TODO: Check if duplicating the dup opcode has side-effects if (arg.OpCode.Code == Code.Ldloc && arg.Operand is ILStackVariable) { ILStackVariable stVar = (ILStackVariable)expr.Operand; ILStackVariable ldVar = (ILStackVariable)arg.Operand; if (stVar.Name == ldVar.Name) { stVar.RefCount--; if (stVar.RefCount <= 0) { ast.RemoveAt(i); } nextExpr.Arguments[j] = expr.Arguments[0]; // Inline the stloc body i -= 2; // Try the same index again break; // Found } } else { break; // This argument might have side effects so we can not move the 'expr' after it. } } } } return ast; }
AstNode TransformExpression(ILExpression expr) { AstNode node = TransformByteCode(expr); Expression astExpr = node as Expression; if (astExpr != null) return Convert(astExpr, expr.InferredType, expr.ExpectedType); else return node; }
AstNode TransformByteCode(ILExpression byteCode) { ILCode opCode = byteCode.Code; object operand = byteCode.Operand; AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference); ILExpression operandAsByteCode = operand as ILExpression; // Do branches first because TransformExpressionArguments does not work on arguments that are branches themselfs // TODO: We should probably have virtual instructions for these and not abuse branch codes as expressions switch(opCode) { case ILCode.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); case ILCode.Brfalse: case ILCode.Brtrue: case ILCode.Beq: case ILCode.Bge: case ILCode.Bge_Un: case ILCode.Bgt: case ILCode.Bgt_Un: case ILCode.Ble: case ILCode.Ble_Un: case ILCode.Blt: case ILCode.Blt_Un: case ILCode.Bne_Un: case ILCode.BrLogicAnd: case ILCode.BrLogicOr: return new Ast.IfElseStatement() { Condition = MakeBranchCondition(byteCode), TrueStatement = new BlockStatement() { new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name) } }; } List<Ast.Expression> args = TransformExpressionArguments(byteCode); Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; Ast.Expression arg3 = args.Count >= 3 ? args[2] : null; switch((Code)opCode) { #region Arithmetic case Code.Add: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Add_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Add_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); case Code.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); case Code.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); case Code.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); case Code.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); case Code.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); case Code.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); case Code.Sub: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); case Code.Sub_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); case Code.Sub_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); case Code.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2); case Code.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2); case Code.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2); case Code.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2); case Code.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); case Code.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); case Code.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1); case Code.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1); #endregion #region Arrays case Code.Newarr: case (Code)ILCode.InitArray: { var ace = new Ast.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 (opCode == ILCode.InitArray) { ace.Initializer = new ArrayInitializerExpression(); ace.Initializer.Elements.AddRange(args); } else { ace.Arguments.Add(arg1); } return ace; } case Code.Ldlen: return arg1.Member("Length"); case Code.Ldelem_I: case Code.Ldelem_I1: case Code.Ldelem_I2: case Code.Ldelem_I4: case Code.Ldelem_I8: case Code.Ldelem_U1: case Code.Ldelem_U2: case Code.Ldelem_U4: case Code.Ldelem_R4: case Code.Ldelem_R8: case Code.Ldelem_Ref: return arg1.Indexer(arg2); case Code.Ldelem_Any: return InlineAssembly(byteCode, args); case Code.Ldelema: return MakeRef(arg1.Indexer(arg2)); case Code.Stelem_I: case Code.Stelem_I1: case Code.Stelem_I2: case Code.Stelem_I4: case Code.Stelem_I8: case Code.Stelem_R4: case Code.Stelem_R8: case Code.Stelem_Ref: case Code.Stelem_Any: return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3); #endregion #region Comparison case Code.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); case Code.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case Code.Cgt_Un: // can also mean Inequality, when used with object references { TypeReference arg1Type = byteCode.Arguments[0].InferredType; if (arg1Type != null && !arg1Type.IsValueType) return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); else return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); } case Code.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); case Code.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); #endregion #region Conversions case Code.Conv_I1: case Code.Conv_I2: case Code.Conv_I4: case Code.Conv_I8: case Code.Conv_U1: case Code.Conv_U2: case Code.Conv_U4: case Code.Conv_U8: return arg1; // conversion is handled by Convert() function using the info from type analysis case Code.Conv_I: return arg1.CastTo(typeof(IntPtr)); // TODO case Code.Conv_U: return arg1.CastTo(typeof(UIntPtr)); // TODO case Code.Conv_R4: return arg1.CastTo(typeof(float)); case Code.Conv_R8: return arg1.CastTo(typeof(double)); case Code.Conv_R_Un: return arg1.CastTo(typeof(double)); // TODO case Code.Conv_Ovf_I1: case Code.Conv_Ovf_I2: case Code.Conv_Ovf_I4: case Code.Conv_Ovf_I8: case Code.Conv_Ovf_U1: case Code.Conv_Ovf_U2: case Code.Conv_Ovf_U4: case Code.Conv_Ovf_U8: case Code.Conv_Ovf_I1_Un: case Code.Conv_Ovf_I2_Un: case Code.Conv_Ovf_I4_Un: case Code.Conv_Ovf_I8_Un: case Code.Conv_Ovf_U1_Un: case Code.Conv_Ovf_U2_Un: case Code.Conv_Ovf_U4_Un: case Code.Conv_Ovf_U8_Un: return arg1; // conversion was handled by Convert() function using the info from type analysis case Code.Conv_Ovf_I: return arg1.CastTo(typeof(IntPtr)); // TODO case Code.Conv_Ovf_U: return arg1.CastTo(typeof(UIntPtr)); case Code.Conv_Ovf_I_Un: return arg1.CastTo(typeof(IntPtr)); case Code.Conv_Ovf_U_Un: return arg1.CastTo(typeof(UIntPtr)); case Code.Castclass: case Code.Unbox_Any: return arg1.CastTo(operandAsTypeRef); case Code.Isinst: return arg1.CastAs(operandAsTypeRef); case Code.Box: return arg1; case Code.Unbox: return InlineAssembly(byteCode, args); #endregion #region Indirect case Code.Ldind_I: case Code.Ldind_I1: case Code.Ldind_I2: case Code.Ldind_I4: case Code.Ldind_I8: case Code.Ldind_U1: case Code.Ldind_U2: case Code.Ldind_U4: case Code.Ldind_R4: case Code.Ldind_R8: case Code.Ldind_Ref: case Code.Ldobj: if (args[0] is DirectionExpression) return ((DirectionExpression)args[0]).Expression.Detach(); else return InlineAssembly(byteCode, args); case Code.Stind_I: case Code.Stind_I1: case Code.Stind_I2: case Code.Stind_I4: case Code.Stind_I8: case Code.Stind_R4: case Code.Stind_R8: case Code.Stind_Ref: case Code.Stobj: if (args[0] is DirectionExpression) return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), args[1]); else return InlineAssembly(byteCode, args); #endregion case Code.Arglist: return InlineAssembly(byteCode, args); case Code.Break: return InlineAssembly(byteCode, args); case Code.Call: return TransformCall(false, operand, methodDef, args); case Code.Callvirt: return TransformCall(true, operand, methodDef, args); case Code.Ldftn: { Cecil.MethodReference cecilMethod = ((MethodReference)operand); var expr = new Ast.IdentifierExpression(cecilMethod.Name); expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); expr.AddAnnotation(cecilMethod); return new IdentifierExpression("ldftn").Invoke(expr) .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false)); } case Code.Ldvirtftn: { Cecil.MethodReference cecilMethod = ((MethodReference)operand); var expr = new Ast.IdentifierExpression(cecilMethod.Name); expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); expr.AddAnnotation(cecilMethod); return new IdentifierExpression("ldvirtftn").Invoke(expr) .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); } case Code.Calli: return InlineAssembly(byteCode, args); case Code.Ckfinite: return InlineAssembly(byteCode, args); case Code.Constrained: return InlineAssembly(byteCode, args); case Code.Cpblk: return InlineAssembly(byteCode, args); case Code.Cpobj: return InlineAssembly(byteCode, args); case Code.Dup: return arg1; case Code.Endfilter: return InlineAssembly(byteCode, args); case Code.Endfinally: return null; case Code.Initblk: return InlineAssembly(byteCode, args); case Code.Initobj: if (args[0] is DirectionExpression) return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), new DefaultValueExpression { Type = operandAsTypeRef }); else return InlineAssembly(byteCode, args); case Code.Jmp: return InlineAssembly(byteCode, args); case Code.Ldarg: if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { if (context.CurrentMethod.DeclaringType.IsValueType) return MakeRef(new Ast.ThisReferenceExpression()); else return new Ast.ThisReferenceExpression(); } else { var expr = new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand); if (((ParameterDefinition)operand).ParameterType is ByReferenceType) return MakeRef(expr); else return expr; } case Code.Ldarga: if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { return MakeRef(new Ast.ThisReferenceExpression()); } else { return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand)); } case Code.Ldc_I4: return MakePrimitive((int)operand, byteCode.InferredType); case Code.Ldc_I8: case Code.Ldc_R4: case Code.Ldc_R8: return new Ast.PrimitiveExpression(operand); case Code.Ldfld: if (arg1 is DirectionExpression) arg1 = ((DirectionExpression)arg1).Expression.Detach(); return arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand); case Code.Ldsfld: return AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) .Member(((FieldReference)operand).Name).WithAnnotation(operand); case Code.Stfld: if (arg1 is DirectionExpression) arg1 = ((DirectionExpression)arg1).Expression.Detach(); return new AssignmentExpression(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand), arg2); case Code.Stsfld: return new AssignmentExpression( AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) .Member(((FieldReference)operand).Name).WithAnnotation(operand), arg1); case Code.Ldflda: return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand)); case Code.Ldsflda: return MakeRef( AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) .Member(((FieldReference)operand).Name).WithAnnotation(operand)); case Code.Ldloc: localVariablesToDefine.Add((ILVariable)operand); return new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand); case Code.Ldloca: localVariablesToDefine.Add((ILVariable)operand); return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand)); case Code.Ldnull: return new Ast.NullReferenceExpression(); case Code.Ldstr: return new Ast.PrimitiveExpression(operand); case Code.Ldtoken: if (operand is Cecil.TypeReference) { return new Ast.TypeOfExpression { Type = operandAsTypeRef }.Member("TypeHandle"); } else { return InlineAssembly(byteCode, args); } case Code.Leave: return null; case Code.Localloc: return InlineAssembly(byteCode, args); case Code.Mkrefany: return InlineAssembly(byteCode, args); case Code.Newobj: { Cecil.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 Ast.ArrayCreateExpression(); ct.ArraySpecifiers.First().Remove(); ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); ace.Type = ct; ace.Arguments.AddRange(args); return ace; } } var oce = new Ast.ObjectCreateExpression(); oce.Type = AstBuilder.ConvertType(declaringType); oce.Arguments.AddRange(args); return oce.WithAnnotation(operand); } case Code.No: return InlineAssembly(byteCode, args); case Code.Nop: return null; case Code.Pop: return arg1; case Code.Readonly: return InlineAssembly(byteCode, args); case Code.Refanytype: return InlineAssembly(byteCode, args); case Code.Refanyval: return InlineAssembly(byteCode, args); case Code.Ret: { if (methodDef.ReturnType.FullName != "System.Void") { return new Ast.ReturnStatement { Expression = arg1 }; } else { return new Ast.ReturnStatement(); } } case Code.Rethrow: return new Ast.ThrowStatement(); case Code.Sizeof: return new Ast.SizeOfExpression { Type = operandAsTypeRef }; case Code.Starg: return new Ast.AssignmentExpression(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand), arg1); case Code.Stloc: { ILVariable locVar = (ILVariable)operand; localVariablesToDefine.Add(locVar); return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1); } case Code.Switch: return InlineAssembly(byteCode, args); case Code.Tail: return InlineAssembly(byteCode, args); case Code.Throw: return new Ast.ThrowStatement { Expression = arg1 }; case Code.Unaligned: return InlineAssembly(byteCode, args); case Code.Volatile: return InlineAssembly(byteCode, args); default: throw new Exception("Unknown OpCode: " + opCode); } }
Ast.Expression MakeBranchCondition(ILExpression expr) { switch(expr.Code) { case ILCode.LogicNot: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(expr.Arguments[0])); case ILCode.BrLogicAnd: return new Ast.BinaryOperatorExpression( MakeBranchCondition(expr.Arguments[0]), BinaryOperatorType.ConditionalAnd, MakeBranchCondition(expr.Arguments[1]) ); case ILCode.BrLogicOr: return new Ast.BinaryOperatorExpression( MakeBranchCondition(expr.Arguments[0]), BinaryOperatorType.ConditionalOr, MakeBranchCondition(expr.Arguments[1]) ); } List<Ast.Expression> args = TransformExpressionArguments(expr); Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; switch((Code)expr.Code) { case Code.Brfalse: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); case Code.Brtrue: return arg1; case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); case Code.Bge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); case Code.Bge_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); case Code.Bgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case Code.Bgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case Code.Ble: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); case Code.Ble_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); case Code.Blt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); case Code.Blt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); case Code.Bne_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); default: throw new Exception("Bad opcode"); } }
static Expression InlineAssembly(ILExpression byteCode, List<Ast.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); }
Ast.Statement TransformExpressionToStatement(ILExpression expr) { object codeExpr = TransformExpression(expr); if (codeExpr == null) { return null; } else if (codeExpr is Ast.Expression) { return new Ast.ExpressionStatement((Ast.Expression)codeExpr); } else if (codeExpr is Ast.Statement) { return (Ast.Statement)codeExpr; } else { throw new Exception(); } }
/// <summary> /// Infers the C# type of <paramref name="expr"/>. /// </summary> /// <param name="expr">The expression</param> /// <param name="expectedType">The expected type of the expression</param> /// <param name="forceInferChildren">Whether direct children should be inferred even if its not necessary. (does not apply to nested children!)</param> /// <returns>The inferred type</returns> TypeReference InferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) { expr.ExpectedType = expectedType; if (forceInferChildren || expr.InferredType == null) expr.InferredType = DoInferTypeForExpression(expr, expectedType, forceInferChildren); return expr.InferredType; }
object TransformExpression(ILExpression expr) { List<Ast.Expression> args = TransformExpressionArguments(expr); return TransformByteCode(methodDef, expr, args); }
TypeReference InferArgumentsInBinaryOperator(ILExpression expr, bool? isSigned) { ILExpression left = expr.Arguments[0]; ILExpression right = expr.Arguments[1]; TypeReference leftPreferred = DoInferTypeForExpression(left, null); TypeReference rightPreferred = DoInferTypeForExpression(right, null); if (leftPreferred == rightPreferred) { return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred; } else if (rightPreferred == DoInferTypeForExpression(left, rightPreferred)) { return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = rightPreferred; } else if (leftPreferred == DoInferTypeForExpression(right, leftPreferred)) { return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred; } else { left.ExpectedType = right.ExpectedType = TypeWithMoreInformation(leftPreferred, rightPreferred); left.InferredType = DoInferTypeForExpression(left, left.ExpectedType); right.InferredType = DoInferTypeForExpression(left, right.ExpectedType); return left.ExpectedType; } }
TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) { switch (expr.Code) { case ILCode.LogicNot: if (forceInferChildren) { InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean); } return typeSystem.Boolean; case ILCode.LogicAnd: case ILCode.LogicOr: if (forceInferChildren) { InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean); InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean); } return typeSystem.Boolean; } switch ((Code)expr.Code) { #region Variable load/store case Code.Stloc: { ILVariable v = (ILVariable)expr.Operand; if (forceInferChildren || v.Type == null) { TypeReference t = InferTypeForExpression(expr.Arguments.Single(), ((ILVariable)expr.Operand).Type); if (v.Type == null) v.Type = t; } return v.Type; } case Code.Ldloc: { ILVariable v = (ILVariable)expr.Operand; if (v.Type == null) { v.Type = expectedType; // Mark the variable as inferred. This is necessary because expectedType might be null // (e.g. the only use of an arg_*-Variable is a pop statement), // so we can't tell from v.Type whether it was already inferred. inferredVariables.Add(v); } return v.Type; } case Code.Starg: if (forceInferChildren) InferTypeForExpression(expr.Arguments.Single(), ((ParameterReference)expr.Operand).ParameterType); return null; case Code.Ldarg: return ((ParameterReference)expr.Operand).ParameterType; case Code.Ldloca: return new ByReferenceType(((ILVariable)expr.Operand).Type); case Code.Ldarga: return new ByReferenceType(((ParameterReference)expr.Operand).ParameterType); #endregion #region Call / NewObj case Code.Call: case Code.Callvirt: { MethodReference method = (MethodReference)expr.Operand; if (forceInferChildren) { for (int i = 0; i < expr.Arguments.Count; i++) { if (i == 0 && method.HasThis) { Instruction constraint = expr.GetPrefix(Code.Constrained); if (constraint != null) InferTypeForExpression(expr.Arguments[i], new ByReferenceType((TypeReference)constraint.Operand)); else InferTypeForExpression(expr.Arguments[i], method.DeclaringType); } else { InferTypeForExpression(expr.Arguments[i], SubstituteTypeArgs(method.Parameters[method.HasThis ? i - 1: i].ParameterType, method)); } } } return SubstituteTypeArgs(method.ReturnType, method); } case Code.Newobj: { MethodReference ctor = (MethodReference)expr.Operand; if (forceInferChildren) { for (int i = 0; i < ctor.Parameters.Count; i++) { InferTypeForExpression(expr.Arguments[i], SubstituteTypeArgs(ctor.Parameters[i].ParameterType, ctor)); } } return ctor.DeclaringType; } #endregion #region Load/Store Fields case Code.Ldfld: if (forceInferChildren) InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).DeclaringType); return GetFieldType((FieldReference)expr.Operand); case Code.Ldsfld: return GetFieldType((FieldReference)expr.Operand); case Code.Ldflda: case Code.Ldsflda: return new ByReferenceType(GetFieldType((FieldReference)expr.Operand)); case Code.Stfld: if (forceInferChildren) { InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).DeclaringType); InferTypeForExpression(expr.Arguments[1], GetFieldType((FieldReference)expr.Operand)); } return null; case Code.Stsfld: if (forceInferChildren) InferTypeForExpression(expr.Arguments[0], GetFieldType((FieldReference)expr.Operand)); return null; #endregion #region Reference/Pointer instructions case Code.Ldind_I: case Code.Ldind_I1: case Code.Ldind_I2: case Code.Ldind_I4: case Code.Ldind_I8: case Code.Ldind_U1: case Code.Ldind_U2: case Code.Ldind_U4: case Code.Ldind_R4: case Code.Ldind_R8: case Code.Ldind_Ref: return UnpackPointer(InferTypeForExpression(expr.Arguments[0], null)); case Code.Stind_I1: case Code.Stind_I2: case Code.Stind_I4: case Code.Stind_I8: case Code.Stind_R4: case Code.Stind_R8: case Code.Stind_I: case Code.Stind_Ref: if (forceInferChildren) { TypeReference elementType = UnpackPointer(InferTypeForExpression(expr.Arguments[0], null)); InferTypeForExpression(expr.Arguments[1], elementType); } return null; case Code.Ldobj: return (TypeReference)expr.Operand; case Code.Stobj: if (forceInferChildren) { InferTypeForExpression(expr.Arguments[1], (TypeReference)expr.Operand); } return null; case Code.Initobj: return null; case Code.Localloc: return typeSystem.IntPtr; #endregion #region Arithmetic instructions case Code.Not: // bitwise complement case Code.Neg: return InferTypeForExpression(expr.Arguments.Single(), expectedType); case Code.Add: case Code.Sub: case Code.Mul: case Code.Or: case Code.And: case Code.Xor: return InferArgumentsInBinaryOperator(expr, null); case Code.Add_Ovf: case Code.Sub_Ovf: case Code.Mul_Ovf: case Code.Div: case Code.Rem: return InferArgumentsInBinaryOperator(expr, true); case Code.Add_Ovf_Un: case Code.Sub_Ovf_Un: case Code.Mul_Ovf_Un: case Code.Div_Un: case Code.Rem_Un: return InferArgumentsInBinaryOperator(expr, false); case Code.Shl: case Code.Shr: if (forceInferChildren) InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); return InferTypeForExpression(expr.Arguments[0], typeSystem.Int32); case Code.Shr_Un: if (forceInferChildren) InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); return InferTypeForExpression(expr.Arguments[0], typeSystem.UInt32); #endregion #region Constant loading instructions case Code.Ldnull: return typeSystem.Object; case Code.Ldstr: return typeSystem.String; case Code.Ldftn: case Code.Ldvirtftn: return typeSystem.IntPtr; case Code.Ldc_I4: return (IsIntegerOrEnum(expectedType) || expectedType == typeSystem.Boolean) ? expectedType : typeSystem.Int32; case Code.Ldc_I8: return (IsIntegerOrEnum(expectedType)) ? expectedType : typeSystem.Int64; case Code.Ldc_R4: return typeSystem.Single; case Code.Ldc_R8: return typeSystem.Double; case Code.Ldtoken: if (expr.Operand is TypeReference) return new TypeReference("System", "RuntimeTypeHandle", module, module, true); else if (expr.Operand is FieldReference) return new TypeReference("System", "RuntimeFieldHandle", module, module, true); else return new TypeReference("System", "RuntimeMethodHandle", module, module, true); case Code.Arglist: return new TypeReference("System", "RuntimeArgumentHandle", module, module, true); #endregion #region Array instructions case Code.Newarr: if (forceInferChildren) InferTypeForExpression(expr.Arguments.Single(), typeSystem.Int32); return new ArrayType((TypeReference)expr.Operand); case Code.Ldlen: return typeSystem.Int32; case Code.Ldelem_U1: case Code.Ldelem_U2: case Code.Ldelem_U4: case Code.Ldelem_I1: case Code.Ldelem_I2: case Code.Ldelem_I4: case Code.Ldelem_I8: case Code.Ldelem_I: case Code.Ldelem_Ref: { ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; if (forceInferChildren) { InferTypeForExpression(expr.Arguments[0], new ArrayType(typeSystem.Byte)); InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); } return arrayType != null ? arrayType.ElementType : null; } case Code.Ldelem_Any: if (forceInferChildren) { InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); } return (TypeReference)expr.Operand; case Code.Ldelema: { ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; if (forceInferChildren) InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); return arrayType != null ? new ByReferenceType(arrayType.ElementType) : null; } case Code.Stelem_I: case Code.Stelem_I1: case Code.Stelem_I2: case Code.Stelem_I4: case Code.Stelem_I8: case Code.Stelem_R4: case Code.Stelem_R8: case Code.Stelem_Ref: case Code.Stelem_Any: if (forceInferChildren) { ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); if (arrayType != null) { InferTypeForExpression(expr.Arguments[2], arrayType.ElementType); } } return null; #endregion #region Conversion instructions case Code.Conv_I1: case Code.Conv_Ovf_I1: return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == true) ? expectedType : typeSystem.SByte; case Code.Conv_I2: case Code.Conv_Ovf_I2: return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int16; case Code.Conv_I4: case Code.Conv_Ovf_I4: return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int32; case Code.Conv_I8: case Code.Conv_Ovf_I8: return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int64; case Code.Conv_U1: case Code.Conv_Ovf_U1: return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == false) ? expectedType : typeSystem.Byte; case Code.Conv_U2: case Code.Conv_Ovf_U2: return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt16; case Code.Conv_U4: case Code.Conv_Ovf_U4: return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt32; case Code.Conv_U8: case Code.Conv_Ovf_U8: return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt64; case Code.Conv_I: case Code.Conv_Ovf_I: return (GetInformationAmount(expectedType) == nativeInt && IsSigned(expectedType) == true) ? expectedType : typeSystem.IntPtr; case Code.Conv_U: case Code.Conv_Ovf_U: return (GetInformationAmount(expectedType) == nativeInt && IsSigned(expectedType) == false) ? expectedType : typeSystem.UIntPtr; case Code.Conv_R4: return typeSystem.Single; case Code.Conv_R8: return typeSystem.Double; case Code.Conv_R_Un: return (expectedType == typeSystem.Single) ? typeSystem.Single : typeSystem.Double; case Code.Castclass: case Code.Isinst: case Code.Unbox_Any: return (TypeReference)expr.Operand; case Code.Box: if (forceInferChildren) InferTypeForExpression(expr.Arguments.Single(), (TypeReference)expr.Operand); return (TypeReference)expr.Operand; #endregion #region Comparison instructions case Code.Ceq: if (forceInferChildren) InferArgumentsInBinaryOperator(expr, null); return typeSystem.Boolean; case Code.Clt: case Code.Cgt: if (forceInferChildren) InferArgumentsInBinaryOperator(expr, true); return typeSystem.Boolean; case Code.Clt_Un: case Code.Cgt_Un: if (forceInferChildren) InferArgumentsInBinaryOperator(expr, false); return typeSystem.Boolean; #endregion #region Branch instructions case Code.Beq: case Code.Bne_Un: if (forceInferChildren) InferArgumentsInBinaryOperator(expr, null); return null; case Code.Brtrue: case Code.Brfalse: if (forceInferChildren) InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean); return null; case Code.Blt: case Code.Ble: case Code.Bgt: case Code.Bge: if (forceInferChildren) InferArgumentsInBinaryOperator(expr, true); return null; case Code.Blt_Un: case Code.Ble_Un: case Code.Bgt_Un: case Code.Bge_Un: if (forceInferChildren) InferArgumentsInBinaryOperator(expr, false); return null; case Code.Br: case Code.Leave: case Code.Endfinally: case Code.Switch: case Code.Throw: case Code.Rethrow: return null; case Code.Ret: if (forceInferChildren && expr.Arguments.Count == 1) InferTypeForExpression(expr.Arguments[0], context.CurrentMethod.ReturnType); return null; #endregion case Code.Pop: return null; case Code.Dup: return InferTypeForExpression(expr.Arguments.Single(), expectedType); default: Debug.WriteLine("Type Inference: Can't handle " + expr.Code.GetName()); return null; } }
Ast.Expression MakeBranchCondition(ILExpression expr) { List<Ast.Expression> args = TransformExpressionArguments(expr); Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; switch(expr.OpCode.Code) { case Code.Brfalse: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); case Code.Brtrue: return arg1; case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); case Code.Bge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); case Code.Bge_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); case Code.Bgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case Code.Bgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case Code.Ble: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); case Code.Ble_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); case Code.Blt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); case Code.Blt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); case Code.Bne_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); default: throw new Exception("Bad opcode"); } /* } else if (branch is ShortCircuitBranch) { ShortCircuitBranch scBranch = (ShortCircuitBranch)branch; switch(scBranch.Operator) { case ShortCircuitOperator.LeftAndRight: return new BinaryOperatorExpression( MakeBranchCondition(scBranch.Left), BinaryOperatorType.ConditionalAnd, MakeBranchCondition(scBranch.Right) ); case ShortCircuitOperator.LeftOrRight: return new BinaryOperatorExpression( MakeBranchCondition(scBranch.Left), BinaryOperatorType.ConditionalOr, MakeBranchCondition(scBranch.Right) ); case ShortCircuitOperator.NotLeftAndRight: return new BinaryOperatorExpression( new UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(scBranch.Left)), BinaryOperatorType.ConditionalAnd, MakeBranchCondition(scBranch.Right) ); case ShortCircuitOperator.NotLeftOrRight: return new BinaryOperatorExpression( new UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(scBranch.Left)), BinaryOperatorType.ConditionalOr, MakeBranchCondition(scBranch.Right) ); default: throw new Exception("Bad operator"); } } else { throw new Exception("Bad type"); } */ }
AstNode TransformByteCode(MethodDefinition methodDef, ILExpression byteCode, List<Ast.Expression> args) { try { AstNode ret = TransformByteCode_Internal(methodDef, byteCode, args); // ret.UserData["Type"] = byteCode.Type; return ret; } catch (NotImplementedException) { // 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.OpCode.Name).Invoke(args); } }
AstNode TransformByteCode_Internal(MethodDefinition methodDef, ILExpression byteCode, List<Ast.Expression> args) { // throw new NotImplementedException(); OpCode opCode = byteCode.OpCode; object operand = byteCode.Operand; AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference); ILExpression operandAsByteCode = operand as ILExpression; Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; Ast.Expression arg3 = args.Count >= 3 ? args[2] : null; BlockStatement branchCommand = null; if (byteCode.Operand is ILLabel) { branchCommand = new BlockStatement(); branchCommand.AddStatement(new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name)); } switch(opCode.Code) { #region Arithmetic case Code.Add: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Add_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Add_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); case Code.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); case Code.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); case Code.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); case Code.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); case Code.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); case Code.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); case Code.Sub: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); case Code.Sub_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); case Code.Sub_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); case Code.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2); case Code.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2); case Code.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2); case Code.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2); case Code.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); case Code.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); case Code.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1); case Code.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1); #endregion #region Arrays case Code.Newarr: operandAsTypeRef = operandAsTypeRef.MakeArrayType(0); return new Ast.ArrayCreateExpression { Type = operandAsTypeRef, Arguments = new Expression[] {arg1} }; case Code.Ldlen: return arg1.Member("Length"); case Code.Ldelem_I: case Code.Ldelem_I1: case Code.Ldelem_I2: case Code.Ldelem_I4: case Code.Ldelem_I8: case Code.Ldelem_U1: case Code.Ldelem_U2: case Code.Ldelem_U4: case Code.Ldelem_R4: case Code.Ldelem_R8: case Code.Ldelem_Ref: return arg1.Indexer(arg2); case Code.Ldelem_Any: throw new NotImplementedException(); case Code.Ldelema: return MakeRef(arg1.Indexer(arg2)); case Code.Stelem_I: case Code.Stelem_I1: case Code.Stelem_I2: case Code.Stelem_I4: case Code.Stelem_I8: case Code.Stelem_R4: case Code.Stelem_R8: case Code.Stelem_Ref: return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3); case Code.Stelem_Any: throw new NotImplementedException(); #endregion #region Branching case Code.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); case Code.Brfalse: return new Ast.IfElseStatement(new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1), branchCommand); case Code.Brtrue: return new Ast.IfElseStatement(arg1, branchCommand); case Code.Beq: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2), branchCommand); case Code.Bge: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2), branchCommand); case Code.Bge_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2), branchCommand); case Code.Bgt: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2), branchCommand); case Code.Bgt_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2), branchCommand); case Code.Ble: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2), branchCommand); case Code.Ble_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2), branchCommand); case Code.Blt: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2), branchCommand); case Code.Blt_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2), branchCommand); case Code.Bne_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2), branchCommand); #endregion #region Comparison case Code.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, ConvertIntToBool(arg2)); case Code.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case Code.Cgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case Code.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); case Code.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); #endregion #region Conversions case Code.Conv_I: return arg1.CastTo(typeof(int)); // TODO case Code.Conv_I1: return arg1.CastTo(typeof(SByte)); case Code.Conv_I2: return arg1.CastTo(typeof(Int16)); case Code.Conv_I4: return arg1.CastTo(typeof(Int32)); case Code.Conv_I8: return arg1.CastTo(typeof(Int64)); case Code.Conv_U: return arg1.CastTo(typeof(uint)); // TODO case Code.Conv_U1: return arg1.CastTo(typeof(Byte)); case Code.Conv_U2: return arg1.CastTo(typeof(UInt16)); case Code.Conv_U4: return arg1.CastTo(typeof(UInt32)); case Code.Conv_U8: return arg1.CastTo(typeof(UInt64)); case Code.Conv_R4: return arg1.CastTo(typeof(float)); case Code.Conv_R8: return arg1.CastTo(typeof(double)); case Code.Conv_R_Un: return arg1.CastTo(typeof(double)); // TODO case Code.Conv_Ovf_I: return arg1.CastTo(typeof(int)); case Code.Conv_Ovf_I1: return arg1.CastTo(typeof(SByte)); case Code.Conv_Ovf_I2: return arg1.CastTo(typeof(Int16)); case Code.Conv_Ovf_I4: return arg1.CastTo(typeof(Int32)); case Code.Conv_Ovf_I8: return arg1.CastTo(typeof(Int64)); case Code.Conv_Ovf_U: return arg1.CastTo(typeof(uint)); case Code.Conv_Ovf_U1: return arg1.CastTo(typeof(Byte)); case Code.Conv_Ovf_U2: return arg1.CastTo(typeof(UInt16)); case Code.Conv_Ovf_U4: return arg1.CastTo(typeof(UInt32)); case Code.Conv_Ovf_U8: return arg1.CastTo(typeof(UInt64)); case Code.Conv_Ovf_I_Un: return arg1.CastTo(typeof(int)); case Code.Conv_Ovf_I1_Un: return arg1.CastTo(typeof(SByte)); case Code.Conv_Ovf_I2_Un: return arg1.CastTo(typeof(Int16)); case Code.Conv_Ovf_I4_Un: return arg1.CastTo(typeof(Int32)); case Code.Conv_Ovf_I8_Un: return arg1.CastTo(typeof(Int64)); case Code.Conv_Ovf_U_Un: return arg1.CastTo(typeof(uint)); case Code.Conv_Ovf_U1_Un: return arg1.CastTo(typeof(Byte)); case Code.Conv_Ovf_U2_Un: return arg1.CastTo(typeof(UInt16)); case Code.Conv_Ovf_U4_Un: return arg1.CastTo(typeof(UInt32)); case Code.Conv_Ovf_U8_Un: return arg1.CastTo(typeof(UInt64)); #endregion #region Indirect case Code.Ldind_I: throw new NotImplementedException(); case Code.Ldind_I1: throw new NotImplementedException(); case Code.Ldind_I2: throw new NotImplementedException(); case Code.Ldind_I4: throw new NotImplementedException(); case Code.Ldind_I8: throw new NotImplementedException(); case Code.Ldind_U1: throw new NotImplementedException(); case Code.Ldind_U2: throw new NotImplementedException(); case Code.Ldind_U4: throw new NotImplementedException(); case Code.Ldind_R4: throw new NotImplementedException(); case Code.Ldind_R8: throw new NotImplementedException(); case Code.Ldind_Ref: throw new NotImplementedException(); case Code.Stind_I: throw new NotImplementedException(); case Code.Stind_I1: throw new NotImplementedException(); case Code.Stind_I2: throw new NotImplementedException(); case Code.Stind_I4: throw new NotImplementedException(); case Code.Stind_I8: throw new NotImplementedException(); case Code.Stind_R4: throw new NotImplementedException(); case Code.Stind_R8: throw new NotImplementedException(); case Code.Stind_Ref: throw new NotImplementedException(); #endregion case Code.Arglist: throw new NotImplementedException(); case Code.Box: throw new NotImplementedException(); case Code.Break: throw new NotImplementedException(); case Code.Call: return TransformCall(false, operand, methodDef, args); case Code.Callvirt: return TransformCall(true, operand, methodDef, args); case Code.Ldftn: { Cecil.MethodReference cecilMethod = ((MethodReference)operand); var expr = new Ast.IdentifierExpression(cecilMethod.Name); expr.TypeArguments = ConvertTypeArguments(cecilMethod); expr.AddAnnotation(cecilMethod); return new IdentifierExpression("ldftn").Invoke(expr) .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false, methodDef.DeclaringType)); } case Code.Ldvirtftn: { Cecil.MethodReference cecilMethod = ((MethodReference)operand); var expr = new Ast.IdentifierExpression(cecilMethod.Name); expr.TypeArguments = ConvertTypeArguments(cecilMethod); expr.AddAnnotation(cecilMethod); return new IdentifierExpression("ldvirtftn").Invoke(expr) .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true, methodDef.DeclaringType)); } case Code.Calli: throw new NotImplementedException(); case Code.Castclass: return arg1.CastTo(operandAsTypeRef); case Code.Ckfinite: throw new NotImplementedException(); case Code.Constrained: throw new NotImplementedException(); case Code.Cpblk: throw new NotImplementedException(); case Code.Cpobj: throw new NotImplementedException(); case Code.Dup: return arg1; case Code.Endfilter: throw new NotImplementedException(); case Code.Endfinally: return null; case Code.Initblk: throw new NotImplementedException(); case Code.Initobj: throw new NotImplementedException(); case Code.Isinst: return arg1.IsType(AstBuilder.ConvertType((Cecil.TypeReference)operand)); case Code.Jmp: throw new NotImplementedException(); case Code.Ldarg: if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { return new Ast.ThisReferenceExpression(); } else { return new Ast.IdentifierExpression(((ParameterDefinition)operand).Name); } case Code.Ldarga: if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { return MakeRef(new Ast.ThisReferenceExpression()); } else { return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name)); } case Code.Ldc_I4: case Code.Ldc_I8: case Code.Ldc_R4: case Code.Ldc_R8: return new Ast.PrimitiveExpression(operand); case Code.Ldfld: return arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand); case Code.Ldsfld: return AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) .Member(((FieldReference)operand).Name).WithAnnotation(operand); case Code.Stfld: return new AssignmentExpression(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand), arg2); case Code.Stsfld: return new AssignmentExpression( AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) .Member(((FieldReference)operand).Name).WithAnnotation(operand), arg1); case Code.Ldflda: return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand)); case Code.Ldsflda: return MakeRef( AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) .Member(((FieldReference)operand).Name).WithAnnotation(operand)); case Code.Ldloc: if (operand is ILVariable) { return new Ast.IdentifierExpression(((ILVariable)operand).Name); } else { return new Ast.IdentifierExpression(((VariableDefinition)operand).Name); } case Code.Ldloca: if (operand is ILVariable) { return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name)); } else { return MakeRef(new Ast.IdentifierExpression(((VariableDefinition)operand).Name)); } case Code.Ldnull: return new Ast.NullReferenceExpression(); case Code.Ldobj: throw new NotImplementedException(); case Code.Ldstr: return new Ast.PrimitiveExpression(operand); case Code.Ldtoken: if (operand is Cecil.TypeReference) { return new Ast.TypeOfExpression { Type = operandAsTypeRef }.Member("TypeHandle"); } else { throw new NotImplementedException(); } case Code.Leave: return null; case Code.Localloc: throw new NotImplementedException(); case Code.Mkrefany: throw new NotImplementedException(); case Code.Newobj: Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; // TODO: Ensure that the corrent overloaded constructor is called if (declaringType is ArrayType) { return new Ast.ArrayCreateExpression { Type = AstBuilder.ConvertType((ArrayType)declaringType), Arguments = args }; } return new Ast.ObjectCreateExpression { Type = AstBuilder.ConvertType(declaringType), Arguments = args }; case Code.No: throw new NotImplementedException(); case Code.Nop: return null; case Code.Pop: return arg1; case Code.Readonly: throw new NotImplementedException(); case Code.Refanytype: throw new NotImplementedException(); case Code.Refanyval: throw new NotImplementedException(); case Code.Ret: { if (methodDef.ReturnType.FullName != Constants.Void) { arg1 = Convert(arg1, methodDef.ReturnType); return new Ast.ReturnStatement { Expression = arg1 }; } else { return new Ast.ReturnStatement(); } } case Code.Rethrow: return new Ast.ThrowStatement(); case Code.Sizeof: return new Ast.SizeOfExpression { Type = AstBuilder.ConvertType(operand as TypeReference) }; case Code.Starg: throw new NotImplementedException(); case Code.Stloc: { ILVariable locVar = (ILVariable)operand; if (!definedLocalVars.Contains(locVar)) { definedLocalVars.Add(locVar); return new Ast.VariableDeclarationStatement() { Type = locVar.Type != null ? AstBuilder.ConvertType(locVar.Type) : new Ast.PrimitiveType("var"), Variables = new[] { new Ast.VariableInitializer(locVar.Name, arg1) } }; } else { return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name), arg1); } } case Code.Stobj: throw new NotImplementedException(); case Code.Switch: throw new NotImplementedException(); case Code.Tail: throw new NotImplementedException(); case Code.Throw: return new Ast.ThrowStatement { Expression = arg1 }; case Code.Unaligned: throw new NotImplementedException(); case Code.Unbox: throw new NotImplementedException(); case Code.Unbox_Any: throw new NotImplementedException(); case Code.Volatile: throw new NotImplementedException(); default: throw new Exception("Unknown OpCode: " + opCode); } }
List<Ast.Expression> TransformExpressionArguments(ILExpression expr) { List<Ast.Expression> args = new List<Ast.Expression>(); // Args generated by nested expressions (which must be closed) foreach(ILExpression arg in expr.Arguments) { args.Add((Ast.Expression)TransformExpression(arg)); } return args; }
AstNode TransformByteCode(ILExpression byteCode, List<Ast.Expression> args) { ILCode opCode = byteCode.Code; object operand = byteCode.Operand; AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference); ILExpression operandAsByteCode = operand as ILExpression; Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; Ast.Expression arg3 = args.Count >= 3 ? args[2] : null; BlockStatement branchCommand = null; if (byteCode.Operand is ILLabel) { branchCommand = new BlockStatement(); branchCommand.Add(new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name)); } switch((Code)opCode) { #region Arithmetic case Code.Add: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Add_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Add_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); case Code.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2); case Code.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); case Code.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); case Code.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2); case Code.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); case Code.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2); case Code.Sub: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); case Code.Sub_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); case Code.Sub_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); case Code.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2); case Code.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2); case Code.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2); case Code.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2); case Code.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); case Code.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2); case Code.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1); case Code.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1); #endregion #region Arrays case Code.Newarr: { var ace = new Ast.ArrayCreateExpression(); ace.Type = operandAsTypeRef; ace.Arguments.Add(arg1); return ace; } case Code.Ldlen: return arg1.Member("Length"); case Code.Ldelem_I: case Code.Ldelem_I1: case Code.Ldelem_I2: case Code.Ldelem_I4: case Code.Ldelem_I8: case Code.Ldelem_U1: case Code.Ldelem_U2: case Code.Ldelem_U4: case Code.Ldelem_R4: case Code.Ldelem_R8: case Code.Ldelem_Ref: return arg1.Indexer(arg2); case Code.Ldelem_Any: return InlineAssembly(byteCode, args); case Code.Ldelema: return MakeRef(arg1.Indexer(arg2)); case Code.Stelem_I: case Code.Stelem_I1: case Code.Stelem_I2: case Code.Stelem_I4: case Code.Stelem_I8: case Code.Stelem_R4: case Code.Stelem_R8: case Code.Stelem_Ref: return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3); case Code.Stelem_Any: return InlineAssembly(byteCode, args); #endregion #region Branching case Code.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); case Code.Brfalse: return new Ast.IfElseStatement(new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1), branchCommand); case Code.Brtrue: return new Ast.IfElseStatement(arg1, branchCommand); case Code.Beq: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2), branchCommand); case Code.Bge: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2), branchCommand); case Code.Bge_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2), branchCommand); case Code.Bgt: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2), branchCommand); case Code.Bgt_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2), branchCommand); case Code.Ble: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2), branchCommand); case Code.Ble_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2), branchCommand); case Code.Blt: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2), branchCommand); case Code.Blt_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2), branchCommand); case Code.Bne_Un: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2), branchCommand); #endregion #region Comparison case Code.Ceq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); case Code.Cgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case Code.Cgt_Un: // can also mean Inequality, when used with object references { TypeReference arg1Type = byteCode.Arguments[0].InferredType; if (arg1Type != null && !arg1Type.IsValueType) return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); else return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); } case Code.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); case Code.Clt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); #endregion #region Conversions case Code.Conv_I1: case Code.Conv_I2: case Code.Conv_I4: case Code.Conv_I8: case Code.Conv_U1: case Code.Conv_U2: case Code.Conv_U4: case Code.Conv_U8: return arg1; // conversion is handled by Convert() function using the info from type analysis case Code.Conv_I: return arg1.CastTo(typeof(IntPtr)); // TODO case Code.Conv_U: return arg1.CastTo(typeof(UIntPtr)); // TODO case Code.Conv_R4: return arg1.CastTo(typeof(float)); case Code.Conv_R8: return arg1.CastTo(typeof(double)); case Code.Conv_R_Un: return arg1.CastTo(typeof(double)); // TODO case Code.Conv_Ovf_I1: case Code.Conv_Ovf_I2: case Code.Conv_Ovf_I4: case Code.Conv_Ovf_I8: case Code.Conv_Ovf_U1: case Code.Conv_Ovf_U2: case Code.Conv_Ovf_U4: case Code.Conv_Ovf_U8: case Code.Conv_Ovf_I1_Un: case Code.Conv_Ovf_I2_Un: case Code.Conv_Ovf_I4_Un: case Code.Conv_Ovf_I8_Un: case Code.Conv_Ovf_U1_Un: case Code.Conv_Ovf_U2_Un: case Code.Conv_Ovf_U4_Un: case Code.Conv_Ovf_U8_Un: return arg1; // conversion was handled by Convert() function using the info from type analysis case Code.Conv_Ovf_I: return arg1.CastTo(typeof(IntPtr)); // TODO case Code.Conv_Ovf_U: return arg1.CastTo(typeof(UIntPtr)); case Code.Conv_Ovf_I_Un: return arg1.CastTo(typeof(IntPtr)); case Code.Conv_Ovf_U_Un: return arg1.CastTo(typeof(UIntPtr)); case Code.Castclass: case Code.Unbox_Any: return arg1.CastTo(operandAsTypeRef); case Code.Isinst: return arg1.CastAs(operandAsTypeRef); case Code.Box: return arg1; case Code.Unbox: return InlineAssembly(byteCode, args); #endregion #region Indirect case Code.Ldind_I: return InlineAssembly(byteCode, args); case Code.Ldind_I1: return InlineAssembly(byteCode, args); case Code.Ldind_I2: return InlineAssembly(byteCode, args); case Code.Ldind_I4: return InlineAssembly(byteCode, args); case Code.Ldind_I8: return InlineAssembly(byteCode, args); case Code.Ldind_U1: return InlineAssembly(byteCode, args); case Code.Ldind_U2: return InlineAssembly(byteCode, args); case Code.Ldind_U4: return InlineAssembly(byteCode, args); case Code.Ldind_R4: return InlineAssembly(byteCode, args); case Code.Ldind_R8: return InlineAssembly(byteCode, args); case Code.Ldind_Ref: return InlineAssembly(byteCode, args); case Code.Stind_I: return InlineAssembly(byteCode, args); case Code.Stind_I1: return InlineAssembly(byteCode, args); case Code.Stind_I2: return InlineAssembly(byteCode, args); case Code.Stind_I4: return InlineAssembly(byteCode, args); case Code.Stind_I8: return InlineAssembly(byteCode, args); case Code.Stind_R4: return InlineAssembly(byteCode, args); case Code.Stind_R8: return InlineAssembly(byteCode, args); case Code.Stind_Ref: return InlineAssembly(byteCode, args); #endregion case Code.Arglist: return InlineAssembly(byteCode, args); case Code.Break: return InlineAssembly(byteCode, args); case Code.Call: return TransformCall(false, operand, methodDef, args); case Code.Callvirt: return TransformCall(true, operand, methodDef, args); case Code.Ldftn: { Cecil.MethodReference cecilMethod = ((MethodReference)operand); var expr = new Ast.IdentifierExpression(cecilMethod.Name); expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); expr.AddAnnotation(cecilMethod); return new IdentifierExpression("ldftn").Invoke(expr) .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false)); } case Code.Ldvirtftn: { Cecil.MethodReference cecilMethod = ((MethodReference)operand); var expr = new Ast.IdentifierExpression(cecilMethod.Name); expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); expr.AddAnnotation(cecilMethod); return new IdentifierExpression("ldvirtftn").Invoke(expr) .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); } case Code.Calli: return InlineAssembly(byteCode, args); case Code.Ckfinite: return InlineAssembly(byteCode, args); case Code.Constrained: return InlineAssembly(byteCode, args); case Code.Cpblk: return InlineAssembly(byteCode, args); case Code.Cpobj: return InlineAssembly(byteCode, args); case Code.Dup: return arg1; case Code.Endfilter: return InlineAssembly(byteCode, args); case Code.Endfinally: return null; case Code.Initblk: return InlineAssembly(byteCode, args); case Code.Initobj: return InlineAssembly(byteCode, args); case Code.Jmp: return InlineAssembly(byteCode, args); case Code.Ldarg: if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { return new Ast.ThisReferenceExpression(); } else { return new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand); } case Code.Ldarga: if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { return MakeRef(new Ast.ThisReferenceExpression()); } else { return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand)); } case Code.Ldc_I4: return PrimitiveExpression((int)operand, byteCode.InferredType); case Code.Ldc_I8: case Code.Ldc_R4: case Code.Ldc_R8: return new Ast.PrimitiveExpression(operand); case Code.Ldfld: return arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand); case Code.Ldsfld: return AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) .Member(((FieldReference)operand).Name).WithAnnotation(operand); case Code.Stfld: return new AssignmentExpression(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand), arg2); case Code.Stsfld: return new AssignmentExpression( AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) .Member(((FieldReference)operand).Name).WithAnnotation(operand), arg1); case Code.Ldflda: return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand)); case Code.Ldsflda: return MakeRef( AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) .Member(((FieldReference)operand).Name).WithAnnotation(operand)); case Code.Ldloc: return new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand); case Code.Ldloca: return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand)); case Code.Ldnull: return new Ast.NullReferenceExpression(); case Code.Ldobj: return InlineAssembly(byteCode, args); case Code.Ldstr: return new Ast.PrimitiveExpression(operand); case Code.Ldtoken: if (operand is Cecil.TypeReference) { return new Ast.TypeOfExpression { Type = operandAsTypeRef }.Member("TypeHandle"); } else { return InlineAssembly(byteCode, args); } case Code.Leave: return null; case Code.Localloc: return InlineAssembly(byteCode, args); case Code.Mkrefany: return InlineAssembly(byteCode, args); case Code.Newobj: { Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; // TODO: Ensure that the corrent overloaded constructor is called /*if (declaringType is ArrayType) { shouldn't this be newarr? return new Ast.ArrayCreateExpression { Type = AstBuilder.ConvertType((ArrayType)declaringType), Arguments = args }; }*/ var oce = new Ast.ObjectCreateExpression(); oce.Type = AstBuilder.ConvertType(declaringType); oce.Arguments.AddRange(args); return oce.WithAnnotation(operand); } case Code.No: return InlineAssembly(byteCode, args); case Code.Nop: return null; case Code.Pop: return arg1; case Code.Readonly: return InlineAssembly(byteCode, args); case Code.Refanytype: return InlineAssembly(byteCode, args); case Code.Refanyval: return InlineAssembly(byteCode, args); case Code.Ret: { if (methodDef.ReturnType.FullName != "System.Void") { return new Ast.ReturnStatement { Expression = arg1 }; } else { return new Ast.ReturnStatement(); } } case Code.Rethrow: return new Ast.ThrowStatement(); case Code.Sizeof: return new Ast.SizeOfExpression { Type = AstBuilder.ConvertType(operand as TypeReference) }; case Code.Starg: return new Ast.AssignmentExpression(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand), arg1); case Code.Stloc: { ILVariable locVar = (ILVariable)operand; if (!localVariablesToDefine.Contains(locVar)) { localVariablesToDefine.Add(locVar); } return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1); } case Code.Stobj: return InlineAssembly(byteCode, args); case Code.Switch: return InlineAssembly(byteCode, args); case Code.Tail: return InlineAssembly(byteCode, args); case Code.Throw: return new Ast.ThrowStatement { Expression = arg1 }; case Code.Unaligned: return InlineAssembly(byteCode, args); case Code.Volatile: return InlineAssembly(byteCode, args); default: throw new Exception("Unknown OpCode: " + opCode); } }
List<ILNode> ConvertToAst(List<ByteCode> body) { List<ILNode> ast = new List<ILNode>(); // Convert stack-based IL code to ILAst tree foreach(ByteCode byteCode in body) { OpCode opCode = byteCode.OpCode; object operand = byteCode.Operand; MethodBodyRocks.ExpandMacro(ref opCode, ref operand, methodDef.Body); ILExpression expr = new ILExpression(opCode, operand); expr.ILRanges.Add(new ILRange() { From = byteCode.Offset, To = byteCode.EndOffset }); // Label for this instruction if (byteCode.Label != null) { ast.Add(byteCode.Label); } // Reference arguments using temporary variables int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { StackSlot slot = byteCode.StackBefore[i]; if (slot.PushedBy != null) { ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, slot.LoadFrom); expr.Arguments.Add(ldExpr); } else { ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new ILVariable() { Name = "ex", IsGenerated = true }); expr.Arguments.Add(ldExpr); } } // Store the result to temporary variable(s) if needed if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0) { ast.Add(expr); } else if (byteCode.StoreTo.Count == 1) { ast.Add(new ILExpression(OpCodes.Stloc, byteCode.StoreTo[0], expr)); } else { ILVariable tmpVar = new ILVariable() { Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true }; ast.Add(new ILExpression(OpCodes.Stloc, tmpVar, expr)); foreach(ILVariable storeTo in byteCode.StoreTo) { ast.Add(new ILExpression(OpCodes.Stloc, storeTo, new ILExpression(OpCodes.Ldloc, tmpVar))); } } } // Try to in-line stloc / ldloc pairs for(int i = 0; i < ast.Count - 1; i++) { if (i < 0) continue; ILExpression currExpr = ast[i] as ILExpression; ILExpression nextExpr = ast[i + 1] as ILExpression; if (currExpr != null && nextExpr != null && currExpr.OpCode.Code == Code.Stloc) { // If the next expression is generated stloc, look inside if (nextExpr.OpCode.Code == Code.Stloc && ((ILVariable)nextExpr.Operand).IsGenerated) { nextExpr = nextExpr.Arguments[0]; } // Find the use of the 'expr' for(int j = 0; j < nextExpr.Arguments.Count; j++) { ILExpression arg = nextExpr.Arguments[j]; // We are moving the expression evaluation past the other aguments. // It is ok to pass ldloc because the expression can not contain stloc and thus the ldcoc will still return the same value if (arg.OpCode.Code == Code.Ldloc) { bool canInline; allowInline.TryGetValue((ILVariable)arg.Operand, out canInline); if (arg.Operand == currExpr.Operand && canInline) { // Assigne the ranges for optimized away instrustions somewhere currExpr.Arguments[0].ILRanges.AddRange(currExpr.ILRanges); currExpr.Arguments[0].ILRanges.AddRange(nextExpr.Arguments[j].ILRanges); ast.RemoveAt(i); nextExpr.Arguments[j] = currExpr.Arguments[0]; // Inline the stloc body i -= 2; // Try the same index again break; // Found } } else { break; // Side-effects } } } } return ast; }
AstNode TransformExpression(ILExpression expr) { List<Ast.Expression> args = TransformExpressionArguments(expr); AstNode node = TransformByteCode(expr, args); Expression astExpr = node as Expression; if (astExpr != null) return Convert(astExpr, expr.InferredType, expr.ExpectedType); else return node; }