Skip to content

Commit

Permalink
fix for dotnet#89883
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelsavara committed Aug 3, 2023
1 parent 58d1e7a commit a8e6e9b
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 35 deletions.
122 changes: 92 additions & 30 deletions src/mono/wasm/runtime/loader/assets.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import MonoWasmThreads from "consts:monoWasmThreads";

import type { AssetEntryInternal, PromiseAndController } from "../types/internal";
import type { AssetBehaviors, AssetEntry, LoadingResource, ResourceList, SingleAssetBehaviors as SingleAssetBehaviors, WebAssemblyBootResourceType } from "../types";
import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
Expand All @@ -9,13 +11,18 @@ import { mono_log_debug } from "./logging";
import { mono_exit } from "./exit";
import { addCachedReponse, findCachedResponse } from "./assetsCache";
import { getIcuResourceName } from "./icu";
import { mono_log_warn } from "./logging";
import { makeURLAbsoluteWithApplicationBase } from "./polyfills";


let throttlingPromise: PromiseAndController<void> | undefined;
// in order to prevent net::ERR_INSUFFICIENT_RESOURCES if we start downloading too many files at same time
let parallel_count = 0;
const containedInSnapshotAssets: AssetEntryInternal[] = [];
const alwaysLoadedAssets: AssetEntryInternal[] = [];
let wasmModuleAsset: AssetEntryInternal;
let jsModuleWorkerAsset: AssetEntryInternal;
let jsModuleNativeAsset: AssetEntryInternal;
let jsModuleRuntimeAsset: AssetEntryInternal;

