Skip to content

[browser][coreCLR] idempotent lazy download of dll/pdb#129666

Open
pavelsavara wants to merge 1 commit into
dotnet:mainfrom
pavelsavara:browser_fix_lazy_load_idempotence
Open

[browser][coreCLR] idempotent lazy download of dll/pdb#129666
pavelsavara wants to merge 1 commit into
dotnet:mainfrom
pavelsavara:browser_fix_lazy_load_idempotence

Conversation

@pavelsavara

@pavelsavara pavelsavara commented Jun 21, 2026

Copy link
Copy Markdown
Member

Summary

Loading the same lazy assembly more than once (e.g. when Blazor fires OnNavigate
repeatedly) incorrectly threw:

<assembly> must be marked with 'BlazorWebAssemblyLazyLoad' item group in your project file to allow lazy-loading.

even though the assembly was correctly marked and had already been loaded once.
This change makes a repeated loadLazyAssembly call an idempotent no-op that
returns false, and adds a regression test.

Root cause

The lazy loader matched and de-duplicated assets using the asset's virtualPath
field. However, fetchAssembly calls normalizeVirtualPath, which mutates
asset.virtualPath in place
— it rewrites the .wasm extension to .dll and
prefixes the path with the app base (and culture). The lazyAssembly resources
array holds references to those same asset objects.

As a result, after the first successful load:

  1. The asset's virtualPath no longer equals the bare Json.dll / Json.wasm
    name the lookup compared against, so the second call failed to find the asset.
  2. The dedup set (loadedLazyAssemblies) stored the already-rewritten path, so
    the early-return guard never matched on the second call either.

With the asset "not found," the loader fell through to the
must be marked with 'BlazorWebAssemblyLazyLoad' error path.

Changes

src/native/libs/Common/JavaScript/loader/assets.ts

  • Added a small lazyAssetFileName(virtualPath) helper that extracts just the
    file name (the segment after the last /), so asset matching is robust to the
    in-place normalization that prefixes the path.
  • Matching of the DLL/WASM asset and the PDB asset now compares the file name
    rather than the full virtualPath.
  • De-duplication now keys off the stable assemblyNameWithoutExtension instead of
    the mutable virtualPath, and the dedup check is moved before the asset
    lookup so a repeated load short-circuits to return false immediately.

src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js

  • Captures and reports the boolean result of loadLazyAssembly (firstJsonLoad).
  • When the loadLazyAssemblyTwice=true query string is set, loads the same lazy
    assembly a second time and reports secondJsonLoad, exercising the idempotent
    path.

src/mono/wasm/Wasm.Build.Tests/LazyLoadingTests.cs

  • Added LoadLazyAssemblyTwiceIsIdempotent, a regression test that:
    • asserts the first load returns true,
    • asserts the second load of the same assembly returns false (idempotent no-op),
    • asserts the test still produces its expected JSON output, and
    • asserts the must be marked with 'BlazorWebAssemblyLazyLoad' error is not
      emitted to the console.

Testing

Added LoadLazyAssemblyTwiceIsIdempotent to Wasm.Build.Tests, which reproduces
the failure without the fix and passes with it.

@pavelsavara pavelsavara added this to the 11.0.0 milestone Jun 21, 2026
@pavelsavara pavelsavara added arch-wasm WebAssembly architecture area-Host labels Jun 21, 2026
Copilot AI review requested due to automatic review settings June 21, 2026 11:23
@pavelsavara pavelsavara self-assigned this Jun 21, 2026
@pavelsavara pavelsavara added the os-browser Browser variant of arch-wasm label Jun 21, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes browser lazy-assembly loading idempotent by making the loader’s “already loaded” tracking and asset lookup resilient to virtualPath being rewritten during the first fetch (e.g., normalization that prefixes _framework/).

Changes:

  • Update lazy assembly/PDB lookup to match by asset filename (basename) instead of full virtualPath, and track loaded lazy assemblies by the assembly’s basename (without extension).
  • Add a regression test that loads the same lazy assembly twice and verifies the second call is a no-op (returns false) and does not throw.
  • Extend the WASM test asset’s main.js to exercise and log the two consecutive lazy-load calls.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/native/libs/Common/JavaScript/loader/assets.ts Makes lazy assembly/PDB matching and deduplication robust against in-place virtualPath normalization/mutation.
src/mono/wasm/Wasm.Build.Tests/LazyLoadingTests.cs Adds regression coverage verifying repeated lazy-load calls are idempotent and don’t surface the “must be marked” error.
src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js Adds a test hook to attempt a second lazy load and emit observable results for the new test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-Host os-browser Browser variant of arch-wasm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants