[browser][CoreCLR] lazy and satelite assemblies#124853
[browser][CoreCLR] lazy and satelite assemblies#124853pavelsavara wants to merge 7 commits intodotnet:mainfrom
Conversation
|
Tagging subscribers to this area: @agocke, @jeffschwMSFT, @elinor-fung |
There was a problem hiding this comment.
Pull request overview
This PR implements support for loading satellite assemblies (localization resources) and lazy-loaded assemblies in the browser/CoreCLR JavaScript loader by wiring new loader export functions end-to-end (loader → cross-module table → interop INTERNAL.* APIs).
Changes:
- Implemented
loadSatelliteAssemblies/loadLazyAssemblyby delegating to loader-side asset fetchers. - Added new loader exports (
fetchSatelliteAssemblies,fetchLazyAssembly) to the cross-module exchange table and exposed them viadotnetLoaderExports. - Added support code for downloading/registration of satellite resources and lazy assemblies, plus PDB registration in the host FS.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/lazy.ts | Implements interop entrypoints by delegating to loader exports. |
| src/native/libs/Common/JavaScript/types/exchange.ts | Extends LoaderExports / LoaderExportsTable with new asset-fetching APIs. |
| src/native/libs/Common/JavaScript/loader/run.ts | Optionally preloads all satellite resources during startup. |
| src/native/libs/Common/JavaScript/loader/index.ts | Publishes the new loader functions into the cross-module table. |
| src/native/libs/Common/JavaScript/loader/config.ts | Updates resource merging to include satellite resource keys (but currently duplicates data). |
| src/native/libs/Common/JavaScript/loader/assets.ts | Adds fetchSatelliteAssemblies/fetchLazyAssembly and culture-aware assembly fetching. |
| src/native/libs/Common/JavaScript/host/assets.ts | Updates assembly name aliasing and implements PDB registration into the VFS. |
| src/native/libs/Common/JavaScript/cross-module/index.ts | Extends loader exports reconstruction from the exchange table. |
| export async function fetchLazyAssembly(assemblyNameToLoad: string): Promise<boolean> { | ||
| const lazyAssemblies = loaderConfig.resources?.lazyAssembly; | ||
| if (!lazyAssemblies) { | ||
| throw new Error("No assemblies have been marked as lazy-loadable. Use the 'BlazorWebAssemblyLazyLoad' item group in your project file to enable lazy loading an assembly."); | ||
| } | ||
|
|
||
| let assemblyNameWithoutExtension = assemblyNameToLoad; | ||
| if (assemblyNameToLoad.endsWith(".dll")) | ||
| assemblyNameWithoutExtension = assemblyNameToLoad.substring(0, assemblyNameToLoad.length - 4); | ||
| else if (assemblyNameToLoad.endsWith(".wasm")) | ||
| assemblyNameWithoutExtension = assemblyNameToLoad.substring(0, assemblyNameToLoad.length - 5); | ||
|
|
||
| const assemblyNameToLoadDll = assemblyNameWithoutExtension + ".dll"; | ||
| const assemblyNameToLoadWasm = assemblyNameWithoutExtension + ".wasm"; | ||
|
|
||
| let dllAsset: AssemblyAsset | null = null; | ||
| for (const asset of lazyAssemblies) { | ||
| if (asset.virtualPath === assemblyNameToLoadDll || asset.virtualPath === assemblyNameToLoadWasm) { | ||
| dllAsset = asset; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (!dllAsset) { | ||
| throw new Error(`${assemblyNameToLoad} must be marked with 'BlazorWebAssemblyLazyLoad' item group in your project file to allow lazy-loading.`); | ||
| } | ||
|
|
||
| await fetchDll(dllAsset); | ||
|
|
||
| if (loaderConfig.debugLevel != 0) { | ||
| const pdbNameToLoad = assemblyNameWithoutExtension + ".pdb"; | ||
| const pdbAssets = loaderConfig.resources?.pdb; | ||
| if (pdbAssets) { | ||
| for (const pdbAsset of pdbAssets) { | ||
| if (pdbAsset.virtualPath === pdbNameToLoad) { | ||
| await fetchPdb(pdbAsset); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| } |
There was a problem hiding this comment.
fetchLazyAssembly always returns true and re-registers the assembly even if it was already loaded previously. The existing WASM loader API returns false when the assembly is already loaded (see src/mono/browser/runtime/lazyLoading.ts) and avoids redundant downloads/allocations; the current behavior can leak memory because registerDllBytes allocates unmanaged memory each time. Track loaded assemblies (e.g., by asset name) and return false when already loaded.
ilonatommy
left a comment
There was a problem hiding this comment.
Apart from automated review (lazy loading / unexpected result of multiple calls of fetchDll) it loos good to me.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Contributes to #120226