From 1d389e6aac5986c55cd8bdddc10882b57e4f575c Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 4 Jul 2024 21:53:32 +1000 Subject: [PATCH 1/5] Allow services to use System.Text.Json for OOP comms --- .../Remote/RazorServiceDescriptorsWrapper.cs | 10 +++++++++ .../Remote/Core/RemoteSerializationOptions.cs | 21 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Tools/ExternalAccess/Razor/Remote/RazorServiceDescriptorsWrapper.cs b/src/Tools/ExternalAccess/Razor/Remote/RazorServiceDescriptorsWrapper.cs index 3446e3fd31fc7..168a441e83b0d 100644 --- a/src/Tools/ExternalAccess/Razor/Remote/RazorServiceDescriptorsWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/Remote/RazorServiceDescriptorsWrapper.cs @@ -39,6 +39,16 @@ public RazorServiceDescriptorsWrapper( IEnumerable<(Type serviceInterface, Type? callbackInterface)> interfaces) => UnderlyingObject = new ServiceDescriptors(componentName, featureDisplayNameProvider, new RemoteSerializationOptions(jsonConverters), interfaces); + /// + /// Creates a service descriptor set for services using System.Text.Json serialization. + /// + public RazorServiceDescriptorsWrapper( + string componentName, + Func featureDisplayNameProvider, + ImmutableArray jsonConverters, + IEnumerable<(Type serviceInterface, Type? callbackInterface)> interfaces) + => UnderlyingObject = new ServiceDescriptors(componentName, featureDisplayNameProvider, new RemoteSerializationOptions(jsonConverters), interfaces); + /// /// To be called from a service factory in OOP. /// diff --git a/src/Workspaces/Remote/Core/RemoteSerializationOptions.cs b/src/Workspaces/Remote/Core/RemoteSerializationOptions.cs index df1a6c4360fac..24714062b5ed3 100644 --- a/src/Workspaces/Remote/Core/RemoteSerializationOptions.cs +++ b/src/Workspaces/Remote/Core/RemoteSerializationOptions.cs @@ -37,11 +37,21 @@ public RemoteSerializationOptions(ImmutableArray addition public RemoteSerializationOptions(ImmutableArray jsonConverters) => _options = jsonConverters; + public RemoteSerializationOptions(ImmutableArray jsonConverters) + => _options = jsonConverters; + public MessagePackSerializerOptions MessagePackOptions => (MessagePackSerializerOptions)_options; public ImmutableArray JsonConverters => (ImmutableArray)_options; + public ImmutableArray SystemTextJsonConverters => (ImmutableArray)_options; public ServiceJsonRpcDescriptor.Formatters Formatter - => _options is MessagePackSerializerOptions ? ServiceJsonRpcDescriptor.Formatters.MessagePack : ServiceJsonRpcDescriptor.Formatters.UTF8; + => _options switch + { + MessagePackSerializerOptions => ServiceJsonRpcDescriptor.Formatters.MessagePack, + ImmutableArray => ServiceJsonRpcDescriptor.Formatters.UTF8, + ImmutableArray => ServiceJsonRpcDescriptor.Formatters.UTF8SystemTextJson, + _ => throw new InvalidOperationException() + }; public ServiceJsonRpcDescriptor.MessageDelimiters MessageDelimiters => _options is MessagePackSerializerOptions @@ -58,6 +68,15 @@ internal IJsonRpcMessageFormatter ConfigureFormatter(IJsonRpcMessageFormatter fo // See https://github.com/neuecc/messagepack-csharp. messagePackFormatter.SetMessagePackSerializerOptions(MessagePackOptions); } + else if (formatter is SystemTextJsonFormatter stjFormatter) + { + var converters = stjFormatter.JsonSerializerOptions.Converters; + + foreach (var converter in SystemTextJsonConverters) + { + converters.Add(converter); + } + } else { var converters = ((JsonMessageFormatter)formatter).JsonSerializer.Converters; From 551bda493728e4abbac80282484ee63f1da0169a Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 8 Jul 2024 16:14:26 +1000 Subject: [PATCH 2/5] Add System.Text.Json compatible equivalents of common parameter types --- .../Remote/JsonSerializableDocumentId.cs | 26 ++++++++++++++++++ ...ializableRazorPinnedSolutionInfoWrapper.cs | 27 +++++++++++++++++++ .../Remote/RazorPinnedSolutionInfoWrapper.cs | 4 ++- 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/Tools/ExternalAccess/Razor/Remote/JsonSerializableDocumentId.cs create mode 100644 src/Tools/ExternalAccess/Razor/Remote/JsonSerializableRazorPinnedSolutionInfoWrapper.cs diff --git a/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableDocumentId.cs b/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableDocumentId.cs new file mode 100644 index 0000000000000..b7c0c4bfa20fe --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableDocumentId.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using System.Text.Json.Serialization; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor +{ + /// + /// Represents a DocumentId that can be used by Razor for OOP services that communicate via System.Text.Json + /// + internal readonly record struct JsonSerializableDocumentId( + [property: JsonPropertyName("projectId")] Guid ProjectId, + [property: JsonPropertyName("id")] Guid Id) + { + public static implicit operator JsonSerializableDocumentId(DocumentId documentId) + { + return new JsonSerializableDocumentId(documentId.ProjectId.Id, documentId.Id); + } + + public static implicit operator DocumentId(JsonSerializableDocumentId serializableDocumentId) + { + return DocumentId.CreateFromSerialized(CodeAnalysis.ProjectId.CreateFromSerialized(serializableDocumentId.ProjectId), serializableDocumentId.Id); + } + } +} diff --git a/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableRazorPinnedSolutionInfoWrapper.cs b/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableRazorPinnedSolutionInfoWrapper.cs new file mode 100644 index 0000000000000..33c57d04c581b --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableRazorPinnedSolutionInfoWrapper.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Text.Json.Serialization; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor +{ + /// + /// A wrapper for a solution that can be used by Razor for OOP services that communicate via System.Text.Json + /// + internal readonly record struct JsonSerializableRazorPinnedSolutionInfoWrapper( + [property: JsonPropertyName("data1")] long Data1, + [property: JsonPropertyName("data2")] long Data2) + { + public static implicit operator JsonSerializableRazorPinnedSolutionInfoWrapper(RazorPinnedSolutionInfoWrapper info) + { + return new JsonSerializableRazorPinnedSolutionInfoWrapper(info.UnderlyingObject.Data1, info.UnderlyingObject.Data2); + } + + public static implicit operator RazorPinnedSolutionInfoWrapper(JsonSerializableRazorPinnedSolutionInfoWrapper serializableDocumentId) + { + return new RazorPinnedSolutionInfoWrapper(new Checksum(serializableDocumentId.Data1, serializableDocumentId.Data2)); + } + } +} diff --git a/src/Tools/ExternalAccess/Razor/Remote/RazorPinnedSolutionInfoWrapper.cs b/src/Tools/ExternalAccess/Razor/Remote/RazorPinnedSolutionInfoWrapper.cs index e98a61d9448e7..eec9f59b0de71 100644 --- a/src/Tools/ExternalAccess/Razor/Remote/RazorPinnedSolutionInfoWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/Remote/RazorPinnedSolutionInfoWrapper.cs @@ -3,10 +3,12 @@ // See the LICENSE file in the project root for more information. using System.Runtime.Serialization; -using Microsoft.CodeAnalysis.Remote; namespace Microsoft.CodeAnalysis.ExternalAccess.Razor { + /// + /// A wrapper for a solution that can be used by Razor for OOP services that communicate via MessagePack + /// [DataContract] internal readonly struct RazorPinnedSolutionInfoWrapper { From a1ef6aa60afdeae5ee2e9df9f96bc177b8fab60f Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 8 Jul 2024 16:14:57 +1000 Subject: [PATCH 3/5] Remove Newtonsoft, and allow existing LSP converters to be used --- .../Remote/RazorServiceDescriptorsWrapper.cs | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/Tools/ExternalAccess/Razor/Remote/RazorServiceDescriptorsWrapper.cs b/src/Tools/ExternalAccess/Razor/Remote/RazorServiceDescriptorsWrapper.cs index 168a441e83b0d..902e9645ab1bb 100644 --- a/src/Tools/ExternalAccess/Razor/Remote/RazorServiceDescriptorsWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/Remote/RazorServiceDescriptorsWrapper.cs @@ -5,12 +5,13 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Runtime; +using System.Text.Json; +using System.Text.Json.Serialization; using MessagePack; using MessagePack.Formatters; +using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.Remote; using Microsoft.ServiceHub.Framework; -using Newtonsoft.Json; namespace Microsoft.CodeAnalysis.ExternalAccess.Razor { @@ -29,23 +30,13 @@ public RazorServiceDescriptorsWrapper( IEnumerable<(Type serviceInterface, Type? callbackInterface)> interfaces) => UnderlyingObject = new ServiceDescriptors(componentName, featureDisplayNameProvider, new RemoteSerializationOptions(additionalFormatters, additionalResolvers), interfaces); - /// - /// Creates a service descriptor set for services using JSON serialization. - /// - public RazorServiceDescriptorsWrapper( - string componentName, - Func featureDisplayNameProvider, - ImmutableArray jsonConverters, - IEnumerable<(Type serviceInterface, Type? callbackInterface)> interfaces) - => UnderlyingObject = new ServiceDescriptors(componentName, featureDisplayNameProvider, new RemoteSerializationOptions(jsonConverters), interfaces); - /// /// Creates a service descriptor set for services using System.Text.Json serialization. /// public RazorServiceDescriptorsWrapper( string componentName, Func featureDisplayNameProvider, - ImmutableArray jsonConverters, + ImmutableArray jsonConverters, IEnumerable<(Type serviceInterface, Type? callbackInterface)> interfaces) => UnderlyingObject = new ServiceDescriptors(componentName, featureDisplayNameProvider, new RemoteSerializationOptions(jsonConverters), interfaces); @@ -55,10 +46,11 @@ public RazorServiceDescriptorsWrapper( public ServiceJsonRpcDescriptor GetDescriptorForServiceFactory(Type serviceInterface) => UnderlyingObject.GetServiceDescriptorForServiceFactory(serviceInterface); - public MessagePackSerializerOptions MessagePackOptions - => UnderlyingObject.Options.MessagePackOptions; - - public ImmutableArray JsonConverters - => UnderlyingObject.Options.JsonConverters; + public static ImmutableArray GetLspConverters() + { + var options = new JsonSerializerOptions(); + ProtocolConversions.AddLspSerializerOptions(options); + return options.Converters.ToImmutableArray(); + } } } From aabb4abd0195a34dad8aac9357273db4055441fe Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 8 Jul 2024 16:15:15 +1000 Subject: [PATCH 4/5] Restricted IVT to where Razor OOP services are defined --- .../Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj index d7404811a27fc..c85748a90fe20 100644 --- a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj +++ b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj @@ -38,6 +38,7 @@ + From 8ff45d7b57baa1fb19125e260c895ed27cd8b2cd Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 8 Jul 2024 22:55:44 +1000 Subject: [PATCH 5/5] Fix header accidenatlly copied from Razor --- .../Razor/Remote/JsonSerializableDocumentId.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableDocumentId.cs b/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableDocumentId.cs index b7c0c4bfa20fe..7ba6d956d9f4e 100644 --- a/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableDocumentId.cs +++ b/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableDocumentId.cs @@ -1,5 +1,6 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT license. See License.txt in the project root for license information. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. using System; using System.Text.Json.Serialization;