Skip to content

Commit

Permalink
Merge pull request #849 from Cysharp/feature/ClientTrimmable
Browse files Browse the repository at this point in the history
Enable client library trim warnings
  • Loading branch information
mayuki authored Oct 8, 2024
2 parents 40c7d40 + c112ffc commit 276be0d
Show file tree
Hide file tree
Showing 45 changed files with 310 additions and 76 deletions.
6 changes: 6 additions & 0 deletions samples/ChatApp/ChatApp.Console/ChatApp.Console.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\ChatApp.Unity\Assets\Scripts\Generated\MessagePack.Generated.cs" Link="MessagePack.Generated.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\MagicOnion.Client.SourceGenerator\MagicOnion.Client.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\..\src\MagicOnion.Client\MagicOnion.Client.csproj" />
Expand Down
17 changes: 15 additions & 2 deletions samples/ChatApp/ChatApp.Console/Program.cs
Original file line number Diff line number Diff line change
@@ -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<IChatHub, IChatHubReceiver>(channel, new ChatHubReceiver(sessionId));
Expand All @@ -30,7 +44,6 @@
[MagicOnionClientGeneration(typeof(IChatHub))]
partial class MagicOnionGeneratedClientInitializer;


class ChatHubReceiver(Guid sessionId) : IChatHubReceiver
{
public void OnJoin(string name)
Expand Down
6 changes: 3 additions & 3 deletions src/MagicOnion.Abstractions/Internal/Box.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,21 @@ internal Box(T value)
Value = value;
}

public bool Equals(Box<T> other)
public bool Equals(Box<T>? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return EqualityComparer<T>.Default.Equals(Value, other.Value);
}

public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return ReferenceEquals(this, obj) || obj is Box<T> other && Equals(other);
}

public override int GetHashCode()
{
return EqualityComparer<T>.Default.GetHashCode(Value);
return EqualityComparer<T>.Default.GetHashCode(Value!);
}

public static bool operator ==(Box<T> valueA, Box<T> valueB)
Expand Down
3 changes: 2 additions & 1 deletion src/MagicOnion.Abstractions/MagicOnion.Abstractions.csproj
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>

<LangVersion>$(_LangVersionUnityBaseline)</LangVersion>
<Nullable>enable</Nullable>
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</IsTrimmable>

<PackageId>MagicOnion.Abstractions</PackageId>
<Description>MagicOnion interfaces and abstractions for server and client.
Expand Down
4 changes: 2 additions & 2 deletions src/MagicOnion.Abstractions/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ MagicOnion.IgnoreAttribute
MagicOnion.IgnoreAttribute.IgnoreAttribute() -> void
MagicOnion.Internal.Box
MagicOnion.Internal.Box<T>
MagicOnion.Internal.Box<T>.Equals(MagicOnion.Internal.Box<T>! other) -> bool
MagicOnion.Internal.Box<T>.Equals(MagicOnion.Internal.Box<T>? other) -> bool
MagicOnion.Internal.IAsyncClientStreamingCallWrapper<TRequest, TResponse>
MagicOnion.Internal.IAsyncClientStreamingCallWrapper<TRequest, TResponse>.RequestStream.get -> Grpc.Core.IClientStreamWriter<TRequest>!
MagicOnion.Internal.IAsyncClientStreamingCallWrapper<TRequest, TResponse>.ResponseAsync.get -> System.Threading.Tasks.Task<TResponse>!
Expand Down Expand Up @@ -155,7 +155,7 @@ MagicOnion.UnaryResult<TResponse>.UnaryResult() -> void
MagicOnion.UnaryResult<TResponse>.UnaryResult(System.Threading.Tasks.Task<MagicOnion.Client.IResponseContext<TResponse>!>! response) -> void
MagicOnion.UnaryResult<TResponse>.UnaryResult(System.Threading.Tasks.Task<TResponse>! rawTaskValue) -> void
MagicOnion.UnaryResult<TResponse>.UnaryResult(TResponse rawValue) -> void
override MagicOnion.Internal.Box<T>.Equals(object! obj) -> bool
override MagicOnion.Internal.Box<T>.Equals(object? obj) -> bool
override MagicOnion.Internal.Box<T>.GetHashCode() -> int
readonly MagicOnion.DynamicArgumentTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>.Item1 -> T1
readonly MagicOnion.DynamicArgumentTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>.Item10 -> T10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,21 @@ internal Box(T value)
Value = value;
}

public bool Equals(Box<T> other)
public bool Equals(Box<T>? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return EqualityComparer<T>.Default.Equals(Value, other.Value);
}

public override bool Equals(object obj)
public override bool Equals(object? obj)
{
return ReferenceEquals(this, obj) || obj is Box<T> other && Equals(other);
}

public override int GetHashCode()
{
return EqualityComparer<T>.Default.GetHashCode(Value);
return EqualityComparer<T>.Default.GetHashCode(Value!);
}

public static bool operator ==(Box<T> valueA, Box<T> valueB)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -20,6 +20,7 @@ protected static class KnownTypes
}
}

