コード例 #1
0
 /// <summary>
 /// Construct an instruction.
 /// </summary>
 /// <param name="reader">The reader that created this instruction.</param>
 /// <param name="op">The instruction opcode.</param>
 /// <param name="arg">The instruction operand.</param>
 /// <param name="label">The instruction's address in the bytecode.</param>
 public Instruction(ILReader reader, OpCode op, Operand arg, IL.Label label)
     : this(reader, op, arg, (ushort)label.pos)
 {
 }
コード例 #2
0
 /// <summary>
 /// Construct an instruction.
 /// </summary>
 /// <param name="reader">The reader that created this instruction.</param>
 /// <param name="op">The instruction opcode.</param>
 /// <param name="label">The instruction's address in the bytecode.</param>
 public Instruction(ILReader reader, OpCode op, IL.Label label)
     : this(reader, op, default(Operand), label)
 {
 }
コード例 #3
0
        static void Process <T>(ILReader il, IExpression <T> exp, Stack <T> eval, T[] args, T[] locals)
        {
            var tailcall = false;

            while (il.MoveNext())
            {
                var x = il.Current;//.Simplify();
                T   rhs;
                switch (x.OpCode.Type())
                {
                //--------------- arithmetic instructions --------------
                case OpType.Add_ovf:
                case OpType.Add_ovf_un:
                    rhs = eval.Pop();
                    eval.Push(exp.AddOverflow(eval.Pop(), rhs));
                    break;

                case OpType.Add:
                    rhs = eval.Pop();
                    eval.Push(exp.Add(eval.Pop(), rhs));
                    break;

                case OpType.Sub:
                    rhs = eval.Pop();
                    eval.Push(exp.Subtract(eval.Pop(), rhs));
                    break;

                case OpType.Sub_ovf:
                case OpType.Sub_ovf_un:
                    rhs = eval.Pop();
                    eval.Push(exp.SubtractOverflow(eval.Pop(), rhs));
                    break;

                case OpType.Mul:
                    rhs = eval.Pop();
                    eval.Push(exp.Multiply(eval.Pop(), rhs));
                    break;

                case OpType.Mul_ovf:
                case OpType.Mul_ovf_un:
                    rhs = eval.Pop();
                    eval.Push(exp.MultiplyOverflow(eval.Pop(), rhs));
                    break;

                case OpType.Div:
                case OpType.Div_un:
                    rhs = eval.Pop();
                    eval.Push(exp.Divide(eval.Pop(), rhs));
                    break;

                case OpType.Neg:
                    eval.Push(exp.Negate(eval.Pop()));
                    break;

                case OpType.Rem:
                case OpType.Rem_un:
                    rhs = eval.Pop();
                    eval.Push(exp.Modulo(eval.Pop(), rhs));
                    break;

                //------------ logical/bitwise operations --------------
                case OpType.And:
                    rhs = eval.Pop();
                    eval.Push(exp.And(eval.Pop(), rhs));
                    break;

                case OpType.Or:
                    rhs = eval.Pop();
                    eval.Push(exp.Or(eval.Pop(), rhs));
                    break;

                case OpType.Not:
                    eval.Push(exp.Not(eval.Pop()));
                    break;

                case OpType.Box:
                    eval.Push(exp.Box(eval.Pop()));
                    break;

                case OpType.Shl:
                    rhs = eval.Pop();
                    eval.Push(exp.LeftShift(eval.Pop(), rhs));
                    break;

                case OpType.Shr:
                case OpType.Shr_un:
                    rhs = eval.Pop();
                    eval.Push(exp.RightShift(eval.Pop(), rhs));
                    break;

                //----------- Branching instructions ---------
                case OpType.Br:
                case OpType.Br_s:
                    il.Seek(x.Operand.Label);     // seek to branch label
                    eval.Push(exp.Goto(eval.Pop()));
                    break;

                case OpType.Beq:
                case OpType.Beq_s:
                    rhs = eval.Pop();
                    eval.Push(exp.Equal(eval.Pop(), rhs));
                    goto case OpType.Brtrue;

                case OpType.Bge:
                case OpType.Bge_un:
                case OpType.Bge_un_s:
                    rhs = eval.Pop();
                    eval.Push(exp.GreaterThanOrEqual(eval.Pop(), rhs));
                    goto case OpType.Brtrue;

                case OpType.Ble:
                case OpType.Ble_un:
                case OpType.Ble_un_s:
                    rhs = eval.Pop();
                    eval.Push(exp.LessThanOrEqual(eval.Pop(), rhs));
                    goto case OpType.Brtrue;

                case OpType.Blt:
                case OpType.Blt_un:
                case OpType.Blt_un_s:
                    rhs = eval.Pop();
                    eval.Push(exp.LessThan(eval.Pop(), rhs));
                    goto case OpType.Brtrue;

                case OpType.Bne_un:
                case OpType.Bne_un_s:
                    rhs = eval.Pop();
                    eval.Push(exp.NotEqual(eval.Pop(), rhs));
                    goto case OpType.Brtrue;

                case OpType.Brfalse:
                case OpType.Brfalse_s:
                    eval.Push(exp.Negate(eval.Pop()));
                    goto case OpType.Brtrue;

                case OpType.Brtrue:
                case OpType.Brtrue_s:
                    //FIXME: this duplicates expressions for every branch instead of unifying them. Need
                    //to track a Dictionary<Label, T>, and should skip Process() calls if the Label
                    //already exists (and patch it in with a goto).
                    var cond = eval.Pop();
                    if (!il.MoveNext())
                    {
                        throw new InvalidOperationException("Expected an instruction after branch!");
                    }
                    var elseStart = il.Mark();    // save current position
                    var thenStart = x.Operand.Label;
                    il.Seek(thenStart);           // seek to _then when branch condition true
                    Process(il, exp, eval, args, locals);
                    var _then = eval.Pop();       // extract _then expression
                    il.Seek(elseStart);           // seek to _else when branch condition false
                    Process(il, exp, eval, args, locals);
                    var _else = eval.Pop();       // extract _else expression
                    eval.Push(exp.If(cond, _then, _else));
                    break;

                case OpType.Switch:
                    var val = eval.Pop();
                    if (!il.MoveNext())
                    {
                        throw new InvalidOperationException("Expected an instruction after switch!");
                    }
                    var swt   = il.Mark();
                    var cases = x.ResolveBranches().Select(kv =>
                    {
                        il.Seek(kv.Value);
                        Process(il, exp, eval, args, locals);
                        return(new KeyValuePair <object, T>(kv.Key, eval.Pop()));
                    });
                    eval.Push(exp.Switch(val, cases));
                    il.Seek(swt);
                    break;

#if DEBUG
                //FIXME: extend ILReader to support consulting exception handling blocks:
                //  method.GetMethodBody().ExceptionHandlingClauses[0].TryOffset/TryLength/HandlerOffset
                //
                case OpType.Leave:
                case OpType.Leave_s:
                    //FIXME: should leave be treated differently from br?
                    //Process(il, exp, eval, args, locals);   // process the .try block
                    il.Seek(x.Operand.Label);     // seek to branch label
                    eval.Push(exp.Goto(eval.Pop()));
                    return;

                case OpType.Endfilter:     // return to try context?
                    //eval.Push()
                    return;

                //eval.Push(exp.EndFilter(eval.Pop()));
                //break;
                case OpType.Endfinally:
                    return;

                    //case OpType.Try:
                    //    Process(il, exp, eval, args, locals);
                    //    var leaveTry = eval.Pop();
                    //    if (il.Current.OpCode.Type() != OpType.Leave || il.Current.OpCode.Type() != OpType.Leave_s || !il.MoveNext())
                    //        throw new InvalidOperationException("Try-catch-finally requires .try block to end with a leave instruction.");
                    //    if (!il.MoveNext()) //FIXME: maybe shouldn't call this?
                    //        return;
                    //    var handlers = new Dictionary<Type, T>();
                    //    do
                    //    {
                    //        Process(il, exp, eval, args, locals);
                    //        if (il.Current.OpCode.Type() != OpType.Endfilter)
                    //    } while (il.Current.OpCode.Type() == OpType.Endfilter);
                    //    Process(il, exp, eval, args, locals);
                    //    if (il.Current.OpCode.Type() != OpType.Leave || il.Current.OpCode.Type() != OpType.Leave_s)
                    //        throw new InvalidOperationException("Try-catch-finally requires .try block to end with a leave instruction.");
                    //    while (il.Current.OpCode.Type() == OpType.Endfinally);
                    //    //eval.Push(exp.TryCatchFinally(leaveTry, ));
                    //    break;
#endif

                    //------------ Method call instructions ---------------
#if DEBUG
                //FIXME: indirect calls and method pointers require decoding method signatures.
                //See incomplete CallSignature type.
                case OpType.Ldftn:
                    eval.Push(exp.GetPointer(x.ResolveMethod(), default(T)));
                    break;

                case OpType.Ldvirtftn:
                    eval.Push(exp.GetPointer(x.ResolveMethod(), eval.Pop()));
                    break;

                case OpType.Calli:
                    var sig = x.ResolveSignature();
                    //var args =
                    var entry = eval.Pop();
                    eval.Push(exp.CallIndirect(sig, entry, tailcall, args));
                    tailcall = false;
                    break;
#endif
                case OpType.Call:
                case OpType.Callvirt:
                    var method  = x.ResolveMethod();
                    var mparams = method.GetParameters();
                    var margs   = mparams.Select((a, i) => Cast(eval.Pop(), mparams[i].ParameterType, exp))
                                  .Reverse()
                                  .ToArray();
                    var minstance = method.IsStatic ? default(T) : eval.Pop();
                    eval.Push(exp.Call(method, tailcall, minstance, margs));
                    tailcall = false;
                    break;

                case OpType.Jmp:
                    eval.Push(exp.Jump(x.ResolveMethod()));
                    break;

                case OpType.Cpblk:
                    rhs = eval.Pop();
                    var src = eval.Pop();
                    eval.Push(exp.BlockCopy(eval.Pop(), src, rhs));
                    break;

                case OpType.Cpobj:
                    rhs = eval.Pop();
                    eval.Push(exp.StructCopy(eval.Pop(), rhs, x.ResolveType()));
                    break;

                case OpType.Initblk:
                    rhs = eval.Pop();
                    var value = eval.Pop();
                    eval.Push(exp.BlockInit(eval.Pop(), value, rhs));
                    break;

                case OpType.Initobj:
                    eval.Push(exp.ObjectInit(eval.Pop(), x.ResolveType()));
                    break;

                //---------------- Load/store instructions -------------
                case OpType.Sizeof:
                    eval.Push(exp.SizeOf(x.ResolveType()));
                    break;

                case OpType.Localloc:
                    eval.Push(exp.LocalAlloc(eval.Pop()));
                    break;

                case OpType.Ldsflda:
                case OpType.Ldflda:
                    var afield = x.ResolveField();
                    eval.Push(exp.AddressOf(exp.Field(afield, afield.IsStatic ? default(T) : eval.Pop())));
                    break;

                case OpType.Ldarga:
                case OpType.Ldarga_s:
                    eval.Push(exp.AddressOf(args[x.Operand.Int16]));
                    break;

                case OpType.Ldloca:
                case OpType.Ldloca_s:
                    eval.Push(exp.AddressOf(locals[x.Operand.Int16]));
                    break;

                case OpType.Ldarg:
                case OpType.Ldarg_s:
                    eval.Push(args[x.Operand.Int16]);
                    break;

                case OpType.Ldarg_0:
                    eval.Push(args[0]);
                    break;

                case OpType.Ldarg_1:
                    eval.Push(args[1]);
                    break;

                case OpType.Ldarg_2:
                    eval.Push(args[2]);
                    break;

                case OpType.Ldarg_3:
                    eval.Push(args[3]);
                    break;

                case OpType.Ldc_i4_m1:
                    eval.Push(exp.Constant(-1));
                    break;

                case OpType.Ldc_i4_0:
                    eval.Push(exp.Constant(0));
                    break;

                case OpType.Ldc_i4_1:
                    eval.Push(exp.Constant(1));
                    break;

                case OpType.Ldc_i4_2:
                    eval.Push(exp.Constant(2));
                    break;

                case OpType.Ldc_i4_3:
                    eval.Push(exp.Constant(3));
                    break;

                case OpType.Ldc_i4_4:
                    eval.Push(exp.Constant(4));
                    break;

                case OpType.Ldc_i4_5:
                    eval.Push(exp.Constant(5));
                    break;

                case OpType.Ldc_i4_6:
                    eval.Push(exp.Constant(6));
                    break;

                case OpType.Ldc_i4_7:
                    eval.Push(exp.Constant(7));
                    break;

                case OpType.Ldc_i4_8:
                    eval.Push(exp.Constant(8));
                    break;

                case OpType.Ldc_i4:
                case OpType.Ldc_i4_s:
                    var i4 = x.Operand.Int32;
                    eval.Push(exp.Constant(i4));
                    break;

                case OpType.Ldc_i8:
                    var i8 = x.Operand.Int64;
                    eval.Push(exp.Constant(i8));
                    break;

                case OpType.Ldc_r8:
                    var r8 = x.Operand.Float64;
                    eval.Push(exp.Constant(r8));
                    break;

                case OpType.Ldc_r4:
                    var r4 = x.Operand.Float32;
                    eval.Push(exp.Constant(r4));
                    break;

                case OpType.Ldfld:
                case OpType.Ldsfld:
                    var lfield = x.ResolveField();
                    eval.Push(exp.Field(lfield, lfield.IsStatic ? default(T) : eval.Pop()));
                    return;

                case OpType.Ldlen:
                    eval.Push(exp.ArrayLength(eval.Pop()));
                    break;

                case OpType.Ldloc_0:
                    eval.Push(locals[0]);
                    break;

                case OpType.Ldloc_1:
                    eval.Push(locals[1]);
                    break;

                case OpType.Ldloc_2:
                    eval.Push(locals[2]);
                    break;

                case OpType.Ldloc_3:
                    eval.Push(locals[3]);
                    break;

                case OpType.Ldloc:
                case OpType.Ldloc_s:
                    eval.Push(locals[x.Operand.Int32]);
                    break;

                case OpType.Stloc_0:
                    eval.Push(exp.Assign(locals[0], eval.Pop()));
                    break;

                case OpType.Stloc_1:
                    eval.Push(exp.Assign(locals[1], eval.Pop()));
                    break;

                case OpType.Stloc_2:
                    eval.Push(exp.Assign(locals[2], eval.Pop()));
                    break;

                case OpType.Stloc_3:
                    eval.Push(exp.Assign(locals[3], eval.Pop()));
                    break;

                case OpType.Stloc:
                case OpType.Stloc_s:
                    eval.Push(exp.Assign(locals[x.Operand.Int16], eval.Pop()));
                    break;

                case OpType.Starg:
                case OpType.Starg_s:
                    eval.Push(exp.Assign(args[x.Operand.Int16], eval.Pop()));
                    break;

                case OpType.Stobj:
                    rhs = eval.Pop();
                    eval.Push(exp.Assign(eval.Pop(), rhs));
                    break;

                case OpType.Stsfld:
                case OpType.Stfld:
                    var sfield = x.ResolveField();
                    rhs = eval.Pop();
                    eval.Push(exp.Assign(exp.Field(sfield, sfield.IsStatic ? default(T) : eval.Pop()), rhs));
                    break;

                case OpType.Ldnull:
                    eval.Push(exp.Constant <object>(null));
                    break;

                case OpType.Ldstr:
                    eval.Push(exp.Constant(x.ResolveString()));
                    break;

                case OpType.Ldtoken:
                    eval.Push(exp.Constant(x.Resolve()));
                    break;

                case OpType.Nop:
                    break;

                case OpType.Pop:
                    eval.Pop();
                    break;

                case OpType.Ret:
                    Debug.Assert(eval.Count == 1);
                    eval.Push(exp.Return(eval.Pop()));
                    break;

                case OpType.Ldelem:
                case OpType.Ldelem_ref:
                case OpType.Ldelem_i:
                case OpType.Ldelem_i1:
                case OpType.Ldelem_i2:
                case OpType.Ldelem_i4:
                case OpType.Ldelem_i8:
                case OpType.Ldelem_r4:
                case OpType.Ldelem_r8:
                case OpType.Ldelem_u1:
                case OpType.Ldelem_u2:
                case OpType.Ldelem_u4:
                    rhs = eval.Pop();
                    eval.Push(exp.ArrayGet(eval.Pop(), rhs));
                    break;

                case OpType.Stelem:
                case OpType.Stelem_i:
                case OpType.Stelem_i1:
                case OpType.Stelem_i2:
                case OpType.Stelem_i4:
                case OpType.Stelem_i8:
                case OpType.Stelem_r4:
                case OpType.Stelem_r8:
                case OpType.Stelem_ref:
                    rhs = eval.Pop();
                    var idx = eval.Pop();
                    eval.Push(exp.ArraySet(eval.Pop(), idx, rhs));
                    break;

                case OpType.Ldind_ref:
                case OpType.Ldind_i:
                case OpType.Ldind_i1:
                case OpType.Ldind_i2:
                case OpType.Ldind_i4:
                case OpType.Ldind_i8:
                case OpType.Ldind_r4:
                case OpType.Ldind_r8:
                case OpType.Ldind_u1:
                case OpType.Ldind_u2:
                case OpType.Ldind_u4:
                    eval.Push(exp.AddressGet(eval.Pop()));
                    break;

                case OpType.Stind_ref:
                case OpType.Stind_i:
                case OpType.Stind_i1:
                case OpType.Stind_i2:
                case OpType.Stind_i4:
                case OpType.Stind_i8:
                case OpType.Stind_r4:
                case OpType.Stind_r8:
                    rhs = eval.Pop();
                    eval.Push(exp.AddressSet(eval.Pop(), rhs));
                    break;

                case OpType.Arglist:
                    eval.Push(exp.ArgumentList());
                    break;

                //-------------- Comparison instructions --------------
                case OpType.Isinst:
                    eval.Push(exp.TypeAs(eval.Pop(), x.ResolveType()));
                    break;

                case OpType.Ceq:
                    // a bool is a native int in CIL, and != is a comparison against 0
                    rhs = eval.Pop();
                    var lhs     = eval.Pop();
                    var tyleft  = exp.TypeOf(lhs);
                    var tyright = exp.TypeOf(rhs);
                    if (tyleft != typeof(bool) || tyleft == tyright)
                    {
                        eval.Push(exp.Equal(lhs, rhs));
                    }
                    else if (tyleft != typeof(bool) && tyright == typeof(bool))
                    {
                        eval.Push(exp.Equal(exp.Cast(lhs, tyright), rhs));
                    }
                    else if (tyright != typeof(bool) && tyleft == typeof(bool))
                    {
                        eval.Push(exp.Equal(lhs, exp.Cast(rhs, tyleft)));
                    }
                    else
                    {
                        throw new InvalidOperationException("Unknown bool comparison!");
                    }
                    break;

                case OpType.Cgt:
                case OpType.Cgt_un:
                    rhs = eval.Pop();
                    eval.Push(exp.GreaterThan(eval.Pop(), rhs));
                    break;

                case OpType.Clt:
                case OpType.Clt_un:
                    rhs = eval.Pop();
                    eval.Push(exp.LessThan(eval.Pop(), rhs));
                    break;

                case OpType.Ckfinite:
                    var isInfinity = exp.TypeOf(eval.Peek()) == typeof(float) ? r4IsInfinity : r8IsInfinity;
                    var ethrow     = exp.Throw(exp.New(arithError, new[] { exp.Constant("Value is not a finite number.") }));
                    var evalue     = eval.Pop();
                    eval.Push(exp.If(exp.Call(isInfinity, false, default(T), new[] { evalue }), ethrow, evalue));
                    break;

                case OpType.Constrained_:
                    break;

                //------------ Conversion instructions -------------
                case OpType.Castclass:
                    eval.Push(exp.Cast(eval.Pop(), x.ResolveType()));
                    break;

                case OpType.Conv_i:
                    eval.Push(exp.Cast(eval.Pop(), nativeInt));
                    break;

                case OpType.Conv_i1:
                    eval.Push(exp.Cast(eval.Pop(), typeof(sbyte)));
                    break;

                case OpType.Conv_i2:
                    eval.Push(exp.Cast(eval.Pop(), typeof(short)));
                    break;

                case OpType.Conv_i4:
                    eval.Push(exp.Cast(eval.Pop(), typeof(int)));
                    break;

                case OpType.Conv_i8:
                    eval.Push(exp.Cast(eval.Pop(), typeof(long)));
                    break;

                case OpType.Conv_ovf_i:
                case OpType.Conv_ovf_i_un:
                    eval.Push(exp.CastOverflow(eval.Pop(), nativeInt));
                    break;

                case OpType.Conv_ovf_i1:
                case OpType.Conv_ovf_i1_un:
                    eval.Push(exp.CastOverflow(eval.Pop(), typeof(sbyte)));
                    break;

                case OpType.Conv_ovf_i2:
                case OpType.Conv_ovf_i2_un:
                    eval.Push(exp.CastOverflow(eval.Pop(), typeof(short)));
                    break;

                case OpType.Conv_ovf_i4:
                case OpType.Conv_ovf_i4_un:
                    eval.Push(exp.CastOverflow(eval.Pop(), typeof(int)));
                    break;

                case OpType.Conv_ovf_i8:
                case OpType.Conv_ovf_i8_un:
                    eval.Push(exp.CastOverflow(eval.Pop(), typeof(long)));
                    break;

                case OpType.Conv_r4:
                case OpType.Conv_r_un:
                    eval.Push(exp.Cast(eval.Pop(), typeof(float)));
                    break;

                case OpType.Conv_r8:
                    eval.Push(exp.Cast(eval.Pop(), typeof(double)));
                    break;

                case OpType.Conv_u:
                    eval.Push(exp.Cast(eval.Pop(), typeof(UIntPtr)));
                    break;

                case OpType.Conv_u1:
                    eval.Push(exp.Cast(eval.Pop(), typeof(byte)));
                    break;

                case OpType.Conv_u2:
                    eval.Push(exp.Cast(eval.Pop(), typeof(ushort)));
                    break;

                case OpType.Conv_u4:
                    eval.Push(exp.Cast(eval.Pop(), typeof(uint)));
                    break;

                case OpType.Conv_u8:
                    eval.Push(exp.Cast(eval.Pop(), typeof(ulong)));
                    break;

                case OpType.Dup:
                    eval.Push(exp.Duplicate(eval.Pop()));
                    break;

                case OpType.Newarr:
                    var atype = x.ResolveType();
                    eval.Push(exp.Array(atype, new[] { eval.Pop() }));
                    break;

                case OpType.Newobj:
                    var ctor    = (ConstructorInfo)x.ResolveMethod();
                    var cparams = ctor.GetParameters();
                    var cargs   = cparams.Select(a => eval.Pop()).Reverse();
                    eval.Push(exp.New(ctor, cargs));
                    break;

                case OpType.Rethrow:
                    eval.Push(exp.Rethrow());
                    break;

                case OpType.Tail_:
                    tailcall = true;
                    break;

                case OpType.Unaligned_:
                    eval.Push(exp.Unaligned(eval.Pop()));
                    break;

                case OpType.Unbox:
                case OpType.Unbox_any:
                    eval.Push(exp.Unbox(eval.Pop(), x.ResolveType()));
                    break;

                //case OpType.Break:
                //    break;
                //case OpType.Volatile_:
                //    break;
                //case OpType.Refanytype:
                //case OpType.Refanyval:
                //case OpType.Mkrefany:
                //case OpType.Localloc:
                //case OpType.Readonly_:
                default:
                    throw new NotSupportedException("Instruction not supported: " + x);
                }
            }
        }