Exemplo n.º 1
0
        Type CreateDelegateType(MethodInfo delegateMethod)
        {
            var args = delegateMethod.GetParameters();

            TypeBuilder   tb   = NetHelpers.DefineGlobalType("$DelegateHelper");
            FieldBuilder  meth = tb.DefineField("meth", typeof(ILuaValue), FieldAttributes.Private);
            FieldBuilder  E    = tb.DefineField("E", typeof(ILuaEnvironment), FieldAttributes.Private);
            MethodBuilder mb   = NetHelpers.CloneMethod(tb, "Do", delegateMethod);
            ILGenerator   gen  = mb.GetILGenerator();

            // loc = new object[{args.Length}];
            LocalBuilder loc = gen.CreateArray(typeof(object), args.Length);

            for (int i = 0; i < args.Length; i++)
            {
                // loc[i] = arg_{i+1};
                gen.Emit(OpCodes.Ldloc, loc);
                gen.Emit(OpCodes.Ldc_I4, i);
                gen.Emit(OpCodes.Ldarg, i + 1);
                if (args[i].ParameterType.IsValueType)
                {
                    gen.Emit(OpCodes.Box, args[i].ParameterType);
                }
                gen.Emit(OpCodes.Stelem, typeof(object));
            }

            // ILuaMultiValue methodArgs = E.Runtime.CreateMultiValueFromObj(loc);
            LocalBuilder methodArgs = gen.DeclareLocal(typeof(ILuaMultiValue));

            gen.Emit(OpCodes.Ldfld, E);
            gen.Emit(OpCodes.Callvirt, typeof(ILuaEnvironment).GetProperty(nameof(ILuaEnvironment.Runtime)).GetGetMethod());
            gen.Emit(OpCodes.Ldloc, loc);
            gen.Emit(OpCodes.Callvirt, typeof(ILuaRuntime).GetMethod(nameof(ILuaRuntime.CreateMultiValueFromObj)));
            gen.Emit(OpCodes.Stloc, methodArgs);

            // ret = this.meth.Invoke(LuaNil.Nil, false, -1, methodArgs)
            LocalBuilder ret = gen.DeclareLocal(typeof(ILuaMultiValue));

            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Ldfld, meth);
            gen.Emit(OpCodes.Ldnull);
            gen.Emit(OpCodes.Ldfld, typeof(LuaNil).GetField(nameof(LuaNil.Nil), BindingFlags.Static | BindingFlags.Public));
            gen.Emit(OpCodes.Ldc_I4_0);
            gen.Emit(OpCodes.Ldc_I4_M1);
            gen.Emit(OpCodes.Ldloc, methodArgs);
            gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetMethod(nameof(ILuaValue.Invoke)));
            gen.Emit(OpCodes.Stloc, ret);

            // Store any by-ref parameters in the arguments
            for (int i = 0; i < args.Length; i++)
            {
                if (args[i].IsOut)
                {
                    // arg_{i+1} = methodArgs[{i}].As<T>();
                    gen.Emit(OpCodes.Ldloc, methodArgs);
                    gen.Emit(OpCodes.Ldc_I4, i);
                    gen.Emit(OpCodes.Callvirt, typeof(ILuaMultiValue).GetMethod("get_Item"));
                    gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetMethod(nameof(ILuaValue.As))
                             .MakeGenericMethod(args[i].ParameterType));
                    gen.Emit(OpCodes.Starg, i + 1);
                }
            }

            // return ret.As<{info.Return}>();
            if (delegateMethod.ReturnType != null && delegateMethod.ReturnType != typeof(void))
            {
                gen.Emit(OpCodes.Ldloc, ret);
                gen.Emit(OpCodes.Callvirt, typeof(ILuaValue).GetMethod(nameof(ILuaValue.As))
                         .MakeGenericMethod(delegateMethod.ReturnType));
            }
            gen.Emit(OpCodes.Ret);

            //// public <>_type_(ILuaEnvironment E, ILuaValue method)
            ConstructorBuilder cb = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard,
                                                         new[] { typeof(ILuaEnvironment), typeof(ILuaValue) });

            gen = cb.GetILGenerator();

            // base();
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));

            // this.E = E;
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Ldarg_1);
            gen.Emit(OpCodes.Stfld, E);

            // this.meth = method;
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Ldarg_2);
            gen.Emit(OpCodes.Stfld, meth);
            gen.Emit(OpCodes.Ret);

            return(tb.CreateType());
        }