diff --git a/src/MagicOnion.Internal/GrpcMethodHelper.cs b/src/MagicOnion.Internal/GrpcMethodHelper.cs index acbc6406b..3b71bef38 100644 --- a/src/MagicOnion.Internal/GrpcMethodHelper.cs +++ b/src/MagicOnion.Internal/GrpcMethodHelper.cs @@ -1,4 +1,4 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using Grpc.Core; using MagicOnion.Serialization; using MessagePack; @@ -9,7 +9,7 @@ internal static class GrpcMethodHelper { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TRaw ToRaw(T obj) - => (obj is ValueType) + => (typeof(T).IsValueType) ? (TRaw)(object)Box.Create(obj) : DangerousDummyNull.GetObjectOrDummyNull(Unsafe.As(ref obj)); diff --git a/tests/MagicOnion.Client.Tests/UnaryTest.cs b/tests/MagicOnion.Client.Tests/UnaryTest.cs index b0742519b..22cda1524 100644 --- a/tests/MagicOnion.Client.Tests/UnaryTest.cs +++ b/tests/MagicOnion.Client.Tests/UnaryTest.cs @@ -1,4 +1,4 @@ -using Grpc.Core; +using Grpc.Core; using MagicOnion.Internal; using NSubstitute; @@ -465,6 +465,61 @@ public async Task ThrowsResponse() _ = callInvokerMock.ReceivedWithAnyArgs().AsyncUnaryCall(default(Method, Box>)!, default, default, default!); } + [Fact] + public async Task Issue1021_AccessViolationException() + { + // Arrange + var callInvokerMock = Substitute.For(); + callInvokerMock.AsyncUnaryCall(Arg.Any, Box>>(), Arg.Any(), Arg.Any(), GrpcMethodHelper.ToRaw>(null)) + .Returns(new AsyncUnaryCall>(Task.FromResult(Box.Create(Nil.Default)), Task.FromResult(Metadata.Empty), () => Status.DefaultSuccess, () => Metadata.Empty, () => { })); + + var a = callInvokerMock.AsyncUnaryCall(new Method, Box>(MethodType.Unary, "", "", new Marshaller>(x => [], x => Box.Create(null)), new Marshaller>(x => [], x => Box.Create(default))), "", new CallOptions(), GrpcMethodHelper.ToRaw>(null)); + + // Act & Assert + var client = MagicOnionClient.Create(callInvokerMock); + for (var i = 0; i < 100000; i++) + { + await client.OneNullableValueTypeParameterNonGenericReturnType(null); + } + } + + [Fact] + public async Task GrpcMethodHelper_NullableValueType() + { + for (var i = 0; i < 100000; i++) + { + var boxed = GrpcMethodHelper.ToRaw>(null); + var unboxed = GrpcMethodHelper.FromRaw, int?>(boxed); + Assert.NotNull(boxed); + Assert.Null(boxed.Value); + Assert.Null(unboxed); + } + } + + [Fact] + public async Task GrpcMethodHelper_ValueType() + { + for (var i = 0; i < 100000; i++) + { + var boxed = GrpcMethodHelper.ToRaw>(12345); + var unboxed = GrpcMethodHelper.FromRaw, int>(boxed); + Assert.Equal(12345, boxed.Value); + Assert.Equal(12345, unboxed); + } + } + + [Fact] + public async Task GrpcMethodHelper_RefType() + { + for (var i = 0; i < 100000; i++) + { + var boxed = GrpcMethodHelper.ToRaw(null); + var unboxed = GrpcMethodHelper.FromRaw(boxed); + Assert.NotNull(boxed); + Assert.Null(unboxed); + } + } + public interface IUnaryTestService : IService { UnaryResult ParameterlessReturnNil(); @@ -480,6 +535,7 @@ public interface IUnaryTestService : IService UnaryResult ParameterlessNonGenericReturnType(); UnaryResult OneRefTypeParameterNonGenericReturnType(string arg1); UnaryResult OneValueTypeParameterNonGenericReturnType(int arg1); + UnaryResult OneNullableValueTypeParameterNonGenericReturnType(int? arg1); UnaryResult TwoParametersNonGenericReturnType(int arg1, string arg2); }