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
2 changes: 1 addition & 1 deletion eng/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
<!--
VS Debugger
-->
<PackageVersion Include="Microsoft.VisualStudio.Debugger.Contracts" Version="17.13.0-beta.25105.1" />
<PackageVersion Include="Microsoft.VisualStudio.Debugger.Contracts" Version="18.0.0-beta.25359.1" />
<PackageVersion Include="Microsoft.VisualStudio.Debugger.Engine-implementation" Version="17.13.1121201-preview" />
<PackageVersion Include="Microsoft.VisualStudio.Debugger.Metadata-implementation" Version="17.13.1121201-preview" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue;
using Microsoft.VisualStudio.Debugger.Contracts.HotReload;
using InternalContracts = Microsoft.CodeAnalysis.Contracts.EditAndContinue;
Expand All @@ -23,6 +25,12 @@ public static InternalContracts.ManagedMethodId ToContract(this ManagedMethodId
public static InternalContracts.SourceSpan ToContract(this SourceSpan id)
=> new(id.StartLine, id.StartColumn, id.EndLine, id.EndColumn);

public static InternalContracts.ProjectInstanceId ToContract(this ProjectInstanceId id)
=> new(id.ProjectFilePath, id.TargetFramework);

public static InternalContracts.RunningProjectInfo ToContract(this RunningProjectInfo id)
=> new(id.ProjectInstanceId.ToContract(), id.RestartAutomatically);

public static InternalContracts.ManagedHotReloadAvailability ToContract(this ManagedHotReloadAvailability value)
=> new((InternalContracts.ManagedHotReloadAvailabilityStatus)value.Status, value.LocalizedMessage);

Expand All @@ -41,7 +49,7 @@ public static ManagedHotReloadUpdate FromContract(this InternalContracts.Managed
exceptionRegions: update.ExceptionRegions.SelectAsArray(FromContract));

public static ManagedHotReloadUpdates FromContract(this InternalContracts.ManagedHotReloadUpdates updates)
=> new(updates.Updates.FromContract(), updates.Diagnostics.FromContract(), updates.ProjectsToRebuild, updates.ProjectsToRestart);
=> new(updates.Updates.FromContract(), updates.Diagnostics.FromContract(), updates.ProjectsToRebuild.SelectAsArray(FromContract), updates.ProjectsToRestart.SelectAsArray(FromContract));

public static ImmutableArray<ManagedHotReloadUpdate> FromContract(this ImmutableArray<InternalContracts.ManagedHotReloadUpdate> diagnostics)
=> diagnostics.SelectAsArray(FromContract);
Expand All @@ -61,6 +69,9 @@ public static ManagedModuleMethodId FromContract(this InternalContracts.ManagedM
public static SourceSpan FromContract(this InternalContracts.SourceSpan id)
=> new(id.StartLine, id.StartColumn, id.EndLine, id.EndColumn);

public static ProjectInstanceId FromContract(this InternalContracts.ProjectInstanceId id)
=> new(id.ProjectFilePath, id.TargetFramework);

public static ManagedExceptionRegionUpdate FromContract(this InternalContracts.ManagedExceptionRegionUpdate update)
=> new(FromContract(update.Method), update.Delta, FromContract(update.NewSpan));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@
namespace Microsoft.CodeAnalysis.EditAndContinue;

[Shared]
[Export(typeof(IManagedHotReloadLanguageService))]
[Export(typeof(IManagedHotReloadLanguageService2))]
[Export(typeof(IManagedHotReloadLanguageService3))]
[Export(typeof(IEditAndContinueSolutionProvider))]
[Export(typeof(EditAndContinueLanguageService))]
[ExportMetadata("UIContext", EditAndContinueUIContext.EncCapableProjectExistsInWorkspaceUIContextString)]
Expand All @@ -34,7 +33,7 @@ internal sealed class EditAndContinueLanguageService(
Lazy<IManagedHotReloadService> debuggerService,
PdbMatchingSourceTextProvider sourceTextProvider,
IEditAndContinueLogReporter logReporter,
IDiagnosticsRefresher diagnosticRefresher) : IManagedHotReloadLanguageService2, IEditAndContinueSolutionProvider
IDiagnosticsRefresher diagnosticRefresher) : IManagedHotReloadLanguageService3
{
private sealed class NoSessionException : InvalidOperationException
{
Expand Down Expand Up @@ -236,59 +235,9 @@ public async ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken)
}
}

