Exemplo n.º 1
0
        private void EmitBody()
        {
            this.builder.AppendLine("{");
            var body = this.method.Body;
            var vars = body.Variables;

            foreach (var v in vars)
            {
                string name = null;
                if (!typeMap.TryGetValue(v.VariableType.FullName, out name))
                {
                    throw new ArgumentException(String.Format("Unsupported type: {0}.", v.VariableType.FullName));
                }
                this.builder.AppendFormat("{0} __V{1};\n", name, v.Index);
            }
            var code = body.Instructions;

            foreach (var instr in code)
            {
                switch (instr.OpCode.OperandType)
                {
                case OperandType.ShortInlineBrTarget:
                case OperandType.InlineBrTarget: {
                    var        sofs = instr.Offset;
                    var        tofs = (instr.Operand as Instruction).Offset;
                    BranchType type;
                    if (instr.OpCode.Code == Code.Br || instr.OpCode.Code == Code.Br_S)
                    {
                        type = BranchType.Goto;
                    }
                    else if (sofs < tofs)
                    {
                        type = BranchType.If;
                    }
                    else
                    {
                        type = BranchType.While;
                    }
                    this.labels.Add(tofs, type);
                    break;
                }

                default:
                    break;
                }
            }
            var stack = new Stack <AstNode>();
            var ndups = 0;

            foreach (var instr in code)
            {
                if (this.labels.ContainsKey(instr.Offset))
                {
                    switch (this.labels[instr.Offset])
                    {
                    case BranchType.Goto:
                        this.builder.AppendFormat("__L{0:x4}: {{ }}\n", instr.Offset);
                        break;

                    case BranchType.If:
                        this.builder.AppendLine("}");
                        break;

                    case BranchType.While:
                        this.builder.AppendLine("do {");
                        break;
                    }
                }
                // ***DEBUG***
//                this.builder.AppendLine(instr.ToString());
                // ***ENDEBUG***
                switch (instr.OpCode.Code)
                {
                case Code.Nop:
                    // nothing to do...
                    break;

                case Code.Dup: {
                    var    node = stack.Pop();
                    string name = null;
                    if (!typeMap.TryGetValue(node.CliType.SystemType.FullName, out name))
                    {
                        throw new ArgumentException(String.Format("Unsupported type: {0}.", node.CliType));
                    }
                    this.builder.AppendFormat("{0} __T{1} = ", name, ndups);
                    node.Accept(printer);
                    this.builder.AppendLine(";");
                    stack.Push(new VarRef(node.CliType, ndups, true));
                    stack.Push(new VarRef(node.CliType, ndups, true));
                    ndups++;
                    break;
                }

                case Code.Ldc_I4_0:
                    stack.Push(new Const <int>(CliType.FromType(typeof(System.Int32)), 0));
                    break;

                case Code.Ldc_I4_1:
                    stack.Push(new Const <int>(CliType.FromType(typeof(System.Int32)), 1));
                    break;

                case Code.Ldc_I4_2:
                    stack.Push(new Const <int>(CliType.FromType(typeof(System.Int32)), 2));
                    break;

                case Code.Ldc_I4_3:
                    stack.Push(new Const <int>(CliType.FromType(typeof(System.Int32)), 3));
                    break;

                case Code.Ldc_I4_4:
                    stack.Push(new Const <int>(CliType.FromType(typeof(System.Int32)), 4));
                    break;

                case Code.Ldc_I4_5:
                    stack.Push(new Const <int>(CliType.FromType(typeof(System.Int32)), 5));
                    break;

                case Code.Ldc_I4_6:
                    stack.Push(new Const <int>(CliType.FromType(typeof(System.Int32)), 6));
                    break;

                case Code.Ldc_I4_7:
                    stack.Push(new Const <int>(CliType.FromType(typeof(System.Int32)), 7));
                    break;

                case Code.Ldc_I4_8:
                    stack.Push(new Const <int>(CliType.FromType(typeof(System.Int32)), 8));
                    break;

                case Code.Ldc_I4_M1:
                    stack.Push(new Const <int>(CliType.FromType(typeof(System.Int32)), -1));
                    break;

                case Code.Ldc_I4:
                    stack.Push(new Const <int>(CliType.FromType(typeof(System.Int32)), (int)instr.Operand));
                    break;

                case Code.Ldc_I4_S:
                    stack.Push(new Const <int>(CliType.FromType(typeof(System.Int32)), (sbyte)instr.Operand));
                    break;

                case Code.Ldc_I8:
                    stack.Push(new Const <long>(CliType.FromType(typeof(System.Int64)), (long)instr.Operand));
                    break;

                case Code.Ldc_R4:
                    stack.Push(new Const <float>(CliType.FromType(typeof(System.Single)), (float)instr.Operand));
                    break;

                case Code.Ldc_R8:
                    stack.Push(new Const <double>(CliType.FromType(typeof(System.Double)), (double)instr.Operand));
                    break;

                case Code.Ldarg_0: {
                    var arg = this.method.Parameters[0];
                    stack.Push(new ParamRef(CliType.FromType(arg.ParameterType), arg.Name));
                    break;
                }

                case Code.Ldarg_1: {
                    var arg = this.method.Parameters[1];
                    stack.Push(new ParamRef(CliType.FromType(arg.ParameterType), arg.Name));
                    break;
                }

                case Code.Ldarg_2: {
                    var arg = this.method.Parameters[2];
                    stack.Push(new ParamRef(CliType.FromType(arg.ParameterType), arg.Name));
                    break;
                }

                case Code.Ldarg_3: {
                    var arg = this.method.Parameters[3];
                    stack.Push(new ParamRef(CliType.FromType(arg.ParameterType), arg.Name));
                    break;
                }

                case Code.Ldarg:
                case Code.Ldarg_S: {
                    var arg = instr.Operand as ParameterDefinition;
                    stack.Push(new ParamRef(CliType.FromType(arg.ParameterType), arg.Name));
                    break;
                }

                case Code.Ldloc_0:
                    stack.Push(new VarRef(CliType.FromType(vars[0].VariableType), 0));
                    break;

                case Code.Ldloc_1:
                    stack.Push(new VarRef(CliType.FromType(vars[1].VariableType), 1));
                    break;

                case Code.Ldloc_2:
                    stack.Push(new VarRef(CliType.FromType(vars[2].VariableType), 2));
                    break;

                case Code.Ldloc_3:
                    stack.Push(new VarRef(CliType.FromType(vars[3].VariableType), 3));
                    break;

                case Code.Ldloc:
                case Code.Ldloc_S: {
                    var loc = instr.Operand as VariableDefinition;
                    stack.Push(new VarRef(CliType.FromType(loc.VariableType), loc.Index));
                    break;
                }

                /*case Code.Ldelem_Any:*/
                case Code.Ldelem_I: {
                    var idx = stack.Pop();
                    var arr = stack.Pop();
                    stack.Push(new ElemRef(CliType.FromType(typeof(System.IntPtr)), arr, idx));
                    break;
                }

                case Code.Ldelem_I1: {
                    var idx = stack.Pop();
                    var arr = stack.Pop();
                    stack.Push(new ElemRef(CliType.FromType(typeof(System.SByte)), arr, idx));
                    break;
                }

                case Code.Ldelem_I2: {
                    var idx = stack.Pop();
                    var arr = stack.Pop();
                    stack.Push(new ElemRef(CliType.FromType(typeof(System.Int16)), arr, idx));
                    break;
                }

                case Code.Ldelem_I4: {
                    var idx = stack.Pop();
                    var arr = stack.Pop();
                    stack.Push(new ElemRef(CliType.FromType(typeof(System.Int32)), arr, idx));
                    break;
                }

                case Code.Ldelem_U1: {
                    var idx = stack.Pop();
                    var arr = stack.Pop();
                    stack.Push(new ElemRef(CliType.FromType(typeof(System.Byte)), arr, idx));
                    break;
                }

                case Code.Ldelem_U2: {
                    var idx = stack.Pop();
                    var arr = stack.Pop();
                    stack.Push(new ElemRef(CliType.FromType(typeof(System.UInt16)), arr, idx));
                    break;
                }

                case Code.Ldelem_U4: {
                    var idx = stack.Pop();
                    var arr = stack.Pop();
                    stack.Push(new ElemRef(CliType.FromType(typeof(System.UInt32)), arr, idx));
                    break;
                }

                case Code.Ldelem_I8: {
                    var idx = stack.Pop();
                    var arr = stack.Pop();
                    stack.Push(new ElemRef(CliType.FromType(typeof(System.Int64)), arr, idx));
                    break;
                }

                case Code.Ldelem_R4: {
                    var idx = stack.Pop();
                    var arr = stack.Pop();
                    stack.Push(new ElemRef(CliType.FromType(typeof(System.Single)), arr, idx));
                    break;
                }

                case Code.Ldelem_R8: {
                    var idx = stack.Pop();
                    var arr = stack.Pop();
                    stack.Push(new ElemRef(CliType.FromType(typeof(System.Double)), arr, idx));
                    break;
                }

                case Code.Ldind_I:
                case Code.Ldind_I1:
                case Code.Ldind_I2:
                case Code.Ldind_I4:
                case Code.Ldind_I8:
                case Code.Ldind_R4:
                case Code.Ldind_R8:
                case Code.Ldobj: {
                    var ptr = stack.Pop();
                    stack.Push(new LoadAddr(ptr));
                    break;
                }

                case Code.Ldarga:
                case Code.Ldarga_S: {
                    var arg  = instr.Operand as ParameterDefinition;
                    var type = new PointerType(arg.ParameterType);
                    stack.Push(new ParamAddr(new CliPointerType(type), arg.Name));
                    break;
                }

                case Code.Ldloca:
                case Code.Ldloca_S: {
                    var loc  = instr.Operand as VariableDefinition;
                    var type = new PointerType(loc.VariableType);
                    stack.Push(new VarAddr(new CliPointerType(type), loc.Index));
                    break;
                }

                case Code.Ldelema: {
                    var idx = stack.Pop();
                    var arr = stack.Pop();
                    stack.Push(new ElemAddr(arr.CliType, arr, idx));
                    break;
                }

                case Code.Localloc: {
                    var size = stack.Pop();
                    stack.Push(new LocAlloc(size));
                    break;
                }

                case Code.Stloc_0: {
                    this.builder.Append("__V0 = ");
                    stack.Pop().Accept(this.printer);
                    this.builder.AppendLine(";");
                    break;
                }

                case Code.Stloc_1: {
                    this.builder.Append("__V1 = ");
                    stack.Pop().Accept(this.printer);
                    this.builder.AppendLine(";");
                    break;
                }

                case Code.Stloc_2: {
                    this.builder.Append("__V2 = ");
                    stack.Pop().Accept(this.printer);
                    this.builder.AppendLine(";");
                    break;
                }

                case Code.Stloc_3: {
                    this.builder.Append("__V3 = ");
                    stack.Pop().Accept(this.printer);
                    this.builder.AppendLine(";");
                    break;
                }

                case Code.Stloc:
                case Code.Stloc_S: {
                    this.builder.AppendFormat("__V{0} = ", (instr.Operand as VariableDefinition).Index);
                    stack.Pop().Accept(this.printer);
                    this.builder.AppendLine(";");
                    break;
                }

                case Code.Stelem_Any:
                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: {
                    var val = stack.Pop();
                    var idx = stack.Pop();
                    var arr = stack.Pop();
                    arr.Accept(this.printer);
                    this.builder.Append("[");
                    idx.Accept(this.printer);
                    this.builder.Append("] = ");
                    val.Accept(this.printer);
                    this.builder.AppendLine(";");
                    break;
                }

                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.Stobj: {
                    var val = stack.Pop();
                    var ptr = stack.Pop();
                    this.builder.Append("*(");
                    ptr.Accept(this.printer);
                    this.builder.Append(") = ");
                    val.Accept(this.printer);
                    this.builder.AppendLine(";");
                    break;
                }

                case Code.Conv_I:
                case Code.Conv_Ovf_I:
                case Code.Conv_Ovf_I_Un:
                    stack.Push(new Conv(typeof(IntPtr), stack.Pop()));
                    break;

                case Code.Conv_I1:
                case Code.Conv_Ovf_I1:
                case Code.Conv_Ovf_I1_Un:
                    stack.Push(new Conv(typeof(SByte), stack.Pop()));
                    break;

                case Code.Conv_I2:
                case Code.Conv_Ovf_I2:
                case Code.Conv_Ovf_I2_Un:
                    stack.Push(new Conv(typeof(Int16), stack.Pop()));
                    break;

                case Code.Conv_I4:
                case Code.Conv_Ovf_I4:
                case Code.Conv_Ovf_I4_Un:
                    stack.Push(new Conv(typeof(Int32), stack.Pop()));
                    break;

                case Code.Conv_I8:
                case Code.Conv_Ovf_I8:
                case Code.Conv_Ovf_I8_Un:
                    stack.Push(new Conv(typeof(Int64), stack.Pop()));
                    break;

                case Code.Conv_U:
                case Code.Conv_Ovf_U:
                case Code.Conv_Ovf_U_Un:
                    stack.Push(new Conv(typeof(UIntPtr), stack.Pop()));
                    break;

                case Code.Conv_U1:
                case Code.Conv_Ovf_U1:
                case Code.Conv_Ovf_U1_Un:
                    stack.Push(new Conv(typeof(Byte), stack.Pop()));
                    break;

                case Code.Conv_U2:
                case Code.Conv_Ovf_U2:
                case Code.Conv_Ovf_U2_Un:
                    stack.Push(new Conv(typeof(UInt16), stack.Pop()));
                    break;

                case Code.Conv_U4:
                case Code.Conv_Ovf_U4:
                case Code.Conv_Ovf_U4_Un:
                    stack.Push(new Conv(typeof(UInt32), stack.Pop()));
                    break;

                case Code.Conv_U8:
                case Code.Conv_Ovf_U8:
                case Code.Conv_Ovf_U8_Un:
                    stack.Push(new Conv(typeof(UInt64), stack.Pop()));
                    break;

                case Code.Conv_R4:
                    stack.Push(new Conv(typeof(Single), stack.Pop()));
                    break;

                case Code.Conv_R8:
                    stack.Push(new Conv(typeof(Double), stack.Pop()));
                    break;

                case Code.Add:
                case Code.Add_Ovf:
                case Code.Add_Ovf_Un: {
                    var r = stack.Pop();
                    var l = stack.Pop();
                    var t = CliType.FromOpAdd(l.CliType, r.CliType);
                    if (l.CliType is CliPointerType)
                    {
                        var e = (l.CliType as CliPointerType).Element;
                        var s = Marshal.SizeOf(e);
                        r = new BinaryOp(r.CliType, BinaryOpCode.Div, r, new Const <int> (r.CliType, s));
                    }
                    stack.Push(new BinaryOp(t, BinaryOpCode.Add, l, r));
                    break;
                }

                case Code.Sub:
                case Code.Sub_Ovf:
                case Code.Sub_Ovf_Un: {
                    var r = stack.Pop();
                    var l = stack.Pop();
                    var t = CliType.FromOpSub(l.CliType, r.CliType);
                    stack.Push(new BinaryOp(t, BinaryOpCode.Sub, l, r));
                    break;
                }

                case Code.Mul:
                case Code.Mul_Ovf:
                case Code.Mul_Ovf_Un: {
                    var r = stack.Pop();
                    var l = stack.Pop();
                    var t = CliType.FromOpMul(l.CliType, r.CliType);
                    stack.Push(new BinaryOp(t, BinaryOpCode.Mul, l, r));
                    break;
                }

                case Code.Div:
                case Code.Div_Un: {
                    var r = stack.Pop();
                    var l = stack.Pop();
                    var t = CliType.FromOpDiv(l.CliType, r.CliType);
                    stack.Push(new BinaryOp(t, BinaryOpCode.Div, l, r));
                    break;
                }

                case Code.And: {
                    var r = stack.Pop();
                    var l = stack.Pop();
                    var t = CliType.FromOpBitwise(l.CliType, r.CliType);
                    stack.Push(new BinaryOp(t, BinaryOpCode.And, l, r));
                    break;
                }

                case Code.Or: {
                    var r = stack.Pop();
                    var l = stack.Pop();
                    var t = CliType.FromOpBitwise(l.CliType, r.CliType);
                    stack.Push(new BinaryOp(t, BinaryOpCode.Or, l, r));
                    break;
                }

                case Code.Xor: {
                    var r = stack.Pop();
                    var l = stack.Pop();
                    var t = CliType.FromOpBitwise(l.CliType, r.CliType);
                    stack.Push(new BinaryOp(t, BinaryOpCode.Xor, l, r));
                    break;
                }

                case Code.Shl: {
                    var r = stack.Pop();
                    var l = stack.Pop();
                    var t = CliType.FromOpBitwise(l.CliType, r.CliType);
                    stack.Push(new BinaryOp(t, BinaryOpCode.Shl, l, r));
                    break;
                }

                case Code.Shr:
                case Code.Shr_Un: {
                    var r = stack.Pop();
                    var l = stack.Pop();
                    var t = CliType.FromOpBitwise(l.CliType, r.CliType);
                    stack.Push(new BinaryOp(t, BinaryOpCode.Shr, l, r));
                    break;
                }

                case Code.Call: {
                    var mref  = instr.Operand as MethodReference;
                    var mdef  = mref.Resolve();
                    var name  = GetMethodName(mdef);
                    var nargs = mref.Parameters.Count;
                    if (mdef.HasThis && !mdef.ExplicitThis)
                    {
                        nargs++;
                    }
                    var args = new AstNode[nargs];
                    for (var i = nargs - 1; i >= 0; i--)
                    {
                        args[i] = stack.Pop();
                    }
                    if (mdef.IsConstructor)
                    {
                        var tdef = mdef.DeclaringType;
                        if (tdef.IsValueType)
                        {
                            // Note: this assumes that the struct constructor
                            // is compatible with C-style compound literals.
                            this.builder.Append("*(");
                            args[0].Accept(this.printer);
                            this.builder.AppendFormat(") = ({0}){{ ", typeMap[tdef.FullName]);
                            for (int i = 1; i < nargs; i++)
                            {
                                if (i > 1)
                                {
                                    this.builder.Append(", ");
                                }
                                args[i].Accept(this.printer);
                            }
                            this.builder.AppendLine(" };");
                        }
                    }
                    else
                    {
                        var rtype = CliType.FromType(mdef.ReturnType);
                        switch (name)
                        {
                        case "op_Addition":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.Add, args[0], args[1]));
                            break;

                        case "op_Subtraction":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.Sub, args[0], args[1]));
                            break;

                        case "op_Multiply":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.Mul, args[0], args[1]));
                            break;

                        case "op_Division":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.Div, args[0], args[1]));
                            break;

                        case "op_Equality":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.Eq, args[0], args[1]));
                            break;

                        case "op_Inequality":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.Neq, args[0], args[1]));
                            break;

                        case "op_LessThan":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.Lt, args[0], args[1]));
                            break;

                        case "op_LessThanOrEqual":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.Le, args[0], args[1]));
                            break;

                        case "op_GreaterThan":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.Gt, args[0], args[1]));
                            break;

                        case "op_GreaterThanOrEqual":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.Ge, args[0], args[1]));
                            break;

                        case "op_BitwiseAnd":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.And, args[0], args[1]));
                            break;

                        case "op_BitwiseOr":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.Or, args[0], args[1]));
                            break;

                        case "op_ExclusiveOr":
                            stack.Push(new BinaryOp(rtype, BinaryOpCode.Xor, args[0], args[1]));
                            break;

                        case "op_OnesComplement":
                            stack.Push(new UnaryOp(UnaryOpCode.Not, args[0]));
                            break;

                        default:
                            if (mdef.HasThis && name.StartsWith("get_"))
                            {
                                // Note: this assumes that the property getter is a valid
                                // C-style field reference.
                                stack.Push(new FieldRef(rtype, name.Substring(4), args[0]));
                            }
                            else if (mdef.HasThis && name.StartsWith("set_"))
                            {
                                // Note: this assumes that the property setter is a valid
                                // C-style field reference.
                                this.builder.Append("(*");
                                args[0].Accept(this.printer);
                                this.builder.AppendFormat(").{0} = ", name.Substring(4));
                                args[1].Accept(this.printer);
                                this.builder.AppendLine(";");
                            }
                            else
                            {
                                stack.Push(new Call(rtype, name, args));
                            }
                            break;
                        }
                    }
                    break;
                }

                case Code.Br:
                case Code.Br_S:
                    this.builder.AppendFormat("goto __L{0:x4};\n", (instr.Operand as Instruction).Offset);
                    break;

                case Code.Brfalse:
                case Code.Brfalse_S:
                case Code.Brtrue:
                case Code.Brtrue_S: {
                    var b = stack.Pop();
                    EmitUnaryBranch(instr, b);
                    break;
                }

                case Code.Beq:
                case Code.Beq_S: {
                    var v = stack.Pop();
                    var u = stack.Pop();
                    EmitBinaryBranch(instr, " == ", u, v);
                    break;
                }

                case Code.Bne_Un:
                case Code.Bne_Un_S: {
                    var v = stack.Pop();
                    var u = stack.Pop();
                    EmitBinaryBranch(instr, " != ", u, v);
                    break;
                }

                case Code.Blt:
                case Code.Blt_S:
                case Code.Blt_Un:
                case Code.Blt_Un_S: {
                    var v = stack.Pop();
                    var u = stack.Pop();
                    EmitBinaryBranch(instr, " < ", u, v);
                    break;
                }

                case Code.Ble:
                case Code.Ble_S:
                case Code.Ble_Un:
                case Code.Ble_Un_S: {
                    var v = stack.Pop();
                    var u = stack.Pop();
                    EmitBinaryBranch(instr, " <= ", u, v);
                    break;
                }

                case Code.Bgt:
                case Code.Bgt_S:
                case Code.Bgt_Un:
                case Code.Bgt_Un_S: {
                    var v = stack.Pop();
                    var u = stack.Pop();
                    EmitBinaryBranch(instr, " > ", u, v);
                    break;
                }

                case Code.Bge:
                case Code.Bge_S:
                case Code.Bge_Un:
                case Code.Bge_Un_S: {
                    var v = stack.Pop();
                    var u = stack.Pop();
                    EmitBinaryBranch(instr, " >= ", u, v);
                    break;
                }

                case Code.Ret:
                    this.builder.Append("return");
                    if (this.method.ReturnType.FullName != "System.Void")
                    {
                        this.builder.Append(" ");
                        stack.Pop().Accept(this.printer);
                    }
                    this.builder.AppendLine(";");
                    break;

                default:
                    throw new ApplicationException(String.Format("Unsupported opcode: {0}.", instr.OpCode));
//                    break;
                }
            }
            this.builder.AppendLine("}");
        }