Skip to content
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 @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.Serialization;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

Expand All @@ -15,11 +16,14 @@ namespace Microsoft.CodeAnalysis
/// workspace.
/// </summary>
[DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
[DataContract]
public sealed class DocumentId : IEquatable<DocumentId>, IObjectWritable
{
[DataMember(Order = 0)]
public ProjectId ProjectId { get; }
[DataMember(Order = 1)]
public Guid Id { get; }

[DataMember(Order = 2)]
private readonly string? _debugName;

private DocumentId(ProjectId projectId, Guid guid, string? debugName)
Expand Down
15 changes: 13 additions & 2 deletions src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,34 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.Serialization;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis
{
#pragma warning disable CA1200 // Avoid using cref tags with a prefix
/// <summary>
/// An identifier that can be used to refer to the same <see cref="Project"/> across versions.
/// </summary>
/// <remarks>
/// This supports the general message-pack <see cref="DataContractAttribute"/> of being serializable. However, in
/// practice, this is not serialized directly, but through the use of a custom formatter <see
/// cref="T:Microsoft.CodeAnalysis.Remote.MessagePackFormatters.ProjectIdFormatter"/>
/// </remarks>
#pragma warning restore CA1200 // Avoid using cref tags with a prefix
[DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
[DataContract]
public sealed class ProjectId : IEquatable<ProjectId>, IObjectWritable
{
private readonly string? _debugName;

/// <summary>
/// The system generated unique id.
/// </summary>
[DataMember(Order = 0)]
public Guid Id { get; }

[DataMember(Order = 1)]
private readonly string? _debugName;

private ProjectId(Guid guid, string? debugName)
{
this.Id = guid;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.Serialization;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis
Expand All @@ -15,13 +16,16 @@ namespace Microsoft.CodeAnalysis
/// An identifier that can be used to refer to the same Solution across versions.
/// </summary>
[DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
[DataContract]
public sealed class SolutionId : IEquatable<SolutionId>, IObjectWritable
{
/// <summary>
/// The unique id of the solution.
/// </summary>
[DataMember(Order = 0)]
public Guid Id { get; }

[DataMember(Order = 1)]
private readonly string _debugName;

private SolutionId(Guid id, string debugName)
Expand Down
109 changes: 7 additions & 102 deletions src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ namespace Microsoft.CodeAnalysis.Remote
internal sealed class MessagePackFormatters
{
internal static readonly ImmutableArray<IMessagePackFormatter> Formatters = ImmutableArray.Create<IMessagePackFormatter>(
SolutionIdFormatter.Instance,
ProjectIdFormatter.Instance,
DocumentIdFormatter.Instance,
// ForceTypelessFormatter<T> needs to be listed here for each Roslyn abstract type T that is being serialized OOP.
// TODO: add a resolver that provides these https://github.com/dotnet/roslyn/issues/60724
new ForceTypelessFormatter<SimplifierOptions>(),
Expand All @@ -41,56 +39,15 @@ internal sealed class MessagePackFormatters
internal static IFormatterResolver CreateResolver(ImmutableArray<IMessagePackFormatter> additionalFormatters, ImmutableArray<IFormatterResolver> additionalResolvers)
=> (additionalFormatters.IsEmpty && additionalResolvers.IsEmpty) ? DefaultResolver : CompositeResolver.Create(Formatters.AddRange(additionalFormatters), s_resolvers.AddRange(additionalResolvers));

internal sealed class SolutionIdFormatter : IMessagePackFormatter<SolutionId?>
{
public static readonly SolutionIdFormatter Instance = new SolutionIdFormatter();

public SolutionId? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
try
{
if (reader.TryReadNil())
{
return null;
}

Contract.ThrowIfFalse(reader.ReadArrayHeader() == 2);
var id = GuidFormatter.Instance.Deserialize(ref reader, options);
var debugName = reader.ReadString();

return SolutionId.CreateFromSerialized(id, debugName);
}
catch (Exception e) when (e is not MessagePackSerializationException)
{
throw new MessagePackSerializationException(e.Message, e);
}
}

public void Serialize(ref MessagePackWriter writer, SolutionId? value, MessagePackSerializerOptions options)
{
try
{
if (value is null)
{
writer.WriteNil();
}
else
{
writer.WriteArrayHeader(2);
GuidFormatter.Instance.Serialize(ref writer, value.Id, options);
writer.Write(value.DebugName);
}
}
catch (Exception e) when (e is not MessagePackSerializationException)
{
throw new MessagePackSerializationException(e.Message, e);
}
}
}

/// <summary>
/// Specialized formatter used so we can cache and reuse <see cref="ProjectId"/> instances. This is valuable as
/// it's very common for a set of results to reuse the same ProjectId across long sequences of results
/// containing <see cref="DocumentId"/>s. This allows a single instance to be created and shared across that
/// entire sequence, saving on allocations.
/// </summary>
internal sealed class ProjectIdFormatter : IMessagePackFormatter<ProjectId?>
{
public static readonly ProjectIdFormatter Instance = new ProjectIdFormatter();
public static readonly ProjectIdFormatter Instance = new();

/// <summary>
/// Keep a copy of the most recent project ID to avoid duplicate instances when many consecutive IDs
Expand Down Expand Up @@ -150,57 +107,5 @@ public void Serialize(ref MessagePackWriter writer, ProjectId? value, MessagePac
}
}
}

internal sealed class DocumentIdFormatter : IMessagePackFormatter<DocumentId?>
{
public static readonly DocumentIdFormatter Instance = new DocumentIdFormatter();

public DocumentId? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
try
{
if (reader.TryReadNil())
{
return null;
}

Contract.ThrowIfFalse(reader.ReadArrayHeader() == 3);

var projectId = ProjectIdFormatter.Instance.Deserialize(ref reader, options);
Contract.ThrowIfNull(projectId);

var id = GuidFormatter.Instance.Deserialize(ref reader, options);
var debugName = reader.ReadString();

return DocumentId.CreateFromSerialized(projectId, id, debugName);
}
catch (Exception e) when (e is not MessagePackSerializationException)
{
throw new MessagePackSerializationException(e.Message, e);
}
}

public void Serialize(ref MessagePackWriter writer, DocumentId? value, MessagePackSerializerOptions options)
{
try
{
if (value is null)
{
writer.WriteNil();
}
else
{
writer.WriteArrayHeader(3);
ProjectIdFormatter.Instance.Serialize(ref writer, value.ProjectId, options);
GuidFormatter.Instance.Serialize(ref writer, value.Id, options);
writer.Write(value.DebugName);
}
}
catch (Exception e) when (e is not MessagePackSerializationException)
{
throw new MessagePackSerializationException(e.Message, e);
}
}
}
}
}