diff --git a/src/MagicOnion/Server/Hubs/StreamingHubHandler.cs b/src/MagicOnion/Server/Hubs/StreamingHubHandler.cs index a9d9e46c0..98d3dbd74 100644 --- a/src/MagicOnion/Server/Hubs/StreamingHubHandler.cs +++ b/src/MagicOnion/Server/Hubs/StreamingHubHandler.cs @@ -167,8 +167,7 @@ Func BuildMethodBodyWithFilter(Func newFilter.Invoke(ctx, next_); + next = new InvokeHelper>(newFilter.Invoke, next).GetDelegate(); } return next; diff --git a/src/MagicOnion/Server/InvokeHelper.cs b/src/MagicOnion/Server/InvokeHelper.cs new file mode 100644 index 000000000..18dc9f673 --- /dev/null +++ b/src/MagicOnion/Server/InvokeHelper.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace MagicOnion.Server +{ + internal class InvokeHelper + where TDelegate : Delegate + { + public Func Invoke; + public TDelegate Next; + + private static readonly Func, TDelegate> InvokeNextFactory; + + static InvokeHelper() + { + var fieldInvoke = typeof(InvokeHelper).GetField("Invoke"); + var fieldNext = typeof(InvokeHelper).GetField("Next"); + var methodInvoke = typeof(Func).GetMethod("Invoke"); + + if (RuntimeInformation.FrameworkDescription.StartsWith(".NET Core 4")) /* .NET Core 2.x returns ".NET Core 4.x.y.z" */ + { + // HACK: If the app is running on .NET Core 2.2 or earlier, the runtime hides dynamic method in the stack trace. + var method = new DynamicMethod("InvokeNext", typeof(ValueTask), new[] { typeof(InvokeHelper), typeof(TArg1) }, restrictedSkipVisibility: true); + { + var il = method.GetILGenerator(); + + // invoke = arg0.Invoke; + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, fieldInvoke); + + // next = arg0.Next; + // return invoke(arg1, next); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, fieldNext); + il.Emit(OpCodes.Callvirt, methodInvoke); + il.Emit(OpCodes.Ret); + } + + InvokeNextFactory = (helper) => (TDelegate)method.CreateDelegate(typeof(TDelegate), helper); + + } + else + { + // HACK: If the app is running on .NET Core 3.0 or later, the runtime hides `AggressiveInlining` method in the stack trace. (If the app is running on .NET Framework 4.x, This hack does not affect.) + // https://github.com/dotnet/coreclr/blob/release/3.0/src/System.Private.CoreLib/shared/System/Diagnostics/StackTrace.cs#L343-L350 + InvokeNextFactory = (helper) => + { + var invokeNext = new Func(helper.InvokeNext); + return (TDelegate)Delegate.CreateDelegate(typeof(TDelegate), invokeNext.Target, invokeNext.Method); + }; + } + } + + public InvokeHelper(Func invoke, TDelegate next) + { + Invoke = invoke; + Next = next; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DebuggerHidden] + private ValueTask InvokeNext(TArg1 arg1) => Invoke(arg1, Next); + + public TDelegate GetDelegate() => InvokeNextFactory(this); + } +} diff --git a/src/MagicOnion/Server/MethodHandler.cs b/src/MagicOnion/Server/MethodHandler.cs index 23fb29608..507fc0157 100644 --- a/src/MagicOnion/Server/MethodHandler.cs +++ b/src/MagicOnion/Server/MethodHandler.cs @@ -292,8 +292,7 @@ Func BuildMethodBodyWithFilter(Func newFilter.Invoke(ctx, next_); + next = new InvokeHelper>(newFilter.Invoke, next).GetDelegate(); } return next;