From 1d15f2140f7eb30a976c66290491ec89cd628da0 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Thu, 29 Dec 2022 15:14:39 +0100 Subject: [PATCH] [wasm] Asserts for externaly configured assets, more pendingDownload (#79886) * asserts for assets * allow to pass .wasm as pendingDownload --- src/mono/wasm/runtime/assets.ts | 52 ++++++++++++++++++-------------- src/mono/wasm/runtime/startup.ts | 4 +-- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/mono/wasm/runtime/assets.ts b/src/mono/wasm/runtime/assets.ts index 2f6ab851f1bb1..d6bb4d25a9cfc 100644 --- a/src/mono/wasm/runtime/assets.ts +++ b/src/mono/wasm/runtime/assets.ts @@ -67,30 +67,18 @@ export async function mono_download_assets(): Promise { // start fetching and instantiating all assets in parallel for (const a of runtimeHelpers.config.assets!) { const asset: AssetEntryInternal = a; + mono_assert(typeof asset === "object", "asset must be object"); + mono_assert(typeof asset.behavior === "string", "asset behavior must be known string"); + mono_assert(typeof asset.name === "string", "asset name must be string"); + mono_assert(!asset.resolvedUrl || typeof asset.resolvedUrl === "string", "asset resolvedUrl could be string"); + mono_assert(!asset.hash || typeof asset.hash === "string", "asset resolvedUrl could be string"); + mono_assert(!asset.pendingDownload || typeof asset.pendingDownload === "object", "asset pendingDownload could be object"); if (!skipInstantiateByAssetTypes[asset.behavior]) { expected_instantiated_assets_count++; } if (!skipDownloadsByAssetTypes[asset.behavior]) { - const headersOnly = skipBufferByAssetTypes[asset.behavior];// `response.arrayBuffer()` can't be called twice. Some usecases are calling it on response in the instantiation. expected_downloaded_assets_count++; - if (asset.pendingDownload) { - asset.pendingDownloadInternal = asset.pendingDownload; - const waitForExternalData: () => Promise = async () => { - const response = await asset.pendingDownloadInternal!.response; - ++actual_downloaded_assets_count; - if (!headersOnly) { - asset.buffer = await response.arrayBuffer(); - } - return { asset, buffer: asset.buffer }; - }; - promises_of_assets_with_buffer.push(waitForExternalData()); - } else { - const waitForExternalData: () => Promise = async () => { - asset.buffer = await start_asset_download_with_retries(asset, !headersOnly); - return { asset, buffer: asset.buffer }; - }; - promises_of_assets_with_buffer.push(waitForExternalData()); - } + promises_of_assets_with_buffer.push(start_asset_download(asset)); } } allDownloadsQueued.promise_control.resolve(); @@ -103,6 +91,7 @@ export async function mono_download_assets(): Promise { if (assetWithBuffer.buffer) { if (!skipInstantiateByAssetTypes[asset.behavior]) { const url = asset.pendingDownloadInternal!.url; + mono_assert(asset.buffer && typeof asset.buffer === "object", "asset buffer must be array or buffer like"); const data = new Uint8Array(asset.buffer!); asset.pendingDownloadInternal = null as any; // GC asset.pendingDownload = null as any; // GC @@ -146,8 +135,25 @@ export async function mono_download_assets(): Promise { } } +export async function start_asset_download(asset: AssetEntryInternal) { + // `response.arrayBuffer()` can't be called twice. Some use-cases are calling it on response in the instantiation. + const headersOnly = skipBufferByAssetTypes[asset.behavior]; + if (asset.pendingDownload) { + asset.pendingDownloadInternal = asset.pendingDownload; + const response = await asset.pendingDownloadInternal!.response; + ++actual_downloaded_assets_count; + if (!headersOnly) { + asset.buffer = await response.arrayBuffer(); + } + return { asset, buffer: asset.buffer }; + } else { + asset.buffer = await start_asset_download_with_retries(asset, !headersOnly); + return { asset, buffer: asset.buffer }; + } +} + // FIXME: Connection reset is probably the only good one for which we should retry -export async function start_asset_download_with_retries(asset: AssetEntryInternal, downloadData: boolean): Promise { +async function start_asset_download_with_retries(asset: AssetEntryInternal, downloadData: boolean): Promise { try { return await start_asset_download_with_throttle(asset, downloadData); } catch (err: any) { @@ -294,7 +300,7 @@ function resolve_path(asset: AssetEntry, sourcePrefix: string): string { : asset.name; } else if (asset.behavior === "resource") { - const path = asset.culture !== "" ? `${asset.culture}/${asset.name}` : asset.name; + const path = asset.culture && asset.culture !== "" ? `${asset.culture}/${asset.name}` : asset.name; attemptUrl = assemblyRootFolder ? (assemblyRootFolder + "/" + path) : path; @@ -422,7 +428,7 @@ function _instantiate_asset(asset: AssetEntry, url: string, bytes: Uint8Array) { Module.printErr(`MONO_WASM: Error loading ICU asset ${asset.name}`); } else if (asset.behavior === "resource") { - cwraps.mono_wasm_add_satellite_assembly(virtualName, asset.culture!, offset!, bytes.length); + cwraps.mono_wasm_add_satellite_assembly(virtualName, asset.culture || "", offset!, bytes.length); } endMeasure(mark, MeasuredBlock.instantiateAsset, asset.name); ++actual_instantiated_assets_count; @@ -433,7 +439,7 @@ export async function instantiate_wasm_asset( wasmModuleImports: WebAssembly.Imports, successCallback: InstantiateWasmSuccessCallback, ): Promise { - mono_assert(pendingAsset && pendingAsset.pendingDownloadInternal, "Can't load dotnet.wasm"); + mono_assert(pendingAsset && pendingAsset.pendingDownloadInternal && pendingAsset.pendingDownloadInternal.response, "Can't load dotnet.wasm"); const response = await pendingAsset.pendingDownloadInternal.response; const contentType = response.headers ? response.headers.get("Content-Type") : undefined; let compiledInstance: WebAssembly.Instance; diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 00eea0f796dfe..ea1a58d43ac45 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -22,7 +22,7 @@ import { init_legacy_exports } from "./net6-legacy/corebindings"; import { cwraps_internal } from "./exports-internal"; import { cwraps_binding_api, cwraps_mono_api } from "./net6-legacy/exports-legacy"; import { CharPtr, InstantiateWasmCallBack, InstantiateWasmSuccessCallback } from "./types/emscripten"; -import { instantiate_wasm_asset, mono_download_assets, resolve_asset_path, start_asset_download_with_retries, wait_for_all_assets } from "./assets"; +import { instantiate_wasm_asset, mono_download_assets, resolve_asset_path, start_asset_download, wait_for_all_assets } from "./assets"; import { BINDING, MONO } from "./net6-legacy/imports"; import { readSymbolMapFile } from "./logging"; import { mono_wasm_init_diagnostics } from "./diagnostics"; @@ -445,7 +445,7 @@ async function instantiate_wasm_module( if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: instantiate_wasm_module"); const assetToLoad = resolve_asset_path("dotnetwasm"); // FIXME: this would not apply re-try (on connection reset during download) for dotnet.wasm because we could not download the buffer before we pass it to instantiate_wasm_asset - await start_asset_download_with_retries(assetToLoad, false); + await start_asset_download(assetToLoad); await beforePreInit.promise; Module.addRunDependency("instantiate_wasm_module"); await instantiate_wasm_asset(assetToLoad, imports, successCallback);