Skip to content

Commit 0ee1a15

Browse files
committed
Implement the core framework services with the NativeAOT safe formatter
1 parent 22b1a94 commit 0ee1a15

13 files changed

+94
-48
lines changed

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.228" />
2929
<PackageVersion Include="Nerdbank.Streams" Version="2.13.16" />
3030
<PackageVersion Include="NuGet.Protocol" Version="6.13.2" />
31-
<PackageVersion Include="StreamJsonRpc" Version="2.24.45-preview" />
31+
<PackageVersion Include="StreamJsonRpc" Version="2.24.53-preview" />
3232
<PackageVersion Include="System.Collections.Immutable" Version="8.0.0" />
3333
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
3434
<PackageVersion Include="System.Text.Json" Version="8.0.6" />

Microsoft.ServiceBroker.slnx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<File Path="test/Directory.Build.targets" />
2525
</Folder>
2626
<Folder Name="/src/">
27+
<File Path="src/AnalyzerUser.targets" />
2728
<File Path="src/OptProf.targets" />
2829
<Project Path="src/Microsoft.ServiceHub.Analyzers.CodeFixes/Microsoft.ServiceHub.Analyzers.CodeFixes.csproj" />
2930
<Project Path="src/Microsoft.ServiceHub.Analyzers.CSharp/Microsoft.ServiceHub.Analyzers.CSharp.csproj" />
@@ -33,7 +34,6 @@
3334
<Project Path="src/Microsoft.ServiceHub.Framework/Microsoft.ServiceHub.Framework.csproj" />
3435
</Folder>
3536
<Folder Name="/test/">
36-
<File Path="test/AnalyzerUser.targets" />
3737
<Project Path="test/ExternalTestAssembly/ExternalTestAssembly.csproj" />
3838
<Project Path="test/Microsoft.ServiceHub.Analyzers.Tests/Microsoft.ServiceHub.Analyzers.Tests.csproj" />
3939
<Project Path="test/Microsoft.ServiceHub.Framework.Testing.Tests/Microsoft.ServiceHub.Framework.Testing.Tests.csproj" />
File renamed without changes.
Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
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 System.Diagnostics.CodeAnalysis;
4+
#pragma warning disable PolyTypeJson
5+
6+
using System.Collections.Immutable;
7+
using System.Text.Json.Serialization;
58
using Microsoft.ServiceHub.Framework.Services;
6-
using Newtonsoft.Json.Converters;
9+
using PolyType;
710
using StreamJsonRpc;
811

12+
[assembly: ExportRpcContractProxies]
13+
914
namespace Microsoft.ServiceHub.Framework;
1015

1116
/// <summary>
1217
/// Services and service contracts that provide core infrastructure.
1318
/// </summary>
1419
[RequiresUnreferencedCode(Reasons.Formatters)]
1520
[RequiresDynamicCode(Reasons.Formatters)]
16-
public static class FrameworkServices
21+
public static partial class FrameworkServices
1722
{
1823
/// <summary>
1924
/// The descriptor for a remote service broker.
@@ -24,8 +29,9 @@ public static class FrameworkServices
2429
/// </remarks>
2530
public static readonly ServiceRpcDescriptor RemoteServiceBroker = new CamelCaseTransformingDescriptor(
2631
new ServiceMoniker(nameof(RemoteServiceBroker)),
27-
ServiceJsonRpcDescriptor.Formatters.UTF8SystemTextJson,
28-
ServiceJsonRpcDescriptor.MessageDelimiters.HttpLikeHeaders);
32+
ServiceJsonRpcPolyTypeDescriptor.Formatters.UTF8,
33+
ServiceJsonRpcPolyTypeDescriptor.MessageDelimiters.HttpLikeHeaders,
34+
Witness.GeneratedTypeShapeProvider).WithRpcTargetMetadata(RpcTargetMetadata.FromShape(SourceGenProvider.IRemoteServiceBroker));
2935

3036
/// <summary>
3137
/// The descriptor for the authorization service.
@@ -36,8 +42,9 @@ public static class FrameworkServices
3642
/// </remarks>
3743
public static readonly ServiceRpcDescriptor Authorization = new CamelCaseTransformingDescriptor(
3844
new ServiceMoniker("Microsoft.ServiceHub.Framework.AuthorizationService"),
39-
ServiceJsonRpcDescriptor.Formatters.UTF8SystemTextJson,
40-
ServiceJsonRpcDescriptor.MessageDelimiters.HttpLikeHeaders);
45+
ServiceJsonRpcPolyTypeDescriptor.Formatters.UTF8,
46+
ServiceJsonRpcPolyTypeDescriptor.MessageDelimiters.HttpLikeHeaders,
47+
Witness.GeneratedTypeShapeProvider).WithRpcTargetMetadata(RpcTargetMetadata.FromShape(SourceGenProvider.IAuthorizationService));
4148

4249
/// <summary>
4350
/// The <see cref="ServiceRpcDescriptor"/> for the manifest service which discloses information about services available at a remote source.
@@ -47,26 +54,29 @@ public static class FrameworkServices
4754
/// </remarks>
4855
public static readonly ServiceRpcDescriptor RemoteBrokeredServiceManifest = new CamelCaseTransformingDescriptor(
4956
new ServiceMoniker("Microsoft.VisualStudio.RemoteBrokeredServiceManifest", new Version(0, 2)),
50-
ServiceJsonRpcDescriptor.Formatters.UTF8SystemTextJson,
51-
ServiceJsonRpcDescriptor.MessageDelimiters.HttpLikeHeaders);
57+
ServiceJsonRpcPolyTypeDescriptor.Formatters.UTF8,
58+
ServiceJsonRpcPolyTypeDescriptor.MessageDelimiters.HttpLikeHeaders,
59+
Witness.GeneratedTypeShapeProvider).WithRpcTargetMetadata(RpcTargetMetadata.FromShape(SourceGenProvider.IBrokeredServiceManifest));
60+
61+
private static PolyType.SourceGenerator.TypeShapeProvider_Microsoft_ServiceHub_Framework SourceGenProvider => PolyType.SourceGenerator.TypeShapeProvider_Microsoft_ServiceHub_Framework.Default;
5262

5363
/// <summary>
54-
/// A <see cref="ServiceJsonRpcDescriptor"/> derived type that applies camelCase naming transforms to method and event names
64+
/// A <see cref="ServiceJsonRpcPolyTypeDescriptor"/> derived type that applies camelCase naming transforms to method and event names
5565
/// and trims off any trailing "Async" suffix.
5666
/// </summary>
5767
[RequiresUnreferencedCode(Reasons.Formatters)]
5868
[RequiresDynamicCode(Reasons.Formatters)]
59-
private class CamelCaseTransformingDescriptor : ServiceJsonRpcDescriptor
69+
private class CamelCaseTransformingDescriptor : ServiceJsonRpcPolyTypeDescriptor
6070
{
6171
private const string AsyncSuffix = "Async";
6272
private static readonly Func<string, string> NameNormalize = name => CommonMethodNameTransforms.CamelCase(name.EndsWith(AsyncSuffix, StringComparison.OrdinalIgnoreCase) ? name.Substring(0, name.Length - AsyncSuffix.Length) : name);
6373

6474
/// <summary>
6575
/// Initializes a new instance of the <see cref="CamelCaseTransformingDescriptor"/> class.
6676
/// </summary>
67-
/// <inheritdoc cref="ServiceJsonRpcDescriptor(ServiceMoniker, Formatters, MessageDelimiters)" />
68-
public CamelCaseTransformingDescriptor(ServiceMoniker serviceMoniker, Formatters formatter, MessageDelimiters messageDelimiter)
69-
: base(serviceMoniker, formatter, messageDelimiter)
77+
/// <inheritdoc cref="ServiceJsonRpcPolyTypeDescriptor(ServiceMoniker, Formatters, MessageDelimiters, ITypeShapeProvider)" />
78+
public CamelCaseTransformingDescriptor(ServiceMoniker serviceMoniker, Formatters formatter, MessageDelimiters messageDelimiter, ITypeShapeProvider typeShapeProvider)
79+
: base(serviceMoniker, formatter, messageDelimiter, typeShapeProvider)
7080
{
7181
}
7282

@@ -92,34 +102,25 @@ protected internal override JsonRpcConnection CreateConnection(JsonRpc jsonRpc)
92102

93103
protected internal override IJsonRpcMessageFormatter CreateFormatter()
94104
{
95-
IJsonRpcMessageFormatter formatter = base.CreateFormatter();
96-
97-
// Avoid referencing any MessagePack or Newtonsoft.Json types in this method except when actually taking this code path
98-
// by pushing such type references to another method. This defers loading assemblies till they're already in use.
99-
switch (formatter)
100-
{
101-
case JsonMessageFormatter jsonFormatter:
102-
ConfigureJsonFormatter(jsonFormatter);
103-
break;
104-
case SystemTextJsonFormatter stjFormatter:
105-
ConfigureJsonFormatter(stjFormatter);
106-
break;
107-
default:
108-
throw new NotSupportedException("Unsupported formatter type: " + formatter.GetType().FullName);
109-
}
105+
var formatter = (PolyTypeJsonFormatter)base.CreateFormatter();
106+
107+
formatter.JsonSerializerOptions.TypeInfoResolver = SourceGenerationContext.Default;
110108

111109
return formatter;
112110
}
113111

114112
protected override ServiceRpcDescriptor Clone() => new CamelCaseTransformingDescriptor(this);
113+
}
115114

