[release/10.0] [mono][wasm] Fix AOT + BlazorWebAssemblyLazyLoad startup crash#131038
[release/10.0] [mono][wasm] Fix AOT + BlazorWebAssemblyLazyLoad startup crash#131038lewing wants to merge 1 commit into
Conversation
When an app is published with AOT and uses BlazorWebAssemblyLazyLoad, the Mono runtime crashes during startup (appdomain.c assertion / interp.c NIY 'should not be reached'). Root cause: an AOT image hard-binds to every assembly it references when the image is loaded (see load_aot_module in aot-runtime.c, which eagerly calls load_image for all referenced images unless aot-lazy-assembly-load is set). A lazy-loaded assembly is not present at runtime startup, so any AOT image that references it is marked 'unusable because dependency <asm> is not found' and its methods fall back to the interpreter, which then hits an unsupported construct and aborts. Enable the runtime's 'aot-lazy-assembly-load' option automatically from the WebAssembly SDK when AOT is combined with BlazorWebAssemblyLazyLoad, so referenced lazy assemblies are only bound when they are actually loaded. Also add AOT + lazy-load regression coverage to LazyLoadingTests, which previously only exercised the interpreter (Debug) configuration. Fixes dotnet#125794 (cherry picked from commit 3816e04)
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This backport fixes a Blazor WebAssembly startup crash when AOT is combined with BlazorWebAssemblyLazyLoad by automatically enabling Mono’s existing --aot-lazy-assembly-load runtime option in that specific configuration. This keeps AOT images usable at startup by deferring binding to lazy assemblies until they are actually loaded.
Changes:
- Append
--aot-lazy-assembly-loadto the generated WebAssembly runtime options whenRunAOTCompilation=trueand at least oneBlazorWebAssemblyLazyLoaditem is present (and the option isn’t already specified). - Add a new publish-time regression test that exercises Release + AOT + lazy-load and validates the app runs and produces expected output.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/mono/wasm/Wasm.Build.Tests/LazyLoadingTests.cs | Adds a Release publish + AOT regression test for lazy assembly loading to prevent startup-crash regressions. |
| src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets | Automatically enables --aot-lazy-assembly-load for AOT + BlazorWebAssemblyLazyLoad builds by updating the boot config runtime options. |
|
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara |
|
Workflow state for the Holistic Review Orchestrator. {
"version": 5,
"last_dispatched_commit": "6147c9744911181d4a2c0791a21fcafbe4349f1a",
"last_dispatched_base_ref": "release/10.0",
"last_dispatched_base_sha": "018b2627823158a827a8222f0327f99f6a970565",
"last_reviewed_commit": "6147c9744911181d4a2c0791a21fcafbe4349f1a",
"last_reviewed_base_ref": "release/10.0",
"last_reviewed_base_sha": "018b2627823158a827a8222f0327f99f6a970565",
"last_recorded_worker_run_id": "29691594791",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "6147c9744911181d4a2c0791a21fcafbe4349f1a",
"review_id": 4730965447
}
]
} |
There was a problem hiding this comment.
Holistic Review
Motivation: This is a clean backport of the merged main PR #131020 to release/10.0, fixing #125794. AOT-published Blazor WebAssembly apps that use BlazorWebAssemblyLazyLoad crash during Mono startup: an AOT image hard-binds to every assembly it references at load time (load_aot_module/load_image), but a lazy-loaded assembly is absent at startup, so the referencing image is marked unusable and its methods fall back to the interpreter, which then aborts on an unsupported construct. The motivation is well substantiated by the linked issue and the root-cause explanation.
Approach: The fix appends the existing runtime option --aot-lazy-assembly-load to _BlazorWebAssemblyRuntimeOptions from the WebAssembly SDK targets, gated on RunAOTCompilation=true AND at least one BlazorWebAssemblyLazyLoad item, and only when the option is not already present. This defers binding of referenced lazy assemblies until they are actually loaded. It reuses an already-supported runtime option rather than adding new runtime machinery, and the MSBuild condition is correctly scoped so non-AOT and non-lazy builds are unaffected. A native-mono regression test (LoadLazyAssemblyWithAOT) exercises the Release + AOT + lazy-load combination that the existing Debug tests do not cover.
Summary: LGTM. The change is minimal, narrowly scoped, and matches the merged main PR (cherry-pick of 3816e04) exactly (28 additions, targets + test only). The MSBuild condition correctly checks all three predicates (AOT enabled, lazy-load items present, option not already set) and idempotently appends the flag with a .Trim() to avoid leading/trailing whitespace. The added test asserts the expected JSON output that only appears when the runtime initializes and lazily loads the assembly successfully, providing meaningful regression coverage. No correctness, safety, or performance concerns for a backport of this size, and the risk is appropriately assessed as low. No actionable findings.
Note
This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.
Generated by Holistic Review · 52.1 AIC · ⌖ 16 AIC · ⊞ 10K
Backport of #131020 to release/10.0
/cc @lewing
g_assert(string_empty_fld)fails on any AOT-compiled Blazor WASM project (.NET 10) #125794main PR: #131020
Description
AOT-published Blazor WebAssembly apps that use
BlazorWebAssemblyLazyLoadcrash during Mono runtime startup — reported as anappdomain.cassertion (condition '<disabled>' not met) and, in other configurations, aninterp.cNIY … should not be reachedabort.An AOT image hard-binds to every assembly it references when the image is loaded (
load_aot_moduleinaot-runtime.ceagerly callsload_imagefor all referenced images unless theaot-lazy-assembly-loadruntime option is set). ABlazorWebAssemblyLazyLoadassembly is not present at runtime startup (it is downloaded on demand), so when the main app's AOT image loads, resolving the lazy dependency fails, the image is marked unusable, and every method in it falls back to the interpreter, which then hits an unsupported construct and aborts.The fix automatically enables the runtime's existing
aot-lazy-assembly-loadoption from the WebAssembly SDK when AOT is combined withBlazorWebAssemblyLazyLoad. This defers binding of a referenced lazy assembly until it is actually loaded, keeping the referencing AOT image usable. The change is scoped toRunAOTCompilation=true+ at least oneBlazorWebAssemblyLazyLoaditem, so non-AOT and non-lazy builds are unaffected.Customer Impact
Any Blazor WebAssembly app that combines AOT compilation with
BlazorWebAssemblyLazyLoadcrashes on startup and is completely unusable. Lazy loading is a common technique to reduce initial download size, and AOT is a common way to improve runtime performance, so customers who adopt both features together are blocked with no workaround other than disabling one of them.Regression
No. This is a long-standing interaction between AOT and lazy loading, not a regression introduced in the most recent release.
Testing
Added
LoadLazyAssemblyWithAOTtoLazyLoadingTests(native-mono, Release + AOT + lazy load), which previously only covered the interpreter (Debug) configuration. The new test reproduces the crash without the fix and passes with it.Verified locally by building the browser (Mono + CoreCLR runtime packs) and workload and running the new test:
module WasmBasicTestApp is unusable because dependency Json is not found→interp.c:4135assertion → timed out waiting forWASM EXIT.AOT: image 'WasmBasicTestApp' found.,firstJsonLoad=true,{"FirstName":"John","LastName":"Doe"},WASM EXIT 0.Risk
Low. The change is a single, narrowly-scoped MSBuild property that only appends
--aot-lazy-assembly-loadwhenRunAOTCompilation=trueand at least oneBlazorWebAssemblyLazyLoaditem is present. Builds that do not combine AOT with lazy loading are unaffected, and the option simply defers assembly binding that would otherwise fail.Note
This backport pull request was authored with GitHub Copilot.