From ed1b70b2aad40fef0d2a8971361c14efd9ef970c Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 7 Sep 2026 22:46:18 +0200 Subject: [PATCH 01/18] [wasm] Dispose the metadata provider in WebcilReader The MetadataReaderProvider owns a memory-mapped section over the underlying stream. Leaving it to the finalizer keeps the file mapped inside long-lived MSBuild task hosts, so a later writer targeting the same path fails with "user-mapped section open". Observed as crossgen2 failing to rewrite an R2R image that an earlier ConvertDllsToWebcil probe had opened. --- src/tasks/Microsoft.NET.WebAssembly.Webcil/WebcilReader.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tasks/Microsoft.NET.WebAssembly.Webcil/WebcilReader.cs b/src/tasks/Microsoft.NET.WebAssembly.Webcil/WebcilReader.cs index 1975b55ecd666e..6c56803b1e15af 100644 --- a/src/tasks/Microsoft.NET.WebAssembly.Webcil/WebcilReader.cs +++ b/src/tasks/Microsoft.NET.WebAssembly.Webcil/WebcilReader.cs @@ -421,6 +421,10 @@ private unsafe ImmutableArray ReadSections() public void Dispose() { + // The provider owns a memory-mapped section over _stream; leaving it to the finalizer keeps the file + // mapped inside long-lived MSBuild task hosts and later writers fail with "user-mapped section open". + _metadataReaderProvider?.Dispose(); + _metadataReaderProvider = null; _stream.Dispose(); } From a30d8e1a20c32ead400b2d6678a42e274adc24cf Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 7 Sep 2026 22:46:18 +0200 Subject: [PATCH 02/18] [wasm] Match prebuilt R2R images by MVID instead of assembly version ConvertDllsToWebcil may stage a prebuilt ReadyToRun image in place of converting IL. The guard compared assembly versions, which almost never change between incremental builds, so a stale image compiled against a previous IL set passed the check and was staged. With cross-module inlining every image in a bundle belongs to one version bubble that the runtime validates by MVID at load, so that stale image is a startup fail-fast rather than a graceful fallback. Compare MVIDs instead, which turns the failure into a build-time fallback to IL conversion. Detect webcil-in-wasm by content (the wasm magic) rather than by extension, because a prebuilt image may still be named *.dll and PEReader would throw on it, returning null and silently bypassing the guard. The "unreadable identity means accept" fallback is preserved. --- .../WebcilInWasmSizesTests.cs | 37 +++++++++++++ .../ConvertDllsToWebCil.cs | 55 +++++++++++++++---- 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/WebcilInWasmSizesTests.cs b/src/mono/wasm/Wasm.Build.Tests/WebcilInWasmSizesTests.cs index b02b1d10bcbf81..fc0719e5f8610f 100644 --- a/src/mono/wasm/Wasm.Build.Tests/WebcilInWasmSizesTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/WebcilInWasmSizesTests.cs @@ -200,6 +200,43 @@ public void ConvertDllsToWebcil_StagesR2RWebcilWithDllExtension() Assert.True(r2rWebcil.SequenceEqual(File.ReadAllBytes(Path.Combine(outputDirectory, "R2RAssembly.wasm")))); } + [Fact] + public void ConvertDllsToWebcil_FallsBackToIL_WhenPrebuiltMvidMismatches() + { + // A prebuilt R2R image whose MVID differs from the candidate must never be staged: it would fail-fast + // at load against the current version bubble. Use two real assemblies with distinct MVIDs. + string candidatePath = typeof(System.Console).Assembly.Location; + string mismatchedAssembly = typeof(object).Assembly.Location; + Assert.True(File.Exists(candidatePath), $"Candidate assembly not found: '{candidatePath}'."); + Assert.True(File.Exists(mismatchedAssembly), $"Mismatched assembly not found: '{mismatchedAssembly}'."); + + using var directory = new TempDirectory(); + string prebuiltDirectory = Path.Combine(directory.Path, "prebuilt"); + string outputDirectory = Path.Combine(directory.Path, "output"); + Directory.CreateDirectory(prebuiltDirectory); + File.Copy(mismatchedAssembly, Path.Combine(prebuiltDirectory, "System.Console.wasm")); + + var candidate = new TaskItem(candidatePath); + candidate.SetMetadata("RelativePath", "System.Console.dll"); + + var task = new ConvertDllsToWebcil + { + BuildEngine = new TestBuildEngine(), + Candidates = [candidate], + IntermediateOutputPath = Path.Combine(directory.Path, "intermediate"), + IsEnabled = true, + OutputPath = outputDirectory, + PrebuiltR2RDirectory = prebuiltDirectory, + }; + + Assert.True(task.Execute()); + + // The output must be a freshly converted IL webcil (no R2R table), not the mismatched prebuilt. + using FileStream output = File.OpenRead(Path.Combine(outputDirectory, "System.Console.wasm")); + Assert.True(WebcilReader.TryReadWebcilInWasmSizes(output, out _, out int tableSize, out string? failureReason), failureReason); + Assert.Equal(0, tableSize); + } + private const byte SectionCustom = 0x00; private const byte SectionData = 0x0b; diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs index ffb61356b95730..d348d2bc79f0e0 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs @@ -260,7 +260,7 @@ private bool PrebuiltR2RMatchesCandidate(string candidateDllPath, string prebuil return true; } - candidateHasILCode = AssemblyHasILCode(candidateDllPath, out Version candidateVersion); + candidateHasILCode = AssemblyHasILCode(candidateDllPath, out Guid candidateMvid); if (!candidateHasILCode) { Log.LogMessage(MessageImportance.Low, @@ -268,15 +268,17 @@ private bool PrebuiltR2RMatchesCandidate(string candidateDllPath, string prebuil return false; } - Version prebuiltVersion = TryReadAssemblyVersion(prebuiltImagePath); + Guid? prebuiltMvid = TryReadMvid(prebuiltImagePath); - // If the prebuilt identity is unreadable, keep it (prior behavior) so the common case where - // every candidate is the pack's own assembly is never regressed. - if (prebuiltVersion is null || candidateVersion.Equals(prebuiltVersion)) + // Compare MVIDs, not assembly versions: with cross-module inlining every image in the bundle shares one + // version bubble the runtime checks by MVID at load, and the assembly version rarely changes between + // incremental builds, so a stale prebuilt R2R would pass a version check yet fail-fast at startup. + // If the prebuilt identity is unreadable, keep it (prior behavior). + if (prebuiltMvid is null || candidateMvid.Equals(prebuiltMvid.Value)) return true; Log.LogMessage(MessageImportance.Normal, - $"Not staging prebuilt R2R image '{prebuiltImagePath}' (v{prebuiltVersion}) for '{candidateDllPath}' (v{candidateVersion}): assembly version mismatch; converting IL instead."); + $"Not staging prebuilt R2R image '{prebuiltImagePath}' (MVID {prebuiltMvid}) for '{candidateDllPath}' (MVID {candidateMvid}): module version mismatch; converting IL instead."); return false; } @@ -287,12 +289,12 @@ private static bool IsR2RWebcil(string path) && tableSize > 0; } - private static bool AssemblyHasILCode(string path, out Version version) + private static bool AssemblyHasILCode(string path, out Guid mvid) { using FileStream stream = File.OpenRead(path); using var peReader = new PEReader(stream); MetadataReader metadataReader = peReader.GetMetadataReader(); - version = metadataReader.GetAssemblyDefinition().Version; + mvid = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); foreach (MethodDefinitionHandle methodDefinitionHandle in metadataReader.MethodDefinitions) { @@ -305,23 +307,52 @@ private static bool AssemblyHasILCode(string path, out Version version) return false; } - private static Version TryReadAssemblyVersion(string path) + private static Guid? TryReadMvid(string path) { try { using FileStream stream = File.OpenRead(path); - if (path.EndsWith(Utils.WebcilInWasmExtension, StringComparison.OrdinalIgnoreCase)) + // Detect webcil-in-wasm by content (the '\0asm' magic), not by extension: a prebuilt R2R image + // may still be named *.dll, and a PEReader would throw on it, returning null and silently + // bypassing the MVID guard. + if (IsWebcilInWasm(stream)) { using var webcilReader = new WebcilReader(stream, path); - return webcilReader.GetMetadataReader().GetAssemblyDefinition().Version; + MetadataReader webcilMetadata = webcilReader.GetMetadataReader(); + return webcilMetadata.GetGuid(webcilMetadata.GetModuleDefinition().Mvid); } using var peReader = new PEReader(stream); - return peReader.GetMetadataReader().GetAssemblyDefinition().Version; + MetadataReader peMetadata = peReader.GetMetadataReader(); + return peMetadata.GetGuid(peMetadata.GetModuleDefinition().Mvid); } catch { return null; } } + + // The WebAssembly module magic "\0asm" (0x00 0x61 0x73 0x6D). Leaves the stream position unchanged. + private static bool IsWebcilInWasm(Stream stream) + { + long position = stream.Position; + try + { + byte[] magic = new byte[4]; + int read = 0; + while (read < magic.Length) + { + int n = stream.Read(magic, read, magic.Length - read); + if (n == 0) + return false; + read += n; + } + + return magic[0] == 0x00 && magic[1] == 0x61 && magic[2] == 0x73 && magic[3] == 0x6D; + } + finally + { + stream.Position = position; + } + } } From d8c1d10b0f3a1730929a71d375d6944f9c1f3caa Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 7 Sep 2026 22:46:18 +0200 Subject: [PATCH 03/18] [wasm][coreclr] Move the ReadyToRun crossgen2 wiring into the WebAssembly SDK pack The crossgen2 resolution override lived in WasmApp.InTree.props, so only in-tree builds could produce per-app R2R; an out-of-tree app fell through to the base SDK, whose ReadyToRun pipeline predates wasm support and emits composite images. Composite strips the assembly manifest, so the runtime fails coreclr_initialize with 0x80131018 at startup. Ship the wiring from the pack instead, in CoreCLR-only files so no Mono path gains a branch. The import is gated on a props-time signal for an in-build crossgen2 (Crossgen2InBuildDir, or Crossgen2SdkOverridePropsPath in-tree, since liveBuilds.targets sets the former at targets-time), and is inert for stock consumers, which keep resolving crossgen2 through the base SDK. The override probes both the raw in-build layout (crossgen2 at the root) and the shipped Microsoft.NETCore.App.Crossgen2 pack layout (under tools/), and sets Crossgen2Tool directly, because the base SDK resolver keys on ResolvedCrossgen2Pack which a standalone app does not populate for a local build. It can be retired once a restorable wasm crossgen2 pack exists (dotnet/sdk#55785). --- src/mono/browser/build/WasmApp.InTree.props | 9 ---- .../browser/build/WasmApp.ReadyToRun.targets | 18 ------- ...ssembly.Browser.CoreCLR.ReadyToRun.targets | 49 +++++++++++++++++++ ....NET.Sdk.WebAssembly.Browser.CoreCLR.props | 26 ++++++++++ ...icrosoft.NET.Sdk.WebAssembly.Browser.props | 8 +++ 5 files changed, 83 insertions(+), 27 deletions(-) delete mode 100644 src/mono/browser/build/WasmApp.ReadyToRun.targets create mode 100644 src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets create mode 100644 src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.props diff --git a/src/mono/browser/build/WasmApp.InTree.props b/src/mono/browser/build/WasmApp.InTree.props index 27bc4d6b0670b7..2eb17ec0c0f94c 100644 --- a/src/mono/browser/build/WasmApp.InTree.props +++ b/src/mono/browser/build/WasmApp.InTree.props @@ -24,15 +24,6 @@ true - - - $(AfterMicrosoftNETSdkTargets);$(Crossgen2SdkOverrideTargetsPath) - $(AfterMicrosoftNETSdkTargets);$(MSBuildThisFileDirectory)WasmApp.ReadyToRun.targets - - - library diff --git a/src/mono/browser/build/WasmApp.ReadyToRun.targets b/src/mono/browser/build/WasmApp.ReadyToRun.targets deleted file mode 100644 index 1194acbb46f69d..00000000000000 --- a/src/mono/browser/build/WasmApp.ReadyToRun.targets +++ /dev/null @@ -1,18 +0,0 @@ - - - - - $([MSBuild]::NormalizePath('$(Crossgen2InBuildDir)', 'crossgen2$(ExeSuffix)')) - - - - - - - diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets new file mode 100644 index 00000000000000..b4cdfcd815852c --- /dev/null +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets @@ -0,0 +1,49 @@ + + + + + <_WasmCrossgen2ExeSuffix Condition="'$(ExeSuffix)' != ''">$(ExeSuffix) + <_WasmCrossgen2ExeSuffix Condition="'$(_WasmCrossgen2ExeSuffix)' == '' and $([MSBuild]::IsOSPlatform('windows'))">.exe + + <_WasmCrossgen2RootExe>$([MSBuild]::NormalizePath('$(Crossgen2InBuildDir)', 'crossgen2$(_WasmCrossgen2ExeSuffix)')) + <_WasmCrossgen2ToolsExe>$([MSBuild]::NormalizePath('$([MSBuild]::NormalizeDirectory('$(Crossgen2InBuildDir)', 'tools'))', 'crossgen2$(_WasmCrossgen2ExeSuffix)')) + $(_WasmCrossgen2RootExe) + $(_WasmCrossgen2ToolsExe) + $(_WasmCrossgen2RootExe) + + <_WasmResolvedCrossgen2Dir>$([MSBuild]::EnsureTrailingSlash($([System.IO.Path]::GetDirectoryName('$(Crossgen2Path)')))) + + <_WasmR2RTargetOS Condition="'$(TargetOS)' != ''">$(TargetOS) + <_WasmR2RTargetOS Condition="'$(_WasmR2RTargetOS)' == ''">browser + <_WasmR2RTargetArch Condition="'$(TargetArchitecture)' != ''">$(TargetArchitecture) + <_WasmR2RTargetArch Condition="'$(_WasmR2RTargetArch)' == ''">wasm + + + + + + + diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.props b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.props new file mode 100644 index 00000000000000..0dc363e8833f6f --- /dev/null +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.props @@ -0,0 +1,26 @@ + + + + + + $(AfterMicrosoftNETSdkTargets);$(Crossgen2SdkOverrideTargetsPath) + $(AfterMicrosoftNETSdkTargets);$(MSBuildThisFileDirectory)Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets + + diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.props b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.props index df683130079f51..902c930716bcf8 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.props +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.props @@ -40,6 +40,14 @@ Copyright (c) .NET Foundation. All rights reserved. true + + + From b4110b188da67522d0161deaa2a677bb4375130f Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 7 Sep 2026 22:46:18 +0200 Subject: [PATCH 04/18] [wasm][coreclr] Wire ReadyToRun into the WebAssembly publish and build pipeline Implements the two modes: a dev-loop build stages the prebuilt framework R2R images from the runtime pack and ships the app as IL, while publish crossgens the whole closure per app, trimmed or untrimmed. The main correctness problems addressed: - Per-app R2R images are named .wasm, but ComputeWasmPublishAssets classifies managed assemblies by the .dll extension, so the images were treated as native and leaked to the publish root, leaving the boot config with no coreAssembly. Restore the IL .dll in the publish list so ConvertDllsToWebcil stages the image from PrebuiltR2RDirectory. This has to happen in both the outer and nested passes: a native relink crossgens inside WasmNestedPublishApp, where ProcessPublishFilesForWasm is never scheduled, and _GatherWasmFilesToPublish filters to .dll, dropping every compiled assembly while exiting 0. - ILLink stamps PostprocessAssembly on its own collection rather than ResolvedFileToPublish on the Blazor/static-web-assets route, so the mainline compile list was empty and crossgen2 never ran. - A trimmed publish flow served the full copy-local set from the runtime pack mixed with the trimmed closure, which mixes version bubbles and lets an untrimmed assembly call a member ILLink removed from the trimmed framework. Restrict the served set to the linker output and repoint it there. - Flag flips left derived outputs behind. Static web assets are content-fingerprinted, so a re-stage adds a new name beside the old file instead of replacing it, leaving two copies of an assembly from two different version bubbles. Record the mode and drop the derived outputs when it changes. - Per-app crossgen inputs are deliberately conservative: cross-module inlining means any change must recompile every image, and stale images in obj/R2R are pruned. Composite and non-wasm container formats are rejected with a comprehensible error instead of producing images that fail at startup, and a missing crossgen2 is reported at the point of use. PublishReadyToRun defaults to false; flipping it belongs to the codegen-quality work stream. --- ...ET.Sdk.WebAssembly.Browser.CoreCLR.targets | 222 ++++++++++++++++-- src/mono/sample/wasm/Directory.Build.props | 1 + 2 files changed, 203 insertions(+), 20 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets index 70d3fc52add4e7..be06f9d64d7df0 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets @@ -11,12 +11,11 @@ Copyright (c) .NET Foundation. All rights reserved. --> - - true + false - --> + - - <_WasmRuntimePackR2RDir Condition="'$(_WasmRuntimePackR2RDir)' == '' and '$(_RuntimePackNativeDir)' != ''">$([MSBuild]::NormalizeDirectory('$(_RuntimePackNativeDir)', 'r2r')) + + <_WasmCoreClrUnderPublish Condition="'$(_IsPublishing)' == 'true' or '$(WasmBuildingForNestedPublish)' == 'true' or '$(WasmBuildOnlyAfterPublish)' == 'true'">true + <_WasmPublishR2RDir Condition="'$(_WasmPublishR2RDir)' == ''">$([MSBuild]::NormalizeDirectory('$(IntermediateOutputPath)', 'R2R')) - <_WasmBuildPrebuiltR2RDirectory Condition="'$(PublishTrimmed)' == 'true'">$(_WasmPublishR2RDir) - <_WasmBuildPrebuiltR2RDirectory Condition="'$(PublishTrimmed)' != 'true'">$(_WasmRuntimePackR2RDir) - <_WasmPublishPrebuiltR2RDirectory Condition="'$(PublishTrimmed)' == 'true'">$(_WasmPublishR2RDir) - <_WasmPublishPrebuiltR2RDirectory Condition="'$(PublishTrimmed)' != 'true'">$(_WasmRuntimePackR2RDir) + <_WasmPublishPrebuiltR2RDirectory>$(_WasmPublishR2RDir) + <_WasmBuildPrebuiltR2RDirectory Condition="'$(_WasmCoreClrUnderPublish)' == 'true'">$(_WasmPublishR2RDir) + <_WasmBuildPrebuiltR2RDirectory Condition="'$(_WasmCoreClrUnderPublish)' != 'true'">$(_WasmRuntimePackR2RDir) + + + + + + + + + + + + + + <_WasmFrameworkCopyToOutputDirectory>Never + + + + + + + + + + + + + + + + + <_WasmWebcilStampProperty Include="PublishTrimmed" /> + + + + + + <_WasmCoreClrModeStamp>$(IntermediateOutputPath)wasm-coreclr-mode.stamp + <_WasmCoreClrMode>PublishReadyToRun=$(PublishReadyToRun);PublishTrimmed=$(PublishTrimmed);WasmBuildNative=$(WasmBuildNative);WasmNativeDebugSymbols=$(WasmNativeDebugSymbols);WasmNativeStrip=$(WasmNativeStrip) + + <_WasmCoreClrServedAssetsDir Condition="'$(WasmRuntimeAssetsLocation)' == '' or '$(WasmRuntimeAssetsLocation)' == '_framework'">$(OutDir)wwwroot\_framework\ + + + + + <_WasmCoreClrStaleModeOutput Include="$(IntermediateOutputPath)R2R\*.wasm;$(IntermediateOutputPath)R2R\*.dll" /> + <_WasmCoreClrStaleModeOutput Include="$(IntermediateOutputPath)webcil\**\*.wasm" /> + + <_WasmCoreClrStaleModeOutput Include="$(IntermediateOutputPath)wasm\for-build\dotnet.native.*" /> + <_WasmCoreClrStaleModeOutput Include="$(IntermediateOutputPath)wasm\for-publish\dotnet.native.*" /> + + <_WasmCoreClrStaleModeOutput Include="$(_WasmCoreClrServedAssetsDir)**\*" Condition="'$(_WasmCoreClrServedAssetsDir)' != ''" /> + + + + + + + + + <_WasmTrimmedClosureDir Condition="'$(_WasmTrimmedClosureDir)' == '' and '$(IntermediateLinkDir)' != ''">$(IntermediateLinkDir) + <_WasmTrimmedClosureDir Condition="'$(_WasmTrimmedClosureDir)' == ''">$([MSBuild]::NormalizeDirectory('$(IntermediateOutputPath)', 'linked')) + + <_WasmCoreClrHasFinalAssemblies Condition="'@(WasmAssembliesFinal)' != ''">true + + + + + <_WasmInTrimmedClosure>false + + + <_WasmInTrimmedClosure>true + + + + + <_WasmTrimmedClosureRedirect Include="@(ReferenceCopyLocalPaths)" Condition="'%(_WasmInTrimmedClosure)' == 'true'" /> + + + + + + + + + + + + + + + @@ -112,4 +262,36 @@ Copyright (c) .NET Foundation. All rights reserved. DependsOnTargets="CreateReadyToRunImages" BeforeTargets="ProcessPublishFilesForWasm" /> + + + + <_ReadyToRunCompilerInputs Include="@(_ReadyToRunCompileList);@(_ReadyToRunAssembliesToReference)" /> + + <_ReadyToRunCompilerInputs Include="$(_WasmResolvedCrossgen2Dir)crossgen2*;$(_WasmResolvedCrossgen2Dir)clrjit_universal_wasm_*" + Condition="'$(_WasmResolvedCrossgen2Dir)' != ''" /> + + + + + + + + <_WasmStalePerAppR2R Include="$(_WasmPublishR2RDir)*.wasm;$(_WasmPublishR2RDir)*.dll" + Exclude="@(_ReadyToRunCompileList->'%(OutputR2RImage)')" /> + + + + diff --git a/src/mono/sample/wasm/Directory.Build.props b/src/mono/sample/wasm/Directory.Build.props index c2daa07e81b05a..b63362260ba5e2 100644 --- a/src/mono/sample/wasm/Directory.Build.props +++ b/src/mono/sample/wasm/Directory.Build.props @@ -53,6 +53,7 @@ $(Nested_RuntimeFlavor) $(Nested_PublishReadyToRun) + false From 9330b802b2e757b78b8772ed3c11c7d20237643a Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 7 Sep 2026 22:46:19 +0200 Subject: [PATCH 05/18] [wasm][coreclr] Trigger the native relink on WasmBuildNative as well The four relink triggers keyed solely on IsBrowserWasmProject, which a Blazor app leaves unset because it resolves the wasm RID late, so WasmBuildNative=true was a silent no-op there and the app shipped the prebuilt dotnet.native.wasm from the runtime pack. OR in WasmBuildNative, which is unambiguous: this file is imported only for CoreCLR browser-wasm apps. Kept as an OR so IsBrowserWasmProject, which also steers ICU and tzdata skipping, is never forced on. Fixes dotnet/runtime#133185 --- src/mono/browser/build/BrowserWasmApp.CoreCLR.targets | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index 4c2944bb0615fd..4bc4813abbec69 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -221,11 +221,14 @@ + + @@ -277,7 +280,7 @@ From d836bcd262817127c0bd088994851663497761db Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 7 Sep 2026 22:46:19 +0200 Subject: [PATCH 06/18] [wasm] Do not reference ILLink.Tasks from the wasm nested publish The nested publish evaluates ILLink.Tasks.csproj with different global properties, so MSBuild builds it a second time and copies obj to bin over the assembly the outer pass has already loaded, failing with MSB3027. The outer pass has built the task by the time the nested publish runs, so the reference is redundant there as well as harmful. --- eng/liveILLink.targets | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eng/liveILLink.targets b/eng/liveILLink.targets index d51194e4c25619..e31b6df4417f52 100644 --- a/eng/liveILLink.targets +++ b/eng/liveILLink.targets @@ -30,7 +30,11 @@ + Date: Mon, 7 Sep 2026 22:46:19 +0200 Subject: [PATCH 07/18] [wasm][coreclr] Add Wasm.Build.Tests coverage for ReadyToRun Covers the dev-loop build (framework R2R staged from the runtime pack, no per-app crossgen), publish trimmed and untrimmed (whole closure compiled per app), both with and without a native relink, and the disabled case. Each publish case drives Home, Counter and Weather in a real browser, which is what distinguishes a bundle that boots from one that merely looks staged. The assertions target failures seen during bring-up that still exit 0: assemblies missing from the staged set relative to the linker closure, duplicate fingerprinted copies of one assembly, managed assemblies leaking outside _framework, and per-app crossgen running (or not) for the mode. Adds the Weather page that the nav menu of the test app already linked to, and ships the in-build crossgen2 plus the wasm-aware Crossgen2Tasks shim as Helix correlation payload so the tests can resolve them there. --- src/libraries/sendtohelix-browser.targets | 9 + .../Common/EnvironmentVariables.cs | 1 + .../wasm/Wasm.Build.Tests/ReadyToRunTests.cs | 255 ++++++++++++++++++ .../App/Pages/Weather.razor | 60 +++++ 4 files changed, 325 insertions(+) create mode 100644 src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs create mode 100644 src/mono/wasm/testassets/BlazorBasicTestApp/App/Pages/Weather.razor diff --git a/src/libraries/sendtohelix-browser.targets b/src/libraries/sendtohelix-browser.targets index 429078ca20d274..1f0e426b752b99 100644 --- a/src/libraries/sendtohelix-browser.targets +++ b/src/libraries/sendtohelix-browser.targets @@ -290,6 +290,15 @@ + + + + + + diff --git a/src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs b/src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs index fb12b0830c7a0f..2bddb349b84042 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs @@ -28,5 +28,6 @@ internal static class EnvironmentVariables internal static readonly string? WasiSdkPath = Environment.GetEnvironmentVariable("WASI_SDK_PATH"); internal static readonly bool WorkloadsTestPreviousVersions = Environment.GetEnvironmentVariable("WORKLOADS_TEST_PREVIOUS_VERSIONS") is "true"; internal static readonly string? RuntimeFlavor = Environment.GetEnvironmentVariable("RUNTIME_FLAVOR_FOR_TESTS"); + internal static readonly string? BaseDir = Environment.GetEnvironmentVariable("BASE_DIR"); } } diff --git a/src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs b/src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs new file mode 100644 index 00000000000000..e24df9b39bd1b7 --- /dev/null +++ b/src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs @@ -0,0 +1,255 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.NET.WebAssembly.Webcil; +using Microsoft.Playwright; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests +{ + // CoreCLR browser-wasm ships ReadyToRun images as webcil-in-wasm; a non-zero R2R table size in the + // System.Private.CoreLib webcil is the marker that R2R was produced and staged. These tests cover R2R in + // both build and publish, with and without IL trimming, driving all pages in a real browser. CoreCLR only. + public class ReadyToRunTests : BlazorWasmTestBase + { + private const int InteractionTimeoutMs = 60_000; + + public ReadyToRunTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + _enablePerTestCleanup = true; + } + + [ConditionalTheory(typeof(BuildTestBase), nameof(IsCoreClrRuntime))] + [InlineData(Configuration.Release)] + [TestCategory("no-workload")] + public async Task BuildRunAllPages(Configuration config) + { + // A build stages the prebuilt framework R2R images from the runtime pack (no per-app crossgen2). + ProjectInfo info = CopyTestAsset(config, aot: false, TestAsset.BlazorBasicTestApp, "r2r_build", + extraProperties: "true"); + BlazorBuild(info, config); + + string webcilDir = GetBuildWebcilDir(config); + AssertCoreLibReadyToRun(webcilDir, expectReadyToRun: true); + AssertNoDuplicateAssemblies(webcilDir); + AssertPerAppCrossgenRan(config, expected: false); + + await RunForBuildWithDotnetRun(new BlazorRunOptions(config, + CheckCounter: false, + ExecuteAfterLoaded: (_, page) => InteractAllPagesAsync(page))); + } + + [ConditionalTheory(typeof(BuildTestBase), nameof(IsCoreClrRuntime))] + [InlineData(Configuration.Release, /*trimmed*/ true)] + [InlineData(Configuration.Release, /*trimmed*/ false)] + [TestCategory("no-workload")] + public Task PublishRunAllPages(Configuration config, bool trimmed) + => PublishRunAllPagesCore(config, trimmed, nativeRelink: false); + + // CoreCLR relinks dotnet.native.wasm for Blazor when WasmBuildNative=true. The relink triggers key on + // WasmBuildNative in addition to IsBrowserWasmProject, so a late-resolved wasm RID cannot make the + // relink a silent no-op. See dotnet/runtime#133185. + [ConditionalTheory(typeof(BuildTestBase), nameof(IsCoreClrRuntime))] + [InlineData(Configuration.Release, /*trimmed*/ true)] + [InlineData(Configuration.Release, /*trimmed*/ false)] + [TestCategory("no-workload")] + public Task PublishRunAllPagesNativeRelink(Configuration config, bool trimmed) + => PublishRunAllPagesCore(config, trimmed, nativeRelink: true); + + private async Task PublishRunAllPagesCore(Configuration config, bool trimmed, bool nativeRelink) + { + // Publish runs per-app crossgen2 for the whole closure, trimmed or not: even the untrimmed CoreLib + // is a per-app image, not the runtime pack's. nativeRelink also relinks dotnet.native.wasm. + string label = $"r2r_pub_{(trimmed ? "trim" : "notrim")}{(nativeRelink ? "_native" : "")}"; + ProjectInfo info = CopyTestAsset(config, aot: false, TestAsset.BlazorBasicTestApp, label, + extraProperties: $"true{(trimmed ? "true" : "false")}"); + string extraArgs = GetR2RBuildArgs(config); + if (nativeRelink) + { + // CoreCLR relinks dotnet.native.wasm via the in-tree targets + EMSDK_PATH, not the browser + // workload; WasmBuildNative=true otherwise forces UsingBrowserRuntimeWorkload=true, which + // demands the (uninstalled) wasm-tools workload and disables the CoreCLR relink targets. + extraArgs += " -p:WasmBuildNative=true -p:UsingBrowserRuntimeWorkload=false"; + } + BlazorPublish(info, config, new PublishOptions(UseCache: false, ExtraMSBuildArgs: extraArgs), + // Assert the native runtime was actually relinked (from obj), not the runtime-pack prebuilt, + // so the relink is proven rather than silently skipped. See dotnet/runtime#133185. + isNativeBuild: nativeRelink ? true : (bool?)null); + + string frameworkDir = GetBlazorBinFrameworkDir(config, forPublish: true); + AssertCoreLibReadyToRun(frameworkDir, expectReadyToRun: true); + AssertNoDuplicateAssemblies(frameworkDir); + AssertNoManagedAssembliesOutsideFramework(frameworkDir); + AssertTrimmedClosureIsFullyStaged(config, frameworkDir); + AssertPerAppCrossgenRan(config, expected: true); + + await RunForPublishWithWebServer(new BlazorRunOptions(config, + CheckCounter: false, + ExecuteAfterLoaded: (_, page) => InteractAllPagesAsync(page))); + } + + [ConditionalTheory(typeof(BuildTestBase), nameof(IsCoreClrRuntime))] + [InlineData(Configuration.Release)] + [TestCategory("no-workload")] + public void FrameworkAssembliesAreNotReadyToRunWhenDisabled(Configuration config) + { + ProjectInfo info = CopyTestAsset(config, aot: false, TestAsset.BlazorBasicTestApp, "r2r_off", + extraProperties: "false"); + BlazorBuild(info, config); + + AssertCoreLibReadyToRun(GetBuildWebcilDir(config), expectReadyToRun: false); + AssertPerAppCrossgenRan(config, expected: false); + } + + // Navigate Home -> Counter (increment 0 -> 1) -> Weather (forecast rows) -> Home, asserting content + // at each step. DetectRuntimeFailures (default) fails the run on any unhandled managed/JS exception. + private static async Task InteractAllPagesAsync(IPage page) + { + var counterLink = page.Locator("text=Counter"); + await counterLink.WaitForAsync(new() { State = WaitForSelectorState.Visible, Timeout = InteractionTimeoutMs }); + await counterLink.ClickAsync(new() { Timeout = InteractionTimeoutMs }); + + var status = page.Locator("p[role='status']"); + await status.WaitForAsync(new() { State = WaitForSelectorState.Visible, Timeout = InteractionTimeoutMs }); + Assert.Equal("Current count: 0", await status.InnerHTMLAsync()); + + var clickMe = page.Locator("text=\"Click me\""); + await clickMe.ClickAsync(new() { Timeout = InteractionTimeoutMs }); + await page.WaitForFunctionAsync( + """selector => document.querySelector(selector)?.textContent?.trim() === 'Current count: 1'""", + "p[role='status']", + new() { Timeout = InteractionTimeoutMs }); + + var weatherLink = page.Locator("text=Weather"); + await weatherLink.WaitForAsync(new() { State = WaitForSelectorState.Visible, Timeout = InteractionTimeoutMs }); + await weatherLink.ClickAsync(new() { Timeout = InteractionTimeoutMs }); + await page.WaitForFunctionAsync( + "() => document.querySelectorAll('table tbody tr').length > 0", + null, + new() { Timeout = InteractionTimeoutMs }); + + var homeLink = page.Locator("text=Home"); + await homeLink.WaitForAsync(new() { State = WaitForSelectorState.Visible, Timeout = InteractionTimeoutMs }); + await homeLink.ClickAsync(new() { Timeout = InteractionTimeoutMs }); + await page.Locator("h1").WaitForAsync(new() { State = WaitForSelectorState.Visible, Timeout = InteractionTimeoutMs }); + } + + private string GetBuildWebcilDir(Configuration config) => + Path.Combine(_projectDir, "obj", config.ToString(), DefaultTargetFrameworkForBlazor, "webcil"); + + private string GetObjSubDir(Configuration config, string name) => + Path.Combine(_projectDir, "obj", config.ToString(), DefaultTargetFrameworkForBlazor, name); + + // Static web assets are fingerprinted as .<10 chars>.wasm. The pattern is deliberately + // case-sensitive: a case-insensitive match also eats real trailing segments like ".Components". + private static string StripFingerprint(string filePath) + => Regex.Replace(Path.GetFileNameWithoutExtension(filePath), @"\.[a-z0-9]{10}$", string.Empty); + + private static string[] GetStagedAssemblyNames(string frameworkDir) + => Directory.EnumerateFiles(frameworkDir, "*.wasm") + .Where(f => !Path.GetFileName(f).StartsWith("dotnet", System.StringComparison.Ordinal)) + .Select(StripFingerprint) + .ToArray(); + + // Fingerprinted assets land beside their predecessors instead of replacing them, so a stale copy of an + // assembly survives as a second file and the runtime can bind the wrong version bubble. + private static void AssertNoDuplicateAssemblies(string frameworkDir) + { + string[] duplicates = GetStagedAssemblyNames(frameworkDir) + .GroupBy(n => n, System.StringComparer.Ordinal) + .Where(g => g.Count() > 1) + .Select(g => $"{g.Key} x{g.Count()}") + .OrderBy(n => n, System.StringComparer.Ordinal) + .ToArray(); + + Assert.True(duplicates.Length == 0, + $"Duplicate assemblies staged in '{frameworkDir}': {string.Join(", ", duplicates)}"); + } + + // A .wasm-named R2R image that is not routed back to a managed asset is treated as native and lands in + // the publish root, leaving the boot config without it. See dotnet/runtime#121257. + private static void AssertNoManagedAssembliesOutsideFramework(string frameworkDir) + { + string? wwwrootDir = Path.GetDirectoryName(frameworkDir); + if (wwwrootDir is null || !Directory.Exists(wwwrootDir)) + return; + + string[] stray = Directory.EnumerateFiles(wwwrootDir, "*.wasm").Select(Path.GetFileName).ToArray()!; + Assert.True(stray.Length == 0, + $"Managed assemblies leaked outside _framework into '{wwwrootDir}': {string.Join(", ", stray)}"); + } + + // Losing every crossgen'd assembly still exits 0 and can still leave a loadable-looking bundle, so + // compare the staged set against the linker's closure rather than trusting the exit code. + private void AssertTrimmedClosureIsFullyStaged(Configuration config, string frameworkDir) + { + string linkedDir = GetObjSubDir(config, "linked"); + if (!Directory.Exists(linkedDir)) + return; + + HashSet staged = new(GetStagedAssemblyNames(frameworkDir), System.StringComparer.Ordinal); + string[] missing = Directory.EnumerateFiles(linkedDir, "*.dll") + .Select(Path.GetFileNameWithoutExtension) + .Where(name => !staged.Contains(name!)) + .OrderBy(name => name, System.StringComparer.Ordinal) + .ToArray()!; + + Assert.True(missing.Length == 0, + $"Assemblies in the trimmed closure but missing from '{frameworkDir}': {string.Join(", ", missing)}"); + } + + // The dev loop serves the runtime pack's prebuilt native/r2r images; only publish crossgens per app. + private void AssertPerAppCrossgenRan(Configuration config, bool expected) + { + string r2rDir = GetObjSubDir(config, "R2R"); + int imageCount = Directory.Exists(r2rDir) ? Directory.EnumerateFiles(r2rDir).Count() : 0; + + if (expected) + Assert.True(imageCount > 0, $"Expected per-app ReadyToRun images under '{r2rDir}'."); + else + Assert.True(imageCount == 0, $"Expected no per-app crossgen2 output, found {imageCount} file(s) under '{r2rDir}'."); + } + + // In-tree publish crossgen2: the base SDK can't resolve a wasm crossgen2 and emits composite R2R + // (which strips the assembly manifest and won't load), so point the CoreCLR R2R override at the + // crossgen2 built under BASE_DIR and activate the wasm-aware Crossgen2Tasks shim. All inert if + // BASE_DIR / the directories aren't present. + private static string GetR2RBuildArgs(Configuration config) + { + string? baseDir = EnvironmentVariables.BaseDir; + if (string.IsNullOrEmpty(baseDir)) + return string.Empty; + + string hostArch = System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); + string crossgenDir = Path.Combine(baseDir, "coreclr", $"browser.wasm.{config}", hostArch, "crossgen2"); + string shimDir = Path.Combine(baseDir, "Crossgen2Tasks", config.ToString()); + string shimProps = Path.Combine(shimDir, "Microsoft.NET.CrossGen.props"); + string shimTargets = Path.Combine(shimDir, "Microsoft.NET.CrossGen.targets"); + return $"-p:Crossgen2InBuildDir={crossgenDir} -p:Crossgen2SdkOverridePropsPath={shimProps} -p:Crossgen2SdkOverrideTargetsPath={shimTargets}"; + } + + private static void AssertCoreLibReadyToRun(string frameworkDir, bool expectReadyToRun) + { + string? coreLib = Directory.EnumerateFiles(frameworkDir, "System.Private.CoreLib*.wasm").FirstOrDefault(); + Assert.True(coreLib is not null, $"Expected a System.Private.CoreLib webcil under '{frameworkDir}'."); + + using FileStream stream = File.OpenRead(coreLib!); + bool ok = WebcilReader.TryReadWebcilInWasmSizes(stream, out _, out int tableSize, out string? failureReason); + Assert.True(ok, failureReason); + + if (expectReadyToRun) + Assert.True(tableSize > 0, $"Expected a ReadyToRun table in '{coreLib}', but the R2R table size was 0."); + else + Assert.Equal(0, tableSize); + } + } +} diff --git a/src/mono/wasm/testassets/BlazorBasicTestApp/App/Pages/Weather.razor b/src/mono/wasm/testassets/BlazorBasicTestApp/App/Pages/Weather.razor new file mode 100644 index 00000000000000..1664dd16d557de --- /dev/null +++ b/src/mono/wasm/testassets/BlazorBasicTestApp/App/Pages/Weather.razor @@ -0,0 +1,60 @@ +@page "/weather" +@using System.Linq + +Weather + +

Weather

+ +

This component demonstrates showing data.

+ +@if (forecasts == null) +{ +

Loading...

+} +else +{ + + + + + + + + + + @foreach (var forecast in forecasts) + { + + + + + + } + +
DateTemp. (C)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.Summary
+} + +@code { + private WeatherForecast[]? forecasts; + + protected override async Task OnInitializedAsync() + { + // Async load with local data (no HttpClient) so the page is self-contained on wasm. + await Task.Yield(); + var startDate = DateOnly.FromDateTime(DateTime.Now); + var summaries = new[] { "Freezing", "Cool", "Mild", "Warm", "Hot" }; + forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = startDate.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = summaries[Random.Shared.Next(summaries.Length)] + }).ToArray(); + } + + private sealed class WeatherForecast + { + public DateOnly Date { get; set; } + public int TemperatureC { get; set; } + public string? Summary { get; set; } + } +} From e00a2dd0070cd648d7d039e00746027ae9dc3fba Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 7 Sep 2026 22:46:20 +0200 Subject: [PATCH 08/18] [wasm][coreclr] Run the JavaScript interop tests with ReadyToRun Gives the R2R pipeline a library-test vehicle: this suite exercises the trimmed publish flow, where the served bundle must be exactly the linker closure staged as per-app R2R images. CoreCLR only; Mono is unaffected. tests.browser.targets already implies PublishTrimmed from PublishReadyToRun. --- .../System.Runtime.InteropServices.JavaScript.Tests.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj index d5a40b9c28f982..f8338254bfdda4 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj @@ -20,6 +20,9 @@ Suppress the NU1511 warning in the whole project as putting it on a P2P doesn't work: https://github.com/NuGet/Home/issues/14121 --> $(NoWarn);NU1511 false + + + true From 9cbc88f4d359687655e3e148b2bf1cf4f9c9e8df Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 8 Sep 2026 14:53:19 +0200 Subject: [PATCH 09/18] [wasm][coreclr] Resolve crossgen2 for ReadyToRun via the SDK-restored pack on Helix Fixes the gap CI uncovered: the no-workload Wasm.Build.Tests Helix leg has no in-build crossgen2, so the per-app publish tests aborted with a NullReferenceException from the base SDK PrepareForReadyToRunCompilation task (empty Crossgen2Tool). The SDK does restore a crossgen2 pack when PublishReadyToRun is set; the failure was that the override ignored it. - The ResolveReadyToRunCompilers override no longer shadows the base/shim resolver into an empty tool. It uses the in-build crossgen2 when present (in-tree / dev builds) and otherwise resolves the tool from the SDK-restored crossgen2 pack (@(ResolvedCrossgen2Pack)), so @(Crossgen2Tool) is never left empty when a tool is available. - The wasm-aware Crossgen2Tasks shim (the wasm-container crossgen tasks, which the base SDK still lacks) now ships as a Helix correlation payload gated on its own path rather than on the crossgen2 bin dir, which the WBT test leg does not carry, so the shim actually reaches the worker. - GetR2RBuildArgs passes Crossgen2InBuildDir and the shim override paths only when each exists under BASE_DIR. On the no-workload leg crossgen2 is resolved from the SDK pack, so passing a non-existent Crossgen2InBuildDir would otherwise break the call-helpers generator. - The Wasm.Build.Tests CoreCLR project setup pins KnownCrossgen2Pack to the locally built pack version, so the SDK restores the wasm-capable crossgen2 pack from the local feed rather than a default one. Validated in-tree: console-node ReadyToRun publish builds and runs under node. --- src/libraries/sendtohelix-browser.targets | 15 +++++--- ...ssembly.Browser.CoreCLR.ReadyToRun.targets | 35 ++++++++++++++----- .../wasm/Wasm.Build.Tests/ReadyToRunTests.cs | 19 +++++++--- .../Templates/WasmTemplateTestsBase.cs | 3 ++ 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/src/libraries/sendtohelix-browser.targets b/src/libraries/sendtohelix-browser.targets index 1f0e426b752b99..4762716bdfc717 100644 --- a/src/libraries/sendtohelix-browser.targets +++ b/src/libraries/sendtohelix-browser.targets @@ -290,14 +290,19 @@
- - - + + + + + diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets index b4cdfcd815852c..5d90061266add3 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets @@ -6,30 +6,31 @@ ReadyToRun compiler override for CoreCLR browser-wasm, appended to AfterMicrosof Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.props so it imports after Microsoft.NET.Publish.targets and its ResolveReadyToRunCompilers wins. Mirrors AppleBuild.ReadyToRun.targets. -Sets the Crossgen2Tool item directly, because the base SDK resolver keys on ResolvedCrossgen2Pack, which a -standalone SDK app does not populate for a local build. Retire this in favour of that once the crossgen2 -pack ships as a restorable package. Inert for normal consumers. +When an in-build crossgen2 is available (in-tree / dev builds) the Crossgen2Tool item is set from it. Otherwise +the SDK-resolved crossgen2 pack (@(ResolvedCrossgen2Pack), which the SDK restores when PublishReadyToRun is set) +is used - so this override never shadows that resolution into an empty tool. Inert for normal consumers. Copyright (c) .NET Foundation. All rights reserved. *********************************************************************************************** --> + <_WasmCrossgen2InBuildUsable Condition="'$(Crossgen2InBuildDir)' != '' and Exists('$(Crossgen2InBuildDir)')">true <_WasmCrossgen2ExeSuffix Condition="'$(ExeSuffix)' != ''">$(ExeSuffix) <_WasmCrossgen2ExeSuffix Condition="'$(_WasmCrossgen2ExeSuffix)' == '' and $([MSBuild]::IsOSPlatform('windows'))">.exe <_WasmCrossgen2RootExe>$([MSBuild]::NormalizePath('$(Crossgen2InBuildDir)', 'crossgen2$(_WasmCrossgen2ExeSuffix)')) <_WasmCrossgen2ToolsExe>$([MSBuild]::NormalizePath('$([MSBuild]::NormalizeDirectory('$(Crossgen2InBuildDir)', 'tools'))', 'crossgen2$(_WasmCrossgen2ExeSuffix)')) - $(_WasmCrossgen2RootExe) - $(_WasmCrossgen2ToolsExe) - $(_WasmCrossgen2RootExe) + $(_WasmCrossgen2RootExe) + $(_WasmCrossgen2ToolsExe) + $(_WasmCrossgen2RootExe) - <_WasmResolvedCrossgen2Dir>$([MSBuild]::EnsureTrailingSlash($([System.IO.Path]::GetDirectoryName('$(Crossgen2Path)')))) + <_WasmResolvedCrossgen2Dir Condition="'$(Crossgen2Path)' != ''">$([MSBuild]::EnsureTrailingSlash($([System.IO.Path]::GetDirectoryName('$(Crossgen2Path)')))) @@ -39,11 +40,27 @@ Copyright (c) .NET Foundation. All rights reserved. <_WasmR2RTargetArch Condition="'$(_WasmR2RTargetArch)' == ''">wasm - + + + + + + + + diff --git a/src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs b/src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs index e24df9b39bd1b7..3d2880b9f84fe3 100644 --- a/src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs @@ -219,10 +219,11 @@ private void AssertPerAppCrossgenRan(Configuration config, bool expected) Assert.True(imageCount == 0, $"Expected no per-app crossgen2 output, found {imageCount} file(s) under '{r2rDir}'."); } - // In-tree publish crossgen2: the base SDK can't resolve a wasm crossgen2 and emits composite R2R - // (which strips the assembly manifest and won't load), so point the CoreCLR R2R override at the - // crossgen2 built under BASE_DIR and activate the wasm-aware Crossgen2Tasks shim. All inert if - // BASE_DIR / the directories aren't present. + // Wire the wasm-aware Crossgen2Tasks shim (the wasm-container crossgen tasks) so R2R images use the + // right container, and the in-build crossgen2 when this leg shipped it. Each is passed only when present + // under BASE_DIR: the no-workload leg ships the shim but resolves crossgen2 itself from the SDK pack (the + // SDK restores it when PublishReadyToRun is set), so passing a non-existent Crossgen2InBuildDir there + // would break the call-helpers generator. All inert if BASE_DIR is unset. private static string GetR2RBuildArgs(Configuration config) { string? baseDir = EnvironmentVariables.BaseDir; @@ -234,7 +235,15 @@ private static string GetR2RBuildArgs(Configuration config) string shimDir = Path.Combine(baseDir, "Crossgen2Tasks", config.ToString()); string shimProps = Path.Combine(shimDir, "Microsoft.NET.CrossGen.props"); string shimTargets = Path.Combine(shimDir, "Microsoft.NET.CrossGen.targets"); - return $"-p:Crossgen2InBuildDir={crossgenDir} -p:Crossgen2SdkOverridePropsPath={shimProps} -p:Crossgen2SdkOverrideTargetsPath={shimTargets}"; + + var args = new List(); + if (Directory.Exists(crossgenDir)) + args.Add($"-p:Crossgen2InBuildDir={crossgenDir}"); + if (File.Exists(shimProps)) + args.Add($"-p:Crossgen2SdkOverridePropsPath={shimProps}"); + if (File.Exists(shimTargets)) + args.Add($"-p:Crossgen2SdkOverrideTargetsPath={shimTargets}"); + return string.Join(" ", args); } private static void AssertCoreLibReadyToRun(string frameworkDir, bool expectReadyToRun) diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs index ff96c139964e50..8bd706ff381ac3 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs @@ -180,6 +180,9 @@ private static void AddCoreClrProjectProperties(ref string extraProperties, ref 11.0.0-{{versionSuffix}} + + 11.0.0-{{versionSuffix}} +
"""; From aee15bfbc43dc51519f86d03d37dbff483957b30 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 8 Sep 2026 15:38:00 +0200 Subject: [PATCH 10/18] [wasm][coreclr] Address PR review feedback on the ReadyToRun wiring - ResolveReadyToRunCompilers override: drop the last-resort Crossgen2Path that named a possibly non-existent executable; when the in-build dir has no crossgen2 the tool is left to the SDK-pack fallback (and ultimately the actionable _WasmCoreClrValidateReadyToRun error) instead of failing with "file not found". - Use Update="@(...)" on the CopyToOutputDirectory metadata item groups so the metadata is stamped on the existing items rather than relying on bare metadata-only elements. - Revert the WasmBuildNative relink trigger OR: the relink is driven by IsBrowserWasmProject only, as before. dotnet/runtime#133185 does not reproduce on current SDKs and the OR was defensive. - Narrow the stale-mode served-assets cleanup to the fingerprinted *.wasm/*.dll we stage, so the SDK never recursively removes app-authored content under the served directory. - Quote the crossgen2 override path arguments passed by the Wasm.Build.Tests ReadyToRun opt-in. --- .../browser/build/BrowserWasmApp.CoreCLR.targets | 11 ++++------- ...WebAssembly.Browser.CoreCLR.ReadyToRun.targets | 10 ++++++---- ...ft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets | 15 +++++++++------ src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs | 12 ++++++------ 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index 4bc4813abbec69..4c2944bb0615fd 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -221,14 +221,11 @@
- - @@ -280,7 +277,7 @@ diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets index 5d90061266add3..6d09e1c20b68f8 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.ReadyToRun.targets @@ -27,7 +27,6 @@ Copyright (c) .NET Foundation. All rights reserved. <_WasmCrossgen2ToolsExe>$([MSBuild]::NormalizePath('$([MSBuild]::NormalizeDirectory('$(Crossgen2InBuildDir)', 'tools'))', 'crossgen2$(_WasmCrossgen2ExeSuffix)')) $(_WasmCrossgen2RootExe) $(_WasmCrossgen2ToolsExe) - $(_WasmCrossgen2RootExe) <_WasmResolvedCrossgen2Dir Condition="'$(Crossgen2Path)' != ''">$([MSBuild]::EnsureTrailingSlash($([System.IO.Path]::GetDirectoryName('$(Crossgen2Path)')))) @@ -40,8 +39,11 @@ Copyright (c) .NET Foundation. All rights reserved. <_WasmR2RTargetArch Condition="'$(_WasmR2RTargetArch)' == ''">wasm - - + + - Never - + @@ -88,9 +88,11 @@ Copyright (c) .NET Foundation. All rights reserved. Condition="'$(_WasmEnableWebcil)' == 'true' and '$(WasmBuildingForNestedPublish)' != 'true'" BeforeTargets="_ComputeWasmBuildCandidates"> - - @@ -128,9 +130,10 @@ Copyright (c) .NET Foundation. All rights reserved. that step has to re-run for a native flag change. --> <_WasmCoreClrStaleModeOutput Include="$(IntermediateOutputPath)wasm\for-build\dotnet.native.*" /> <_WasmCoreClrStaleModeOutput Include="$(IntermediateOutputPath)wasm\for-publish\dotnet.native.*" /> - - <_WasmCoreClrStaleModeOutput Include="$(_WasmCoreClrServedAssetsDir)**\*" Condition="'$(_WasmCoreClrServedAssetsDir)' != ''" /> + + <_WasmCoreClrStaleModeOutput Include="$(_WasmCoreClrServedAssetsDir)*.wasm;$(_WasmCoreClrServedAssetsDir)*.dll" Condition="'$(_WasmCoreClrServedAssetsDir)' != ''" /> diff --git a/src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs b/src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs index 3d2880b9f84fe3..4788ef6bfd32e3 100644 --- a/src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/ReadyToRunTests.cs @@ -55,9 +55,9 @@ await RunForBuildWithDotnetRun(new BlazorRunOptions(config, public Task PublishRunAllPages(Configuration config, bool trimmed) => PublishRunAllPagesCore(config, trimmed, nativeRelink: false); - // CoreCLR relinks dotnet.native.wasm for Blazor when WasmBuildNative=true. The relink triggers key on - // WasmBuildNative in addition to IsBrowserWasmProject, so a late-resolved wasm RID cannot make the - // relink a silent no-op. See dotnet/runtime#133185. + // CoreCLR relinks dotnet.native.wasm for Blazor when WasmBuildNative=true; the relink is driven by the + // IsBrowserWasmProject triggers in BrowserWasmApp.CoreCLR.targets. AssertBundle(isNativeBuild: true) + // proves the served dotnet.native.wasm was relinked rather than the runtime-pack prebuilt. [ConditionalTheory(typeof(BuildTestBase), nameof(IsCoreClrRuntime))] [InlineData(Configuration.Release, /*trimmed*/ true)] [InlineData(Configuration.Release, /*trimmed*/ false)] @@ -238,11 +238,11 @@ private static string GetR2RBuildArgs(Configuration config) var args = new List(); if (Directory.Exists(crossgenDir)) - args.Add($"-p:Crossgen2InBuildDir={crossgenDir}"); + args.Add($"-p:Crossgen2InBuildDir=\"{crossgenDir}\""); if (File.Exists(shimProps)) - args.Add($"-p:Crossgen2SdkOverridePropsPath={shimProps}"); + args.Add($"-p:Crossgen2SdkOverridePropsPath=\"{shimProps}\""); if (File.Exists(shimTargets)) - args.Add($"-p:Crossgen2SdkOverrideTargetsPath={shimTargets}"); + args.Add($"-p:Crossgen2SdkOverrideTargetsPath=\"{shimTargets}\""); return string.Join(" ", args); } From 89fdaf42847d052e90dcc07c39aa517afe88e967 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Wed, 9 Sep 2026 13:55:51 +0200 Subject: [PATCH 11/18] [wasm][coreclr] Move test-only asset-copy targets to the test infra _WasmCoreClrSuppressNestedPublishAssetCopy and _WasmCoreClrRestoreCopyToOutputDirectory only do anything when _WasmFrameworkCopyToOutputDirectory=PreserveNewest, which is set exclusively by eng/testing/tests.browser.targets so the xunit runner gets framework assets in bin/. For a real app the default is Never, making both a no-op, so they move out of the shipped WebAssembly SDK into the test infra, gated on RuntimeFlavor=CoreCLR. Also drop _WasmCoreClrInvalidateStaleModeOutputs, which deleted stale derived outputs on a mode flip. Incremental mode-flip cleanup will be reimplemented later without deleting files; every test/CI matrix cleans obj per case, so nothing exercised it. --- eng/testing/tests.browser.targets | 33 +++++++++ ...ET.Sdk.WebAssembly.Browser.CoreCLR.targets | 71 ------------------- 2 files changed, 33 insertions(+), 71 deletions(-) diff --git a/eng/testing/tests.browser.targets b/eng/testing/tests.browser.targets index ea5afc888b17a7..0276d1a0ec8aea 100644 --- a/eng/testing/tests.browser.targets +++ b/eng/testing/tests.browser.targets @@ -60,6 +60,39 @@ so copy framework files to the output directory. See https://github.com/dotnet/runtime/issues/127257. --> <_WasmFrameworkCopyToOutputDirectory>PreserveNewest + + + + + <_WasmFrameworkCopyToOutputDirectory>Never + + + + + + + + + + + + + + true true diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets index 3194a646bc7fd2..e003674a3c3ce6 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets @@ -65,83 +65,12 @@ Copyright (c) .NET Foundation. All rights reserved. Text="PublishReadyToRun=true for CoreCLR browser-wasm requires a wasm-capable crossgen2, but none was resolved. Set Crossgen2InBuildDir to an in-build crossgen2 directory (or provide a ResolvedCrossgen2Pack) until wasm crossgen2 support flows through the base SDK (dotnet/sdk#55785)." /> - - - - <_WasmFrameworkCopyToOutputDirectory>Never - - - - - - - - - - - - - - <_WasmWebcilStampProperty Include="PublishTrimmed" /> - - - - <_WasmCoreClrModeStamp>$(IntermediateOutputPath)wasm-coreclr-mode.stamp - <_WasmCoreClrMode>PublishReadyToRun=$(PublishReadyToRun);PublishTrimmed=$(PublishTrimmed);WasmBuildNative=$(WasmBuildNative);WasmNativeDebugSymbols=$(WasmNativeDebugSymbols);WasmNativeStrip=$(WasmNativeStrip) - - <_WasmCoreClrServedAssetsDir Condition="'$(WasmRuntimeAssetsLocation)' == '' or '$(WasmRuntimeAssetsLocation)' == '_framework'">$(OutDir)wwwroot\_framework\ - - - - - - <_WasmCoreClrStaleModeOutput Include="$(IntermediateOutputPath)R2R\*.wasm;$(IntermediateOutputPath)R2R\*.dll" /> - <_WasmCoreClrStaleModeOutput Include="$(IntermediateOutputPath)webcil\**\*.wasm" /> - - <_WasmCoreClrStaleModeOutput Include="$(IntermediateOutputPath)wasm\for-build\dotnet.native.*" /> - <_WasmCoreClrStaleModeOutput Include="$(IntermediateOutputPath)wasm\for-publish\dotnet.native.*" /> - - <_WasmCoreClrStaleModeOutput Include="$(_WasmCoreClrServedAssetsDir)*.wasm;$(_WasmCoreClrServedAssetsDir)*.dll" Condition="'$(_WasmCoreClrServedAssetsDir)' != ''" /> - - - - - - - - From 528ec511a559a2ffa70febd69621e9ddf1d15f4f Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Wed, 9 Sep 2026 14:11:17 +0200 Subject: [PATCH 12/18] Update ReferenceCopyLocalPaths for trimmed closure Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets index e003674a3c3ce6..9eb57a91d75882 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets @@ -105,10 +105,11 @@ Copyright (c) .NET Foundation. All rights reserved. - + <_WasmInTrimmedClosure>false - + <_WasmInTrimmedClosure>true From 1407d1282653b9927eeae8d5d7b4cab4d358bdd7 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Wed, 9 Sep 2026 15:55:11 +0200 Subject: [PATCH 13/18] fix --- .../wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs index 0d2ca611d9f233..b44664c5b0f532 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs @@ -182,7 +182,7 @@ private static void AddCoreClrProjectProperties(ref string extraProperties, ref {{runtimePackVersion}} - 11.0.0-{{versionSuffix}} + {{runtimePackVersion}}
From b126e047481d8308502b4b225cfaa800a6a0a4b7 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 10 Sep 2026 12:47:17 +0200 Subject: [PATCH 14/18] publish incrementality fix --- ...rosoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets index 9eb57a91d75882..cb7de8a0102550 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.CoreCLR.targets @@ -176,11 +176,12 @@ Copyright (c) .NET Foundation. All rights reserved. Condition="'%(ResolvedFileToPublish.PostprocessAssembly)' == 'true' and Exists('$(IntermediateLinkDir)%(ResolvedFileToPublish.FileName).dll')" /> - - <_WasmReadyToRunCompileInput Include="@(ManagedAssemblyToLink->'$(IntermediateLinkDir)%(FileName)%(Extension)')" - Condition="Exists('$(IntermediateLinkDir)%(FileName)%(Extension)')" /> + + <_WasmReadyToRunCompileInput Include="$(IntermediateLinkDir)*.dll" /> Date: Thu, 10 Sep 2026 13:15:57 -0500 Subject: [PATCH 15/18] Honor dynamic-code compilation in CoreLib trimming substitutions Only embed the JIT-specific IsDynamicCodeCompiled substitution when FeatureDynamicCodeCompiled is enabled. Interpreter-only CoreCLR must retain its false getter after trimming. Fixes #133615 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../System.Private.CoreLib/System.Private.CoreLib.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index b55f5fdfb79209..1d5bc4e03ef3bc 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -21,7 +21,7 @@ - + From 6ceb8f2d0cb633a27c67c8269a433fae2f363c2f Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Thu, 10 Sep 2026 21:49:26 -0500 Subject: [PATCH 16/18] Enable trimmed CoreCLR WebAssembly R2R library tests Honor browser aggressive trimming and add a test-scoped ReadyToRun switch with matching build and Helix CI lanes. Preserve existing browser descriptors instead of importing Apple-only roots. Keep TestUtilities interpreted for the tracked platform-probe issue, preserve original conformance assertions, and quarantine known WebAssembly R2R failures with a shared browser/WASI predicate. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../testing/libraries/testing-wasm.md | 53 +++++++++++++++++++ .../templates/wasm-coreclr-library-tests.yml | 12 +++-- .../runtime-extra-platforms-wasm.yml | 15 ++++++ eng/pipelines/runtime.yml | 13 +++++ eng/testing/tests.browser.targets | 8 ++- .../TestUtilities/System/PlatformDetection.cs | 1 + src/libraries/Directory.Build.props | 5 ++ .../System.Runtime.Tests.csproj | 2 +- .../System.Runtime.Tests/System/ArrayTests.cs | 1 + .../System/DelegateTests.cs | 1 + .../System/ExceptionTests.cs | 1 + .../Reflection/CustomAttributeDataTests.cs | 5 ++ .../System/Reflection/MethodBaseTests.cs | 2 + .../MethodImplAttributeTests.cs | 1 + .../AsyncProfilerV1Tests.cs | 3 ++ .../AsyncProfilerV2Tests.cs | 37 +++++++++++++ 16 files changed, 155 insertions(+), 5 deletions(-) diff --git a/docs/workflow/testing/libraries/testing-wasm.md b/docs/workflow/testing/libraries/testing-wasm.md index bc6965cd5dc4e4..739dc943b5d35a 100644 --- a/docs/workflow/testing/libraries/testing-wasm.md +++ b/docs/workflow/testing/libraries/testing-wasm.md @@ -144,6 +144,59 @@ At the moment supported values are: By default, `chrome` browser is used. +## CoreCLR ReadyToRun library tests + +Build the browser CoreCLR runtime, libraries, packs, and host crossgen2 first: + +```bash +./build.sh clr+libs+host+packs -os browser -c Release /p:AotHostArchitecture=arm64 /p:AotHostOS=osx +``` + +Use the architecture and OS of the build machine for `AotHostArchitecture` and `AotHostOS` +(for example, `x64` and `linux` on an x64 Linux host). +On macOS, ensure a supported Python installation is ahead of Xcode's Python on `PATH`. + +Run a library suite with trimmed ReadyToRun images: + +```bash +XHARNESS_COMMAND=test-browser ./dotnet.sh build /t:Test \ + src/libraries/System.Runtime/tests/System.IO.UnmanagedMemoryStream.Tests/System.IO.UnmanagedMemoryStream.Tests.csproj \ + /p:TargetOS=browser /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release \ + /p:TestWasmReadyToRun=true /p:EnableAggressiveTrimming=true \ + /p:Scenario=WasmTestOnChrome /p:InstallChromeForTests=true +``` + +`TestWasmReadyToRun` enables `PublishReadyToRun` only in browser CoreCLR library test projects. +Do not pass `PublishReadyToRun=true` globally to `build.sh`: that also attempts to publish +host-side build tools with ReadyToRun. +`EnableAggressiveTrimming` selects the shared mobile test trimming configuration, including +the xUnit and test-utility descriptors and trimming-aware test exclusions. +Library-specific descriptors retain their existing platform conditions; Apple-only roots +are not enabled for browser tests. The property must also be passed when building the test utilities. +R2R browser test apps also set `TEST_READY_TO_RUN_MODE=1`, so existing +`PlatformDetection.IsReadyToRunCompiled` conditions apply. Quarantines for shared WebAssembly +R2R issues use `PlatformDetection.IsWasmReadyToRun`, covering browser and WASI R2R while +leaving interpreter coverage enabled on both hosts. +`TestUtilities.dll` stays interpreted while its guarded platform probes are affected by +[the R2R platform-probe issue](https://github.com/dotnet/runtime/issues/133614). +The library and test assemblies still use ReadyToRun. + +For the existing CoreCLR library smoke set, use: + +```bash +XHARNESS_COMMAND=test-browser ./build.sh libs.tests -test -os browser -c Release \ + /p:RuntimeFlavor=CoreCLR /p:TestWasmReadyToRun=true /p:EnableAggressiveTrimming=true \ + /p:RunSmokeTestsOnly=true /p:Scenario=WasmTestOnChrome /p:InstallChromeForTests=true +``` + +Omit `RunSmokeTestsOnly` to build and run all supported library suites. To isolate trimming +from R2R, keep `EnableAggressiveTrimming=true` and pass `PublishReadyToRun=false`. +Use clean project-specific `bin` and `obj` browser-wasm outputs when switching modes; +stale staged assets can otherwise cause assembly-loading failures before tests start. + +The `LibraryTestsCoreCLR_R2R` CI jobs use the same configuration and archive the published +tests for execution on Helix. The existing interpreter jobs remain separate. + ## AOT library tests - Building library tests with AOT, and (even) with `EnableAggressiveTrimming` takes 3-9mins on CI, and that adds up for all the assemblies, causing diff --git a/eng/pipelines/common/templates/wasm-coreclr-library-tests.yml b/eng/pipelines/common/templates/wasm-coreclr-library-tests.yml index 61bb488f777113..bb6067cbeff477 100644 --- a/eng/pipelines/common/templates/wasm-coreclr-library-tests.yml +++ b/eng/pipelines/common/templates/wasm-coreclr-library-tests.yml @@ -6,6 +6,7 @@ parameters: isWasmOnlyBuild: false nameSuffix: '' platforms: [] + readyToRun: false scenarios: ['WasmTestOnChrome'] shouldContinueOnError: false shouldRunSmokeOnly: false @@ -77,12 +78,17 @@ jobs: value: /p:InstallV8ForTests=true ${{ else }}: value: '' + - name: wasmReadyToRunArgs + ${{ if eq(parameters.readyToRun, true) }}: + value: /p:TestWasmReadyToRun=true /p:EnableAggressiveTrimming=true + ${{ else }}: + value: '' jobParameters: isExtraPlatforms: ${{ parameters.isExtraPlatformsBuild }} testGroup: innerloop nameSuffix: LibraryTestsCoreCLR${{ parameters.nameSuffix }} - buildArgs: -s clr+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=$(_hostedOs) $(_wasmRunSmokeTestsOnlyArg) $(chromeInstallArg) $(firefoxInstallArg) $(v8InstallArg) /maxcpucount:1 ${{ parameters.extraBuildArgs }} + buildArgs: -s clr+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=$(_hostedOs) $(_wasmRunSmokeTestsOnlyArg) $(chromeInstallArg) $(firefoxInstallArg) $(v8InstallArg) $(wasmReadyToRunArgs) /maxcpucount:1 ${{ parameters.extraBuildArgs }} timeoutInMinutes: 240 # if !alwaysRun, then: # if this is runtime-wasm (isWasmOnlyBuild): @@ -98,7 +104,7 @@ jobs: - template: /eng/pipelines/libraries/helix.yml parameters: creator: dotnet-bot - testRunNamePrefixSuffix: CoreCLR_$(_BuildConfig) - extraHelixArguments: /p:BrowserHost=$(_hostedOs) $(_wasmRunSmokeTestsOnlyArg) ${{ parameters.extraHelixArguments }} + testRunNamePrefixSuffix: CoreCLR${{ parameters.nameSuffix }}_$(_BuildConfig) + extraHelixArguments: /p:BrowserHost=$(_hostedOs) $(_wasmRunSmokeTestsOnlyArg) $(wasmReadyToRunArgs) ${{ parameters.extraHelixArguments }} scenarios: ${{ parameters.scenarios }} useHelixMonitor: ${{ parameters.useHelixMonitor }} diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml index 0dfb371196d872..92b23acd48afb9 100644 --- a/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml +++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml @@ -80,6 +80,21 @@ jobs: scenarios: - WasmTestOnChrome + # CoreCLR ReadyToRun library tests, complementary to the default pipeline's lane. + - template: /eng/pipelines/common/templates/wasm-coreclr-library-tests.yml + parameters: + platforms: + - browser_wasm + nameSuffix: _R2R + readyToRun: true + extraBuildArgs: /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS) + isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }} + isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }} + alwaysRun: ${{ parameters.isWasmOnlyBuild }} + useHelixMonitor: ${{ parameters.useHelixMonitor }} + scenarios: + - WasmTestOnChrome + # Library tests with full threading - template: /eng/pipelines/common/templates/wasm-library-tests.yml parameters: diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index e58e2d50c0b2af..947a201cb8355e 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -1267,6 +1267,19 @@ extends: scenarios: - WasmTestOnChrome + # WebAssembly CoreCLR with trimmed ReadyToRun library tests + - template: /eng/pipelines/common/templates/wasm-coreclr-library-tests.yml + parameters: + platforms: + - browser_wasm + alwaysRun: ${{ variables.isRollingBuild }} + nameSuffix: _R2R + readyToRun: true + extraBuildArgs: /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS) + useHelixMonitor: ${{ variables.enableHelixJobMonitor }} + scenarios: + - WasmTestOnChrome + # WebAssembly CoreCLR - smoke tests only on Firefox and V8 - template: /eng/pipelines/common/templates/wasm-coreclr-library-tests.yml parameters: diff --git a/eng/testing/tests.browser.targets b/eng/testing/tests.browser.targets index 0276d1a0ec8aea..b120109f20a504 100644 --- a/eng/testing/tests.browser.targets +++ b/eng/testing/tests.browser.targets @@ -14,7 +14,7 @@ false false - true + true <_WasmInTreeDefaults>false false @@ -100,6 +100,12 @@ true + + + + + + false diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 7e352915ac39e3..47db2845b1777b 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -276,6 +276,7 @@ private static bool GetLinqExpressionsBuiltWithIsInterpretingOnly() public static bool IsSingleFile => !HasAssemblyFiles; public static bool IsReadyToRunCompiled => Environment.GetEnvironmentVariable("TEST_READY_TO_RUN_MODE") == "1"; + public static bool IsWasmReadyToRun => IsWasm && IsReadyToRunCompiled; private static volatile Tuple s_lazyNonZeroLowerBoundArraySupported; public static bool IsNonZeroLowerBoundArraySupported diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index b85797ae211c21..976eccc487e5b0 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -94,6 +94,11 @@ true + + + true + + --interpreter diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj index e62c619d9c1bf9..4dcede7eb71324 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj @@ -375,7 +375,7 @@ - + diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs index 621185f3a32c6c..ba03fd1e58ac9c 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs @@ -479,6 +479,7 @@ public static IEnumerable BinarySearch_TypesNotComparable_TestData() [Theory] [MemberData(nameof(BinarySearch_TypesNotComparable_TestData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133613", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public static void BinarySearch_TypesNotIComparable_ThrowsInvalidOperationException(T[] array, object value) { Assert.Throws(() => Array.BinarySearch(array, value)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs index 6494af3a1341cd..7486fdd6641030 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs @@ -48,6 +48,7 @@ private static void EmptyFunc() { } public delegate TestStruct StructReturningDelegate(); [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133618", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public static void ClosedStaticDelegate() { TestClass foo = new TestClass(); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs index 879dfff4cd8d23..ec34cf6a8c1c26 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs @@ -61,6 +61,7 @@ public static void Exception_GetBaseException() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public static void Exception_TargetSite() { bool caught = false; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/CustomAttributeDataTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/CustomAttributeDataTests.cs index 1c1f930d445649..75e3f7e289d9f6 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/CustomAttributeDataTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/CustomAttributeDataTests.cs @@ -13,6 +13,7 @@ namespace System.Reflection.Tests public static class CustomAttributeDataTests { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] [My] public static void Test_CustomAttributeData_ConstructorNullary() { @@ -34,6 +35,7 @@ public static void Test_CustomAttributeData_ConstructorNullary() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] [My((short)5)] public static void Test_CustomAttributeData_Constructor1() { @@ -75,6 +77,7 @@ public static void Test_CustomAttribute_Constructor_CrossAssembly1() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] [ComVisible(false)] [ActiveIssue("https://github.com/dotnet/linker/issues/2078", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming)) /* Descriptors tell us to remove ComVisibleAttribute */] @@ -130,6 +133,7 @@ public static void Test_EqualsMethod() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] [My(3)] public static void Test_CustomAttributeData_ToString() { @@ -147,6 +151,7 @@ public static void Test_CustomAttributeData_ToString() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] [ActiveIssue("https://github.com/dotnet/runtime/issues/119292", TestRuntimes.Mono)] [MyEnumArray(MyTestEnum.Value, null, [], [MyTestEnum.Value, MyTestEnum.Value])] public static void Test_CustomAttributeData_EnumArray() diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs index 9d69ce855c6a41..bcbb1ddcf4bc40 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs @@ -13,6 +13,7 @@ namespace System.Reflection.Tests public static class MethodBaseTests { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public static void Test_GetCurrentMethod() { MethodBase m = MethodBase.GetCurrentMethod(); @@ -24,6 +25,7 @@ public static void Test_GetCurrentMethod() [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/60334", TestPlatforms.iOS | TestPlatforms.tvOS)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public static void Test_GetCurrentMethod_Inlineable() { // Verify that the result is not affected by inlining optimizations diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/MethodImplAttributeTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/MethodImplAttributeTests.cs index e5920b896e6f12..e9de2e0ff5d907 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/MethodImplAttributeTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/MethodImplAttributeTests.cs @@ -9,6 +9,7 @@ namespace System.Runtime.CompilerServices.Tests public static class MethodImplAttributeTests { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static void AggressiveOptimizationTest() { diff --git a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Runtime.CompilerServices/AsyncProfilerV1Tests.cs b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Runtime.CompilerServices/AsyncProfilerV1Tests.cs index a64cdfdab07c09..32b1687d9e8f1e 100644 --- a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Runtime.CompilerServices/AsyncProfilerV1Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Runtime.CompilerServices/AsyncProfilerV1Tests.cs @@ -3182,6 +3182,7 @@ private static async Task StateMachineAsync_SingleThread_ChainEventsAndCallstack } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsStateMachineAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task StateMachineAsync_SingleThread_ChainEventsAndCallstack() { var events = await CollectEventsAsync(ResumeStateMachineAsyncCallstackKeyword | StateMachineAsyncCoreKeywords | StateMachineAsyncMethodKeywords, async () => @@ -3255,6 +3256,7 @@ private static async ValueTask StateMachineAsync_ValueTask_SingleThread_ChainEve } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsStateMachineAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task StateMachineAsync_ValueTask_SingleThread_ChainEventsAndCallstack() { var events = await CollectEventsAsync(ResumeStateMachineAsyncCallstackKeyword | StateMachineAsyncCoreKeywords | StateMachineAsyncMethodKeywords, async () => @@ -3331,6 +3333,7 @@ private static async ValueTask StateMachineAsync_PoolingValueTask_SingleThread_C } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsStateMachineAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task StateMachineAsync_PoolingValueTask_SingleThread_ChainEventsAndCallstack() { var events = await CollectEventsAsync(ResumeStateMachineAsyncCallstackKeyword | StateMachineAsyncCoreKeywords | StateMachineAsyncMethodKeywords, async () => diff --git a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Runtime.CompilerServices/AsyncProfilerV2Tests.cs b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Runtime.CompilerServices/AsyncProfilerV2Tests.cs index 927a1341d05ce9..f9824d2fde045c 100644 --- a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Runtime.CompilerServices/AsyncProfilerV2Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Runtime.CompilerServices/AsyncProfilerV2Tests.cs @@ -251,6 +251,7 @@ private static async Task RuntimeAsync_SuspendResumeCompleteEvents_Marker() } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_SuspendResumeCompleteEvents() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_SuspendResumeCompleteEvents_Marker); @@ -282,6 +283,7 @@ private static async Task RuntimeAsync_ContextEventIdLifecycle_Marker() } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_ContextEventIdLifecycle() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_ContextEventIdLifecycle_Marker); @@ -328,6 +330,7 @@ private static async Task RuntimeAsync_EventSequenceOrder_Marker() } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_EventSequenceOrder() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_EventSequenceOrder_Marker); @@ -377,6 +380,7 @@ private static async Task RuntimeAsync_CreateAsyncCallstackEmittedOnFirstAwait_M } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CreateAsyncCallstackEmittedOnFirstAwait() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_CreateAsyncCallstackEmittedOnFirstAwait_Marker); @@ -404,6 +408,7 @@ private static async Task RuntimeAsync_CreateCallstackDepthMatchesChain_Marker() } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CreateCallstackDepthMatchesChain() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_CreateCallstackDepthMatchesChain_Marker); @@ -432,6 +437,7 @@ private static async Task RuntimeAsync_SuspendAsyncCallstackEmittedOnAwait_Marke } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_SuspendAsyncCallstackEmittedOnAwait() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_SuspendAsyncCallstackEmittedOnAwait_Marker); @@ -459,6 +465,7 @@ private static async Task RuntimeAsync_SuspendCallstackDepthMatchesChain_Marker( } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_SuspendCallstackDepthMatchesChain() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_SuspendCallstackDepthMatchesChain_Marker); @@ -487,6 +494,7 @@ private static async Task RuntimeAsync_SuspendCallstackPrecedesComplete_Marker() } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_SuspendCallstackPrecedesComplete() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_SuspendCallstackPrecedesComplete_Marker); @@ -522,6 +530,7 @@ private static async Task RuntimeAsync_SuspendCallstackDeeperThanInitialResume_M } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_SuspendCallstackDeeperThanInitialResume() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_SuspendCallstackDeeperThanInitialResume_Marker); @@ -551,6 +560,7 @@ private static async Task RuntimeAsync_CreateCallstackPrecedesResumeCallstack_Ma } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CreateCallstackPrecedesResumeCallstack() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_CreateCallstackPrecedesResumeCallstack_Marker); @@ -588,6 +598,7 @@ private static async Task RuntimeAsync_CreateAndFirstResumeCallstacksMatch_Marke } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CreateAndFirstResumeCallstacksMatch() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_CreateAndFirstResumeCallstacksMatch_Marker); @@ -629,6 +640,7 @@ private static async Task RuntimeAsync_CallstackEmittedOnResume_Marker() } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CallstackEmittedOnResume() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_CallstackEmittedOnResume_Marker); @@ -656,6 +668,7 @@ private static async Task RuntimeAsync_CallstackDepthMatchesChain_Marker() } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CallstackDepthMatchesChain() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_CallstackDepthMatchesChain_Marker); @@ -683,6 +696,7 @@ private static async Task RuntimeAsync_MethodEventCountMatchesChainDepth_Marker( } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_MethodEventCountMatchesChainDepth() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords | RuntimeAsyncMethodKeywords, RuntimeAsync_MethodEventCountMatchesChainDepth_Marker); @@ -717,6 +731,7 @@ private static async Task RuntimeAsync_CallstackFramesHaveDistinctMethodIds_Mark } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CallstackFramesHaveDistinctMethodIds() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_CallstackFramesHaveDistinctMethodIds_Marker); @@ -766,6 +781,7 @@ private static async Task RuntimeAsync_YieldAtEachLevel_CallstackShrinks_Marker( } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_YieldAtEachLevel_CallstackShrinks() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_YieldAtEachLevel_CallstackShrinks_Marker); @@ -793,6 +809,7 @@ private static async Task RuntimeAsync_CallstackSimulation_NormalCompletion_Mark } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CallstackSimulation_NormalCompletion() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_CallstackSimulation_NormalCompletion_Marker); @@ -811,6 +828,7 @@ private static async Task RuntimeAsync_CallstackSimulation_HandledException_Mark } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CallstackSimulation_HandledException() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_CallstackSimulation_HandledException_Marker); @@ -842,6 +860,7 @@ private static async Task RuntimeAsync_CallstackSimulation_UnhandledException_Ca } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CallstackSimulation_UnhandledException() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_CallstackSimulation_UnhandledException_Catcher_Marker); @@ -873,6 +892,8 @@ private static async Task RuntimeAsync_UnhandledExceptionUnwind_Catcher_Marker() } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/132311", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_UnhandledExceptionUnwind() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_UnhandledExceptionUnwind_Catcher_Marker); @@ -907,6 +928,7 @@ private static async Task RuntimeAsync_HandledExceptionUnwind_Marker() } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_HandledExceptionUnwind() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_HandledExceptionUnwind_Marker); @@ -1274,6 +1296,7 @@ private static async Task RuntimeAsync_KeywordGatekeeping_Marker() // Test parallelization is already disabled via XunitAssemblyAttributes.cs. [ConditionalTheory(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] [MemberData(nameof(KeywordGatekeepingData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/132311", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_KeywordGatekeeping(long keywordValue, AsyncEventID[] allowedEventIds) { EventKeywords kw = (EventKeywords)keywordValue; @@ -1364,6 +1387,8 @@ private static async Task RuntimeAsync_CallstackNativeIPDeltaRoundtrip_Marker() } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/132311", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CallstackNativeIPDeltaRoundtrip() { // Verify that delta-encoded NativeIPs in callstacks roundtrip correctly, @@ -1733,6 +1758,7 @@ await Task.WhenAll( } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_WhenAll_TracksAllBranches() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_WhenAll_TracksAllBranches_Marker); @@ -1807,6 +1833,7 @@ private static async Task RuntimeAsync_WhenAny_TracksAllBranches_Marker() } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133627", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_WhenAny_TracksAllBranches() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_WhenAny_TracksAllBranches_Marker); @@ -1913,6 +1940,7 @@ private static async Task RuntimeAsync_TaskCancellation_Marker() } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_TaskCancellation() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_TaskCancellation_Marker); @@ -1963,6 +1991,7 @@ private static async Task RuntimeAsync_CustomSyncContext_EmitsContextEventsAndCa } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CustomSyncContext_EmitsContextEventsAndCallstack() { s_runtimeAsyncSyncContextCtx = new InlinePostSynchronizationContext(); @@ -2004,6 +2033,7 @@ private static async Task RuntimeAsync_CustomTaskScheduler_EmitsContextEventsAnd } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_CustomTaskScheduler_EmitsContextEventsAndCallstack() { var scheduler = new InlineRunTaskScheduler(); @@ -2052,6 +2082,7 @@ private static async ValueTask RuntimeAsync_ValueTask_EventSequenceOrder_Marker( } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_ValueTask_EventSequenceOrder() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, async () => await RuntimeAsync_ValueTask_EventSequenceOrder_Marker()); @@ -2113,6 +2144,7 @@ private static async ValueTask RuntimeAsync_ValueTask_CallstackDepthMatchesChain } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_ValueTask_CallstackDepthMatchesChainDepth() { var events = await CollectValueTaskEventsAsync(RuntimeAsyncCallstackKeywords, RuntimeAsync_ValueTask_CallstackDepthMatchesChainDepth_Marker); @@ -2136,6 +2168,7 @@ private static async ValueTask RuntimeAsync_ValueTask_CallstackFramesHaveDistinc } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_ValueTask_CallstackFramesHaveDistinctMethodIds() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords, async () => await RuntimeAsync_ValueTask_CallstackFramesHaveDistinctMethodIds_Marker()); @@ -2180,6 +2213,7 @@ private static async ValueTask RuntimeAsync_ValueTask_HandledException_EmitsUnwi } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/132311", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_ValueTask_HandledException_EmitsUnwindAndComplete() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords | UnwindRuntimeAsyncExceptionKeyword, async () => await RuntimeAsync_ValueTask_HandledException_EmitsUnwindAndComplete_Marker()); @@ -2230,6 +2264,7 @@ private static async ValueTask RuntimeAsync_ValueTask_UnhandledException_EmitsUn } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_ValueTask_UnhandledException_EmitsUnwindAndComplete() { var events = await CollectEventsAsync(RuntimeAsyncCallstackKeywords | UnwindRuntimeAsyncExceptionKeyword, async () => @@ -2290,6 +2325,7 @@ private static async Task RuntimeAsync_ResetContext_ReplaysPendingV2Chain_Outer_ } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_ResetContext_ReplaysPendingV2Chain() { var events = await CollectEventsAsync(AllRuntimeAsyncKeywords, RuntimeAsync_ResetContext_ReplaysPendingV2Chain_Outer_Marker); @@ -2486,6 +2522,7 @@ private static async Task RuntimeAsync_ResetContext_ReplayResumeCompleteBalance_ } [ConditionalFact(typeof(AsyncProfilerTests), nameof(IsRuntimeAsyncSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/133626", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public async Task RuntimeAsync_ResetContext_ReplayResumeCompleteBalance() { var events = await CollectEventsAsync(AllRuntimeAsyncKeywords, RuntimeAsync_ResetContext_ReplayResumeCompleteBalance_Outer_Marker); From 282d12dfe220a1f34d0bb81a85239cab60c779ce Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 11 Sep 2026 10:08:12 +0200 Subject: [PATCH 17/18] [wasm][coreclr] Raise R2R library-test lane timeout to 480 min --- .../common/templates/wasm-coreclr-library-tests.yml | 5 ++++- .../extra-platforms/runtime-extra-platforms-wasm.yml | 1 + eng/pipelines/runtime.yml | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/eng/pipelines/common/templates/wasm-coreclr-library-tests.yml b/eng/pipelines/common/templates/wasm-coreclr-library-tests.yml index bb6067cbeff477..6147157819f384 100644 --- a/eng/pipelines/common/templates/wasm-coreclr-library-tests.yml +++ b/eng/pipelines/common/templates/wasm-coreclr-library-tests.yml @@ -10,6 +10,9 @@ parameters: scenarios: ['WasmTestOnChrome'] shouldContinueOnError: false shouldRunSmokeOnly: false + # Default matches the fast interpreter lane. The R2R lane crossgen2-publishes every + # test app serially (/maxcpucount:1), which is ~5.5x slower, so its callers raise this. + timeoutInMinutes: 240 useHelixMonitor: false jobs: @@ -89,7 +92,7 @@ jobs: testGroup: innerloop nameSuffix: LibraryTestsCoreCLR${{ parameters.nameSuffix }} buildArgs: -s clr+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=$(_hostedOs) $(_wasmRunSmokeTestsOnlyArg) $(chromeInstallArg) $(firefoxInstallArg) $(v8InstallArg) $(wasmReadyToRunArgs) /maxcpucount:1 ${{ parameters.extraBuildArgs }} - timeoutInMinutes: 240 + timeoutInMinutes: ${{ parameters.timeoutInMinutes }} # if !alwaysRun, then: # if this is runtime-wasm (isWasmOnlyBuild): # - then run only if it would not have run on default pipelines (based diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml index 92b23acd48afb9..798b775a8f13a7 100644 --- a/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml +++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml @@ -87,6 +87,7 @@ jobs: - browser_wasm nameSuffix: _R2R readyToRun: true + timeoutInMinutes: 480 extraBuildArgs: /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS) isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }} isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }} diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index 947a201cb8355e..35e0704c3c1e97 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -1275,6 +1275,7 @@ extends: alwaysRun: ${{ variables.isRollingBuild }} nameSuffix: _R2R readyToRun: true + timeoutInMinutes: 480 extraBuildArgs: /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS) useHelixMonitor: ${{ variables.enableHelixJobMonitor }} scenarios: From d7517ee2b46b91442466beedfc476c3c874dc719 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Fri, 11 Sep 2026 21:34:49 -0500 Subject: [PATCH 18/18] Re-enable WebAssembly caller-identification conformance tests Remove the nine #133617 ActiveIssue annotations now that #133707 has landed. Preserve every original GetCurrentMethod call and assertion and retain unrelated platform and trimming exclusions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../tests/System.Runtime.Tests/System/ExceptionTests.cs | 1 - .../System/Reflection/CustomAttributeDataTests.cs | 5 ----- .../System/Reflection/MethodBaseTests.cs | 2 -- .../Runtime/CompilerServices/MethodImplAttributeTests.cs | 1 - 4 files changed, 9 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs index ec34cf6a8c1c26..879dfff4cd8d23 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs @@ -61,7 +61,6 @@ public static void Exception_GetBaseException() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public static void Exception_TargetSite() { bool caught = false; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/CustomAttributeDataTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/CustomAttributeDataTests.cs index 75e3f7e289d9f6..1c1f930d445649 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/CustomAttributeDataTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/CustomAttributeDataTests.cs @@ -13,7 +13,6 @@ namespace System.Reflection.Tests public static class CustomAttributeDataTests { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] [My] public static void Test_CustomAttributeData_ConstructorNullary() { @@ -35,7 +34,6 @@ public static void Test_CustomAttributeData_ConstructorNullary() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] [My((short)5)] public static void Test_CustomAttributeData_Constructor1() { @@ -77,7 +75,6 @@ public static void Test_CustomAttribute_Constructor_CrossAssembly1() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] [ComVisible(false)] [ActiveIssue("https://github.com/dotnet/linker/issues/2078", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming)) /* Descriptors tell us to remove ComVisibleAttribute */] @@ -133,7 +130,6 @@ public static void Test_EqualsMethod() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] [My(3)] public static void Test_CustomAttributeData_ToString() { @@ -151,7 +147,6 @@ public static void Test_CustomAttributeData_ToString() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] [ActiveIssue("https://github.com/dotnet/runtime/issues/119292", TestRuntimes.Mono)] [MyEnumArray(MyTestEnum.Value, null, [], [MyTestEnum.Value, MyTestEnum.Value])] public static void Test_CustomAttributeData_EnumArray() diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs index bcbb1ddcf4bc40..9d69ce855c6a41 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs @@ -13,7 +13,6 @@ namespace System.Reflection.Tests public static class MethodBaseTests { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public static void Test_GetCurrentMethod() { MethodBase m = MethodBase.GetCurrentMethod(); @@ -25,7 +24,6 @@ public static void Test_GetCurrentMethod() [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/60334", TestPlatforms.iOS | TestPlatforms.tvOS)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] public static void Test_GetCurrentMethod_Inlineable() { // Verify that the result is not affected by inlining optimizations diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/MethodImplAttributeTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/MethodImplAttributeTests.cs index e9de2e0ff5d907..e5920b896e6f12 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/MethodImplAttributeTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/MethodImplAttributeTests.cs @@ -9,7 +9,6 @@ namespace System.Runtime.CompilerServices.Tests public static class MethodImplAttributeTests { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/133617", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmReadyToRun))] [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static void AggressiveOptimizationTest() {