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
7 changes: 5 additions & 2 deletions src/BuiltInTools/HotReloadClient/DefaultHotReloadClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,15 @@ async Task<ImmutableArray<string>> ConnectAsync()
// When the client connects, the first payload it sends is the initialization payload which includes the apply capabilities.

var capabilities = (await ClientInitializationResponse.ReadAsync(_pipe, cancellationToken)).Capabilities;
Logger.Log(LogEvents.Capabilities, capabilities);

var result = AddImplicitCapabilities(capabilities.Split(' '));

Logger.Log(LogEvents.Capabilities, string.Join(" ", result));

// fire and forget:
_ = ListenForResponsesAsync(cancellationToken);

return [.. capabilities.Split(' ')];
return result;
}
catch (Exception e) when (e is not OperationCanceledException)
{
Expand Down
7 changes: 7 additions & 0 deletions src/BuiltInTools/HotReloadClient/HotReloadClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ internal abstract class HotReloadClient(ILogger logger, ILogger agentLogger) : I
internal Task PendingUpdates
=> _pendingUpdates;

/// <summary>
/// .NET Framework runtime does not support adding MethodImpl entries, therefore the capability is not in the baseline capability set.
/// All other runtimes (.NET and Mono) support it and rather than servicing all of them we include the capability here.
/// </summary>
protected static ImmutableArray<string> AddImplicitCapabilities(IEnumerable<string> capabilities)
=> [.. capabilities, "AddExplicitInterfaceImplementation"];

public abstract void ConfigureLaunchEnvironment(IDictionary<string, string> environmentBuilder);

/// <summary>
Expand Down
24 changes: 15 additions & 9 deletions src/BuiltInTools/HotReloadClient/Web/WebAssemblyHotReloadClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,30 @@ internal sealed class WebAssemblyHotReloadClient(

private static ImmutableArray<string> GetUpdateCapabilities(ILogger logger, ImmutableArray<string> projectHotReloadCapabilities, Version projectTargetFrameworkVersion)
{
var capabilities = projectHotReloadCapabilities;

if (capabilities.IsEmpty)
{
logger.LogDebug("Using capabilities based on project target framework version: '{Version}'.", projectTargetFrameworkVersion);

capabilities = projectTargetFrameworkVersion.Major switch
var capabilities = projectHotReloadCapabilities.IsEmpty
? projectTargetFrameworkVersion.Major switch
{
9 => s_defaultCapabilities90,
8 => s_defaultCapabilities80,
7 => s_defaultCapabilities70,
6 => s_defaultCapabilities60,
_ => [],
};
}
: projectHotReloadCapabilities;

if (capabilities is not [])
{
capabilities = AddImplicitCapabilities(capabilities);
}

var capabilitiesStr = string.Join(", ", capabilities);
if (projectHotReloadCapabilities.IsEmpty)
{
logger.LogDebug("Project specifies capabilities: {Capabilities}.", capabilitiesStr);
}
else
{
logger.LogDebug("Project specifies capabilities: '{Capabilities}'", string.Join(" ", capabilities));
logger.LogDebug("Using capabilities based on project target framework version: '{Version}': {Capabilities}.", projectTargetFrameworkVersion, capabilitiesStr);
}

return capabilities;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public async Task ApplyManagedCodeUpdates_ProcessNotSuspended()
await using var test = new Test(output, agent);

var actualCapabilities = await test.Client.GetUpdateCapabilitiesAsync(CancellationToken.None);
AssertEx.SequenceEqual(["Baseline", "AddMethodToExistingType", "AddStaticFieldToExistingType"], actualCapabilities);
AssertEx.SequenceEqual(["Baseline", "AddMethodToExistingType", "AddStaticFieldToExistingType", "AddExplicitInterfaceImplementation"], actualCapabilities);

var update = new HotReloadManagedCodeUpdate(
moduleId: moduleId,
Expand Down Expand Up @@ -86,7 +86,7 @@ public async Task ApplyManagedCodeUpdates_ProcessSuspended()
await using var test = new Test(output, agent);

var actualCapabilities = await test.Client.GetUpdateCapabilitiesAsync(CancellationToken.None);
AssertEx.SequenceEqual(["Baseline", "AddMethodToExistingType", "AddStaticFieldToExistingType"], actualCapabilities);
AssertEx.SequenceEqual(["Baseline", "AddMethodToExistingType", "AddStaticFieldToExistingType", "AddExplicitInterfaceImplementation"], actualCapabilities);

var update = new HotReloadManagedCodeUpdate(
moduleId: moduleId,
Expand Down Expand Up @@ -125,7 +125,7 @@ public async Task ApplyManagedCodeUpdates_Failure()
await using var test = new Test(output, agent);

var actualCapabilities = await test.Client.GetUpdateCapabilitiesAsync(CancellationToken.None);
AssertEx.SequenceEqual(["Baseline", "AddMethodToExistingType", "AddStaticFieldToExistingType"], actualCapabilities);
AssertEx.SequenceEqual(["Baseline", "AddMethodToExistingType", "AddStaticFieldToExistingType", "AddExplicitInterfaceImplementation"], actualCapabilities);

var update = new HotReloadManagedCodeUpdate(
moduleId: Guid.NewGuid(),
Expand Down
10 changes: 6 additions & 4 deletions test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public static void Print()
UpdateSourceFile(Path.Combine(dependencyDir, "Foo.cs"), newSrc);

await App.AssertOutputLineStartsWith("Changed!");

App.AssertOutputContains("dotnet watch 🔥 Hot reload capabilities: AddExplicitInterfaceImplementation AddFieldRva AddInstanceFieldToExistingType AddMethodToExistingType AddStaticFieldToExistingType Baseline ChangeCustomAttributes GenericAddFieldToExistingType GenericAddMethodToExistingType GenericUpdateMethod NewTypeDefinition UpdateParameters.");
}

[Fact]
Expand Down Expand Up @@ -892,11 +894,11 @@ public async Task BlazorWasm(bool projectSpecifiesCapabilities)
// check project specified capapabilities:
if (projectSpecifiesCapabilities)
{
App.AssertOutputContains("dotnet watch 🔥 Hot reload capabilities: Baseline AddMethodToExistingType.");
App.AssertOutputContains("dotnet watch 🔥 Hot reload capabilities: AddExplicitInterfaceImplementation AddMethodToExistingType Baseline.");
}
else
{
App.AssertOutputContains("dotnet watch 🔥 Hot reload capabilities: Baseline AddMethodToExistingType AddStaticFieldToExistingType NewTypeDefinition ChangeCustomAttributes AddInstanceFieldToExistingType GenericAddMethodToExistingType GenericUpdateMethod UpdateParameters GenericAddFieldToExistingType.");
App.AssertOutputContains("dotnet watch 🔥 Hot reload capabilities: AddExplicitInterfaceImplementation AddFieldRva AddInstanceFieldToExistingType AddMethodToExistingType AddStaticFieldToExistingType Baseline ChangeCustomAttributes GenericAddFieldToExistingType GenericAddMethodToExistingType GenericUpdateMethod NewTypeDefinition UpdateParameters.");
}
}

Expand Down Expand Up @@ -963,10 +965,10 @@ public async Task BlazorWasmHosted()
App.AssertOutputContains(MessageDescriptor.ApplicationKind_BlazorHosted);

// client capabilities:
App.AssertOutputContains($"dotnet watch ⌚ [blazorhosted ({tfm})] Project 'blazorwasm ({tfm})' specifies capabilities: 'Baseline AddMethodToExistingType AddStaticFieldToExistingType NewTypeDefinition ChangeCustomAttributes AddInstanceFieldToExistingType GenericAddMethodToExistingType GenericUpdateMethod UpdateParameters GenericAddFieldToExistingType'");
App.AssertOutputContains($"dotnet watch ⌚ [blazorhosted ({tfm})] Project specifies capabilities: Baseline AddMethodToExistingType AddStaticFieldToExistingType NewTypeDefinition ChangeCustomAttributes AddInstanceFieldToExistingType GenericAddMethodToExistingType GenericUpdateMethod UpdateParameters GenericAddFieldToExistingType AddExplicitInterfaceImplementation.");

// server capabilities:
App.AssertOutputContains($"dotnet watch ⌚ [blazorhosted ({tfm})] Capabilities: 'Baseline AddMethodToExistingType AddStaticFieldToExistingType AddInstanceFieldToExistingType NewTypeDefinition ChangeCustomAttributes UpdateParameters GenericUpdateMethod GenericAddMethodToExistingType GenericAddFieldToExistingType AddFieldRva'");
App.AssertOutputContains($"dotnet watch ⌚ [blazorhosted ({tfm})] Capabilities: Baseline AddMethodToExistingType AddStaticFieldToExistingType AddInstanceFieldToExistingType NewTypeDefinition ChangeCustomAttributes UpdateParameters GenericUpdateMethod GenericAddMethodToExistingType GenericAddFieldToExistingType AddFieldRva AddExplicitInterfaceImplementation.");
}

[PlatformSpecificFact(TestPlatforms.Windows)] // https://github.com/dotnet/aspnetcore/issues/63759
Expand Down
Loading