public async ValueTask UpdateBaselinesAsync(ImmutableArray<string> projectPaths, CancellationToken cancellationToken)
{
if (_disabled)
{
return;
}

var currentDesignTimeSolution = GetCurrentDesignTimeSolution();
var currentCompileTimeSolution = GetCurrentCompileTimeSolution(currentDesignTimeSolution);

try
{
SolutionCommitted?.Invoke(currentDesignTimeSolution);
}
catch (Exception e) when (FatalError.ReportAndCatch(e))
{
}

_committedDesignTimeSolution = currentDesignTimeSolution;
var projectIds = GetProjectIds(projectPaths, currentCompileTimeSolution);

try
{
await GetDebuggingSession().UpdateBaselinesAsync(currentCompileTimeSolution, projectIds, cancellationToken).ConfigureAwait(false);
}
catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken))
{
}

foreach (var projectId in projectIds)
{
workspaceProvider.Value.Workspace.EnqueueUpdateSourceGeneratorVersion(projectId, forceRegeneration: false);
}
}

private ImmutableArray<ProjectId> GetProjectIds(ImmutableArray<string> projectPaths, Solution solution)
{
using var _ = ArrayBuilder<ProjectId>.GetInstance(out var projectIds);
foreach (var path in projectPaths)
{
var projectId = solution.Projects.FirstOrDefault(project => project.FilePath == path)?.Id;
if (projectId != null)
{
projectIds.Add(projectId);
}
else
{
logReporter.Report($"Project with path '{path}' not found in the current solution.", LogMessageSeverity.Info);
}
}

return projectIds.ToImmutable();
}
[Obsolete]
public ValueTask UpdateBaselinesAsync(ImmutableArray<string> projectPaths, CancellationToken cancellationToken)
=> throw new NotImplementedException();