116-
private static void ConfigureJsonFormatter(JsonMessageFormatter jsonFormatter)
117-
{
118-
jsonFormatter.JsonSerializer.Converters.Add(new VersionConverter());
119-
}
115+
[GenerateShapeFor<string>]
116+
private partial class Witness;
120117

121-
private static void ConfigureJsonFormatter(SystemTextJsonFormatter jsonFormatter)
122-
{
123-
}
124-
}
118+
[JsonSerializable(typeof(IReadOnlyCollection<ServiceMoniker>))]
119+
[JsonSerializable(typeof(ImmutableSortedSet<Version>))]
120+
[JsonSerializable(typeof(RemoteServiceConnectionInfo))]
121+
[JsonSerializable(typeof(ServiceActivationOptions))]
122+
[JsonSerializable(typeof(ServiceBrokerClientMetadata))]
123+
[JsonSerializable(typeof(BrokeredServicesChangedEventArgs))]
124+
[JsonSerializable(typeof(Guid))]
125+
private partial class SourceGenerationContext : JsonSerializerContext;
125126
}

src/Microsoft.ServiceHub.Framework/IRemoteServiceBroker.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
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+
using StreamJsonRpc;
6+
47
namespace Microsoft.ServiceHub.Framework;
58

69
/// <summary>
710
/// Describes a remotable service broker.
811
/// </summary>
9-
public interface IRemoteServiceBroker
12+
[JsonRpcContract]
13+
[GenerateShape(IncludeMethods = MethodShapeFlags.PublicInstance)]
14+
public partial interface IRemoteServiceBroker
1015
{
1116
/// <summary>
1217
/// Occurs when a service previously queried for since the last <see cref="AvailabilityChanged"/> event may have changed availability.

src/Microsoft.ServiceHub.Framework/Microsoft.ServiceHub.Framework.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)','net8.0'))">true</IsAotCompatible>
88
<PolySharpIncludeRuntimeSupportedAttributes>true</PolySharpIncludeRuntimeSupportedAttributes>
99
<DefineConstants>$(DefineConstants);SHIPPING</DefineConstants>
10+
<!-- Our library is filled with GetProxyAsync calls that we return the result to, so we shouldn't dispose of them. -->
11+
<NoWarn>$(NoWarn);ISB001</NoWarn>
1012
</PropertyGroup>
1113

1214
<ItemGroup>
@@ -23,5 +25,6 @@
2325
</ItemGroup>
2426

2527
<Import Project="..\OptProf.targets" />
28+
<Import Project="..\AnalyzerUser.targets" />
2629

2730
</Project>

src/Microsoft.ServiceHub.Framework/Reflection/ProxyBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public static IClientProxy CreateProxy(object target, in ProxyInputs proxyInputs
7979
(target as IDisposable)?.Dispose();
8080
}
8181

82-
throw new NotImplementedException($"Unable to find a source generated proxy filling the specified requirements: {proxyInputs.Requirements}. Research the NativeAOT topic in the documentation at https://microsoft.github.io/vs-streamjsonrpc");
82+
throw new NotImplementedException($"Unable to find a source generated proxy for *local* services filling the specified requirements: {proxyInputs.Requirements}. Research the NativeAOT topic in the documentation at https://microsoft.github.io/vs-streamjsonrpc");
8383
}
8484
}
8585

src/Microsoft.ServiceHub.Framework/ServiceJsonRpcPolyTypeDescriptor.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
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+
#pragma warning disable PolyTypeJson
5+
46
using System.Collections.Immutable;
57
using System.Diagnostics;
68
using System.IO.Pipelines;
@@ -9,6 +11,7 @@
911
using PolyType;
1012
using StreamJsonRpc;
1113
using StreamJsonRpc.Reflection;
14+
using STJ = System.Text.Json;
1215

1316
namespace Microsoft.ServiceHub.Framework;
1417

@@ -69,6 +72,7 @@ protected ServiceJsonRpcPolyTypeDescriptor([ValidatedNotNull] ServiceJsonRpcPoly
6972
this.ExceptionStrategy = copyFrom.ExceptionStrategy;
7073
this.AdditionalServiceInterfaces = copyFrom.AdditionalServiceInterfaces;
7174
this.TypeShapeProvider = copyFrom.TypeShapeProvider;
75+
this.RpcTargetMetadata = copyFrom.RpcTargetMetadata;
7276
}
7377

7478
/// <summary>
@@ -81,6 +85,12 @@ public enum Formatters
8185
/// using <see cref="NerdbankMessagePackFormatter"/> (the <see href="https://github.com/AArnott/Nerdbank.MessagePack">Nerdbank.MessagePack serializer</see>).
8286
/// </summary>
8387
NerdbankMessagePack,
88+
89+
/// <summary>
90+
/// Format messages with UTF-8 text for a human readable JSON representation using the <see cref="System.Text.Json.JsonSerializer"/> serializer.
91+
/// </summary>
92+
[Experimental("PolyTypeJson")]
93+
UTF8,
8494
}
8595

8696
/// <summary>
@@ -486,6 +496,7 @@ protected internal virtual IJsonRpcMessageFormatter CreateFormatter()
486496
=> this.Formatter switch
487497
{
488498
Formatters.NerdbankMessagePack => this.CreateNerdbankMessagePackFormatter(this.TypeShapeProvider),
499+
Formatters.UTF8 => this.CreatePolyTypeJsonFormatter(this.TypeShapeProvider),
489500
_ => throw new NotSupportedException(Strings.FormatFormatterNotSupported(this.Formatter, this.Protocol)),
490501
};
491502

@@ -506,6 +517,21 @@ protected internal virtual JsonRpc CreateJsonRpc(IJsonRpcMessageHandler handler)
506517

507518
private IJsonRpcMessageFormatter CreateNerdbankMessagePackFormatter(ITypeShapeProvider provider) => new NerdbankMessagePackFormatter { TypeShapeProvider = provider };
508519

520+
private IJsonRpcMessageFormatter CreatePolyTypeJsonFormatter(ITypeShapeProvider provider)
521+
{
522+
return new PolyTypeJsonFormatter
523+
{
524+
TypeShapeProvider = provider,
525+
MultiplexingStream = this.MultiplexingStream,
526+
JsonSerializerOptions =
527+
{
528+
DictionaryKeyPolicy = null,
529+
PropertyNamingPolicy = STJ.JsonNamingPolicy.CamelCase,
530+
DefaultIgnoreCondition = STJ.Serialization.JsonIgnoreCondition.WhenWritingNull,
531+
},
532+
};
533+
}
534+
509535
/// <summary>
510536
/// Create first seed channel if not being assigned by the owner of the service descriptor.
511537
/// Sets the protocol version to be used. 1 is the original. 2 is a

src/Microsoft.ServiceHub.Framework/Services/IAuthorizationService.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
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+
using StreamJsonRpc;
6+
47
namespace Microsoft.ServiceHub.Framework.Services;
58

69
/// <summary>
@@ -10,7 +13,9 @@ namespace Microsoft.ServiceHub.Framework.Services;
1013
/// For improved performance, clients may pass an instance of this interface to <see cref="AuthorizationServiceClient"/> and use that
1114
/// so that queries are locally cached.
1215
/// </remarks>
13-
public interface IAuthorizationService
16+
[JsonRpcContract]
17+
[GenerateShape(IncludeMethods = MethodShapeFlags.PublicInstance)]
18+
public partial interface IAuthorizationService
1419
{
1520
/// <summary>
1621
/// Occurs when the credentials previously supplied to this service are at or near expiry.

src/Microsoft.ServiceHub.Framework/Services/IBrokeredServiceManifest.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System.Collections.Immutable;
5+
using PolyType;
6+
using StreamJsonRpc;
57

68
namespace Microsoft.ServiceHub.Framework.Services;
79

@@ -14,7 +16,9 @@ namespace Microsoft.ServiceHub.Framework.Services;
1416
/// For example if an instance of this service is obtained by a Live Share guest
1517
/// the results from method calls may vary from an instance of this service obtained by a Codespaces client.
1618
/// </remarks>
17-
public interface IBrokeredServiceManifest
19+
[JsonRpcContract]
20+
[GenerateShape(IncludeMethods = MethodShapeFlags.PublicInstance)]
21+
public partial interface IBrokeredServiceManifest
1822
{
1923
/// <summary>
2024
/// Gets the list of services that are available from an <see cref="IServiceBroker"/>.

0 commit comments

Comments
 (0)