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

Hot Reload watch service #51967

Merged
merged 4 commits into from
Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -154,8 +154,10 @@ public void EndDebuggingSession(out ImmutableArray<DocumentId> documentsToReanal
internal static bool SupportsEditAndContinue(Project project)
=> project.LanguageServices.GetService<IEditAndContinueAnalyzer>() != null;

// Note: source generated files have relative paths: https://github.com/dotnet/roslyn/issues/51998
internal static bool SupportsEditAndContinue(DocumentState documentState)
=> !documentState.Attributes.DesignTimeOnly && documentState.SupportsSyntaxTree && PathUtilities.IsAbsolute(documentState.FilePath);
=> !documentState.Attributes.DesignTimeOnly && documentState.SupportsSyntaxTree &&
(PathUtilities.IsAbsolute(documentState.FilePath) || documentState is SourceGeneratedDocumentState);

public async ValueTask<ImmutableArray<Diagnostic>> GetDocumentDiagnosticsAsync(Document document, DocumentActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// 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.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.ExternalAccess.Watch.Api
{
internal sealed class WatchHotReloadService
{
private sealed class DebuggerService : IManagedEditAndContinueDebuggerService
{
public static readonly DebuggerService Instance = new();

public Task<ImmutableArray<ManagedActiveStatementDebugInfo>> GetActiveStatementsAsync(CancellationToken cancellationToken)
=> Task.FromResult(ImmutableArray<ManagedActiveStatementDebugInfo>.Empty);

public Task<ManagedEditAndContinueAvailability> GetAvailabilityAsync(Guid module, CancellationToken cancellationToken)
=> Task.FromResult(new ManagedEditAndContinueAvailability(ManagedEditAndContinueAvailabilityStatus.Available));

public Task PrepareModuleForUpdateAsync(Guid module, CancellationToken cancellationToken)
=> Task.CompletedTask;
}

public readonly struct Update
{
public readonly Guid ModuleId;
public readonly ImmutableArray<byte> ILDelta;
public readonly ImmutableArray<byte> MetadataDelta;
public readonly ImmutableArray<byte> PdbDelta;

public Update(Guid moduleId, ImmutableArray<byte> ilDelta, ImmutableArray<byte> metadataDelta, ImmutableArray<byte> pdbDelta)
{
ModuleId = moduleId;
ILDelta = ilDelta;
MetadataDelta = metadataDelta;
PdbDelta = pdbDelta;
}
}

private static readonly SolutionActiveStatementSpanProvider s_solutionActiveStatementSpanProvider =
(_, _) => ValueTaskFactory.FromResult(ImmutableArray<TextSpan>.Empty);

private readonly IEditAndContinueWorkspaceService _encService;

public WatchHotReloadService(HostWorkspaceServices services)
=> _encService = services.GetRequiredService<IEditAndContinueWorkspaceService>();

/// <summary>
/// Starts the watcher.
/// </summary>
/// <param name="solution">Solution that represents sources that match the built binaries on disk.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns></returns>
public async Task StartSessionAsync(Solution solution, CancellationToken cancellationToken)
=> await _encService.StartDebuggingSessionAsync(solution, captureMatchingDocuments: true, cancellationToken).ConfigureAwait(false);

public async Task<(ImmutableArray<Update> updates, ImmutableArray<DiagnosticData> diagnostics)> EmitSolutionUpdateAsync(Solution solution, CancellationToken cancellationToken)
tmat marked this conversation as resolved.
Show resolved Hide resolved
{
_encService.StartEditSession(DebuggerService.Instance, out _);

var (updates, diagnostics) = await _encService.EmitSolutionUpdateAsync(solution, s_solutionActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false);

if (updates.Status == ManagedModuleUpdateStatus.Ready)
{
_encService.CommitSolutionUpdate();
}

_encService.EndEditSession(out _);

if (updates.Status == ManagedModuleUpdateStatus.Blocked)
{
return default;
}

return (updates.Updates.SelectAsArray(update => new Update(update.Module, update.ILDelta, update.MetadataDelta, update.PdbDelta)), diagnostics);
}

public void EndSession()
=> _encService.EndDebuggingSession(out _);
}
}