const jsModulesAssetTypes: {
[k: string]: boolean
Expand All @@ -24,6 +31,16 @@ const jsModulesAssetTypes: {
"js-module-runtime": true,
"js-module-dotnet": true,
"js-module-native": true,
"js-module-library-initializer": true,
};

// append query to asset url to prevent reusing state
const appendQueryAssetTypes: {
[k: string]: boolean
} = {
...jsModulesAssetTypes,
"manifest": true,
// TODO consider adding query to appsettings too ?
};

// don't `fetch` javaScript and wasm files
Expand Down Expand Up @@ -67,59 +84,74 @@ export function shouldLoadIcuAsset(asset: AssetEntryInternal): boolean {
return !(asset.behavior == "icu" && asset.name != loaderHelpers.preferredIcuAsset);
}

function getSingleAssetWithResolvedUrl(resources: ResourceList | undefined, behavior: SingleAssetBehaviors): AssetEntry {
const keys = Object.keys(resources || {});
function convertSingleAsset(resource: ResourceList | undefined, behavior: SingleAssetBehaviors): AssetEntryInternal {
const keys = Object.keys(resource || {});
mono_assert(keys.length == 1, `Expect to have one ${behavior} asset in resources`);

const name = keys[0];
// FIXME, this creates another copy of the asset object

const asset = {
name,
hash: resources![name],
hash: resource![name],
behavior,
resolvedUrl: appendUniqueQuery(loaderHelpers.locateFile(name), behavior)
};

const customSrc = invokeLoadBootResource(asset);
if (typeof (customSrc) === "string") {
asset.resolvedUrl = makeURLAbsoluteWithApplicationBase(customSrc);
} else if (customSrc) {
mono_log_warn(`For ${behavior} resource: ${name}, custom loaders must supply a URI string.`);
// we apply a default URL
}
return asset;
}

function get_single_asset(behavior: SingleAssetBehaviors): AssetEntryInternal {
let asset: AssetEntryInternal;
switch (behavior) {
case "dotnetwasm":
asset = wasmModuleAsset;
break;
case "js-module-threads":
asset = jsModuleWorkerAsset;
break;
case "js-module-native":
asset = jsModuleNativeAsset;
break;
case "js-module-runtime":
asset = jsModuleRuntimeAsset;
break;
default:
throw new Error(`Unknown single asset behavior ${behavior}`);
}
return asset;
}

export function resolve_single_asset_path(behavior: SingleAssetBehaviors): AssetEntryInternal {
const resources = loaderHelpers.config.resources;
mono_assert(resources, "Can't find resources in config");
const asset = get_single_asset(behavior);
asset.resolvedUrl = appendUniqueQuery(loaderHelpers.locateFile(asset.name), asset.behavior);

let customLoadResult;
switch (behavior) {
case "dotnetwasm":
return getSingleAssetWithResolvedUrl(resources.wasmNative, behavior);
case "js-module-threads":
return getSingleAssetWithResolvedUrl(resources.jsModuleWorker, behavior);
case "js-module-native":
return getSingleAssetWithResolvedUrl(resources.jsModuleNative, behavior);
case "js-module-runtime":
return getSingleAssetWithResolvedUrl(resources.jsModuleRuntime, behavior);
// give loadBootResource chance to override the url for JS modules with 'dotnetjs' type
customLoadResult = invokeLoadBootResource(asset);
if (customLoadResult) {
mono_assert(typeof customLoadResult === "string", "loadBootResource response for 'dotnetjs' type should be a string");
asset.resolvedUrl = customLoadResult;
}
break;
case "dotnetwasm":
// other types except 'dotnetjs' may call real fetch in loadBootResource, which is not wrapped by cache here
break;
default:
throw new Error(`Unknown single asset behavior ${behavior}`);
}
return asset;
}

export async function mono_download_assets(): Promise<void> {
mono_log_debug("mono_download_assets");
loaderHelpers.maxParallelDownloads = loaderHelpers.config.maxParallelDownloads || loaderHelpers.maxParallelDownloads;
loaderHelpers.enableDownloadRetry = loaderHelpers.config.enableDownloadRetry || loaderHelpers.enableDownloadRetry;
try {
const alwaysLoadedAssets: AssetEntryInternal[] = [];
const containedInSnapshotAssets: AssetEntryInternal[] = [];
const promises_of_assets: Promise<AssetEntryInternal>[] = [];

prepareAssets(containedInSnapshotAssets, alwaysLoadedAssets);

const countAndStartDownload = (asset: AssetEntryInternal) => {
if (!skipInstantiateByAssetTypes[asset.behavior] && shouldLoadIcuAsset(asset)) {
loaderHelpers.expected_instantiated_assets_count++;
Expand Down Expand Up @@ -223,7 +255,7 @@ export async function mono_download_assets(): Promise<void> {
}
}

function prepareAssets(containedInSnapshotAssets: AssetEntryInternal[], alwaysLoadedAssets: AssetEntryInternal[]) {
export function prepareAssets() {
const config = loaderHelpers.config;

// if assets exits, we will assume Net7 legacy and not process resources object
Expand All @@ -241,9 +273,35 @@ function prepareAssets(containedInSnapshotAssets: AssetEntryInternal[], alwaysLo
} else {
alwaysLoadedAssets.push(asset);
}
switch (asset.behavior) {
case "dotnetwasm":
wasmModuleAsset = asset;
break;
case "js-module-native":
jsModuleNativeAsset = asset;
break;
case "js-module-runtime":
jsModuleRuntimeAsset = asset;
break;
case "js-module-threads":
jsModuleWorkerAsset = asset;
break;
}
}
} else if (config.resources) {
const resources = config.resources;

mono_assert(resources.wasmNative, "resources.wasmNative must be defined");
mono_assert(resources.jsModuleNative, "resources.jsModuleNative must be defined");
mono_assert(resources.jsModuleRuntime, "resources.jsModuleRuntime must be defined");
mono_assert(!MonoWasmThreads || resources.jsModuleWorker, "resources.jsModuleWorker must be defined");
wasmModuleAsset = convertSingleAsset(resources.wasmNative, "dotnetwasm");
jsModuleNativeAsset = convertSingleAsset(resources.jsModuleNative, "js-module-native");
jsModuleRuntimeAsset = convertSingleAsset(resources.jsModuleRuntime, "js-module-runtime");
if (MonoWasmThreads) {
jsModuleWorkerAsset = convertSingleAsset(resources.jsModuleWorker, "js-module-threads");
}

if (resources.assembly) {
for (const name in resources.assembly) {
containedInSnapshotAssets.push({
Expand Down Expand Up @@ -321,9 +379,9 @@ function prepareAssets(containedInSnapshotAssets: AssetEntryInternal[], alwaysLo
const configFileName = fileName(configUrl);
if (configFileName === "appsettings.json" || configFileName === `appsettings.${config.applicationEnvironment}.json`) {
alwaysLoadedAssets.push({
name: configFileName,
resolvedUrl: appendUniqueQuery(loaderHelpers.locateFile(configUrl), "vfs"),
name: configUrl,
behavior: "vfs",
// TODO what should be the virtualPath ?
noCache: true,
useCredentials: true
});
Expand Down Expand Up @@ -528,7 +586,7 @@ function resolve_path(asset: AssetEntry, sourcePrefix: string): string {

export function appendUniqueQuery(attemptUrl: string, behavior: AssetBehaviors): string {
// apply unique query to js modules to make the module state independent of the other runtime instances
if (loaderHelpers.modulesUniqueQuery && jsModulesAssetTypes[behavior]) {
if (loaderHelpers.modulesUniqueQuery && appendQueryAssetTypes[behavior]) {
attemptUrl = attemptUrl + loaderHelpers.modulesUniqueQuery;
}

Expand Down Expand Up @@ -589,7 +647,7 @@ function fetchResource(asset: AssetEntryInternal): Promise<Response> {
// They are supplying an entire custom response, so just use that
return customLoadResult;
} else if (typeof customLoadResult === "string") {
url = makeURLAbsoluteWithApplicationBase(customLoadResult);
url = customLoadResult;
}
}

Expand Down Expand Up @@ -635,7 +693,11 @@ function invokeLoadBootResource(asset: AssetEntryInternal): string | Promise<Res

const resourceType = monoToBlazorAssetTypeMap[asset.behavior];
if (resourceType) {
return loaderHelpers.loadBootResource(resourceType, asset.name, url, requestHash, asset.behavior);
const customLoadResult = loaderHelpers.loadBootResource(resourceType, asset.name, url, requestHash, asset.behavior);
if (typeof customLoadResult === "string") {
return makeURLAbsoluteWithApplicationBase(customLoadResult);
}
return customLoadResult;
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/mono/wasm/runtime/loader/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { mono_log_error, mono_log_debug } from "./logging";
import { invokeLibraryInitializers } from "./libraryInitializers";
import { mono_exit } from "./exit";
import { makeURLAbsoluteWithApplicationBase } from "./polyfills";
import { appendUniqueQuery } from "./assets";

export function deep_merge_config(target: MonoConfigInternal, source: MonoConfigInternal): MonoConfigInternal {
// no need to merge the same object
Expand Down Expand Up @@ -259,7 +260,7 @@ export function hasDebuggingEnabled(config: MonoConfigInternal): boolean {
}

async function loadBootConfig(module: DotnetModuleInternal): Promise<void> {
const defaultConfigSrc = loaderHelpers.locateFile(module.configSrc!);
const defaultConfigSrc = appendUniqueQuery(loaderHelpers.locateFile(module.configSrc!), "manifest");

const loaderResponse = loaderHelpers.loadBootResource !== undefined ?
loaderHelpers.loadBootResource("manifest", "blazor.boot.json", defaultConfigSrc, "", "manifest") :
Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/runtime/loader/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export function setLoaderGlobals(
getPromiseController,
assertIsControllablePromise,
mono_download_assets,
resolve_asset_path: resolve_single_asset_path,
resolve_single_asset_path,
setup_proxy_console,
logDownloadStatsToConsole,
purgeUnusedCacheEntriesAsync,
Expand Down
4 changes: 3 additions & 1 deletion src/mono/wasm/runtime/loader/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, exportedRuntimeAPI, globalObje
import { deep_merge_config, deep_merge_module, mono_wasm_load_config } from "./config";
import { mono_exit } from "./exit";
import { setup_proxy_console, mono_log_info } from "./logging";
import { resolve_single_asset_path, start_asset_download } from "./assets";
import { prepareAssets, resolve_single_asset_path, start_asset_download } from "./assets";
import { detect_features_and_polyfill } from "./polyfills";
import { runtimeHelpers, loaderHelpers } from "./globals";
import { init_globalization } from "./icu";
Expand Down Expand Up @@ -466,6 +466,8 @@ async function createEmscriptenMain(): Promise<RuntimeAPI> {
// download config
await mono_wasm_load_config(module);

prepareAssets();

const promises = importModules();

await initCacheToUseIfEnabled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function replaceEmscriptenPThreadLibrary(replacements: PThreadReplacement
/// We replace Module["PThreads"].allocateUnusedWorker with this version that knows about assets
function replacementAllocateUnusedWorker(): void {
mono_log_debug("replacementAllocateUnusedWorker");
const asset = loaderHelpers.resolve_asset_path("js-module-threads");
const asset = loaderHelpers.resolve_single_asset_path("js-module-threads");
const uri = asset.resolvedUrl;
mono_assert(uri !== undefined, "could not resolve the uri for the js-module-threads asset");
const worker = new Worker(uri);
Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/runtime/types/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export type LoaderHelpers = {
getPromiseController: <T>(promise: ControllablePromise<T>) => PromiseController<T>,
assertIsControllablePromise: <T>(promise: Promise<T>) => asserts promise is ControllablePromise<T>,
mono_download_assets: () => Promise<void>,
resolve_asset_path: (behavior: AssetBehaviors) => AssetEntryInternal,
resolve_single_asset_path: (behavior: AssetBehaviors) => AssetEntryInternal,
setup_proxy_console: (id: string, console: Console, origin: string) => void
fetch_like: (url: string, init?: RequestInit) => Promise<Response>;
locateFile: (path: string, prefix?: string) => string,
Expand Down

0 comments on commit a8e6e9b

Please sign in to comment.