[RequiresUnreferencedCode(nameof(DynamicClientBuilder<T>) + " is incompatible with trimming and Native AOT.")]
internal class DynamicClientBuilder<T> : DynamicClientBuilder
where T : IService<T>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public bool TryGetFactory<T>([NotNullWhen(true)] out MagicOnionClientFactoryDele
/// <summary>
/// Provides to get a MagicOnionClient factory of the specified service type. The provider is backed by DynamicMagicOnionClientBuilder.
/// </summary>
[RequiresUnreferencedCode(nameof(DynamicMagicOnionClientFactoryProvider) + " is incompatible with trimming and Native AOT.")]
public class DynamicMagicOnionClientFactoryProvider : IMagicOnionClientFactoryProvider
{
public static IMagicOnionClientFactoryProvider Instance { get; } = new DynamicMagicOnionClientFactoryProvider();
Expand All @@ -30,6 +31,7 @@ public bool TryGetFactory<T>([NotNullWhen(true)] out MagicOnionClientFactoryDele
return true;
}

[RequiresUnreferencedCode(nameof(DynamicMagicOnionClientFactoryProvider) + "." + nameof(Cache<T>) + " is incompatible with trimming and Native AOT.")]
static class Cache<T> where T : IService<T>
{
public static readonly MagicOnionClientFactoryDelegate<T> Factory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -14,6 +15,7 @@

namespace MagicOnion.Client.DynamicClient
{
[RequiresUnreferencedCode(nameof(DynamicStreamingHubClientAssemblyHolder) + " is incompatible with trimming and Native AOT.")]
#if ENABLE_SAVE_ASSEMBLY
public
#else
Expand Down Expand Up @@ -41,6 +43,7 @@ public static AssemblyBuilder Save()
#endif
}

[RequiresUnreferencedCode(nameof(DynamicStreamingHubClientBuilder<TStreamingHub, TReceiver>) + " is incompatible with trimming and Native AOT.")]
#if ENABLE_SAVE_ASSEMBLY
public
#else
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public bool TryGetFactory<TStreamingHub, TReceiver>([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();
Expand All @@ -27,6 +28,7 @@ public bool TryGetFactory<TStreamingHub, TReceiver>([NotNullWhen(true)] out Stre
return true;
}

[RequiresUnreferencedCode(nameof(DynamicStreamingHubClientFactoryProvider) + "." + nameof(Cache<TStreamingHub, TReceiver>) + " is incompatible with trimming and Native AOT.")]
static class Cache<TStreamingHub, TReceiver> where TStreamingHub : IStreamingHub<TStreamingHub, TReceiver>
{
public static readonly StreamingHubClientFactoryDelegate<TStreamingHub, TReceiver> Factory
Expand Down
Original file line number Diff line number Diff line change
@@ -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)!;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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; }
Expand All @@ -20,6 +22,7 @@ public ServiceClientDefinition(Type serviceInterfaceType, IReadOnlyList<MagicOni
Methods = methods;
}

[RequiresUnreferencedCode(nameof(MagicOnionServiceMethodInfo) + " is incompatible with trimming and Native AOT.")]
public class MagicOnionServiceMethodInfo
{
public MethodType MethodType { get; }
Expand Down Expand Up @@ -69,7 +72,7 @@ public static MagicOnionServiceMethodInfo Create(Type serviceType, MethodInfo me
return method;
}

private void Verify()
void Verify()
{
switch (MethodType)
{
Expand All @@ -91,7 +94,7 @@ private void Verify()
}
}

private static (MethodType MethodType, Type? RequestType, Type ResponseType) GetMethodTypeAndResponseTypeFromMethod(MethodInfo methodInfo)
static (MethodType MethodType, Type? RequestType, Type ResponseType) GetMethodTypeAndResponseTypeFromMethod(MethodInfo methodInfo)
{
const string UnsupportedReturnTypeErrorMessage =
"The method of a service must return 'UnaryResult<T>', 'Task<ClientStreamingResult<TRequest, TResponse>>', 'Task<ServerStreamingResult<T>>' or 'DuplexStreamingResult<TRequest, TResponse>'.";
Expand Down Expand Up @@ -171,7 +174,7 @@ public static ServiceClientDefinition CreateFromType<T>()
return new ServiceClientDefinition(typeof(T), GetServiceMethods(typeof(T)));
}

private static IReadOnlyList<MagicOnionServiceMethodInfo> GetServiceMethods(Type serviceType)
static IReadOnlyList<MagicOnionServiceMethodInfo> GetServiceMethods(Type serviceType)
{
return serviceType
.GetInterfaces()
Expand Down Expand Up @@ -209,7 +212,7 @@ private static IReadOnlyList<MagicOnionServiceMethodInfo> GetServiceMethods(Type
/// </summary>
/// <param name="methodInfo"></param>
/// <returns></returns>
private static Type GetRequestTypeFromMethod(MethodInfo methodInfo)
static Type GetRequestTypeFromMethod(MethodInfo methodInfo)
{
var parameterTypes = methodInfo.GetParameters().Select(x => x.ParameterType).ToArray();
switch (parameterTypes.Length)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<,>)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using MessagePack;
using System;
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;

Expand All @@ -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<StreamingHubPayload> StreamingHubMarshaller { get; } = new(
public static Marshaller<StreamingHubPayload> StreamingHubMarshaller { get; } = new(
serializer: static (payload, context) =>
{
context.SetPayloadLength(payload.Length);
Expand All @@ -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)
{
Expand All @@ -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();
}
}

}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 276be0d

Please sign in to comment.