예제 #1
0
        /// <summary>Returns an instruction to call the specified closure</summary>
        /// <typeparam name="T">The delegate type to emit</typeparam>
        /// <param name="closure">The closure that defines the method to call</param>
        /// <returns>A <see cref="CodeInstruction"/> that calls the closure as a method</returns>
        ///
        public static CodeInstruction CallClosure <T>(T closure) where T : Delegate
        {
            if (closure.Method.IsStatic && closure.Target == null)
            {
                return(new CodeInstruction(OpCodes.Call, closure.Method));
            }

            var parameters    = closure.Method.GetParameters().Select(x => x.ParameterType).ToArray();
            var closureMethod = new DynamicMethodDefinition(closure.Method.Name, closure.Method.ReturnType, parameters);

            var il         = closureMethod.GetILGenerator();
            var targetType = closure.Target.GetType();

            var preserveContext = closure.Target != null && targetType.GetFields().Any(x => !x.IsStatic);

            if (preserveContext)
            {
                var n = State.closureCache.Count;
                State.closureCache[n] = closure;
                il.Emit(OpCodes.Ldsfld, AccessTools.Field(typeof(Transpilers), nameof(State.closureCache)));
                il.Emit(OpCodes.Ldc_I4, n);
                il.Emit(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Dictionary <int, Delegate>), "Item"));
            }
            else
            {
                if (closure.Target == null)
                {
                    il.Emit(OpCodes.Ldnull);
                }
                else
                {
                    il.Emit(OpCodes.Newobj, AccessTools.FirstConstructor(targetType, x => x.IsStatic == false && x.GetParameters().Length == 0));
                }

                il.Emit(OpCodes.Ldftn, closure.Method);
                il.Emit(OpCodes.Newobj, AccessTools.Constructor(typeof(T), new[] { typeof(object), typeof(IntPtr) }));
            }

            for (var i = 0; i < parameters.Length; i++)
            {
                il.Emit(OpCodes.Ldarg, i);
            }

            il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(T), nameof(Action.Invoke)));
            il.Emit(OpCodes.Ret);

            return(new CodeInstruction(OpCodes.Call, closureMethod.Generate()));
        }