Skip to content
Draft
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 @@ -61,6 +61,6 @@ public static void ApplyUpdate(Assembly assembly, ReadOnlySpan<byte> metadataDel
/// Returns true if the apply assembly update is enabled and available.
/// </summary>
[FeatureSwitchDefinition("System.Reflection.Metadata.MetadataUpdater.IsSupported")]
public static bool IsSupported { get; } = IsApplyUpdateSupported();
public static bool IsSupported { get; } = !IsHotReloadDisabled && IsApplyUpdateSupported();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace System.Reflection.Metadata
{
public static class MetadataUpdater
public static partial class MetadataUpdater
{
public static void ApplyUpdate(Assembly assembly, ReadOnlySpan<byte> metadataDelta, ReadOnlySpan<byte> ilDelta, ReadOnlySpan<byte> pdbDelta)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\TypeInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\TypeNameResolver.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Metadata\MetadataUpdateHandlerAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Metadata\MetadataUpdater.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ResolveEventArgs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ResolveEventHandler.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Resources\FastResourceComparer.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Reflection.Metadata
{
public static partial class MetadataUpdater
{
/// <summary>
/// Returns true if hot reload is explicitly disabled via the DOTNET_HOTRELOAD_DISABLED environment variable
/// or the System.Reflection.Metadata.HotReloadDisabled AppContext switch set to true.
/// </summary>
internal static bool IsHotReloadDisabled =>
Copy link
Member

@jkotas jkotas Jan 29, 2026

Choose a reason for hiding this comment

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

We have DOTNET_MODIFIABLE_ASSEMBLIES environment variable that is mean to control the set updateable assemblies. Currently, the only valid value is debug that makes all assemblies compiled as debug modifiable. Would it work to extend this env variable - e.g. recognize none as a value that makes all assemblies unmodifiable and thus disables metadata updater?

AppContextConfigHelper.GetBooleanConfig("System.Reflection.Metadata.HotReloadDisabled", "DOTNET_HOTRELOAD_DISABLED");
Copy link
Member

@jkotas jkotas Jan 29, 2026

Choose a reason for hiding this comment

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

This should not be called HotReload. HotReload is the larger Visual Studio feature. The name of the runtime sub-feature is metadata updater. Can this be called System.Reflection.Metadata.MetadataUpdater.IsSupported same name as the switch used for trimming MetadataUpdater support via FeatureSwitchDefinition?

}
}
16 changes: 16 additions & 0 deletions src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;

namespace System.Reflection.Metadata
Expand Down Expand Up @@ -1157,5 +1158,20 @@ void TestIncreaseMetadataRowSize()
Assert.Equal("x800", pars[0].Name);
});
}

[ConditionalFact(typeof(ApplyUpdateUtil), nameof(ApplyUpdateUtil.IsRemoteExecutorSupported))]
void TestHotReloadDisabledEnvironmentVariable()
{
// Check that DOTNET_HOTRELOAD_DISABLED=1 disables MetadataUpdater.IsSupported.
var options = new RemoteInvokeOptions();
options.StartInfo.EnvironmentVariables.Add(
ApplyUpdateUtil.DotNetModifiableAssembliesSwitch, ApplyUpdateUtil.DotNetModifiableAssembliesValue);
options.StartInfo.EnvironmentVariables.Add("DOTNET_HOTRELOAD_DISABLED", "1");

RemoteExecutor.Invoke(static () =>
{
Assert.False(MetadataUpdater.IsSupported);
}, options).Dispose();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace System.Reflection.Metadata
{
public static class MetadataUpdater
public static partial class MetadataUpdater
{
/// <summary>
/// Updates the specified assembly using the provided metadata, IL and PDB deltas.
Expand Down Expand Up @@ -51,7 +51,7 @@ public static void ApplyUpdate(Assembly assembly, ReadOnlySpan<byte> metadataDel
internal static string GetCapabilities() => s_ApplyUpdateCapabilities.Value;

[FeatureSwitchDefinition("System.Reflection.Metadata.MetadataUpdater.IsSupported")]
public static bool IsSupported { get; } = ApplyUpdateEnabled(justComponentCheck: 0) != 0;
public static bool IsSupported { get; } = !IsHotReloadDisabled && ApplyUpdateEnabled(justComponentCheck: 0) != 0;

private static readonly Lazy<string> s_ApplyUpdateCapabilities = new Lazy<string>(InitializeApplyUpdateCapabilities);

Expand Down
Loading