public async ValueTask EndSessionAsync(CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -359,9 +308,13 @@ public async ValueTask<bool> HasChangesAsync(string? sourceFilePath, Cancellatio

[Obsolete]
public ValueTask<ManagedHotReloadUpdates> GetUpdatesAsync(CancellationToken cancellationToken)
=> GetUpdatesAsync(runningProjects: [], cancellationToken);
=> throw new NotImplementedException();

public async ValueTask<ManagedHotReloadUpdates> GetUpdatesAsync(ImmutableArray<string> runningProjects, CancellationToken cancellationToken)
[Obsolete]
public ValueTask<ManagedHotReloadUpdates> GetUpdatesAsync(ImmutableArray<string> runningProjects, CancellationToken cancellationToken)
=> throw new NotImplementedException();

public async ValueTask<ManagedHotReloadUpdates> GetUpdatesAsync(ImmutableArray<RunningProjectInfo> runningProjects, CancellationToken cancellationToken)
{
if (_disabled)
{
Expand All @@ -371,22 +324,13 @@ public async ValueTask<ManagedHotReloadUpdates> GetUpdatesAsync(ImmutableArray<s
var designTimeSolution = GetCurrentDesignTimeSolution();
var solution = GetCurrentCompileTimeSolution(designTimeSolution);
var activeStatementSpanProvider = GetActiveStatementSpanProvider(solution);
var runningProjectOptions = runningProjects.ToRunningProjectOptions(solution, static info => (info.ProjectInstanceId.ProjectFilePath, info.ProjectInstanceId.TargetFramework, info.RestartAutomatically));

using var _ = PooledHashSet<string>.GetInstance(out var runningProjectPaths);
runningProjectPaths.AddAll(runningProjects);

// TODO: Update once implemented: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2449700
var runningProjectInfos = solution.Projects.Where(p => p.FilePath != null && runningProjectPaths.Contains(p.FilePath)).ToImmutableDictionary(
keySelector: static p => p.Id,
elementSelector: static p => new RunningProjectInfo { RestartWhenChangesHaveNoEffect = false, AllowPartialUpdate = false });

var result = await GetDebuggingSession().EmitSolutionUpdateAsync(solution, runningProjectInfos, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false);
var result = await GetDebuggingSession().EmitSolutionUpdateAsync(solution, runningProjectOptions, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false);

switch (result.ModuleUpdates.Status)
{
case ModuleUpdateStatus.Ready when result.ProjectsToRebuild.IsEmpty:
// We have updates to be applied and no rude edits.
//
case ModuleUpdateStatus.Ready:
// The debugger will call Commit/Discard on the solution
// based on whether the updates will be applied successfully or not.
_pendingUpdatedDesignTimeSolution = designTimeSolution;
Expand Down Expand Up @@ -422,10 +366,14 @@ await solution.GetDocumentAsync(diagnostic.DocumentId, includeSourceGenerated: t
return new ManagedHotReloadUpdates(
result.ModuleUpdates.Updates.FromContract(),
result.GetAllDiagnostics().FromContract(),
GetProjectPaths(result.ProjectsToRebuild),
GetProjectPaths(result.ProjectsToRestart.Keys));
ToProjectIntanceIds(result.ProjectsToRebuild),
ToProjectIntanceIds(result.ProjectsToRestart.Keys));

ImmutableArray<string> GetProjectPaths(IEnumerable<ProjectId> ids)
=> ids.SelectAsArray(id => solution.GetRequiredProject(id).FilePath!);
ImmutableArray<ProjectInstanceId> ToProjectIntanceIds(IEnumerable<ProjectId> ids)
=> ids.SelectAsArray(id =>
{
var project = solution.GetRequiredProject(id);
return new ProjectInstanceId(project.FilePath!, project.State.NameAndFlavor.flavor ?? "");
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue;
/// TODO (https://github.com/dotnet/roslyn/issues/72713):
/// Once debugger is updated to use the brokered service, this class should be removed and <see cref="EditAndContinueLanguageService"/> should be exported directly.
/// </summary>
internal sealed partial class ManagedEditAndContinueLanguageServiceBridge(EditAndContinueLanguageService service) : IManagedHotReloadLanguageService2
internal sealed partial class ManagedEditAndContinueLanguageServiceBridge(EditAndContinueLanguageService service) : IManagedHotReloadLanguageService3
{
public ValueTask StartSessionAsync(CancellationToken cancellationToken)
=> service.StartSessionAsync(cancellationToken);
Expand All @@ -34,16 +34,21 @@ public ValueTask OnCapabilitiesChangedAsync(CancellationToken cancellationToken)

[Obsolete]
public ValueTask<ManagedHotReloadUpdates> GetUpdatesAsync(CancellationToken cancellationToken)
=> service.GetUpdatesAsync(cancellationToken);
=> throw new NotImplementedException();

[Obsolete]
public ValueTask<ManagedHotReloadUpdates> GetUpdatesAsync(ImmutableArray<string> runningProjects, CancellationToken cancellationToken)
=> throw new NotImplementedException();

public ValueTask<ManagedHotReloadUpdates> GetUpdatesAsync(ImmutableArray<RunningProjectInfo> runningProjects, CancellationToken cancellationToken)
=> service.GetUpdatesAsync(runningProjects, cancellationToken);

public ValueTask CommitUpdatesAsync(CancellationToken cancellationToken)
=> service.CommitUpdatesAsync(cancellationToken);

[Obsolete]
public ValueTask UpdateBaselinesAsync(ImmutableArray<string> projectPaths, CancellationToken cancellationToken)
=> service.UpdateBaselinesAsync(projectPaths, cancellationToken);
=> throw new NotImplementedException();

public ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken)
=> service.DiscardUpdatesAsync(cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public void EndDebuggingSession()

public async ValueTask<ManagedHotReloadUpdates> GetUpdatesAsync(Solution solution, CancellationToken cancellationToken)
{
var results = (await _encService.EmitSolutionUpdateAsync(GetSessionId(), solution, runningProjects: ImmutableDictionary<ProjectId, RunningProjectInfo>.Empty, s_noActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false)).Dehydrate();
return new ManagedHotReloadUpdates(results.ModuleUpdates.Updates.FromContract(), results.GetAllDiagnostics().FromContract(), [], []);
var results = (await _encService.EmitSolutionUpdateAsync(GetSessionId(), solution, runningProjects: ImmutableDictionary<ProjectId, RunningProjectOptions>.Empty, s_noActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false)).Dehydrate();
return new ManagedHotReloadUpdates(results.ModuleUpdates.Updates.FromContract(), results.GetAllDiagnostics().FromContract(), projectInstancesToRebuild: [], projectInstancesToRestart: []);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,13 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution
};
};

var updates = await localService.GetUpdatesAsync(runningProjects: [project.FilePath], CancellationToken.None);
var runningProjectInfo = new DebuggerContracts.RunningProjectInfo()
{
ProjectInstanceId = new DebuggerContracts.ProjectInstanceId(project.FilePath, "net9.0"),
RestartAutomatically = false,
};

var updates = await localService.GetUpdatesAsync(runningProjects: [runningProjectInfo], CancellationToken.None);

Assert.Equal(++observedDiagnosticVersion, diagnosticRefresher.GlobalStateVersion);

Expand Down Expand Up @@ -281,7 +287,7 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution
};
};

updates = await localService.GetUpdatesAsync(runningProjects: [project.FilePath], CancellationToken.None);
updates = await localService.GetUpdatesAsync(runningProjects: [runningProjectInfo], CancellationToken.None);

Assert.Equal(++observedDiagnosticVersion, diagnosticRefresher.GlobalStateVersion);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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;
Expand All @@ -15,6 +16,7 @@ internal interface IManagedHotReloadLanguageService
ValueTask EndSessionAsync(CancellationToken cancellationToken);
ValueTask EnterBreakStateAsync(CancellationToken cancellationToken);
ValueTask ExitBreakStateAsync(CancellationToken cancellationToken);
[Obsolete]
ValueTask<ManagedHotReloadUpdates> GetUpdatesAsync(CancellationToken cancellationToken);
ValueTask<bool> HasChangesAsync(string? sourceFilePath, CancellationToken cancellationToken);
ValueTask OnCapabilitiesChangedAsync(CancellationToken cancellationToken);
Expand All @@ -23,6 +25,14 @@ internal interface IManagedHotReloadLanguageService

internal interface IManagedHotReloadLanguageService2 : IManagedHotReloadLanguageService
{
[Obsolete]
ValueTask<ManagedHotReloadUpdates> GetUpdatesAsync(ImmutableArray<string> runningProjects, CancellationToken cancellationToken);

[Obsolete]
ValueTask UpdateBaselinesAsync(ImmutableArray<string> projectPaths, CancellationToken cancellationToken);
}

internal interface IManagedHotReloadLanguageService3 : IManagedHotReloadLanguageService2
{
ValueTask<ManagedHotReloadUpdates> GetUpdatesAsync(ImmutableArray<RunningProjectInfo> runningProjects, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace Microsoft.CodeAnalysis.Contracts.EditAndContinue;

[DataContract]
internal readonly struct ManagedHotReloadUpdates(ImmutableArray<ManagedHotReloadUpdate> updates, ImmutableArray<ManagedHotReloadDiagnostic> diagnostics, ImmutableArray<string> projectsToRebuild, ImmutableArray<string> projectsToRestart)
internal readonly struct ManagedHotReloadUpdates(ImmutableArray<ManagedHotReloadUpdate> updates, ImmutableArray<ManagedHotReloadDiagnostic> diagnostics, ImmutableArray<ProjectInstanceId> projectsToRebuild, ImmutableArray<ProjectInstanceId> projectsToRestart)
{
[DataMember(Name = "updates")]
public ImmutableArray<ManagedHotReloadUpdate> Updates { get; } = updates;
Expand All @@ -17,8 +17,8 @@ internal readonly struct ManagedHotReloadUpdates(ImmutableArray<ManagedHotReload
public ImmutableArray<ManagedHotReloadDiagnostic> Diagnostics { get; } = diagnostics;

[DataMember(Name = "projectsToRebuild")]
public ImmutableArray<string> ProjectsToRebuild { get; } = projectsToRebuild;
public ImmutableArray<ProjectInstanceId> ProjectsToRebuild { get; } = projectsToRebuild;

[DataMember(Name = "projectsToRestart")]
public ImmutableArray<string> ProjectsToRestart { get; } = projectsToRestart;
public ImmutableArray<ProjectInstanceId> ProjectsToRestart { get; } = projectsToRestart;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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.Runtime.Serialization;

namespace Microsoft.CodeAnalysis.Contracts.EditAndContinue;

[DataContract]
internal readonly struct ProjectInstanceId(string projectFilePath, string targetFramework)
{
[DataMember(Name = "projectFilePath")]
public string ProjectFilePath { get; } = projectFilePath;

[DataMember(Name = "targetFramework")]
public string TargetFramework { get; } = targetFramework;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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.Runtime.Serialization;

namespace Microsoft.CodeAnalysis.Contracts.EditAndContinue;

[DataContract]
internal readonly struct RunningProjectInfo(ProjectInstanceId projectInstanceId, bool restartAutomatically)
{
[DataMember(Name = "projectInstanceId")]
public ProjectInstanceId ProjectInstanceId { get; } = projectInstanceId;

[DataMember(Name = "restartAutomatically")]
public bool RestartAutomatically { get; } = restartAutomatically;
}
Original file line number Diff line number Diff line change
Expand Up @@ -455,16 +455,12 @@ await TryGetMatchingSourceTextAsync(log, sourceText, sourceFilePath, currentDocu
}
}

public void CommitChanges(Solution solution, ImmutableDictionary<ProjectId, Guid>? staleProjects, ImmutableArray<ProjectId>? projectsToUnstale = null)
public void CommitChanges(Solution solution, ImmutableDictionary<ProjectId, Guid> staleProjects)
{
Contract.ThrowIfTrue(staleProjects is null && projectsToUnstale is null);

lock (_guard)
{
_solution = solution;

staleProjects ??= _staleProjects.RemoveRange(projectsToUnstale!);

var oldStaleProjects = _staleProjects;
_staleProjects = staleProjects;

Expand Down
Loading