From f3053fd18e9e35617727f7cfe0d7eb1d35a9839a Mon Sep 17 00:00:00 2001 From: Artyom Sovetnikov <2056864+Elringus@users.noreply.github.com> Date: Fri, 17 Apr 2026 20:37:46 +0300 Subject: [PATCH 1/7] fix symbols buffer --- src/mono/browser/runtime/loader/assets.ts | 39 +++++++++---------- .../Wasm.Build.Tests/ModuleConfigTests.cs | 17 ++++++++ .../WasmBasicTestApp/App/wwwroot/main.js | 22 +++++++++++ 3 files changed, 57 insertions(+), 21 deletions(-) diff --git a/src/mono/browser/runtime/loader/assets.ts b/src/mono/browser/runtime/loader/assets.ts index 96acacb04dcf73..76b3291d7de9f9 100644 --- a/src/mono/browser/runtime/loader/assets.ts +++ b/src/mono/browser/runtime/loader/assets.ts @@ -186,6 +186,17 @@ export async function mono_download_assets (): Promise { const instantiate = async (downloadPromise: Promise) => { const asset = await downloadPromise; + const headersOnly = skipBufferByAssetTypes[asset.behavior]; + + if (headersOnly) { + if (asset.behavior === "symbols") { + await runtimeHelpers.instantiate_symbols_asset(asset); + cleanupAsset(asset); + } + ++loaderHelpers.actual_downloaded_assets_count; + return; + } + if (asset.buffer) { if (!skipInstantiateByAssetTypes[asset.behavior]) { mono_assert(asset.buffer && typeof asset.buffer === "object", "asset buffer must be array-like or buffer-like or promise of these"); @@ -202,24 +213,12 @@ export async function mono_download_assets (): Promise { runtimeHelpers.instantiate_asset(asset, url, data); } } else { - const headersOnly = skipBufferByAssetTypes[asset.behavior]; - if (!headersOnly) { - mono_assert(asset.isOptional, "Expected asset to have the downloaded buffer"); - if (!skipDownloadsByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) { - loaderHelpers.expected_downloaded_assets_count--; - } - if (!skipInstantiateByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) { - loaderHelpers.expected_instantiated_assets_count--; - } - } else { - if (asset.behavior === "symbols") { - await runtimeHelpers.instantiate_symbols_asset(asset); - cleanupAsset(asset); - } - - if (skipBufferByAssetTypes[asset.behavior]) { - ++loaderHelpers.actual_downloaded_assets_count; - } + mono_assert(asset.isOptional, "Expected asset to have the downloaded buffer"); + if (!skipDownloadsByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) { + loaderHelpers.expected_downloaded_assets_count--; + } + if (!skipInstantiateByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) { + loaderHelpers.expected_instantiated_assets_count--; } } }; @@ -529,9 +528,7 @@ async function start_asset_download_sources (asset: AssetEntryInternal): Promise ok: true, arrayBuffer: () => buffer, json: () => JSON.parse(new TextDecoder("utf-8").decode(buffer)), - text: () => { - throw new Error("NotImplementedException"); - }, + text: () => new TextDecoder("utf-8").decode(buffer), headers: { get: () => undefined, } diff --git a/src/mono/wasm/Wasm.Build.Tests/ModuleConfigTests.cs b/src/mono/wasm/Wasm.Build.Tests/ModuleConfigTests.cs index 699cc706756a8a..a7cc8a3381d3cc 100644 --- a/src/mono/wasm/Wasm.Build.Tests/ModuleConfigTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/ModuleConfigTests.cs @@ -95,6 +95,23 @@ public async Task AssetIntegrity() ); } + [ConditionalFact(typeof(BuildTestBase), nameof(IsMonoRuntime)), TestCategory("bundler-friendly")] + public async Task BufferedAssetsTest() + { + Configuration config = Configuration.Debug; + ProjectInfo info = CopyTestAsset( + config, + aot: false, + TestAsset.WasmBasicTestApp, + "ModuleConfigTests_BufferedAssetsTest", + extraProperties: "true"); + PublishProject(info, config); + await RunForPublishWithWebServer(new BrowserRunOptions( + Configuration: config, + TestScenario: "BufferedAssetsTest" + )); + } + [Theory] [InlineData(false)] [InlineData(true)] diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js index 84c77206bcf6fe..e3b585bbdf531f 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js @@ -178,6 +178,25 @@ switch (testCase) { case "MainWithArgs": dotnet.withApplicationArgumentsFromQuery(); break; + case "BufferedAssetsTest": + const originalFetch3 = globalThis.fetch.bind(globalThis); + dotnet.withModuleConfig({ + onConfigLoaded: (config) => { + const bufferedAssets = [ + ...config.resources.wasmNative, + ...config.resources.coreAssembly, + ...config.resources.assembly, + ...config.resources.corePdb, + ...config.resources.pdb, + ...config.resources.wasmSymbols, + ]; + for (const asset of bufferedAssets) { + const url = new URL(`./_framework/${asset.name}`, location.href); + asset.buffer = originalFetch3(url).then(r => r.arrayBuffer()); + } + } + }); + break; } const { setModuleImports, Module, getAssemblyExports, getConfig, INTERNAL } = await dotnet.create(); @@ -356,6 +375,9 @@ try { exit(foundB && retB == 42 ? 0 : 1); + break; + case "BufferedAssetsTest": + await dotnet.runMainAndExit(); break; default: console.error(`Unknown test case: ${testCase}`); From b0dadf3c5d57d618a14d28a1a38dee67408c3d31 Mon Sep 17 00:00:00 2001 From: Artyom Sovetnikov <2056864+Elringus@users.noreply.github.com> Date: Mon, 20 Apr 2026 11:41:34 +0300 Subject: [PATCH 2/7] remove missing assets --- src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js index e3b585bbdf531f..46750fac5fff2b 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js @@ -184,9 +184,7 @@ switch (testCase) { onConfigLoaded: (config) => { const bufferedAssets = [ ...config.resources.wasmNative, - ...config.resources.coreAssembly, ...config.resources.assembly, - ...config.resources.corePdb, ...config.resources.pdb, ...config.resources.wasmSymbols, ]; From b03afe13363fc5f30a33eede2924debc736eea48 Mon Sep 17 00:00:00 2001 From: Artyom Sovetnikov <2056864+Elringus@users.noreply.github.com> Date: Mon, 8 Jun 2026 16:52:45 +0300 Subject: [PATCH 3/7] fix var name collision --- src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js index 2062a6a2efad1c..245f390a86a7d9 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js @@ -217,7 +217,7 @@ switch (testCase) { dotnet.withApplicationArgumentsFromQuery(); break; case "BufferedAssetsTest": - const originalFetch3 = globalThis.fetch.bind(globalThis); + const originalFetch4 = globalThis.fetch.bind(globalThis); dotnet.withModuleConfig({ onConfigLoaded: (config) => { const bufferedAssets = [ @@ -228,7 +228,7 @@ switch (testCase) { ]; for (const asset of bufferedAssets) { const url = new URL(`./_framework/${asset.name}`, location.href); - asset.buffer = originalFetch3(url).then(r => r.arrayBuffer()); + asset.buffer = originalFetch4(url).then(r => r.arrayBuffer()); } } }); From c1f38f77a4706a44474530ca7b4c6996551dea76 Mon Sep 17 00:00:00 2001 From: Artyom Sovetnikov <2056864+Elringus@users.noreply.github.com> Date: Mon, 8 Jun 2026 16:53:15 +0300 Subject: [PATCH 4/7] support js.symbols fingerprinting --- .../Wasm.Build.Tests/ProjectProviderBase.cs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs b/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs index 74f9e4fdee2b7e..c1480d70fa91ee 100644 --- a/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs @@ -112,9 +112,6 @@ public IReadOnlyDictionary FindAndAssertDotnetFiles( foreach ((string expectedFilename, bool expectFingerprint) in superSet.OrderByDescending(kvp => kvp.Key)) { - string prefix = Path.GetFileNameWithoutExtension(expectedFilename); - string extension = Path.GetExtension(expectedFilename).Substring(1); - dotnetFiles = dotnetFiles .Where(actualFile => { @@ -127,7 +124,7 @@ public IReadOnlyDictionary FindAndAssertDotnetFiles( expectFingerprintOnDotnetJs: expectFingerprintOnDotnetJs, expectFingerprintForThisFile: expectFingerprint)) { - string pattern = $"^{prefix}{s_dotnetVersionHashRegex}{extension}$"; + string pattern = GetFingerprintRegexPattern(expectedFilename); var match = Regex.Match(actualFilename, pattern); if (!match.Success) return true; @@ -418,6 +415,19 @@ private string[] GetFilesMatchingNameConsideringFingerprinting(string filePath, public bool ShouldCheckFingerprint(string expectedFilename, bool? expectFingerprintOnDotnetJs, bool expectFingerprintForThisFile) => IsFingerprintingEnabled && ((expectedFilename == "dotnet.js" && expectFingerprintOnDotnetJs == true) || expectFingerprintForThisFile); + private static string GetFingerprintRegexPattern(string expectedFilename) + { + const string jsSymbolsSuffix = ".js.symbols"; + if (expectedFilename.EndsWith(jsSymbolsSuffix, StringComparison.Ordinal)) + { + string prefix = expectedFilename[..^jsSymbolsSuffix.Length]; + return $"^{Regex.Escape(prefix)}{s_dotnetVersionHashRegex}js\\.symbols$"; + } + + string defaultPrefix = Path.GetFileNameWithoutExtension(expectedFilename); + string extension = Path.GetExtension(expectedFilename).Substring(1); + return $"^{Regex.Escape(defaultPrefix)}{s_dotnetVersionHashRegex}{Regex.Escape(extension)}$"; + } public static void AssertRuntimePackPath(string buildOutput, string targetFramework, RuntimeVariant runtimeType = RuntimeVariant.SingleThreaded) { @@ -614,14 +624,11 @@ public BootJsonData AssertBootJson(AssertBundleOptions options) bool expectFingerprint = knownSet[expectedFilename]; expectedEntries[expectedFilename] = item => { - string prefix = Path.GetFileNameWithoutExtension(expectedFilename); - string extension = Path.GetExtension(expectedFilename).Substring(1); - if (ShouldCheckFingerprint(expectedFilename: expectedFilename, expectFingerprintOnDotnetJs: options.ExpectDotnetJsFingerprinting, expectFingerprintForThisFile: expectFingerprint)) { - return Regex.Match(item, $"{prefix}{s_dotnetVersionHashRegex}{extension}").Success; + return Regex.Match(item, GetFingerprintRegexPattern(expectedFilename)).Success; } else { From fba083cbd0fcdb8ec07a045471ae1d4c26fb48dc Mon Sep 17 00:00:00 2001 From: Artyom Sovetnikov <2056864+Elringus@users.noreply.github.com> Date: Mon, 8 Jun 2026 16:59:06 +0300 Subject: [PATCH 5/7] assert http response ok --- .../wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js index 245f390a86a7d9..e3d3988ef07340 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js @@ -228,7 +228,10 @@ switch (testCase) { ]; for (const asset of bufferedAssets) { const url = new URL(`./_framework/${asset.name}`, location.href); - asset.buffer = originalFetch4(url).then(r => r.arrayBuffer()); + asset.buffer = originalFetch4(url).then(r => { + if (!r.ok) throw new Error(`Failed to fetch buffered asset '${url}': ${r.status} ${r.statusText}`); + return r.arrayBuffer(); + }); } } }); From d021dd51bf1d6016a6c3590c831455337acfe431 Mon Sep 17 00:00:00 2001 From: Artyom Sovetnikov <2056864+Elringus@users.noreply.github.com> Date: Mon, 8 Jun 2026 21:10:33 +0300 Subject: [PATCH 6/7] disable bundle assert --- src/mono/wasm/Wasm.Build.Tests/ModuleConfigTests.cs | 2 +- src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/ModuleConfigTests.cs b/src/mono/wasm/Wasm.Build.Tests/ModuleConfigTests.cs index 8b35eb6084ef73..4e6a05cedf4bd6 100644 --- a/src/mono/wasm/Wasm.Build.Tests/ModuleConfigTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/ModuleConfigTests.cs @@ -131,7 +131,7 @@ public async Task BufferedAssetsTest() TestAsset.WasmBasicTestApp, "ModuleConfigTests_BufferedAssetsTest", extraProperties: "true"); - PublishProject(info, config); + PublishProject(info, config, new PublishOptions(AssertAppBundle: false)); await RunForPublishWithWebServer(new BrowserRunOptions( Configuration: config, TestScenario: "BufferedAssetsTest" diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js index e3d3988ef07340..0af2e8184995cd 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js @@ -223,7 +223,7 @@ switch (testCase) { const bufferedAssets = [ ...config.resources.wasmNative, ...config.resources.assembly, - ...config.resources.pdb, + ...(config.resources.pdb ?? []), ...config.resources.wasmSymbols, ]; for (const asset of bufferedAssets) { From 034bd9368fccd4f3cd2244680952deb810937ee4 Mon Sep 17 00:00:00 2001 From: Artyom Sovetnikov <2056864+Elringus@users.noreply.github.com> Date: Mon, 8 Jun 2026 23:58:03 +0300 Subject: [PATCH 7/7] try resolve asset url --- src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js index 0af2e8184995cd..901a2a9e6fe5ef 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js @@ -227,7 +227,7 @@ switch (testCase) { ...config.resources.wasmSymbols, ]; for (const asset of bufferedAssets) { - const url = new URL(`./_framework/${asset.name}`, location.href); + const url = new URL(asset.resolvedUrl ?? `./_framework/${asset.name}`, location.href); asset.buffer = originalFetch4(url).then(r => { if (!r.ok) throw new Error(`Failed to fetch buffered asset '${url}': ${r.status} ${r.statusText}`); return r.arrayBuffer();