Skip to content

Commit c72f8cd

Browse files
committed
Add RpcTargetMetadata.FromShape<T> overloads that work on .NET Framework
1 parent 5beb5c3 commit c72f8cd

File tree

5 files changed

+63
-5
lines changed

5 files changed

+63
-5
lines changed

src/StreamJsonRpc/JsonMessageFormatter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,7 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer
10501050
/// Converts an enumeration token to an <see cref="IAsyncEnumerable{T}"/>.
10511051
/// </summary>
10521052
[RequiresDynamicCode(RuntimeReasons.CloseGenerics)]
1053+
[RequiresUnreferencedCode(RuntimeReasons.CloseGenerics)]
10531054
private class AsyncEnumerableConsumerConverter : JsonConverter
10541055
{
10551056
private static readonly MethodInfo ReadJsonOpenGenericMethod = typeof(AsyncEnumerableConsumerConverter).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).Single(m => m.Name == nameof(ReadJson) && m.IsGenericMethod);
@@ -1109,6 +1110,7 @@ private IAsyncEnumerable<T> ReadJson<T>(JsonReader reader, JsonSerializer serial
11091110
/// Converts an instance of <see cref="IAsyncEnumerable{T}"/> to an enumeration token.
11101111
/// </summary>
11111112
[RequiresDynamicCode(RuntimeReasons.CloseGenerics)]
1113+
[RequiresUnreferencedCode(RuntimeReasons.CloseGenerics)]
11121114
private class AsyncEnumerableGeneratorConverter : JsonConverter
11131115
{
11141116
private static readonly MethodInfo WriteJsonOpenGenericMethod = typeof(AsyncEnumerableGeneratorConverter).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Single(m => m.Name == nameof(WriteJson) && m.IsGenericMethod);
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System.ComponentModel;
2+
using System.Diagnostics.CodeAnalysis;
3+
using PolyType.Abstractions;
4+
5+
namespace StreamJsonRpc;
6+
7+
/// <summary>
8+
/// Extension methods for the <see cref="RpcTargetMetadata"/> class.
9+
/// </summary>
10+
public static class RpcTargetMetadataExtensions
11+
{
12+
#if NET8_0
13+
/// <summary>
14+
/// A message to use as the argument to <see cref="RequiresDynamicCodeAttribute"/>
15+
/// for methods that call into <see cref="TypeShapeResolver.ResolveDynamicOrThrow{T}"/>.
16+
/// </summary>
17+
/// <seealso href="https://github.com/dotnet/runtime/issues/119440#issuecomment-3269894751"/>
18+
internal const string ResolveDynamicMessage =
19+
"Dynamic resolution of IShapeable<T> interface may require dynamic code generation in .NET 8 Native AOT. " +
20+
"It is recommended to switch to statically resolved IShapeable<T> APIs or upgrade your app to .NET 9 or later.";
21+
#endif
22+
23+
extension(RpcTargetMetadata)
24+
{
25+
/// <summary>
26+
/// Creates an <see cref="RpcTargetMetadata"/> instance from the specified shape.
27+
/// </summary>
28+
/// <typeparam name="T">The type for which a shape should be obtained and <see cref="RpcTargetMetadata"/> generated for.</typeparam>
29+
/// <returns>An <see cref="RpcTargetMetadata"/> instance initialized from the shape of the <typeparamref name="T"/>.</returns>
30+
#if NET8_0
31+
[RequiresDynamicCode(ResolveDynamicMessage)]
32+
#endif
33+
#if NET
34+
[EditorBrowsable(EditorBrowsableState.Never)]
35+
[Obsolete("Use the RpcTargetMetadata.FromShape<T>() method instead. If using the extension method syntax, check that your type argument actually has a [GenerateShape] attribute or otherwise implements IShapeable<T> to avoid a runtime failure.", error: true)]
36+
#endif
37+
public static RpcTargetMetadata FromShape<T>()
38+
=> RpcTargetMetadata.FromShape(TypeShapeResolver.ResolveDynamicOrThrow<T>());
39+
40+
/// <summary>
41+
/// Creates an <see cref="RpcTargetMetadata"/> instance from the specified shape.
42+
/// </summary>
43+
/// <typeparam name="T">The type for which a shape should be obtained and <see cref="RpcTargetMetadata"/> generated for.</typeparam>
44+
/// <typeparam name="TProvider">The provider of type shapes from which to obtain the shape.</typeparam>
45+
/// <returns>An <see cref="RpcTargetMetadata"/> instance initialized from the shape of the <typeparamref name="T"/>.</returns>
46+
#if NET8_0
47+
[RequiresDynamicCode(ResolveDynamicMessage)]
48+
#endif
49+
#if NET
50+
[EditorBrowsable(EditorBrowsableState.Never)]
51+
[Obsolete("Use the RpcTargetMetadata.FromShape<T, TProvider>() method instead. If using the extension method syntax, check that your type argument actually has a [GenerateShape] attribute or otherwise implements IShapeable<T> to avoid a runtime failure.", error: true)]
52+
#endif
53+
public static RpcTargetMetadata FromShape<T, TProvider>()
54+
=> RpcTargetMetadata.FromShape(TypeShapeResolver.ResolveDynamicOrThrow<TProvider>());
55+
}
56+
}

src/StreamJsonRpc/StreamJsonRpc.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0</TargetFrameworks>
3+
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks>
44
<ErrorReport>prompt</ErrorReport>
55
<WarningLevel>4</WarningLevel>
66
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -13,6 +13,8 @@
1313
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</IsAotCompatible>
1414
<EnableStreamJsonRpcInterceptors>false</EnableStreamJsonRpcInterceptors>
1515
<PackageValidationBaselineVersion>2.22.11</PackageValidationBaselineVersion>
16+
<!-- StyleCop.Analyzers cannot handle the new C# 14 extension syntax we use in RpcTargetMetadataExtensions. -->
17+
<NoWarn>$(NoWarn);SA1201</NoWarn>
1618
</PropertyGroup>
1719
<ItemGroup>
1820
<EmbeddedResource Update="Resources.resx" EmitFormatMethods="true" />

test/StreamJsonRpc.Tests/RpcTargetMetadataTests.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4-
using PolyType;
5-
64
public partial class RpcTargetMetadataTests
75
{
86
internal interface IRpcContractBase
@@ -45,7 +43,7 @@ internal partial interface IShapedContract
4543
[Fact]
4644
public void FromShape()
4745
{
48-
RpcTargetMetadata metadata = RpcTargetMetadata.FromShape<IShapedContract>(Witness.GeneratedTypeShapeProvider);
46+
RpcTargetMetadata metadata = RpcTargetMetadata.FromShape<IShapedContract>();
4947

5048
var addAsync = Assert.Single(metadata.Methods["AddAsync"]);
5149
var add = Assert.Single(metadata.AliasedMethods["Add"]);

test/StreamJsonRpc.Tests/StreamJsonRpc.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net8.0</TargetFrameworks>
4+
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
55
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('Windows'))">$(TargetFrameworks);net472</TargetFrameworks>
66
<OutputType>exe</OutputType>
77
<RootNamespace />

0 commit comments

Comments
 (0)