/// <summary> /// Returns an instruction to call the specified delegate. /// </summary> /// <typeparam name="T">The delegate type to emit.</typeparam> /// <param name="action">The delegate to emit.</param> /// <returns>The instruction to </returns> public static CodeInstruction EmitDelegate <T>(T action) where T : Delegate { if (action.Method.IsStatic && action.Target == null) { return(new CodeInstruction(OpCodes.Call, action.Method)); } var paramTypes = action.Method.GetParameters().Select(x => x.ParameterType).ToArray(); var dynamicMethod = new DynamicMethodDefinition(action.Method.Name, action.Method.ReturnType, paramTypes); var il = dynamicMethod.GetILGenerator(); var targetType = action.Target.GetType(); var preserveContext = action.Target != null && targetType.GetFields().Any(x => !x.IsStatic); if (preserveContext) { var currentDelegateCounter = _delegateCounter++; _delegateCache[currentDelegateCounter] = action; var cacheField = AccessTools.Field(typeof(Transpilers), nameof(_delegateCache)); var getMethod = AccessTools.Method(typeof(Dictionary <int, Delegate>), "get_Item"); il.Emit(OpCodes.Ldsfld, cacheField); il.Emit(OpCodes.Ldc_I4, currentDelegateCounter); il.Emit(OpCodes.Callvirt, getMethod); } else { if (action.Target == null) { il.Emit(OpCodes.Ldnull); } else { il.Emit(OpCodes.Newobj, AccessTools.FirstConstructor(targetType, x => x.GetParameters().Length == 0 && !x.IsStatic)); } il.Emit(OpCodes.Ldftn, action.Method); il.Emit(OpCodes.Newobj, AccessTools.Constructor(typeof(T), new[] { typeof(object), typeof(IntPtr) })); } for (var i = 0; i < paramTypes.Length; i++) { il.Emit(OpCodes.Ldarg_S, (short)i); } il.Emit(OpCodes.Callvirt, typeof(T).GetMethod("Invoke")); il.Emit(OpCodes.Ret); return(new CodeInstruction(OpCodes.Call, dynamicMethod.Generate())); }
/// <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())); }