diff --git a/samples/ChatApp/ChatApp.Console/ChatApp.Console.csproj b/samples/ChatApp/ChatApp.Console/ChatApp.Console.csproj index aafa3e639..0dc2bb1c2 100644 --- a/samples/ChatApp/ChatApp.Console/ChatApp.Console.csproj +++ b/samples/ChatApp/ChatApp.Console/ChatApp.Console.csproj @@ -5,8 +5,14 @@ net8.0 enable enable + true + true + + + + diff --git a/samples/ChatApp/ChatApp.Console/Program.cs b/samples/ChatApp/ChatApp.Console/Program.cs index 71c1c4429..e6baae822 100644 --- a/samples/ChatApp/ChatApp.Console/Program.cs +++ b/samples/ChatApp/ChatApp.Console/Program.cs @@ -1,12 +1,26 @@ // See https://aka.ms/new-console-template for more information +using System.Runtime.CompilerServices; using ChatApp.Shared.Hubs; using ChatApp.Shared.MessagePackObjects; using Grpc.Net.Client; using MagicOnion.Client; +using MessagePack; +using MessagePack.Resolvers; -var channel = GrpcChannel.ForAddress("http://localhost:5000"); +if (!RuntimeFeature.IsDynamicCodeSupported) +{ + // Running on Native AOT + StaticCompositeResolver.Instance.Register( + BuiltinResolver.Instance, + PrimitiveObjectResolver.Instance, + MagicOnionGeneratedClientInitializer.Resolver, + MessagePack.Resolvers.GeneratedResolver.Instance + ); + MessagePackSerializer.DefaultOptions = MessagePackSerializer.DefaultOptions.WithResolver(StaticCompositeResolver.Instance); +} +var channel = GrpcChannel.ForAddress("http://localhost:5000"); var sessionId = Guid.NewGuid(); Console.WriteLine("Connecting..."); var hub = await StreamingHubClient.ConnectAsync(channel, new ChatHubReceiver(sessionId)); @@ -30,7 +44,6 @@ [MagicOnionClientGeneration(typeof(IChatHub))] partial class MagicOnionGeneratedClientInitializer; - class ChatHubReceiver(Guid sessionId) : IChatHubReceiver { public void OnJoin(string name) diff --git a/src/MagicOnion.Abstractions/Internal/Box.cs b/src/MagicOnion.Abstractions/Internal/Box.cs index 3a0acc1a4..63b6db870 100644 --- a/src/MagicOnion.Abstractions/Internal/Box.cs +++ b/src/MagicOnion.Abstractions/Internal/Box.cs @@ -18,21 +18,21 @@ internal Box(T value) Value = value; } - public bool Equals(Box other) + public bool Equals(Box? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return EqualityComparer.Default.Equals(Value, other.Value); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return ReferenceEquals(this, obj) || obj is Box other && Equals(other); } public override int GetHashCode() { - return EqualityComparer.Default.GetHashCode(Value); + return EqualityComparer.Default.GetHashCode(Value!); } public static bool operator ==(Box valueA, Box valueB) diff --git a/src/MagicOnion.Abstractions/MagicOnion.Abstractions.csproj b/src/MagicOnion.Abstractions/MagicOnion.Abstractions.csproj index 748f240b8..bc66af320 100644 --- a/src/MagicOnion.Abstractions/MagicOnion.Abstractions.csproj +++ b/src/MagicOnion.Abstractions/MagicOnion.Abstractions.csproj @@ -1,10 +1,11 @@ - netstandard2.0 + netstandard2.0;net8.0 $(_LangVersionUnityBaseline) enable + true MagicOnion.Abstractions MagicOnion interfaces and abstractions for server and client. diff --git a/src/MagicOnion.Abstractions/PublicAPI.Shipped.txt b/src/MagicOnion.Abstractions/PublicAPI.Shipped.txt index 31e7e16d6..c7261b555 100644 --- a/src/MagicOnion.Abstractions/PublicAPI.Shipped.txt +++ b/src/MagicOnion.Abstractions/PublicAPI.Shipped.txt @@ -90,7 +90,7 @@ MagicOnion.IgnoreAttribute MagicOnion.IgnoreAttribute.IgnoreAttribute() -> void MagicOnion.Internal.Box MagicOnion.Internal.Box -MagicOnion.Internal.Box.Equals(MagicOnion.Internal.Box! other) -> bool +MagicOnion.Internal.Box.Equals(MagicOnion.Internal.Box? other) -> bool MagicOnion.Internal.IAsyncClientStreamingCallWrapper MagicOnion.Internal.IAsyncClientStreamingCallWrapper.RequestStream.get -> Grpc.Core.IClientStreamWriter! MagicOnion.Internal.IAsyncClientStreamingCallWrapper.ResponseAsync.get -> System.Threading.Tasks.Task! @@ -155,7 +155,7 @@ MagicOnion.UnaryResult.UnaryResult() -> void MagicOnion.UnaryResult.UnaryResult(System.Threading.Tasks.Task!>! response) -> void MagicOnion.UnaryResult.UnaryResult(System.Threading.Tasks.Task! rawTaskValue) -> void MagicOnion.UnaryResult.UnaryResult(TResponse rawValue) -> void -override MagicOnion.Internal.Box.Equals(object! obj) -> bool +override MagicOnion.Internal.Box.Equals(object? obj) -> bool override MagicOnion.Internal.Box.GetHashCode() -> int readonly MagicOnion.DynamicArgumentTuple.Item1 -> T1 readonly MagicOnion.DynamicArgumentTuple.Item10 -> T10 diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/Internal/Box.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/Internal/Box.cs index 3a0acc1a4..63b6db870 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/Internal/Box.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Abstractions/Internal/Box.cs @@ -18,21 +18,21 @@ internal Box(T value) Value = value; } - public bool Equals(Box other) + public bool Equals(Box? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return EqualityComparer.Default.Equals(Value, other.Value); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return ReferenceEquals(this, obj) || obj is Box other && Equals(other); } public override int GetHashCode() { - return EqualityComparer.Default.GetHashCode(Value); + return EqualityComparer.Default.GetHashCode(Value!); } public static bool operator ==(Box valueA, Box valueB) diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicClientAssemblyHolder.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicClientAssemblyHolder.cs index 119ec5f69..3c0343c02 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicClientAssemblyHolder.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicClientAssemblyHolder.cs @@ -1,7 +1,9 @@ +using System.Diagnostics.CodeAnalysis; using MagicOnion.Internal.Reflection; namespace MagicOnion.Client.DynamicClient { + [RequiresUnreferencedCode(nameof(DynamicClientAssemblyHolder) + " is incompatible with trimming and Native AOT.")] #if ENABLE_SAVE_ASSEMBLY public #else diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs index 4e5193192..7be683746 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Reflection.Emit; -using System.Text; using Grpc.Core; using MagicOnion.Client.Internal; using MagicOnion.Serialization; @@ -20,6 +20,7 @@ protected static class KnownTypes } } + [RequiresUnreferencedCode(nameof(DynamicClientBuilder) + " is incompatible with trimming and Native AOT.")] internal class DynamicClientBuilder : DynamicClientBuilder where T : IService { diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicMagicOnionClientFactoryProvider.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicMagicOnionClientFactoryProvider.cs index fefc4c174..47a7d3d42 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicMagicOnionClientFactoryProvider.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicMagicOnionClientFactoryProvider.cs @@ -18,6 +18,7 @@ public bool TryGetFactory([NotNullWhen(true)] out MagicOnionClientFactoryDele /// /// Provides to get a MagicOnionClient factory of the specified service type. The provider is backed by DynamicMagicOnionClientBuilder. /// + [RequiresUnreferencedCode(nameof(DynamicMagicOnionClientFactoryProvider) + " is incompatible with trimming and Native AOT.")] public class DynamicMagicOnionClientFactoryProvider : IMagicOnionClientFactoryProvider { public static IMagicOnionClientFactoryProvider Instance { get; } = new DynamicMagicOnionClientFactoryProvider(); @@ -30,6 +31,7 @@ public bool TryGetFactory([NotNullWhen(true)] out MagicOnionClientFactoryDele return true; } + [RequiresUnreferencedCode(nameof(DynamicMagicOnionClientFactoryProvider) + "." + nameof(Cache) + " is incompatible with trimming and Native AOT.")] static class Cache where T : IService { public static readonly MagicOnionClientFactoryDelegate Factory diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs index 09d4d4355..9173e8f06 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs @@ -6,6 +6,7 @@ using MessagePack; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Reflection.Emit; @@ -14,6 +15,7 @@ namespace MagicOnion.Client.DynamicClient { + [RequiresUnreferencedCode(nameof(DynamicStreamingHubClientAssemblyHolder) + " is incompatible with trimming and Native AOT.")] #if ENABLE_SAVE_ASSEMBLY public #else @@ -41,6 +43,7 @@ public static AssemblyBuilder Save() #endif } + [RequiresUnreferencedCode(nameof(DynamicStreamingHubClientBuilder) + " is incompatible with trimming and Native AOT.")] #if ENABLE_SAVE_ASSEMBLY public #else @@ -709,6 +712,7 @@ static void DefineMethodsFireAndForget(TypeBuilder typeBuilder, Type interfaceTy } } + [RequiresUnreferencedCode(nameof(MethodInfoCache) + " is incompatible with trimming and Native AOT.")] static class MethodInfoCache { // ReSharper disable StaticMemberInGenericType diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientFactoryProvider.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientFactoryProvider.cs index 0f1d0c53a..d6ee35879 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientFactoryProvider.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientFactoryProvider.cs @@ -15,6 +15,7 @@ public bool TryGetFactory([NotNullWhen(true)] out Stre } } + [RequiresUnreferencedCode(nameof(DynamicStreamingHubClientFactoryProvider) + " is incompatible with trimming and Native AOT.")] public class DynamicStreamingHubClientFactoryProvider : IStreamingHubClientFactoryProvider { public static IStreamingHubClientFactoryProvider Instance { get; } = new DynamicStreamingHubClientFactoryProvider(); @@ -27,6 +28,7 @@ public bool TryGetFactory([NotNullWhen(true)] out Stre return true; } + [RequiresUnreferencedCode(nameof(DynamicStreamingHubClientFactoryProvider) + "." + nameof(Cache) + " is incompatible with trimming and Native AOT.")] static class Cache where TStreamingHub : IStreamingHub { public static readonly StreamingHubClientFactoryDelegate Factory diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs index 74499020b..80a85c9b3 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; -using Grpc.Core; using MagicOnion.Client.Internal; namespace MagicOnion.Client.DynamicClient { + [RequiresUnreferencedCode(nameof(RawMethodInvokerTypes) + " is incompatible with trimming and Native AOT.")] internal static class RawMethodInvokerTypes { static readonly MethodInfo create_RefType_RefType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_RefType_RefType), BindingFlags.Static | BindingFlags.Public)!; diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs index 94b8c361d..8e4519d22 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Threading.Tasks; @@ -9,6 +10,7 @@ namespace MagicOnion.Client.DynamicClient { + [RequiresUnreferencedCode(nameof(ServiceClientDefinition) + " is incompatible with trimming and Native AOT.")] internal class ServiceClientDefinition { public Type ServiceInterfaceType { get; } @@ -20,6 +22,7 @@ public ServiceClientDefinition(Type serviceInterfaceType, IReadOnlyList() return new ServiceClientDefinition(typeof(T), GetServiceMethods(typeof(T))); } - private static IReadOnlyList GetServiceMethods(Type serviceType) + static IReadOnlyList GetServiceMethods(Type serviceType) { return serviceType .GetInterfaces() @@ -209,7 +212,7 @@ private static IReadOnlyList GetServiceMethods(Type /// /// /// - private static Type GetRequestTypeFromMethod(MethodInfo methodInfo) + static Type GetRequestTypeFromMethod(MethodInfo methodInfo) { var parameterTypes = methodInfo.GetParameters().Select(x => x.ParameterType).ToArray(); switch (parameterTypes.Length) diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/BroadcasterHelper.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/BroadcasterHelper.cs index eab0c5c9d..f87114cb5 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/BroadcasterHelper.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/BroadcasterHelper.cs @@ -1,11 +1,13 @@ using MagicOnion.Server.Hubs; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; namespace MagicOnion.Internal { + [RequiresUnreferencedCode("BroadcastHelper is incompatible with trimming.")] internal static class BroadcasterHelper { internal static Type[] DynamicArgumentTupleTypes { get; } = typeof(DynamicArgumentTuple<,>) diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/MagicOnionMarshallers.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/MagicOnionMarshallers.cs index af9233361..33360672c 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/MagicOnionMarshallers.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/MagicOnionMarshallers.cs @@ -2,6 +2,7 @@ using MessagePack; using System; using System.Buffers; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -10,13 +11,7 @@ namespace MagicOnion.Internal // invoke from dynamic methods so must be public internal static class MagicOnionMarshallers { - static readonly Type[] dynamicArgumentTupleTypes = typeof(DynamicArgumentTuple<,>).GetTypeInfo().Assembly - .GetTypes() - .Where(x => x.Name.StartsWith("DynamicArgumentTuple") && !x.Name.Contains("Formatter")) - .OrderBy(x => x.GetGenericArguments().Length) - .ToArray(); - - internal static Marshaller StreamingHubMarshaller { get; } = new( + public static Marshaller StreamingHubMarshaller { get; } = new( serializer: static (payload, context) => { context.SetPayloadLength(payload.Length); @@ -32,7 +27,8 @@ internal static class MagicOnionMarshallers } ); - internal static Type CreateRequestType(ParameterInfo[] parameters) + [RequiresUnreferencedCode(nameof(MagicOnionMarshallers) + "." + nameof(CreateRequestType) + " is incompatible with trimming and Native AOT.")] + public static Type CreateRequestType(ParameterInfo[] parameters) { if (parameters.Length == 0) { @@ -50,18 +46,29 @@ internal static Type CreateRequestType(ParameterInfo[] parameters) else { // start from T2 - var tupleTypeBase = dynamicArgumentTupleTypes[parameters.Length - 2]; + var tupleTypeBase = DynamicArgumentTupleTypesCache.Types[parameters.Length - 2]; var t = tupleTypeBase.MakeGenericType(parameters.Select(x => x.ParameterType).ToArray()); return t; } } + [RequiresUnreferencedCode(nameof(MagicOnionMarshallers) + "." + nameof(InstantiateDynamicArgumentTuple) + " is incompatible with trimming and Native AOT.")] public static object InstantiateDynamicArgumentTuple(Type[] typeParameters, object[] arguments) { // start from T2 - var tupleTypeBase = dynamicArgumentTupleTypes[arguments.Length - 2]; + var tupleTypeBase = DynamicArgumentTupleTypesCache.Types[arguments.Length - 2]; return Activator.CreateInstance(tupleTypeBase.MakeGenericType(typeParameters), arguments)!; } + + [RequiresUnreferencedCode(nameof(DynamicArgumentTupleTypesCache) + " is incompatible with trimming and Native AOT.")] + static class DynamicArgumentTupleTypesCache + { + public static readonly Type[] Types = typeof(DynamicArgumentTuple<,>).GetTypeInfo().Assembly + .GetTypes() + .Where(x => x.Name.StartsWith("DynamicArgumentTuple") && !x.Name.Contains("Formatter")) + .OrderBy(x => x.GetGenericArguments().Length) + .ToArray(); + } } } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil.meta new file mode 100644 index 000000000..1fcc953fc --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d0740739ff0548f42abb6937a771bcfd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil/RequiresUnreferencedCodeAttribute.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil/RequiresUnreferencedCodeAttribute.cs new file mode 100644 index 000000000..a59a86c95 --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil/RequiresUnreferencedCodeAttribute.cs @@ -0,0 +1,20 @@ +#if !NET6_0_OR_GREATER +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] + internal class RequiresUnreferencedCodeAttribute : Attribute + { + public string Message { get; } + public string? Url { get; } + + public RequiresUnreferencedCodeAttribute(string message) + { + Message = message; + } + } +} +#endif diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil/RequiresUnreferencedCodeAttribute.cs.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil/RequiresUnreferencedCodeAttribute.cs.meta new file mode 100644 index 000000000..b87290575 --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil/RequiresUnreferencedCodeAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1d0a558474e947549a1d7c4f4455c44c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil/UnconditionalSuppressMessageAttribute.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil/UnconditionalSuppressMessageAttribute.cs new file mode 100644 index 000000000..4e5dff129 --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil/UnconditionalSuppressMessageAttribute.cs @@ -0,0 +1,25 @@ +#if !NET6_0_OR_GREATER +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] + internal class UnconditionalSuppressMessageAttribute : Attribute + { + public string Category { get; } + public string CheckId { get; } + public string? Scope { get; set; } + public string? Target { get; set; } + public string? Justification { get; set; } + public string? MessageId { get; set; } + + public UnconditionalSuppressMessageAttribute(string category, string checkId) + { + Category = category; + CheckId = checkId; + } + } +} +#endif diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil/UnconditionalSuppressMessageAttribute.cs.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil/UnconditionalSuppressMessageAttribute.cs.meta new file mode 100644 index 000000000..5a26a820d --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Polyfil/UnconditionalSuppressMessageAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 337e745c02c4896408b607de73abe11b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Reflection/DynamicAssembly.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Reflection/DynamicAssembly.cs index 83972a8c8..6914ec17f 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Reflection/DynamicAssembly.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal.Shared/Reflection/DynamicAssembly.cs @@ -1,10 +1,12 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; namespace MagicOnion.Internal.Reflection { + [RequiresUnreferencedCode(nameof(DynamicAssembly) + " is incompatible with trimming and Native AOT.")] #if ENABLE_SAVE_ASSEMBLY public #else diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal/DictionaryExtensions.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal/DictionaryExtensions.cs index ca34940c8..6ff83e76a 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal/DictionaryExtensions.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/Internal/DictionaryExtensions.cs @@ -1,3 +1,4 @@ +#if NETSTANDARD2_0 using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -8,7 +9,6 @@ namespace System.Collections.Generic { internal static class DictionaryExtensions { -#if NETSTANDARD2_0 public static bool Remove(this IDictionary dict, TKey key, [NotNullWhen(true)] out TValue? value) { if (dict.TryGetValue(key, out var v)) @@ -21,6 +21,6 @@ public static bool Remove(this IDictionary dict, TKe value = default; return false; } -#endif } } +#endif diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnionClientFactoryProvider.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnionClientFactoryProvider.cs index 345cb1f08..10f1365b1 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnionClientFactoryProvider.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/MagicOnionClientFactoryProvider.cs @@ -9,6 +9,7 @@ namespace MagicOnion.Client /// /// Provides to get a MagicOnionClient factory of the specified service type. /// + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "ClientFactoryProvider is resolved at runtime.")] public static class MagicOnionClientFactoryProvider { /// diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClientFactoryProvider.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClientFactoryProvider.cs index 50f7be83c..63ef1ba01 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClientFactoryProvider.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Client/StreamingHubClientFactoryProvider.cs @@ -9,6 +9,7 @@ namespace MagicOnion.Client /// /// Provides to get a StreamingHubClient factory of the specified service type. /// + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "ClientFactoryProvider is resolved at runtime.")] public static class StreamingHubClientFactoryProvider { /// diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Serialization.MessagePack/MessagePackMagicOnionSerializerProvider.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Serialization.MessagePack/MessagePackMagicOnionSerializerProvider.cs index 76e0e9411..298bb14cc 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Serialization.MessagePack/MessagePackMagicOnionSerializerProvider.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/MagicOnion.Serialization.MessagePack/MessagePackMagicOnionSerializerProvider.cs @@ -2,6 +2,7 @@ using System.Buffers; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Grpc.Core; using MessagePack; using MessagePack.Formatters; @@ -42,26 +43,43 @@ public MessagePackMagicOnionSerializerProvider WithEnableFallback(bool enableFal return new MessagePackMagicOnionSerializerProvider(SerializerOptions, enableFallback); } + +#if NET6_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "ClientFactoryProvider is resolved at runtime.")] +#endif public IMagicOnionSerializer Create(MethodType methodType, MethodInfo? methodInfo) { var serializerOptions = EnableFallback && methodInfo != null ? WrapFallbackResolverIfNeeded(methodInfo.GetParameters()) : SerializerOptions; return new MessagePackMagicOnionSerializer(serializerOptions); } - static readonly Type[] dynamicArgumentTupleTypes = typeof(DynamicArgumentTuple<,>).GetTypeInfo().Assembly - .GetTypes() - .Where(x => x.Name.StartsWith("DynamicArgumentTuple") && !x.Name.Contains("Formatter")) - .OrderBy(x => x.GetGenericArguments().Length) - .ToArray(); +#if NET6_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(nameof(DynamicArgumentTupleTypeCache) + " is incompatible with trimming and Native AOT.")] +#endif + static class DynamicArgumentTupleTypeCache + { + public static readonly Type[] Types = typeof(DynamicArgumentTuple<,>).GetTypeInfo().Assembly + .GetTypes() + .Where(x => x.Name.StartsWith("DynamicArgumentTuple") && !x.Name.Contains("Formatter")) + .OrderBy(x => x.GetGenericArguments().Length) + .ToArray(); + + public static readonly Type[] FormatterTypes = typeof(DynamicArgumentTupleFormatter<,,>).GetTypeInfo().Assembly + .GetTypes() + .Where(x => x.Name.StartsWith("DynamicArgumentTupleFormatter")) + .OrderBy(x => x.GetGenericArguments().Length) + .ToArray(); + } - static readonly Type[] dynamicArgumentTupleFormatterTypes = typeof(DynamicArgumentTupleFormatter<,,>).GetTypeInfo().Assembly - .GetTypes() - .Where(x => x.Name.StartsWith("DynamicArgumentTupleFormatter")) - .OrderBy(x => x.GetGenericArguments().Length) - .ToArray(); +#if NET6_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(nameof(DynamicArgumentTupleTypeCache) + " is incompatible with trimming and Native AOT.")] +#endif MessagePackSerializerOptions WrapFallbackResolverIfNeeded(ParameterInfo[] parameters) { +#if !NETSTANDARD2_0 + if (!RuntimeFeature.IsDynamicCodeSupported) throw new NotSupportedException("When running with AOT runtime, DynamicArgumentTupleFormatter does not support fallback resolver fro optional parameters."); +#endif // If the method has no parameter or one parameter, we don't need to create fallback resolver for optional parameters. if (parameters.Length < 2) { @@ -69,8 +87,8 @@ MessagePackSerializerOptions WrapFallbackResolverIfNeeded(ParameterInfo[] parame } // start from T2 - var tupleTypeBase = dynamicArgumentTupleTypes[parameters.Length - 2]; - var formatterTypeBase = dynamicArgumentTupleFormatterTypes[parameters.Length - 2]; + var tupleTypeBase = DynamicArgumentTupleTypeCache.Types[parameters.Length - 2]; + var formatterTypeBase = DynamicArgumentTupleTypeCache.FormatterTypes[parameters.Length - 2]; var t = tupleTypeBase.MakeGenericType(parameters.Select(x => x.ParameterType).ToArray()); var formatterType = formatterTypeBase.MakeGenericType(parameters.Select(x => x.ParameterType).ToArray()); diff --git a/src/MagicOnion.Client/DynamicClient/DynamicClientAssemblyHolder.cs b/src/MagicOnion.Client/DynamicClient/DynamicClientAssemblyHolder.cs index 119ec5f69..3c0343c02 100644 --- a/src/MagicOnion.Client/DynamicClient/DynamicClientAssemblyHolder.cs +++ b/src/MagicOnion.Client/DynamicClient/DynamicClientAssemblyHolder.cs @@ -1,7 +1,9 @@ +using System.Diagnostics.CodeAnalysis; using MagicOnion.Internal.Reflection; namespace MagicOnion.Client.DynamicClient { + [RequiresUnreferencedCode(nameof(DynamicClientAssemblyHolder) + " is incompatible with trimming and Native AOT.")] #if ENABLE_SAVE_ASSEMBLY public #else diff --git a/src/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs b/src/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs index 4e5193192..7be683746 100644 --- a/src/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs +++ b/src/MagicOnion.Client/DynamicClient/DynamicClientBuilder.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Reflection.Emit; -using System.Text; using Grpc.Core; using MagicOnion.Client.Internal; using MagicOnion.Serialization; @@ -20,6 +20,7 @@ protected static class KnownTypes } } + [RequiresUnreferencedCode(nameof(DynamicClientBuilder) + " is incompatible with trimming and Native AOT.")] internal class DynamicClientBuilder : DynamicClientBuilder where T : IService { diff --git a/src/MagicOnion.Client/DynamicClient/DynamicMagicOnionClientFactoryProvider.cs b/src/MagicOnion.Client/DynamicClient/DynamicMagicOnionClientFactoryProvider.cs index fefc4c174..47a7d3d42 100644 --- a/src/MagicOnion.Client/DynamicClient/DynamicMagicOnionClientFactoryProvider.cs +++ b/src/MagicOnion.Client/DynamicClient/DynamicMagicOnionClientFactoryProvider.cs @@ -18,6 +18,7 @@ public bool TryGetFactory([NotNullWhen(true)] out MagicOnionClientFactoryDele /// /// Provides to get a MagicOnionClient factory of the specified service type. The provider is backed by DynamicMagicOnionClientBuilder. /// + [RequiresUnreferencedCode(nameof(DynamicMagicOnionClientFactoryProvider) + " is incompatible with trimming and Native AOT.")] public class DynamicMagicOnionClientFactoryProvider : IMagicOnionClientFactoryProvider { public static IMagicOnionClientFactoryProvider Instance { get; } = new DynamicMagicOnionClientFactoryProvider(); @@ -30,6 +31,7 @@ public bool TryGetFactory([NotNullWhen(true)] out MagicOnionClientFactoryDele return true; } + [RequiresUnreferencedCode(nameof(DynamicMagicOnionClientFactoryProvider) + "." + nameof(Cache) + " is incompatible with trimming and Native AOT.")] static class Cache where T : IService { public static readonly MagicOnionClientFactoryDelegate Factory diff --git a/src/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs b/src/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs index 09d4d4355..9173e8f06 100644 --- a/src/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs +++ b/src/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientBuilder.cs @@ -6,6 +6,7 @@ using MessagePack; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Reflection.Emit; @@ -14,6 +15,7 @@ namespace MagicOnion.Client.DynamicClient { + [RequiresUnreferencedCode(nameof(DynamicStreamingHubClientAssemblyHolder) + " is incompatible with trimming and Native AOT.")] #if ENABLE_SAVE_ASSEMBLY public #else @@ -41,6 +43,7 @@ public static AssemblyBuilder Save() #endif } + [RequiresUnreferencedCode(nameof(DynamicStreamingHubClientBuilder) + " is incompatible with trimming and Native AOT.")] #if ENABLE_SAVE_ASSEMBLY public #else @@ -709,6 +712,7 @@ static void DefineMethodsFireAndForget(TypeBuilder typeBuilder, Type interfaceTy } } + [RequiresUnreferencedCode(nameof(MethodInfoCache) + " is incompatible with trimming and Native AOT.")] static class MethodInfoCache { // ReSharper disable StaticMemberInGenericType diff --git a/src/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientFactoryProvider.cs b/src/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientFactoryProvider.cs index 0f1d0c53a..d6ee35879 100644 --- a/src/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientFactoryProvider.cs +++ b/src/MagicOnion.Client/DynamicClient/DynamicStreamingHubClientFactoryProvider.cs @@ -15,6 +15,7 @@ public bool TryGetFactory([NotNullWhen(true)] out Stre } } + [RequiresUnreferencedCode(nameof(DynamicStreamingHubClientFactoryProvider) + " is incompatible with trimming and Native AOT.")] public class DynamicStreamingHubClientFactoryProvider : IStreamingHubClientFactoryProvider { public static IStreamingHubClientFactoryProvider Instance { get; } = new DynamicStreamingHubClientFactoryProvider(); @@ -27,6 +28,7 @@ public bool TryGetFactory([NotNullWhen(true)] out Stre return true; } + [RequiresUnreferencedCode(nameof(DynamicStreamingHubClientFactoryProvider) + "." + nameof(Cache) + " is incompatible with trimming and Native AOT.")] static class Cache where TStreamingHub : IStreamingHub { public static readonly StreamingHubClientFactoryDelegate Factory diff --git a/src/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs b/src/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs index 74499020b..80a85c9b3 100644 --- a/src/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs +++ b/src/MagicOnion.Client/DynamicClient/RawMethodInvokerTypes.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; -using Grpc.Core; using MagicOnion.Client.Internal; namespace MagicOnion.Client.DynamicClient { + [RequiresUnreferencedCode(nameof(RawMethodInvokerTypes) + " is incompatible with trimming and Native AOT.")] internal static class RawMethodInvokerTypes { static readonly MethodInfo create_RefType_RefType = typeof(RawMethodInvoker).GetMethod(nameof(RawMethodInvoker.Create_RefType_RefType), BindingFlags.Static | BindingFlags.Public)!; diff --git a/src/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs b/src/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs index 94b8c361d..8e4519d22 100644 --- a/src/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs +++ b/src/MagicOnion.Client/DynamicClient/ServiceClientDefinition.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Threading.Tasks; @@ -9,6 +10,7 @@ namespace MagicOnion.Client.DynamicClient { + [RequiresUnreferencedCode(nameof(ServiceClientDefinition) + " is incompatible with trimming and Native AOT.")] internal class ServiceClientDefinition { public Type ServiceInterfaceType { get; } @@ -20,6 +22,7 @@ public ServiceClientDefinition(Type serviceInterfaceType, IReadOnlyList() return new ServiceClientDefinition(typeof(T), GetServiceMethods(typeof(T))); } - private static IReadOnlyList GetServiceMethods(Type serviceType) + static IReadOnlyList GetServiceMethods(Type serviceType) { return serviceType .GetInterfaces() @@ -209,7 +212,7 @@ private static IReadOnlyList GetServiceMethods(Type /// /// /// - private static Type GetRequestTypeFromMethod(MethodInfo methodInfo) + static Type GetRequestTypeFromMethod(MethodInfo methodInfo) { var parameterTypes = methodInfo.GetParameters().Select(x => x.ParameterType).ToArray(); switch (parameterTypes.Length) diff --git a/src/MagicOnion.Client/Internal/DictionaryExtensions.cs b/src/MagicOnion.Client/Internal/DictionaryExtensions.cs index ca34940c8..6ff83e76a 100644 --- a/src/MagicOnion.Client/Internal/DictionaryExtensions.cs +++ b/src/MagicOnion.Client/Internal/DictionaryExtensions.cs @@ -1,3 +1,4 @@ +#if NETSTANDARD2_0 using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -8,7 +9,6 @@ namespace System.Collections.Generic { internal static class DictionaryExtensions { -#if NETSTANDARD2_0 public static bool Remove(this IDictionary dict, TKey key, [NotNullWhen(true)] out TValue? value) { if (dict.TryGetValue(key, out var v)) @@ -21,6 +21,6 @@ public static bool Remove(this IDictionary dict, TKe value = default; return false; } -#endif } } +#endif diff --git a/src/MagicOnion.Client/MagicOnion.Client.csproj b/src/MagicOnion.Client/MagicOnion.Client.csproj index 4afd3dd60..b62cda061 100644 --- a/src/MagicOnion.Client/MagicOnion.Client.csproj +++ b/src/MagicOnion.Client/MagicOnion.Client.csproj @@ -5,6 +5,7 @@ $(_LangVersionUnityBaseline) enable + true MagicOnion.Client diff --git a/src/MagicOnion.Client/MagicOnionClientFactoryProvider.cs b/src/MagicOnion.Client/MagicOnionClientFactoryProvider.cs index 345cb1f08..10f1365b1 100644 --- a/src/MagicOnion.Client/MagicOnionClientFactoryProvider.cs +++ b/src/MagicOnion.Client/MagicOnionClientFactoryProvider.cs @@ -9,6 +9,7 @@ namespace MagicOnion.Client /// /// Provides to get a MagicOnionClient factory of the specified service type. /// + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "ClientFactoryProvider is resolved at runtime.")] public static class MagicOnionClientFactoryProvider { /// diff --git a/src/MagicOnion.Client/StreamingHubClientFactoryProvider.cs b/src/MagicOnion.Client/StreamingHubClientFactoryProvider.cs index 50f7be83c..63ef1ba01 100644 --- a/src/MagicOnion.Client/StreamingHubClientFactoryProvider.cs +++ b/src/MagicOnion.Client/StreamingHubClientFactoryProvider.cs @@ -9,6 +9,7 @@ namespace MagicOnion.Client /// /// Provides to get a StreamingHubClient factory of the specified service type. /// + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "ClientFactoryProvider is resolved at runtime.")] public static class StreamingHubClientFactoryProvider { /// diff --git a/src/MagicOnion.Internal/BroadcasterHelper.cs b/src/MagicOnion.Internal/BroadcasterHelper.cs index eab0c5c9d..f87114cb5 100644 --- a/src/MagicOnion.Internal/BroadcasterHelper.cs +++ b/src/MagicOnion.Internal/BroadcasterHelper.cs @@ -1,11 +1,13 @@ using MagicOnion.Server.Hubs; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; namespace MagicOnion.Internal { + [RequiresUnreferencedCode("BroadcastHelper is incompatible with trimming.")] internal static class BroadcasterHelper { internal static Type[] DynamicArgumentTupleTypes { get; } = typeof(DynamicArgumentTuple<,>) diff --git a/src/MagicOnion.Internal/MagicOnion.Internal.csproj b/src/MagicOnion.Internal/MagicOnion.Internal.csproj index e6ab71734..fc4b60c88 100644 --- a/src/MagicOnion.Internal/MagicOnion.Internal.csproj +++ b/src/MagicOnion.Internal/MagicOnion.Internal.csproj @@ -1,4 +1,4 @@ - + netstandard2.0;netstandard2.1;net6.0;net8.0 diff --git a/src/MagicOnion.Internal/MagicOnionMarshallers.cs b/src/MagicOnion.Internal/MagicOnionMarshallers.cs index af9233361..33360672c 100644 --- a/src/MagicOnion.Internal/MagicOnionMarshallers.cs +++ b/src/MagicOnion.Internal/MagicOnionMarshallers.cs @@ -2,6 +2,7 @@ using MessagePack; using System; using System.Buffers; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -10,13 +11,7 @@ namespace MagicOnion.Internal // invoke from dynamic methods so must be public internal static class MagicOnionMarshallers { - static readonly Type[] dynamicArgumentTupleTypes = typeof(DynamicArgumentTuple<,>).GetTypeInfo().Assembly - .GetTypes() - .Where(x => x.Name.StartsWith("DynamicArgumentTuple") && !x.Name.Contains("Formatter")) - .OrderBy(x => x.GetGenericArguments().Length) - .ToArray(); - - internal static Marshaller StreamingHubMarshaller { get; } = new( + public static Marshaller StreamingHubMarshaller { get; } = new( serializer: static (payload, context) => { context.SetPayloadLength(payload.Length); @@ -32,7 +27,8 @@ internal static class MagicOnionMarshallers } ); - internal static Type CreateRequestType(ParameterInfo[] parameters) + [RequiresUnreferencedCode(nameof(MagicOnionMarshallers) + "." + nameof(CreateRequestType) + " is incompatible with trimming and Native AOT.")] + public static Type CreateRequestType(ParameterInfo[] parameters) { if (parameters.Length == 0) { @@ -50,18 +46,29 @@ internal static Type CreateRequestType(ParameterInfo[] parameters) else { // start from T2 - var tupleTypeBase = dynamicArgumentTupleTypes[parameters.Length - 2]; + var tupleTypeBase = DynamicArgumentTupleTypesCache.Types[parameters.Length - 2]; var t = tupleTypeBase.MakeGenericType(parameters.Select(x => x.ParameterType).ToArray()); return t; } } + [RequiresUnreferencedCode(nameof(MagicOnionMarshallers) + "." + nameof(InstantiateDynamicArgumentTuple) + " is incompatible with trimming and Native AOT.")] public static object InstantiateDynamicArgumentTuple(Type[] typeParameters, object[] arguments) { // start from T2 - var tupleTypeBase = dynamicArgumentTupleTypes[arguments.Length - 2]; + var tupleTypeBase = DynamicArgumentTupleTypesCache.Types[arguments.Length - 2]; return Activator.CreateInstance(tupleTypeBase.MakeGenericType(typeParameters), arguments)!; } + + [RequiresUnreferencedCode(nameof(DynamicArgumentTupleTypesCache) + " is incompatible with trimming and Native AOT.")] + static class DynamicArgumentTupleTypesCache + { + public static readonly Type[] Types = typeof(DynamicArgumentTuple<,>).GetTypeInfo().Assembly + .GetTypes() + .Where(x => x.Name.StartsWith("DynamicArgumentTuple") && !x.Name.Contains("Formatter")) + .OrderBy(x => x.GetGenericArguments().Length) + .ToArray(); + } } } diff --git a/src/MagicOnion.Internal/Polyfil/RequiresUnreferencedCodeAttribute.cs b/src/MagicOnion.Internal/Polyfil/RequiresUnreferencedCodeAttribute.cs new file mode 100644 index 000000000..a59a86c95 --- /dev/null +++ b/src/MagicOnion.Internal/Polyfil/RequiresUnreferencedCodeAttribute.cs @@ -0,0 +1,20 @@ +#if !NET6_0_OR_GREATER +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] + internal class RequiresUnreferencedCodeAttribute : Attribute + { + public string Message { get; } + public string? Url { get; } + + public RequiresUnreferencedCodeAttribute(string message) + { + Message = message; + } + } +} +#endif diff --git a/src/MagicOnion.Internal/Polyfil/UnconditionalSuppressMessageAttribute.cs b/src/MagicOnion.Internal/Polyfil/UnconditionalSuppressMessageAttribute.cs new file mode 100644 index 000000000..4e5dff129 --- /dev/null +++ b/src/MagicOnion.Internal/Polyfil/UnconditionalSuppressMessageAttribute.cs @@ -0,0 +1,25 @@ +#if !NET6_0_OR_GREATER +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] + internal class UnconditionalSuppressMessageAttribute : Attribute + { + public string Category { get; } + public string CheckId { get; } + public string? Scope { get; set; } + public string? Target { get; set; } + public string? Justification { get; set; } + public string? MessageId { get; set; } + + public UnconditionalSuppressMessageAttribute(string category, string checkId) + { + Category = category; + CheckId = checkId; + } + } +} +#endif diff --git a/src/MagicOnion.Internal/Reflection/DynamicAssembly.cs b/src/MagicOnion.Internal/Reflection/DynamicAssembly.cs index 83972a8c8..6914ec17f 100644 --- a/src/MagicOnion.Internal/Reflection/DynamicAssembly.cs +++ b/src/MagicOnion.Internal/Reflection/DynamicAssembly.cs @@ -1,10 +1,12 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; namespace MagicOnion.Internal.Reflection { + [RequiresUnreferencedCode(nameof(DynamicAssembly) + " is incompatible with trimming and Native AOT.")] #if ENABLE_SAVE_ASSEMBLY public #else diff --git a/src/MagicOnion.Serialization.MessagePack/MagicOnion.Serialization.MessagePack.csproj b/src/MagicOnion.Serialization.MessagePack/MagicOnion.Serialization.MessagePack.csproj index 1f602aaa5..4ec87bb50 100644 --- a/src/MagicOnion.Serialization.MessagePack/MagicOnion.Serialization.MessagePack.csproj +++ b/src/MagicOnion.Serialization.MessagePack/MagicOnion.Serialization.MessagePack.csproj @@ -2,10 +2,11 @@ - netstandard2.0 + netstandard2.0;netstandard2.1;net8.0 enable enable + true MagicOnion.Serialization.MessagePack diff --git a/src/MagicOnion.Serialization.MessagePack/MessagePackMagicOnionSerializerProvider.cs b/src/MagicOnion.Serialization.MessagePack/MessagePackMagicOnionSerializerProvider.cs index 76e0e9411..298bb14cc 100644 --- a/src/MagicOnion.Serialization.MessagePack/MessagePackMagicOnionSerializerProvider.cs +++ b/src/MagicOnion.Serialization.MessagePack/MessagePackMagicOnionSerializerProvider.cs @@ -2,6 +2,7 @@ using System.Buffers; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Grpc.Core; using MessagePack; using MessagePack.Formatters; @@ -42,26 +43,43 @@ public MessagePackMagicOnionSerializerProvider WithEnableFallback(bool enableFal return new MessagePackMagicOnionSerializerProvider(SerializerOptions, enableFallback); } + +#if NET6_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "ClientFactoryProvider is resolved at runtime.")] +#endif public IMagicOnionSerializer Create(MethodType methodType, MethodInfo? methodInfo) { var serializerOptions = EnableFallback && methodInfo != null ? WrapFallbackResolverIfNeeded(methodInfo.GetParameters()) : SerializerOptions; return new MessagePackMagicOnionSerializer(serializerOptions); } - static readonly Type[] dynamicArgumentTupleTypes = typeof(DynamicArgumentTuple<,>).GetTypeInfo().Assembly - .GetTypes() - .Where(x => x.Name.StartsWith("DynamicArgumentTuple") && !x.Name.Contains("Formatter")) - .OrderBy(x => x.GetGenericArguments().Length) - .ToArray(); +#if NET6_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(nameof(DynamicArgumentTupleTypeCache) + " is incompatible with trimming and Native AOT.")] +#endif + static class DynamicArgumentTupleTypeCache + { + public static readonly Type[] Types = typeof(DynamicArgumentTuple<,>).GetTypeInfo().Assembly + .GetTypes() + .Where(x => x.Name.StartsWith("DynamicArgumentTuple") && !x.Name.Contains("Formatter")) + .OrderBy(x => x.GetGenericArguments().Length) + .ToArray(); + + public static readonly Type[] FormatterTypes = typeof(DynamicArgumentTupleFormatter<,,>).GetTypeInfo().Assembly + .GetTypes() + .Where(x => x.Name.StartsWith("DynamicArgumentTupleFormatter")) + .OrderBy(x => x.GetGenericArguments().Length) + .ToArray(); + } - static readonly Type[] dynamicArgumentTupleFormatterTypes = typeof(DynamicArgumentTupleFormatter<,,>).GetTypeInfo().Assembly - .GetTypes() - .Where(x => x.Name.StartsWith("DynamicArgumentTupleFormatter")) - .OrderBy(x => x.GetGenericArguments().Length) - .ToArray(); +#if NET6_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(nameof(DynamicArgumentTupleTypeCache) + " is incompatible with trimming and Native AOT.")] +#endif MessagePackSerializerOptions WrapFallbackResolverIfNeeded(ParameterInfo[] parameters) { +#if !NETSTANDARD2_0 + if (!RuntimeFeature.IsDynamicCodeSupported) throw new NotSupportedException("When running with AOT runtime, DynamicArgumentTupleFormatter does not support fallback resolver fro optional parameters."); +#endif // If the method has no parameter or one parameter, we don't need to create fallback resolver for optional parameters. if (parameters.Length < 2) { @@ -69,8 +87,8 @@ MessagePackSerializerOptions WrapFallbackResolverIfNeeded(ParameterInfo[] parame } // start from T2 - var tupleTypeBase = dynamicArgumentTupleTypes[parameters.Length - 2]; - var formatterTypeBase = dynamicArgumentTupleFormatterTypes[parameters.Length - 2]; + var tupleTypeBase = DynamicArgumentTupleTypeCache.Types[parameters.Length - 2]; + var formatterTypeBase = DynamicArgumentTupleTypeCache.FormatterTypes[parameters.Length - 2]; var t = tupleTypeBase.MakeGenericType(parameters.Select(x => x.ParameterType).ToArray()); var formatterType = formatterTypeBase.MakeGenericType(parameters.Select(x => x.ParameterType).ToArray()); diff --git a/tests/MagicOnion.Client.Tests/StreamingHubClientHeartbeatManagerTest.cs b/tests/MagicOnion.Client.Tests/StreamingHubClientHeartbeatManagerTest.cs index 16f00f3a1..8b705a8d5 100644 --- a/tests/MagicOnion.Client.Tests/StreamingHubClientHeartbeatManagerTest.cs +++ b/tests/MagicOnion.Client.Tests/StreamingHubClientHeartbeatManagerTest.cs @@ -281,9 +281,9 @@ public async Task Timeout_IntervalLongerThanTimeout_Keep() Assert.False(manager.TimeoutToken.IsCancellationRequested); // Respond to the first message. but it does not respond to subsequent messages. - manager.ProcessClientHeartbeatResponse(StreamingHubPayloadPool.Shared.RentOrCreate([0x95 /* Array(5) */, 0x7e /* 0x7e(127) */, 0x0 /* Sequence(0) */, .. ToMessagePackBytes(origin.AddSeconds(1)) /* ClientSentAt * /, 0xc0 /* Nil */, 0xc0 /* Nil */])); - manager.ProcessClientHeartbeatResponse(StreamingHubPayloadPool.Shared.RentOrCreate([0x95 /* Array(5) */, 0x7e /* 0x7e(127) */, 0x1 /* Sequence(1) */, .. ToMessagePackBytes(origin.AddSeconds(2)) /* ClientSentAt * /, 0xc0 /* Nil */, 0xc0 /* Nil */])); - manager.ProcessClientHeartbeatResponse(StreamingHubPayloadPool.Shared.RentOrCreate([0x95 /* Array(5) */, 0x7e /* 0x7e(127) */, 0x2 /* Sequence(2) */, .. ToMessagePackBytes(origin.AddSeconds(3)) /* ClientSentAt * /, 0xc0 /* Nil */, 0xc0 /* Nil */])); + manager.ProcessClientHeartbeatResponse(StreamingHubPayloadPool.Shared.RentOrCreate([0x95 /* Array(5) */, 0x7e /* 0x7e(127) */, 0x0 /* Sequence(0) */, .. ToMessagePackBytes(origin.AddSeconds(1)) /* ClientSentAt */, 0xc0 /* Nil */, 0xc0 /* Nil */])); + manager.ProcessClientHeartbeatResponse(StreamingHubPayloadPool.Shared.RentOrCreate([0x95 /* Array(5) */, 0x7e /* 0x7e(127) */, 0x1 /* Sequence(1) */, .. ToMessagePackBytes(origin.AddSeconds(2)) /* ClientSentAt */, 0xc0 /* Nil */, 0xc0 /* Nil */])); + manager.ProcessClientHeartbeatResponse(StreamingHubPayloadPool.Shared.RentOrCreate([0x95 /* Array(5) */, 0x7e /* 0x7e(127) */, 0x2 /* Sequence(2) */, .. ToMessagePackBytes(origin.AddSeconds(3)) /* ClientSentAt */, 0xc0 /* Nil */, 0xc0 /* Nil */])); timeProvider.Advance(TimeSpan.FromMilliseconds(100)); // 3s has elapsed since the first message. await Task.Delay(10);