Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow services to use System.Text.Json for OOP comms in Razor #74280

Merged
merged 6 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.ExternalAccess.Razor" />
<!-- Restricted IVT is direct for protocol types only -->
<RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.Remote.Razor" Namespace="Roslyn.LanguageServer.Protocol" Partner="Razor" Key="$(RazorKey)" />
<RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.Razor.Workspaces" Namespace="Roslyn.LanguageServer.Protocol" Partner="Razor" Key="$(RazorKey)" />
<RestrictedInternalsVisibleTo Include="Microsoft.VisualStudio.LanguageServices.Razor" Namespace="Roslyn.LanguageServer.Protocol" Partner="Razor" Key="$(RazorKey)" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.ExternalAccess.Xaml" />
<!-- Restricted IVT is direct for protocol types only -->
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Represents a DocumentId that can be used by Razor for OOP services that communicate via System.Text.Json
/// </summary>
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// A wrapper for a solution that can be used by Razor for OOP services that communicate via System.Text.Json
/// </summary>
internal readonly record struct JsonSerializableRazorPinnedSolutionInfoWrapper(
[property: JsonPropertyName("data1")] long Data1,
[property: JsonPropertyName("data2")] long Data2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting. would like to know more about this. when are you guuys pinning solutions?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eiriktsarpalis how do longs get serialized in STJ? Are they guaranteed to roundtrip?

Copy link
Contributor Author

@davidwengier davidwengier Jul 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting. would like to know more about this. when are you guuys pinning solutions?

hopefully never? This is just a version of RazorPinnedSolutionInfoWrapper, which is really just Checksum, but this serializes with STJ and that uses messagepack. I could add STJ attributes to RazorPinnedSolutionInfoWrapper, but then I'd need them on Checksum too.

This type is only used to call OOP, and is then passed back to roslyn to get the real solution

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eiriktsarpalis how do longs get serialized in STJ? Are they guaranteed to roundtrip?

They serialize to JSON numbers which have arbitrary precision. They are guaranteed to roundtrip.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

{
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));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
/// A wrapper for a solution that can be used by Razor for OOP services that communicate via MessagePack
/// </summary>
[DataContract]
internal readonly struct RazorPinnedSolutionInfoWrapper
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -30,7 +31,7 @@ public RazorServiceDescriptorsWrapper(
=> UnderlyingObject = new ServiceDescriptors(componentName, featureDisplayNameProvider, new RemoteSerializationOptions(additionalFormatters, additionalResolvers), interfaces);

/// <summary>
/// Creates a service descriptor set for services using JSON serialization.
/// Creates a service descriptor set for services using System.Text.Json serialization.
/// </summary>
public RazorServiceDescriptorsWrapper(
string componentName,
Expand All @@ -45,10 +46,11 @@ public RazorServiceDescriptorsWrapper(
public ServiceJsonRpcDescriptor GetDescriptorForServiceFactory(Type serviceInterface)
=> UnderlyingObject.GetServiceDescriptorForServiceFactory(serviceInterface);

public MessagePackSerializerOptions MessagePackOptions
=> UnderlyingObject.Options.MessagePackOptions;

public ImmutableArray<JsonConverter> JsonConverters
=> UnderlyingObject.Options.JsonConverters;
public static ImmutableArray<JsonConverter> GetLspConverters()
{
var options = new JsonSerializerOptions();
ProtocolConversions.AddLspSerializerOptions(options);
return options.Converters.ToImmutableArray();
}
}
}
21 changes: 20 additions & 1 deletion src/Workspaces/Remote/Core/RemoteSerializationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,21 @@ public RemoteSerializationOptions(ImmutableArray<IMessagePackFormatter> addition
public RemoteSerializationOptions(ImmutableArray<JsonConverter> jsonConverters)
=> _options = jsonConverters;

public RemoteSerializationOptions(ImmutableArray<System.Text.Json.Serialization.JsonConverter> jsonConverters)
=> _options = jsonConverters;

public MessagePackSerializerOptions MessagePackOptions => (MessagePackSerializerOptions)_options;
public ImmutableArray<JsonConverter> JsonConverters => (ImmutableArray<JsonConverter>)_options;
public ImmutableArray<System.Text.Json.Serialization.JsonConverter> SystemTextJsonConverters => (ImmutableArray<System.Text.Json.Serialization.JsonConverter>)_options;

public ServiceJsonRpcDescriptor.Formatters Formatter
=> _options is MessagePackSerializerOptions ? ServiceJsonRpcDescriptor.Formatters.MessagePack : ServiceJsonRpcDescriptor.Formatters.UTF8;
=> _options switch
{
MessagePackSerializerOptions => ServiceJsonRpcDescriptor.Formatters.MessagePack,
ImmutableArray<JsonConverter> => ServiceJsonRpcDescriptor.Formatters.UTF8,
ImmutableArray<System.Text.Json.Serialization.JsonConverter> => ServiceJsonRpcDescriptor.Formatters.UTF8SystemTextJson,
_ => throw new InvalidOperationException()
};

public ServiceJsonRpcDescriptor.MessageDelimiters MessageDelimiters
=> _options is MessagePackSerializerOptions
Expand All @@ -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;
Expand Down
Loading