diff --git a/src/mono/sample/wasm/browser-minimal-config/main.js b/src/mono/sample/wasm/browser-minimal-config/main.js index c2f518f461f43..acd81b2899a39 100644 --- a/src/mono/sample/wasm/browser-minimal-config/main.js +++ b/src/mono/sample/wasm/browser-minimal-config/main.js @@ -1,24 +1,35 @@ import { dotnet } from "./_framework/dotnet.js"; +import * as runtime from "./_framework/dotnet.runtime.js"; -async function fetchBinary(uri) { - return new Uint8Array(await (await fetch(uri)).arrayBuffer()); +async function fetchBinary(url) { + return (await fetch(url, { cache: "no-cache" })).arrayBuffer(); +} + +function fetchWasm(url) { + return { + response: fetch(url, { cache: "no-cache" }), + url, + name: url.substring(url.lastIndexOf("/") + 1) + }; } const assets = [ { name: "dotnet.native.js", + // demo dynamic import + moduleExports: import("./_framework/dotnet.native.js"), behavior: "js-module-native" }, - { - name: "dotnet.js", - behavior: "js-module-dotnet" - }, { name: "dotnet.runtime.js", + // demo static import + moduleExports: runtime, behavior: "js-module-runtime" }, { name: "dotnet.native.wasm", + // demo pending download promise + pendingDownload: fetchWasm("./_framework/dotnet.native.wasm"), behavior: "dotnetwasm" }, { @@ -31,7 +42,8 @@ const assets = [ }, { name: "Wasm.Browser.Config.Sample.wasm", - buffer: await fetchBinary("./_framework/Wasm.Browser.Config.Sample.wasm"), + // demo buffer promise + buffer: fetchBinary("./_framework/Wasm.Browser.Config.Sample.wasm"), behavior: "assembly" }, { diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts index 1d5e74e2a9fbb..a25058f2a2e80 100644 --- a/src/mono/wasm/runtime/dotnet.d.ts +++ b/src/mono/wasm/runtime/dotnet.d.ts @@ -266,7 +266,12 @@ interface AssetEntry { * If provided, runtime doesn't have to fetch the data. * Runtime would set the buffer to null after instantiation to free the memory. */ - buffer?: ArrayBuffer; + buffer?: ArrayBuffer | Promise; + /** + * If provided, runtime doesn't have to import it's JavaScript modules. + * This will not work for multi-threaded runtime. + */ + moduleExports?: any | Promise; /** * It's metadata + fetch-like Promise * If provided, the runtime doesn't have to initiate the download. It would just await the response. diff --git a/src/mono/wasm/runtime/loader/assets.ts b/src/mono/wasm/runtime/loader/assets.ts index b39ce6a48b98f..ba18815a08db8 100644 --- a/src/mono/wasm/runtime/loader/assets.ts +++ b/src/mono/wasm/runtime/loader/assets.ts @@ -201,10 +201,11 @@ export async function mono_download_assets(): Promise { const asset = await downloadPromise; if (asset.buffer) { if (!skipInstantiateByAssetTypes[asset.behavior]) { - mono_assert(asset.buffer && typeof asset.buffer === "object", "asset buffer must be array or buffer like"); + mono_assert(asset.buffer && typeof asset.buffer === "object", "asset buffer must be array-like or buffer-like or promise of these"); mono_assert(typeof asset.resolvedUrl === "string", "resolvedUrl must be string"); const url = asset.resolvedUrl!; - const data = new Uint8Array(asset.buffer!); + const buffer = await asset.buffer; + const data = new Uint8Array(buffer); cleanupAsset(asset); // wait till after onRuntimeInitialized and after memory snapshot is loaded or skipped @@ -487,7 +488,7 @@ async function start_asset_download_sources(asset: AssetEntryInternal): Promise< return asset.pendingDownloadInternal.response; } if (asset.buffer) { - const buffer = asset.buffer; + const buffer = await asset.buffer; if (!asset.resolvedUrl) { asset.resolvedUrl = "undefined://" + asset.name; } @@ -707,6 +708,7 @@ export function cleanupAsset(asset: AssetEntryInternal) { asset.pendingDownloadInternal = null as any; // GC asset.pendingDownload = null as any; // GC asset.buffer = null as any; // GC + asset.moduleExports = null as any; // GC } function fileName(name: string) { diff --git a/src/mono/wasm/runtime/loader/run.ts b/src/mono/wasm/runtime/loader/run.ts index e6bd20142258c..a3cb7977a30f0 100644 --- a/src/mono/wasm/runtime/loader/run.ts +++ b/src/mono/wasm/runtime/loader/run.ts @@ -428,16 +428,29 @@ export async function createEmscripten(moduleFactory: DotnetModuleConfig | ((api : createEmscriptenMain(); } +// in the future we can use feature detection to load different flavors function importModules() { const jsModuleRuntimeAsset = resolve_single_asset_path("js-module-runtime"); const jsModuleNativeAsset = resolve_single_asset_path("js-module-native"); - mono_log_debug(`Attempting to import '${jsModuleRuntimeAsset.resolvedUrl}' for ${jsModuleRuntimeAsset.name}`); - mono_log_debug(`Attempting to import '${jsModuleNativeAsset.resolvedUrl}' for ${jsModuleNativeAsset.name}`); - return [ - // keep js module names dynamic by using config, in the future we can use feature detection to load different flavors - import(/* webpackIgnore: true */jsModuleRuntimeAsset.resolvedUrl!), - import(/* webpackIgnore: true */jsModuleNativeAsset.resolvedUrl!), - ]; + + let jsModuleRuntimePromise: Promise; + let jsModuleNativePromise: Promise; + + if (typeof jsModuleRuntimeAsset.moduleExports === "object") { + jsModuleRuntimePromise = jsModuleRuntimeAsset.moduleExports; + } else { + mono_log_debug(`Attempting to import '${jsModuleRuntimeAsset.resolvedUrl}' for ${jsModuleRuntimeAsset.name}`); + jsModuleRuntimePromise = import(/* webpackIgnore: true */jsModuleRuntimeAsset.resolvedUrl!); + } + + if (typeof jsModuleNativeAsset.moduleExports === "object") { + jsModuleNativePromise = jsModuleNativeAsset.moduleExports; + } else { + mono_log_debug(`Attempting to import '${jsModuleNativeAsset.resolvedUrl}' for ${jsModuleNativeAsset.name}`); + jsModuleNativePromise = import(/* webpackIgnore: true */jsModuleNativeAsset.resolvedUrl!); + } + + return [jsModuleRuntimePromise, jsModuleNativePromise]; } async function initializeModules(es6Modules: [RuntimeModuleExportsInternal, NativeModuleExportsInternal]) { diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 777d54d23b938..f67a8b7c18ba9 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -480,6 +480,7 @@ async function instantiate_wasm_module( assetToLoad.pendingDownloadInternal = null as any; // GC assetToLoad.pendingDownload = null as any; // GC assetToLoad.buffer = null as any; // GC + assetToLoad.moduleExports = null as any; // GC mono_log_debug("instantiate_wasm_module done"); diff --git a/src/mono/wasm/runtime/types/index.ts b/src/mono/wasm/runtime/types/index.ts index 9c0ea73b0ec70..816080c85e817 100644 --- a/src/mono/wasm/runtime/types/index.ts +++ b/src/mono/wasm/runtime/types/index.ts @@ -205,7 +205,14 @@ export interface AssetEntry { * If provided, runtime doesn't have to fetch the data. * Runtime would set the buffer to null after instantiation to free the memory. */ - buffer?: ArrayBuffer + buffer?: ArrayBuffer | Promise, + + /** + * If provided, runtime doesn't have to import it's JavaScript modules. + * This will not work for multi-threaded runtime. + */ + moduleExports?: any | Promise, + /** * It's metadata + fetch-like Promise * If provided, the runtime doesn't have to initiate the download. It would just await the response.