diff --git a/src/mono/sample/wasm/Directory.Build.targets b/src/mono/sample/wasm/Directory.Build.targets
index 6979b8536abaa5..be23863e311932 100644
--- a/src/mono/sample/wasm/Directory.Build.targets
+++ b/src/mono/sample/wasm/Directory.Build.targets
@@ -38,7 +38,7 @@
-
+
diff --git a/src/mono/sample/wasm/browser-bench/frame-main.js b/src/mono/sample/wasm/browser-bench/frame-main.js
index bf767c9e7ebc5f..9045a9544988da 100644
--- a/src/mono/sample/wasm/browser-bench/frame-main.js
+++ b/src/mono/sample/wasm/browser-bench/frame-main.js
@@ -38,11 +38,11 @@ try {
console.error(...arguments);
}
},
- onConfigLoaded: () => {
+ onConfigLoaded: (config) => {
if (window.parent != window) {
window.parent.resolveAppStartEvent("onConfigLoaded");
}
- // Module.config.diagnosticTracing = true;
+ // config.diagnosticTracing = true;
},
onAbort: (error) => {
wasm_exit(1, error);
diff --git a/src/mono/wasm/runtime/assets.ts b/src/mono/wasm/runtime/assets.ts
new file mode 100644
index 00000000000000..362d6ba0f38a55
--- /dev/null
+++ b/src/mono/wasm/runtime/assets.ts
@@ -0,0 +1,415 @@
+import cwraps from "./cwraps";
+import { mono_wasm_load_icu_data } from "./icu";
+import { ENVIRONMENT_IS_WEB, Module, runtimeHelpers } from "./imports";
+import { mono_wasm_load_bytes_into_heap } from "./memory";
+import { MONO } from "./net6-legacy/imports";
+import { createPromiseController, PromiseAndController } from "./promise-controller";
+import { delay } from "./promise-utils";
+import { beforeOnRuntimeInitialized } from "./startup";
+import { AssetBehaviours, AssetEntry, LoadingResource, mono_assert, ResourceRequest } from "./types";
+import { InstantiateWasmSuccessCallback, VoidPtr } from "./types/emscripten";
+
+const allAssetsInMemory = createPromiseController();
+const allDownloadsQueued = createPromiseController();
+let downloded_assets_count = 0;
+let instantiated_assets_count = 0;
+const loaded_files: { url: string, file: string }[] = [];
+const loaded_assets: { [id: string]: [VoidPtr, number] } = Object.create(null);
+// in order to prevent net::ERR_INSUFFICIENT_RESOURCES if we start downloading too many files at same time
+let parallel_count = 0;
+let throttling: PromiseAndController | undefined;
+const skipDownloadsAssetTypes: {
+ [k: string]: boolean
+} = {
+ "js-module-crypto": true,
+ "js-module-threads": true,
+};
+
+export function resolve_asset_path(behavior: AssetBehaviours) {
+ const asset: AssetEntry | undefined = runtimeHelpers.config.assets?.find(a => a.behavior == behavior);
+ mono_assert(asset, () => `Can't find asset for ${behavior}`);
+ if (!asset.resolvedUrl) {
+ asset.resolvedUrl = resolve_path(asset, "");
+ }
+ return asset;
+}
+
+export async function mono_download_assets(): Promise {
+ if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: mono_download_assets");
+ runtimeHelpers.maxParallelDownloads = runtimeHelpers.config.maxParallelDownloads || runtimeHelpers.maxParallelDownloads;
+ try {
+ const download_promises: Promise[] = [];
+ // start fetching and instantiating all assets in parallel
+ for (const asset of runtimeHelpers.config.assets!) {
+ if (!asset.pending && !skipDownloadsAssetTypes[asset.behavior]) {
+ download_promises.push(start_asset_download(asset));
+ }
+ }
+ allDownloadsQueued.promise_control.resolve();
+
+ const asset_promises: Promise[] = [];
+ for (const downloadPromise of download_promises) {
+ const downloadedAsset = await downloadPromise;
+ if (downloadedAsset) {
+ asset_promises.push((async () => {
+ const url = downloadedAsset.pending!.url;
+ const response = await downloadedAsset.pending!.response;
+ downloadedAsset.pending = undefined; //GC
+ const buffer = await response.arrayBuffer();
+ await beforeOnRuntimeInitialized.promise;
+ // this is after onRuntimeInitialized
+ _instantiate_asset(downloadedAsset, url, new Uint8Array(buffer));
+ })());
+ }
+ }
+
+ // this await will get past the onRuntimeInitialized because we are not blocking via addRunDependency
+ // and we are not awating it here
+ Promise.all(asset_promises).then(() => allAssetsInMemory.promise_control.resolve());
+ // OPTIMIZATION explained:
+ // we do it this way so that we could allocate memory immediately after asset is downloaded (and after onRuntimeInitialized which happened already)
+ // spreading in time
+ // rather than to block all downloads after onRuntimeInitialized or block onRuntimeInitialized after all downloads are done. That would create allocation burst.
+ } catch (err: any) {
+ Module.printErr("MONO_WASM: Error in mono_download_assets: " + err);
+ throw err;
+ }
+}
+
+export async function start_asset_download(asset: AssetEntry): Promise {
+ try {
+ return await start_asset_download_throttle(asset);
+ } catch (err: any) {
+ if (err && err.status == 404) {
+ throw err;
+ }
+ // second attempt only after all first attempts are queued
+ await allDownloadsQueued.promise;
+ try {
+ return await start_asset_download_throttle(asset);
+ } catch (err) {
+ // third attempt after small delay
+ await delay(100);
+ return await start_asset_download_throttle(asset);
+ }
+ }
+}
+
+function resolve_path(asset: AssetEntry, sourcePrefix: string): string {
+ let attemptUrl;
+ const assemblyRootFolder = runtimeHelpers.config.assemblyRootFolder;
+ if (!asset.resolvedUrl) {
+ if (sourcePrefix === "") {
+ if (asset.behavior === "assembly" || asset.behavior === "pdb")
+ attemptUrl = assemblyRootFolder + "/" + asset.name;
+ else if (asset.behavior === "resource") {
+ const path = asset.culture !== "" ? `${asset.culture}/${asset.name}` : asset.name;
+ attemptUrl = assemblyRootFolder + "/" + path;
+ }
+ else {
+ attemptUrl = asset.name;
+ }
+ } else {
+ attemptUrl = sourcePrefix + asset.name;
+ }
+ attemptUrl = runtimeHelpers.locateFile(attemptUrl);
+ }
+ else {
+ attemptUrl = asset.resolvedUrl;
+ }
+ return attemptUrl;
+}
+
+function download_resource(request: ResourceRequest): LoadingResource {
+ if (typeof Module.downloadResource === "function") {
+ const loading = Module.downloadResource(request);
+ if (loading) return loading;
+ }
+ const options: any = {};
+ if (request.hash) {
+ options.integrity = request.hash;
+ }
+ const response = runtimeHelpers.fetch_like(request.resolvedUrl!, options);
+ return {
+ name: request.name, url: request.resolvedUrl!, response
+ };
+}
+
+async function start_asset_download_sources(asset: AssetEntry): Promise {
+ // we don't addRunDependency to allow download in parallel with onRuntimeInitialized event!
+ if (asset.buffer) {
+ ++downloded_assets_count;
+ const buffer = asset.buffer;
+ asset.buffer = undefined;//GC later
+ asset.pending = {
+ url: "undefined://" + asset.name,
+ name: asset.name,
+ response: Promise.resolve({
+ arrayBuffer: () => buffer,
+ headers: {
+ get: () => undefined,
+ }
+ }) as any
+ };
+ return Promise.resolve(asset);
+ }
+ if (asset.pending) {
+ ++downloded_assets_count;
+ return asset;
+ }
+
+ const sourcesList = asset.loadRemote && runtimeHelpers.config.remoteSources ? runtimeHelpers.config.remoteSources : [""];
+ let response: Response | undefined = undefined;
+ for (let sourcePrefix of sourcesList) {
+ sourcePrefix = sourcePrefix.trim();
+ // HACK: Special-case because MSBuild doesn't allow "" as an attribute
+ if (sourcePrefix === "./")
+ sourcePrefix = "";
+
+ const attemptUrl = resolve_path(asset, sourcePrefix);
+ if (asset.name === attemptUrl) {
+ if (runtimeHelpers.diagnosticTracing)
+ console.debug(`MONO_WASM: Attempting to download '${attemptUrl}'`);
+ } else {
+ if (runtimeHelpers.diagnosticTracing)
+ console.debug(`MONO_WASM: Attempting to download '${attemptUrl}' for ${asset.name}`);
+ }
+ try {
+ const loadingResource = download_resource({
+ name: asset.name,
+ resolvedUrl: attemptUrl,
+ hash: asset.hash,
+ behavior: asset.behavior
+ });
+ asset.pending = loadingResource;
+ response = await loadingResource.response;
+ if (!response.ok) {
+ continue;// next source
+ }
+ ++downloded_assets_count;
+ return asset;
+ }
+ catch (err) {
+ continue; //next source
+ }
+ }
+ throw response;
+}
+
+async function start_asset_download_throttle(asset: AssetEntry): Promise {
+ // we don't addRunDependency to allow download in parallel with onRuntimeInitialized event!
+ while (throttling) {
+ await throttling.promise;
+ }
+ try {
+ ++parallel_count;
+ if (parallel_count == runtimeHelpers.maxParallelDownloads) {
+ if (runtimeHelpers.diagnosticTracing)
+ console.debug("MONO_WASM: Throttling further parallel downloads");
+ throttling = createPromiseController();
+ }
+ return await start_asset_download_sources(asset);
+ }
+ catch (response: any) {
+ const isOkToFail = asset.isOptional || (asset.name.match(/\.pdb$/) && runtimeHelpers.config.ignorePdbLoadErrors);
+ if (!isOkToFail) {
+ const err: any = new Error(`MONO_WASM: download '${response.url}' for ${asset.name} failed ${response.status} ${response.statusText}`);
+ err.status = response.status;
+ throw err;
+ }
+ }
+ finally {
+ --parallel_count;
+ if (throttling && parallel_count == runtimeHelpers.maxParallelDownloads - 1) {
+ if (runtimeHelpers.diagnosticTracing)
+ console.debug("MONO_WASM: Resuming more parallel downloads");
+ const old_throttling = throttling;
+ throttling = undefined;
+ old_throttling.promise_control.resolve();
+ }
+ }
+}
+
+// this need to be run only after onRuntimeInitialized event, when the memory is ready
+function _instantiate_asset(asset: AssetEntry, url: string, bytes: Uint8Array) {
+ if (runtimeHelpers.diagnosticTracing)
+ console.debug(`MONO_WASM: Loaded:${asset.name} as ${asset.behavior} size ${bytes.length} from ${url}`);
+
+ const virtualName: string = typeof (asset.virtualPath) === "string"
+ ? asset.virtualPath
+ : asset.name;
+ let offset: VoidPtr | null = null;
+
+ switch (asset.behavior) {
+ case "dotnetwasm":
+ case "js-module-crypto":
+ case "js-module-threads":
+ // do nothing
+ break;
+ case "resource":
+ case "assembly":
+ case "pdb":
+ loaded_files.push({ url: url, file: virtualName });
+ // falls through
+ case "heap":
+ case "icu":
+ offset = mono_wasm_load_bytes_into_heap(bytes);
+ loaded_assets[virtualName] = [offset, bytes.length];
+ break;
+
+ case "vfs": {
+ // FIXME
+ const lastSlash = virtualName.lastIndexOf("/");
+ let parentDirectory = (lastSlash > 0)
+ ? virtualName.substr(0, lastSlash)
+ : null;
+ let fileName = (lastSlash > 0)
+ ? virtualName.substr(lastSlash + 1)
+ : virtualName;
+ if (fileName.startsWith("/"))
+ fileName = fileName.substr(1);
+ if (parentDirectory) {
+ if (runtimeHelpers.diagnosticTracing)
+ console.debug(`MONO_WASM: Creating directory '${parentDirectory}'`);
+
+ Module.FS_createPath(
+ "/", parentDirectory, true, true // fixme: should canWrite be false?
+ );
+ } else {
+ parentDirectory = "/";
+ }
+
+ if (runtimeHelpers.diagnosticTracing)
+ console.debug(`MONO_WASM: Creating file '${fileName}' in directory '${parentDirectory}'`);
+
+ if (!mono_wasm_load_data_archive(bytes, parentDirectory)) {
+ Module.FS_createDataFile(
+ parentDirectory, fileName,
+ bytes, true /* canRead */, true /* canWrite */, true /* canOwn */
+ );
+ }
+ break;
+ }
+ default:
+ throw new Error(`Unrecognized asset behavior:${asset.behavior}, for asset ${asset.name}`);
+ }
+
+ if (asset.behavior === "assembly") {
+ // this is reading flag inside the DLL about the existence of PDB
+ // it doesn't relate to whether the .pdb file is downloaded at all
+ const hasPpdb = cwraps.mono_wasm_add_assembly(virtualName, offset!, bytes.length);
+
+ if (!hasPpdb) {
+ const index = loaded_files.findIndex(element => element.file == virtualName);
+ loaded_files.splice(index, 1);
+ }
+ }
+ else if (asset.behavior === "icu") {
+ if (!mono_wasm_load_icu_data(offset!))
+ 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);
+ }
+ ++instantiated_assets_count;
+}
+
+export async function instantiate_wasm_asset(
+ pendingAsset: AssetEntry,
+ wasmModuleImports: WebAssembly.Imports,
+ successCallback: InstantiateWasmSuccessCallback,
+) {
+ mono_assert(pendingAsset && pendingAsset.pending, "Can't load dotnet.wasm");
+ const response = await pendingAsset.pending.response;
+ const contentType = response.headers ? response.headers.get("Content-Type") : undefined;
+ let compiledInstance: WebAssembly.Instance;
+ let compiledModule: WebAssembly.Module;
+ if (typeof WebAssembly.instantiateStreaming === "function" && contentType === "application/wasm") {
+ if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: instantiate_wasm_module streaming");
+ const streamingResult = await WebAssembly.instantiateStreaming(response, wasmModuleImports!);
+ compiledInstance = streamingResult.instance;
+ compiledModule = streamingResult.module;
+ } else {
+ if (ENVIRONMENT_IS_WEB && contentType !== "application/wasm") {
+ console.warn("MONO_WASM: WebAssembly resource does not have the expected content type \"application/wasm\", so falling back to slower ArrayBuffer instantiation.");
+ }
+ const arrayBuffer = await response.arrayBuffer();
+ if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: instantiate_wasm_module buffered");
+ const arrayBufferResult = await WebAssembly.instantiate(arrayBuffer, wasmModuleImports!);
+ compiledInstance = arrayBufferResult.instance;
+ compiledModule = arrayBufferResult.module;
+ }
+ ++instantiated_assets_count;
+ successCallback(compiledInstance, compiledModule);
+}
+
+// used from Blazor
+export function mono_wasm_load_data_archive(data: Uint8Array, prefix: string): boolean {
+ if (data.length < 8)
+ return false;
+
+ const dataview = new DataView(data.buffer);
+ const magic = dataview.getUint32(0, true);
+ // get magic number
+ if (magic != 0x626c6174) {
+ return false;
+ }
+ const manifestSize = dataview.getUint32(4, true);
+ if (manifestSize == 0 || data.length < manifestSize + 8)
+ return false;
+
+ let manifest;
+ try {
+ const manifestContent = Module.UTF8ArrayToString(data, 8, manifestSize);
+ manifest = JSON.parse(manifestContent);
+ if (!(manifest instanceof Array))
+ return false;
+ } catch (exc) {
+ return false;
+ }
+
+ data = data.slice(manifestSize + 8);
+
+ // Create the folder structure
+ // /usr/share/zoneinfo
+ // /usr/share/zoneinfo/Africa
+ // /usr/share/zoneinfo/Asia
+ // ..
+
+ const folders = new Set();
+ manifest.filter(m => {
+ const file = m[0];
+ const last = file.lastIndexOf("/");
+ const directory = file.slice(0, last + 1);
+ folders.add(directory);
+ });
+ folders.forEach(folder => {
+ Module["FS_createPath"](prefix, folder, true, true);
+ });
+
+ for (const row of manifest) {
+ const name = row[0];
+ const length = row[1];
+ const bytes = data.slice(0, length);
+ Module["FS_createDataFile"](prefix, name, bytes, true, true);
+ data = data.slice(length);
+ }
+ return true;
+}
+
+export async function wait_for_all_assets() {
+ // wait for all assets in memory
+ await allAssetsInMemory.promise;
+ if (runtimeHelpers.config.assets) {
+ const expected_asset_count = runtimeHelpers.config.assets.filter(a => !skipDownloadsAssetTypes[a.behavior]).length;
+ mono_assert(downloded_assets_count == expected_asset_count, "Expected assets to be downloaded");
+ mono_assert(instantiated_assets_count == expected_asset_count, "Expected assets to be in memory");
+ loaded_files.forEach(value => MONO.loaded_files.push(value.url));
+ if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: all assets are loaded in wasm memory");
+ }
+}
+
+// Used by the debugger to enumerate loaded dlls and pdbs
+export function mono_wasm_get_loaded_files(): string[] {
+ return MONO.loaded_files;
+}
diff --git a/src/mono/wasm/runtime/crypto-worker.ts b/src/mono/wasm/runtime/crypto-worker.ts
index adde81a9ec9bca..82fafbf3e6f050 100644
--- a/src/mono/wasm/runtime/crypto-worker.ts
+++ b/src/mono/wasm/runtime/crypto-worker.ts
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+import { resolve_asset_path } from "./assets";
import { Module, runtimeHelpers } from "./imports";
import { mono_assert } from "./types";
@@ -107,22 +108,24 @@ export function init_crypto(): void {
console.debug("MONO_WASM: Initializing Crypto WebWorker");
const chan = LibraryChannel.create(1024); // 1024 is the buffer size in char units.
- const worker = new Worker("dotnet-crypto-worker.js");
+ const asset = resolve_asset_path("js-module-crypto");
+ mono_assert(asset && asset.resolvedUrl, "Can't find js-module-crypto");
+ const worker = new Worker(asset.resolvedUrl);
mono_wasm_crypto = {
channel: chan,
worker: worker,
};
const messageData: InitCryptoMessageData = {
- config: JSON.stringify(runtimeHelpers.config),
+ config: JSON.stringify(runtimeHelpers.config),// there could be things in config which could not be cloned to worker
comm_buf: chan.get_comm_buffer(),
msg_buf: chan.get_msg_buffer(),
msg_char_len: chan.get_msg_len()
};
- worker.postMessage(messageData);
worker.onerror = event => {
console.warn(`MONO_WASM: Error in Crypto WebWorker. Cryptography digest calls will fallback to managed implementation. Error: ${event.message}`);
mono_wasm_crypto = null;
};
+ worker.postMessage(messageData);
}
}
diff --git a/src/mono/wasm/runtime/debug.ts b/src/mono/wasm/runtime/debug.ts
index 0367fc71f59620..43e9ffb83d3e11 100644
--- a/src/mono/wasm/runtime/debug.ts
+++ b/src/mono/wasm/runtime/debug.ts
@@ -1,14 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-import Configuration from "consts:configuration";
+import BuildConfiguration from "consts:configuration";
import { INTERNAL, Module, runtimeHelpers } from "./imports";
import { toBase64StringImpl } from "./base64";
import cwraps from "./cwraps";
import { VoidPtr, CharPtr } from "./types/emscripten";
-import { MONO } from "./net6-legacy/imports";
const commands_received: any = new Map();
-const wasm_func_map = new Map();
commands_received.remove = function (key: number): CommandResponse { const value = this.get(key); this.delete(key); return value; };
let _call_function_res_cache: any = {};
let _next_call_function_res_id = 0;
@@ -17,23 +15,6 @@ let _debugger_buffer: VoidPtr;
let _assembly_name_str: string; //keep this variable, it's used by BrowserDebugProxy
let _entrypoint_method_token: number; //keep this variable, it's used by BrowserDebugProxy
-const regexes: any[] = [];
-
-// V8
-// at :wasm-function[1900]:0x83f63
-// at dlfree (:wasm-function[18739]:0x2328ef)
-regexes.push(/at (?[^:()]+:wasm-function\[(?\d+)\]:0x[a-fA-F\d]+)((?![^)a-fA-F\d])|$)/);
-
-//# 5: WASM [009712b2], function #111 (''), pc=0x7c16595c973 (+0x53), pos=38740 (+11)
-regexes.push(/(?:WASM \[[\da-zA-Z]+\], (?function #(?[\d]+) \(''\)))/);
-
-//# chrome
-//# at http://127.0.0.1:63817/dotnet.wasm:wasm-function[8963]:0x1e23f4
-regexes.push(/(?[a-z]+:\/\/[^ )]*:wasm-function\[(?\d+)\]:0x[a-fA-F\d]+)/);
-
-//# >.wasm-function[8962]
-regexes.push(/(?<[^ >]+>[.:]wasm-function\[(?[0-9]+)\])/);
-
export function mono_wasm_runtime_ready(): void {
INTERNAL.mono_wasm_runtime_is_ready = runtimeHelpers.mono_wasm_runtime_is_ready = true;
@@ -49,7 +30,6 @@ export function mono_wasm_runtime_ready(): void {
else
console.debug("mono_wasm_runtime_ready", "fe00e07a-5519-4dfe-b35a-f867dbaf2e28");
- _readSymbolMapFile("dotnet.js.symbols");
}
export function mono_wasm_fire_debugger_agent_message(): void {
@@ -143,11 +123,6 @@ export function mono_wasm_raise_debug_event(event: WasmEvent, args = {}): void {
console.debug("mono_wasm_debug_event_raised:aef14bca-5519-4dfe-b35a-f867abc123ae", JSON.stringify(event), JSON.stringify(args));
}
-// Used by the debugger to enumerate loaded dlls and pdbs
-export function mono_wasm_get_loaded_files(): string[] {
- return MONO.loaded_files;
-}
-
export function mono_wasm_wait_for_debugger(): Promise {
return new Promise((resolve) => {
const interval = setInterval(() => {
@@ -365,193 +340,11 @@ export function mono_wasm_debugger_log(level: number, message_ptr: CharPtr): voi
return;
}
- if (Configuration === "Debug") {
+ if (BuildConfiguration === "Debug") {
console.debug(`MONO_WASM: Debugger.Debug: ${message}`);
}
}
-function _readSymbolMapFile(filename: string): void {
- try {
- const res = Module.FS_readFile(filename, { flags: "r", encoding: "utf8" });
- res.split(/[\r\n]/).forEach((line: string) => {
- const parts: string[] = line.split(/:/);
- if (parts.length < 2)
- return;
-
- parts[1] = parts.splice(1).join(":");
- wasm_func_map.set(Number(parts[0]), parts[1]);
- });
- if (Configuration === "Debug") {
- console.debug(`MONO_WASM: Loaded ${wasm_func_map.size} symbols`);
- }
- } catch (error: any) {
- if (error.errno == 44) {// NOENT
- if (Configuration === "Debug") {
- console.debug(`MONO_WASM: Could not find symbols file ${filename}. Ignoring.`);
- }
- }
- else {
- console.log(`MONO_WASM: Error loading symbol file ${filename}: ${JSON.stringify(error)}`);
- }
- return;
- }
-}
-
-export function mono_wasm_symbolicate_string(message: string): string {
- try {
- if (wasm_func_map.size == 0)
- return message;
-
- const origMessage = message;
-
- for (let i = 0; i < regexes.length; i++) {
- const newRaw = message.replace(new RegExp(regexes[i], "g"), (substring, ...args) => {
- const groups = args.find(arg => {
- return typeof (arg) == "object" && arg.replaceSection !== undefined;
- });
-
- if (groups === undefined)
- return substring;
-
- const funcNum = groups.funcNum;
- const replaceSection = groups.replaceSection;
- const name = wasm_func_map.get(Number(funcNum));
-
- if (name === undefined)
- return substring;
-
- return substring.replace(replaceSection, `${name} (${replaceSection})`);
- });
-
- if (newRaw !== origMessage)
- return newRaw;
- }
-
- return origMessage;
- } catch (error) {
- console.debug(`MONO_WASM: failed to symbolicate: ${error}`);
- return message;
- }
-}
-
-export function mono_wasm_stringify_as_error_with_stack(err: Error | string): string {
- let errObj: any = err;
- if (!(err instanceof Error))
- errObj = new Error(err);
-
- // Error
- return mono_wasm_symbolicate_string(errObj.stack);
-}
-
-export function mono_wasm_trace_logger(log_domain_ptr: CharPtr, log_level_ptr: CharPtr, message_ptr: CharPtr, fatal: number, user_data: VoidPtr): void {
- const origMessage = Module.UTF8ToString(message_ptr);
- const isFatal = !!fatal;
- const domain = Module.UTF8ToString(log_domain_ptr);
- const dataPtr = user_data;
- const log_level = Module.UTF8ToString(log_level_ptr);
-
- const message = `[MONO] ${origMessage}`;
-
- if (INTERNAL["logging"] && typeof INTERNAL.logging["trace"] === "function") {
- INTERNAL.logging.trace(domain, log_level, message, isFatal, dataPtr);
- return;
- }
-
- switch (log_level) {
- case "critical":
- case "error":
- console.error(mono_wasm_stringify_as_error_with_stack(message));
- break;
- case "warning":
- console.warn(message);
- break;
- case "message":
- console.log(message);
- break;
- case "info":
- console.info(message);
- break;
- case "debug":
- console.debug(message);
- break;
- default:
- console.log(message);
- break;
- }
-}
-
-export function setup_proxy_console(id: string, console: any, origin: string): void {
- function proxyConsoleMethod(prefix: string, func: any, asJson: boolean) {
- return function (...args: any[]) {
- try {
- let payload = args[0];
- if (payload === undefined) payload = "undefined";
- else if (payload === null) payload = "null";
- else if (typeof payload === "function") payload = payload.toString();
- else if (typeof payload !== "string") {
- try {
- payload = JSON.stringify(payload);
- } catch (e) {
- payload = payload.toString();
- }
- }
-
- if (typeof payload === "string")
- payload = `[${id}] ${payload}`;
-
- if (asJson) {
- func(JSON.stringify({
- method: prefix,
- payload: payload,
- arguments: args
- }));
- } else {
- func([prefix + payload, ...args.slice(1)]);
- }
- } catch (err) {
- originalConsole.error(`proxyConsole failed: ${err}`);
- }
- };
- }
-
- const originalConsole = {
- log: console.log,
- error: console.error
- };
- const methods = ["debug", "trace", "warn", "info", "error"];
- for (const m of methods) {
- if (typeof (console[m]) !== "function") {
- console[m] = proxyConsoleMethod(`console.${m}: `, originalConsole.log, false);
- }
- }
-
- const consoleUrl = `${origin}/console`.replace("https://", "wss://").replace("http://", "ws://");
-
- const consoleWebSocket = new WebSocket(consoleUrl);
- consoleWebSocket.onopen = function () {
- originalConsole.log(`browser: [${id}] Console websocket connected.`);
- };
- consoleWebSocket.onerror = function (event) {
- originalConsole.error(`[${id}] websocket error: ${event}`, event);
- };
- consoleWebSocket.onclose = function (event) {
- originalConsole.error(`[${id}] websocket closed: ${event}`, event);
- };
-
- const send = (msg: string) => {
- if (consoleWebSocket.readyState === WebSocket.OPEN) {
- consoleWebSocket.send(msg);
- }
- else {
- originalConsole.log(msg);
- }
- };
-
- // redirect output early, so that when emscripten starts it's already redirected
- for (const m of ["log", ...methods])
- console[m] = proxyConsoleMethod(`console.${m}`, send, true);
-}
-
type CallDetails = {
value: string
}
diff --git a/src/mono/wasm/runtime/dotnet-legacy.d.ts b/src/mono/wasm/runtime/dotnet-legacy.d.ts
index a3b7591887d3ac..6bfdd64e14bcc7 100644
--- a/src/mono/wasm/runtime/dotnet-legacy.d.ts
+++ b/src/mono/wasm/runtime/dotnet-legacy.d.ts
@@ -304,4 +304,4 @@ declare type MONOType = {
getF64: (offset: MemOffset) => number;
};
-export { BINDINGType, MONOType };
+export { BINDINGType, MONOType, MonoArray, MonoObject, MonoString };
diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts
index 131f2bb4da5442..eacddc7a017bbe 100644
--- a/src/mono/wasm/runtime/dotnet.d.ts
+++ b/src/mono/wasm/runtime/dotnet.d.ts
@@ -47,7 +47,7 @@ declare interface EmscriptenModule {
stackRestore(stack: VoidPtr): void;
stackAlloc(size: number): VoidPtr;
ready: Promise;
- instantiateWasm?: (imports: WebAssembly.Imports, successCallback: (instance: WebAssembly.Instance, module: WebAssembly.Module) => void) => any;
+ instantiateWasm?: InstantiateWasmCallBack;
preInit?: (() => any)[] | (() => any);
preRun?: (() => any)[] | (() => any);
onRuntimeInitialized?: () => any;
@@ -56,6 +56,8 @@ declare interface EmscriptenModule {
(error: any): void;
};
}
+declare type InstantiateWasmSuccessCallback = (instance: WebAssembly.Instance, module: WebAssembly.Module) => void;
+declare type InstantiateWasmCallBack = (imports: WebAssembly.Imports, successCallback: InstantiateWasmSuccessCallback) => any;
declare type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array;
declare type MonoConfig = {
@@ -94,7 +96,7 @@ interface AssetEntry extends ResourceRequest {
buffer?: ArrayBuffer;
pending?: LoadingResource;
}
-declare type AssetBehaviours = "resource" | "assembly" | "pdb" | "heap" | "icu" | "vfs" | "dotnetwasm";
+declare type AssetBehaviours = "resource" | "assembly" | "pdb" | "heap" | "icu" | "vfs" | "dotnetwasm" | "js-module-crypto" | "js-module-threads";
declare type GlobalizationMode = "icu" | // load ICU globalization data from any runtime assets with behavior "icu".
"invariant" | // operate in invariant globalization mode.
"auto";
diff --git a/src/mono/wasm/runtime/exports-internal.ts b/src/mono/wasm/runtime/exports-internal.ts
index a5c9219b2933e3..b2b8c4b958332c 100644
--- a/src/mono/wasm/runtime/exports-internal.ts
+++ b/src/mono/wasm/runtime/exports-internal.ts
@@ -1,6 +1,6 @@
import { mono_wasm_cancel_promise } from "./cancelable-promise";
import cwraps from "./cwraps";
-import { mono_wasm_symbolicate_string, mono_wasm_stringify_as_error_with_stack, mono_wasm_get_loaded_files, mono_wasm_send_dbg_command_with_parms, mono_wasm_send_dbg_command, mono_wasm_get_dbg_command_info, mono_wasm_get_details, mono_wasm_release_object, mono_wasm_call_function_on, mono_wasm_debugger_resume, mono_wasm_detach_debugger, mono_wasm_raise_debug_event, mono_wasm_change_debugger_log_level, mono_wasm_debugger_attached } from "./debug";
+import { mono_wasm_send_dbg_command_with_parms, mono_wasm_send_dbg_command, mono_wasm_get_dbg_command_info, mono_wasm_get_details, mono_wasm_release_object, mono_wasm_call_function_on, mono_wasm_debugger_resume, mono_wasm_detach_debugger, mono_wasm_raise_debug_event, mono_wasm_change_debugger_log_level, mono_wasm_debugger_attached } from "./debug";
import { get_dotnet_instance } from "./exports";
import { http_wasm_supports_streaming_response, http_wasm_create_abort_controler, http_wasm_abort_request, http_wasm_abort_response, http_wasm_fetch, http_wasm_fetch_bytes, http_wasm_get_response_header_names, http_wasm_get_response_header_values, http_wasm_get_response_bytes, http_wasm_get_response_length, http_wasm_get_streamed_response_bytes } from "./http";
import { Module, runtimeHelpers } from "./imports";
@@ -8,7 +8,9 @@ import { get_property, set_property, has_property, get_typeof_property, get_glob
import { mono_method_resolve } from "./net6-legacy/method-binding";
import { mono_wasm_set_runtime_options } from "./startup";
import { mono_intern_string } from "./strings";
+import { mono_wasm_stringify_as_error_with_stack } from "./logging";
import { ws_wasm_create, ws_wasm_open, ws_wasm_send, ws_wasm_receive, ws_wasm_close, ws_wasm_abort } from "./web-socket";
+import { mono_wasm_get_loaded_files } from "./assets";
export function export_internal(): any {
return {
@@ -24,8 +26,6 @@ export function export_internal(): any {
// with mono_wasm_debugger_log and mono_wasm_trace_logger
logging: undefined,
- //
- mono_wasm_symbolicate_string,
mono_wasm_stringify_as_error_with_stack,
// used in debugger DevToolsHelper.cs
diff --git a/src/mono/wasm/runtime/exports-linker.ts b/src/mono/wasm/runtime/exports-linker.ts
index a8d2c08453d75d..6a97b55421ef73 100644
--- a/src/mono/wasm/runtime/exports-linker.ts
+++ b/src/mono/wasm/runtime/exports-linker.ts
@@ -1,6 +1,6 @@
import MonoWasmThreads from "consts:monoWasmThreads";
import { dotnet_browser_can_use_subtle_crypto_impl, dotnet_browser_simple_digest_hash, dotnet_browser_sign, dotnet_browser_encrypt_decrypt, dotnet_browser_derive_bits } from "./crypto-worker";
-import { mono_wasm_fire_debugger_agent_message, mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_trace_logger, mono_wasm_set_entrypoint_breakpoint } from "./debug";
+import { mono_wasm_fire_debugger_agent_message, mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_set_entrypoint_breakpoint } from "./debug";
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
import { mono_wasm_load_icu_data, mono_wasm_get_icudt_name } from "./icu";
import { mono_wasm_bind_cs_function } from "./invoke-cs";
@@ -19,6 +19,7 @@ import { mono_wasm_diagnostic_server_on_runtime_server_init, mono_wasm_event_pip
import { mono_wasm_diagnostic_server_stream_signal_work_available } from "./diagnostics/server_pthread/stream-queue";
import { mono_wasm_create_cs_owned_object_ref } from "./net6-legacy/cs-to-js";
import { mono_wasm_typed_array_to_array_ref } from "./net6-legacy/js-to-cs";
+import { mono_wasm_trace_logger } from "./logging";
// the methods would be visible to EMCC linker
// --- keep in sync with dotnet.cjs.lib.js ---
diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts
index 46eede120336b0..ea46886cfe674d 100644
--- a/src/mono/wasm/runtime/exports.ts
+++ b/src/mono/wasm/runtime/exports.ts
@@ -2,8 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
import ProductVersion from "consts:productVersion";
-import BuildConfiguration from "consts:configuration";
import MonoWasmThreads from "consts:monoWasmThreads";
+import BuildConfiguration from "consts:configuration";
import { ENVIRONMENT_IS_PTHREAD, set_imports_exports } from "./imports";
import { DotnetModule, is_nullish, EarlyImports, EarlyExports, EarlyReplacements, MonoConfig } from "./types";
diff --git a/src/mono/wasm/runtime/icu.ts b/src/mono/wasm/runtime/icu.ts
index 93ea54d9d9fab0..3e6e7105335c48 100644
--- a/src/mono/wasm/runtime/icu.ts
+++ b/src/mono/wasm/runtime/icu.ts
@@ -3,7 +3,6 @@
import cwraps from "./cwraps";
import { Module, runtimeHelpers } from "./imports";
-import { MonoConfig } from "./types";
import { VoidPtr } from "./types/emscripten";
let num_icu_assets_loaded_successfully = 0;
@@ -30,7 +29,7 @@ export function mono_wasm_get_icudt_name(culture: string): string {
// "auto" will use "icu" if any ICU data archives have been loaded,
// otherwise "invariant".
export function mono_wasm_globalization_init(): void {
- const config = Module.config as MonoConfig;
+ const config = runtimeHelpers.config;
let invariantMode = false;
if (!config.globalizationMode)
config.globalizationMode = "auto";
diff --git a/src/mono/wasm/runtime/imports.ts b/src/mono/wasm/runtime/imports.ts
index e12bc3857f6a2f..2e4a619238fd7b 100644
--- a/src/mono/wasm/runtime/imports.ts
+++ b/src/mono/wasm/runtime/imports.ts
@@ -3,6 +3,7 @@
/* eslint-disable @typescript-eslint/triple-slash-reference */
///
+///
import { DotnetModule, EarlyExports, EarlyImports, RuntimeHelpers } from "./types";
import { EmscriptenModule } from "./types/emscripten";
diff --git a/src/mono/wasm/runtime/invoke-js.ts b/src/mono/wasm/runtime/invoke-js.ts
index f6a65f43f1d3f8..e078ef8e52ea7f 100644
--- a/src/mono/wasm/runtime/invoke-js.ts
+++ b/src/mono/wasm/runtime/invoke-js.ts
@@ -11,7 +11,7 @@ import { Int32Ptr } from "./types/emscripten";
import { IMPORTS, INTERNAL, Module, runtimeHelpers } from "./imports";
import { generate_arg_marshal_to_js } from "./marshal-to-js";
import { mono_wasm_new_external_root } from "./roots";
-import { mono_wasm_symbolicate_string } from "./debug";
+import { mono_wasm_symbolicate_string } from "./logging";
export function mono_wasm_bind_js_function(function_name: MonoStringRef, module_name: MonoStringRef, signature: JSFunctionSignature, function_js_handle: Int32Ptr, is_exception: Int32Ptr, result_address: MonoObjectRef): void {
const function_name_root = mono_wasm_new_external_root(function_name),
diff --git a/src/mono/wasm/runtime/logging.ts b/src/mono/wasm/runtime/logging.ts
new file mode 100644
index 00000000000000..34de60da7929d2
--- /dev/null
+++ b/src/mono/wasm/runtime/logging.ts
@@ -0,0 +1,210 @@
+//! Licensed to the .NET Foundation under one or more agreements.
+//! The .NET Foundation licenses this file to you under the MIT license.
+
+import BuildConfiguration from "consts:configuration";
+import { INTERNAL, Module, runtimeHelpers } from "./imports";
+import { CharPtr, VoidPtr } from "./types/emscripten";
+
+const wasm_func_map = new Map();
+const regexes: any[] = [];
+
+// V8
+// at :wasm-function[1900]:0x83f63
+// at dlfree (:wasm-function[18739]:0x2328ef)
+regexes.push(/at (?[^:()]+:wasm-function\[(?\d+)\]:0x[a-fA-F\d]+)((?![^)a-fA-F\d])|$)/);
+
+//# 5: WASM [009712b2], function #111 (''), pc=0x7c16595c973 (+0x53), pos=38740 (+11)
+regexes.push(/(?:WASM \[[\da-zA-Z]+\], (?function #(?[\d]+) \(''\)))/);
+
+//# chrome
+//# at http://127.0.0.1:63817/dotnet.wasm:wasm-function[8963]:0x1e23f4
+regexes.push(/(?[a-z]+:\/\/[^ )]*:wasm-function\[(?\d+)\]:0x[a-fA-F\d]+)/);
+
+//# >.wasm-function[8962]
+regexes.push(/(?<[^ >]+>[.:]wasm-function\[(?[0-9]+)\])/);
+
+export function mono_wasm_symbolicate_string(message: string): string {
+ try {
+ if (wasm_func_map.size == 0)
+ return message;
+
+ const origMessage = message;
+
+ for (let i = 0; i < regexes.length; i++) {
+ const newRaw = message.replace(new RegExp(regexes[i], "g"), (substring, ...args) => {
+ const groups = args.find(arg => {
+ return typeof (arg) == "object" && arg.replaceSection !== undefined;
+ });
+
+ if (groups === undefined)
+ return substring;
+
+ const funcNum = groups.funcNum;
+ const replaceSection = groups.replaceSection;
+ const name = wasm_func_map.get(Number(funcNum));
+
+ if (name === undefined)
+ return substring;
+
+ return substring.replace(replaceSection, `${name} (${replaceSection})`);
+ });
+
+ if (newRaw !== origMessage)
+ return newRaw;
+ }
+
+ return origMessage;
+ } catch (error) {
+ console.debug(`MONO_WASM: failed to symbolicate: ${error}`);
+ return message;
+ }
+}
+
+export function mono_wasm_stringify_as_error_with_stack(err: Error | string): string {
+ let errObj: any = err;
+ if (!(err instanceof Error))
+ errObj = new Error(err);
+
+ // Error
+ return mono_wasm_symbolicate_string(errObj.stack);
+}
+
+export function mono_wasm_trace_logger(log_domain_ptr: CharPtr, log_level_ptr: CharPtr, message_ptr: CharPtr, fatal: number, user_data: VoidPtr): void {
+ const origMessage = Module.UTF8ToString(message_ptr);
+ const isFatal = !!fatal;
+ const domain = Module.UTF8ToString(log_domain_ptr);
+ const dataPtr = user_data;
+ const log_level = Module.UTF8ToString(log_level_ptr);
+
+ const message = `[MONO] ${origMessage}`;
+
+ if (INTERNAL["logging"] && typeof INTERNAL.logging["trace"] === "function") {
+ INTERNAL.logging.trace(domain, log_level, message, isFatal, dataPtr);
+ return;
+ }
+
+ switch (log_level) {
+ case "critical":
+ case "error":
+ console.error(mono_wasm_stringify_as_error_with_stack(message));
+ break;
+ case "warning":
+ console.warn(message);
+ break;
+ case "message":
+ console.log(message);
+ break;
+ case "info":
+ console.info(message);
+ break;
+ case "debug":
+ console.debug(message);
+ break;
+ default:
+ console.log(message);
+ break;
+ }
+}
+
+export function setup_proxy_console(id: string, console: Console, origin: string): void {
+ // this need to be copy, in order to keep reference to original methods
+ const originalConsole = {
+ log: console.log,
+ error: console.error
+ };
+ const anyConsole = console as any;
+
+ function proxyConsoleMethod(prefix: string, func: any, asJson: boolean) {
+ return function (...args: any[]) {
+ try {
+ let payload = args[0];
+ if (payload === undefined) payload = "undefined";
+ else if (payload === null) payload = "null";
+ else if (typeof payload === "function") payload = payload.toString();
+ else if (typeof payload !== "string") {
+ try {
+ payload = JSON.stringify(payload);
+ } catch (e) {
+ payload = payload.toString();
+ }
+ }
+
+ if (typeof payload === "string")
+ payload = `[${id}] ${payload}`;
+
+ if (asJson) {
+ func(JSON.stringify({
+ method: prefix,
+ payload: payload,
+ arguments: args
+ }));
+ } else {
+ func([prefix + payload, ...args.slice(1)]);
+ }
+ } catch (err) {
+ originalConsole.error(`proxyConsole failed: ${err}`);
+ }
+ };
+ }
+
+ const methods = ["debug", "trace", "warn", "info", "error"];
+ for (const m of methods) {
+ if (typeof (anyConsole[m]) !== "function") {
+ anyConsole[m] = proxyConsoleMethod(`console.${m}: `, console.log, false);
+ }
+ }
+
+ const consoleUrl = `${origin}/console`.replace("https://", "wss://").replace("http://", "ws://");
+
+ const consoleWebSocket = new WebSocket(consoleUrl);
+ consoleWebSocket.addEventListener("open", () => {
+ originalConsole.log(`browser: [${id}] Console websocket connected.`);
+ });
+ consoleWebSocket.addEventListener("error", (event) => {
+ originalConsole.error(`[${id}] websocket error: ${event}`, event);
+ });
+ consoleWebSocket.addEventListener("close", (event) => {
+ originalConsole.error(`[${id}] websocket closed: ${event}`, event);
+ });
+
+ const send = (msg: string) => {
+ if (consoleWebSocket.readyState === WebSocket.OPEN) {
+ consoleWebSocket.send(msg);
+ }
+ else {
+ originalConsole.log(msg);
+ }
+ };
+
+ for (const m of ["log", ...methods])
+ anyConsole[m] = proxyConsoleMethod(`console.${m}`, send, true);
+}
+
+export function readSymbolMapFile(filename: string): void {
+ if (runtimeHelpers.mono_wasm_symbols_are_ready) return;
+ runtimeHelpers.mono_wasm_symbols_are_ready = true;
+ try {
+ const res = Module.FS_readFile(filename, { flags: "r", encoding: "utf8" });
+ res.split(/[\r\n]/).forEach((line: string) => {
+ const parts: string[] = line.split(/:/);
+ if (parts.length < 2)
+ return;
+
+ parts[1] = parts.splice(1).join(":");
+ wasm_func_map.set(Number(parts[0]), parts[1]);
+ });
+ if (BuildConfiguration === "Debug") {
+ console.debug(`MONO_WASM: Loaded ${wasm_func_map.size} symbols`);
+ }
+ } catch (error: any) {
+ if (error.errno == 44) {// NOENT
+ if (BuildConfiguration === "Debug") {
+ console.debug(`MONO_WASM: Could not find symbols file ${filename}. Ignoring.`);
+ }
+ }
+ else {
+ console.log(`MONO_WASM: Error loading symbol file ${filename}: ${JSON.stringify(error)}`);
+ }
+ return;
+ }
+}
diff --git a/src/mono/wasm/runtime/net6-legacy/export-types.ts b/src/mono/wasm/runtime/net6-legacy/export-types.ts
index 22291a91491802..84c8171cbb67d1 100644
--- a/src/mono/wasm/runtime/net6-legacy/export-types.ts
+++ b/src/mono/wasm/runtime/net6-legacy/export-types.ts
@@ -1,4 +1,4 @@
-import { MonoArray, MonoObject, MonoObjectRef, MonoString, WasmRoot, WasmRootBuffer, MemOffset, NumberOrPointer } from "../types";
+import { MemOffset, MonoArray, MonoObject, MonoObjectRef, MonoString, NumberOrPointer, WasmRoot, WasmRootBuffer } from "../types";
import { VoidPtr } from "../types/emscripten";
/**
@@ -247,4 +247,6 @@ export type MONOType = {
* @deprecated Please use getHeapF64
*/
getF64: (offset: MemOffset) => number;
-};
\ No newline at end of file
+};
+
+export { MonoArray, MonoObject, MonoString };
\ No newline at end of file
diff --git a/src/mono/wasm/runtime/net6-legacy/exports-legacy.ts b/src/mono/wasm/runtime/net6-legacy/exports-legacy.ts
index 4202cf7ebc3406..7ead0a1000dd17 100644
--- a/src/mono/wasm/runtime/net6-legacy/exports-legacy.ts
+++ b/src/mono/wasm/runtime/net6-legacy/exports-legacy.ts
@@ -5,7 +5,7 @@ import { runtimeHelpers } from "../imports";
import { mono_wasm_load_bytes_into_heap, setB32, setI8, setI16, setI32, setI52, setU52, setI64Big, setU8, setU16, setU32, setF32, setF64, getB32, getI8, getI16, getI32, getI52, getU52, getI64Big, getU8, getU16, getU32, getF32, getF64 } from "../memory";
import { mono_wasm_new_root_buffer, mono_wasm_new_root, mono_wasm_new_external_root, mono_wasm_release_roots } from "../roots";
import { mono_run_main, mono_run_main_and_exit } from "../run";
-import { mono_wasm_setenv, mono_wasm_load_data_archive, mono_wasm_load_config, mono_load_runtime_and_bcl_args } from "../startup";
+import { mono_wasm_setenv, mono_wasm_load_config, mono_load_runtime_and_bcl_args } from "../startup";
import { js_string_to_mono_string, conv_string, js_string_to_mono_string_root, conv_string_root } from "../strings";
import { MonoConfig, MonoConfigError } from "../types";
import { mono_array_to_js_array, unbox_mono_obj, unbox_mono_obj_root, mono_array_root_to_js_array } from "./cs-to-js";
@@ -13,6 +13,7 @@ import { js_typed_array_to_array, js_to_mono_obj, js_typed_array_to_array_root,
import { mono_bind_static_method, mono_call_assembly_entry_point } from "./method-calls";
import { mono_wasm_load_runtime } from "../startup";
import { BINDINGType, MONOType } from "./export-types";
+import { mono_wasm_load_data_archive } from "../assets";
export function export_mono_api(): MONOType {
return {
diff --git a/src/mono/wasm/runtime/polyfills.ts b/src/mono/wasm/runtime/polyfills.ts
index 1e56ac4e813a5f..afd92c71074896 100644
--- a/src/mono/wasm/runtime/polyfills.ts
+++ b/src/mono/wasm/runtime/polyfills.ts
@@ -1,4 +1,4 @@
-import Configuration from "consts:configuration";
+import BuildConfiguration from "consts:configuration";
import MonoWasmThreads from "consts:monoWasmThreads";
import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, ENVIRONMENT_IS_WORKER, INTERNAL, Module, runtimeHelpers } from "./imports";
import { afterUpdateGlobalBufferAndViews } from "./memory";
@@ -144,7 +144,7 @@ export function init_polyfills(replacements: EarlyReplacements): void {
// script location
runtimeHelpers.scriptDirectory = replacements.scriptDirectory = detectScriptDirectory(replacements);
anyModule.mainScriptUrlOrBlob = replacements.scriptUrl;// this is needed by worker threads
- if (Configuration === "Debug") {
+ if (BuildConfiguration === "Debug") {
console.debug(`MONO_WASM: starting script ${replacements.scriptUrl}`);
console.debug(`MONO_WASM: starting in ${runtimeHelpers.scriptDirectory}`);
}
diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts
index c26bdf352a0bb3..4e35254d7e456b 100644
--- a/src/mono/wasm/runtime/startup.ts
+++ b/src/mono/wasm/runtime/startup.ts
@@ -1,49 +1,45 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+import BuildConfiguration from "consts:configuration";
import MonoWasmThreads from "consts:monoWasmThreads";
-import { mono_assert, CharPtrNull, DotnetModule, MonoConfig, MonoConfigError, LoadingResource, AssetEntry, ResourceRequest } from "./types";
+import { CharPtrNull, DotnetModule, MonoConfig, MonoConfigError } from "./types";
import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, INTERNAL, Module, runtimeHelpers } from "./imports";
import cwraps, { init_c_exports } from "./cwraps";
import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug";
-import { mono_wasm_globalization_init, mono_wasm_load_icu_data } from "./icu";
+import { mono_wasm_globalization_init } from "./icu";
import { toBase64StringImpl } from "./base64";
import { mono_wasm_init_aot_profiler, mono_wasm_init_coverage_profiler } from "./profiler";
-import { VoidPtr, CharPtr } from "./types/emscripten";
import { mono_on_abort, set_exit_code } from "./run";
import { initialize_marshalers_to_cs } from "./marshal-to-cs";
import { initialize_marshalers_to_js } from "./marshal-to-js";
import { init_crypto } from "./crypto-worker";
import { init_polyfills_async } from "./polyfills";
import * as pthreads_worker from "./pthreads/worker";
-import { createPromiseController, PromiseAndController } from "./promise-controller";
+import { createPromiseController } from "./promise-controller";
import { string_decoder } from "./strings";
-import { delay } from "./promise-utils";
import { init_managed_exports } from "./managed-exports";
import { init_legacy_exports } from "./net6-legacy/corebindings";
-import { mono_wasm_load_bytes_into_heap } from "./memory";
import { cwraps_internal } from "./exports-internal";
import { cwraps_binding_api, cwraps_mono_api } from "./net6-legacy/exports-legacy";
-import { DotnetPublicAPI } from "./export-types";
+import { DotnetPublicAPI } from "./exports";
+import { CharPtr, InstantiateWasmCallBack, InstantiateWasmSuccessCallback } from "./types/emscripten";
+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";
-let all_assets_loaded_in_memory: Promise | null = null;
-const loaded_files: { url: string, file: string }[] = [];
-const loaded_assets: { [id: string]: [VoidPtr, number] } = Object.create(null);
-let instantiated_assets_count = 0;
-let downloded_assets_count = 0;
-// in order to prevent net::ERR_INSUFFICIENT_RESOURCES if we start downloading too many files at same time
-let parallel_count = 0;
let config: MonoConfig = undefined as any;
-
-const afterInstantiateWasm = createPromiseController();
-const beforePreInit = createPromiseController();
-const afterPreInit = createPromiseController();
-const afterPreRun = createPromiseController();
-const beforeOnRuntimeInitialized = createPromiseController();
-const afterOnRuntimeInitialized = createPromiseController();
-const afterPostRun = createPromiseController();
+let configLoaded = false;
+let isCustomStartup = false;
+export const afterConfigLoaded = createPromiseController();
+export const afterInstantiateWasm = createPromiseController();
+export const beforePreInit = createPromiseController();
+export const afterPreInit = createPromiseController();
+export const afterPreRun = createPromiseController();
+export const beforeOnRuntimeInitialized = createPromiseController();
+export const afterOnRuntimeInitialized = createPromiseController();
+export const afterPostRun = createPromiseController();
// we are making emscripten startup async friendly
// emscripten is executing the events without awaiting it and so we need to block progress via PromiseControllers above
@@ -56,17 +52,18 @@ export function configure_emscripten_startup(module: DotnetModule, exportedAPI:
const userpostRun: (() => void)[] = !module.postRun ? [] : typeof module.postRun === "function" ? [module.postRun] : module.postRun as any;
// eslint-disable-next-line @typescript-eslint/no-empty-function
const userOnRuntimeInitialized: () => void = module.onRuntimeInitialized ? module.onRuntimeInitialized : () => { };
- const isCustomStartup = !module.configSrc && !module.config; // like blazor
+ // when assets don't contain DLLs it means this is Blazor or another custom startup
+ isCustomStartup = !module.configSrc && (!module.config || !module.config.assets || module.config.assets.findIndex(a => a.behavior === "assembly") != -1); // like blazor
// execution order == [0] ==
// - default or user Module.instantiateWasm (will start downloading dotnet.wasm)
module.instantiateWasm = (imports, callback) => instantiateWasm(imports, callback, userInstantiateWasm);
// execution order == [1] ==
- module.preInit = [() => preInit(isCustomStartup, userPreInit)];
+ module.preInit = [() => preInit(userPreInit)];
// execution order == [2] ==
module.preRun = [() => preRunAsync(userPreRun)];
// execution order == [4] ==
- module.onRuntimeInitialized = () => onRuntimeInitializedAsync(isCustomStartup, userOnRuntimeInitialized);
+ module.onRuntimeInitialized = () => onRuntimeInitializedAsync(userOnRuntimeInitialized);
// execution order == [5] ==
module.postRun = [() => postRunAsync(userpostRun)];
// execution order == [6] ==
@@ -83,13 +80,11 @@ export function configure_emscripten_startup(module: DotnetModule, exportedAPI:
}
}
-let wasm_module_imports: WebAssembly.Imports | null = null;
-let wasm_success_callback: null | ((instance: WebAssembly.Instance, module: WebAssembly.Module) => void) = null;
function instantiateWasm(
imports: WebAssembly.Imports,
- successCallback: (instance: WebAssembly.Instance, module: WebAssembly.Module) => void,
- userInstantiateWasm?: (imports: WebAssembly.Imports, successCallback: (instance: WebAssembly.Instance, module: WebAssembly.Module) => void) => any): any[] {
+ successCallback: InstantiateWasmSuccessCallback,
+ userInstantiateWasm?: InstantiateWasmCallBack): any[] {
// this is called so early that even Module exports like addRunDependency don't exist yet
if (!Module.configSrc && !Module.config && !userInstantiateWasm) {
@@ -101,9 +96,6 @@ function instantiateWasm(
config = runtimeHelpers.config = Module.config = {} as any;
}
runtimeHelpers.diagnosticTracing = !!config.diagnosticTracing;
- if (!config.assets) {
- config.assets = [];
- }
if (userInstantiateWasm) {
const exports = userInstantiateWasm(imports, (instance: WebAssembly.Instance, module: WebAssembly.Module) => {
@@ -113,13 +105,11 @@ function instantiateWasm(
return exports;
}
- wasm_module_imports = imports;
- wasm_success_callback = successCallback;
- instantiate_wasm_module();
+ instantiate_wasm_module(imports, successCallback);
return []; // No exports
}
-function preInit(isCustomStartup: boolean, userPreInit: (() => void)[]) {
+function preInit(userPreInit: (() => void)[]) {
Module.addRunDependency("mono_pre_init");
try {
mono_wasm_pre_init_essential();
@@ -172,7 +162,7 @@ async function preRunAsync(userPreRun: (() => void)[]) {
Module.removeRunDependency("mono_pre_run_async");
}
-async function onRuntimeInitializedAsync(isCustomStartup: boolean, userOnRuntimeInitialized: () => void) {
+async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) {
// wait for previous stage
await afterPreRun.promise;
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: onRuntimeInitialized");
@@ -180,13 +170,7 @@ async function onRuntimeInitializedAsync(isCustomStartup: boolean, userOnRuntime
beforeOnRuntimeInitialized.promise_control.resolve();
try {
if (!isCustomStartup) {
- // wait for all assets in memory
- await all_assets_loaded_in_memory;
- const expected_asset_count = config.assets ? config.assets.length : 0;
- mono_assert(downloded_assets_count == expected_asset_count, "Expected assets to be downloaded");
- mono_assert(instantiated_assets_count == expected_asset_count, "Expected assets to be in memory");
- if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: all assets are loaded in wasm memory");
-
+ await wait_for_all_assets();
// load runtime
await mono_wasm_before_user_runtime_initialized();
}
@@ -246,7 +230,6 @@ function mono_wasm_pre_init_essential(): void {
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: mono_wasm_pre_init_essential");
// init_polyfills() is already called from export.ts
- init_crypto();
init_c_exports();
cwraps_internal(INTERNAL);
cwraps_mono_api(MONO);
@@ -261,6 +244,8 @@ async function mono_wasm_pre_init_essential_async(): Promise {
Module.addRunDependency("mono_wasm_pre_init_essential_async");
await init_polyfills_async();
+ await mono_wasm_load_config(Module.configSrc);
+ init_crypto();
Module.removeRunDependency("mono_wasm_pre_init_essential_async");
}
@@ -270,9 +255,6 @@ async function mono_wasm_pre_init_full(): Promise {
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: mono_wasm_pre_init_full");
Module.addRunDependency("mono_wasm_pre_init_full");
- if (Module.configSrc) {
- await mono_wasm_load_config(Module.configSrc);
- }
await mono_download_assets();
Module.removeRunDependency("mono_wasm_pre_init_full");
@@ -282,21 +264,14 @@ async function mono_wasm_pre_init_full(): Promise {
async function mono_wasm_before_user_runtime_initialized(): Promise {
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: mono_wasm_before_user_runtime_initialized");
- if (!Module.config) {
- return;
- }
-
try {
- loaded_files.forEach(value => MONO.loaded_files.push(value.url));
- if (!loaded_files || loaded_files.length == 0) {
- Module.print("MONO_WASM: no files were loaded into runtime");
- }
-
await _apply_configuration_from_args();
mono_wasm_globalization_init();
if (!runtimeHelpers.mono_wasm_load_runtime_done) mono_wasm_load_runtime("unused", config.debugLevel || 0);
if (!runtimeHelpers.mono_wasm_runtime_is_ready) mono_wasm_runtime_ready();
+ if (!runtimeHelpers.mono_wasm_symbols_are_ready) readSymbolMapFile("dotnet.js.symbols");
+
setTimeout(() => {
// when there are free CPU cycles
string_decoder.init_fields();
@@ -382,137 +357,30 @@ export function mono_wasm_set_runtime_options(options: string[]): void {
}
-async function instantiate_wasm_module(): Promise {
+async function instantiate_wasm_module(
+ imports: WebAssembly.Imports,
+ successCallback: InstantiateWasmSuccessCallback,
+): Promise {
// this is called so early that even Module exports like addRunDependency don't exist yet
try {
- if (!config.assets && Module.configSrc) {
- // when we are starting with mono-config,json, it could have dotnet.wasm location in it, we have to wait for it
- await mono_wasm_load_config(Module.configSrc);
- }
+ await mono_wasm_load_config(Module.configSrc);
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: instantiate_wasm_module");
- let assetToLoad: AssetEntry = {
- name: "dotnet.wasm",
- behavior: "dotnetwasm"
- };
- const assetfromConfig = config.assets!.find(a => a.behavior === "dotnetwasm");
- if (assetfromConfig) {
- assetToLoad = assetfromConfig;
- } else {
- config.assets!.push(assetToLoad);
- }
-
+ const assetToLoad = resolve_asset_path("dotnetwasm");
const pendingAsset = await start_asset_download(assetToLoad);
await beforePreInit.promise;
Module.addRunDependency("instantiate_wasm_module");
- mono_assert(pendingAsset && pendingAsset.pending, () => `Can't load ${assetToLoad.name}`);
-
- const response = await pendingAsset.pending.response;
- const contentType = response.headers ? response.headers.get("Content-Type") : undefined;
- let compiledInstance: WebAssembly.Instance;
- let compiledModule: WebAssembly.Module;
- if (typeof WebAssembly.instantiateStreaming === "function" && contentType === "application/wasm") {
- if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: instantiate_wasm_module streaming");
- const streamingResult = await WebAssembly.instantiateStreaming(response, wasm_module_imports!);
- compiledInstance = streamingResult.instance;
- compiledModule = streamingResult.module;
- } else {
- const arrayBuffer = await response.arrayBuffer();
- if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: instantiate_wasm_module buffered");
- const arrayBufferResult = await WebAssembly.instantiate(arrayBuffer, wasm_module_imports!);
- compiledInstance = arrayBufferResult.instance;
- compiledModule = arrayBufferResult.module;
- }
- ++instantiated_assets_count;
- wasm_success_callback!(compiledInstance, compiledModule);
+ instantiate_wasm_asset(pendingAsset!, imports, successCallback);
+
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: instantiate_wasm_module done");
afterInstantiateWasm.promise_control.resolve();
- wasm_success_callback = null;
- wasm_module_imports = null;
} catch (err) {
- _print_error("MONO_WASM: _instantiate_wasm_module() failed", err);
+ _print_error("MONO_WASM: instantiate_wasm_module() failed", err);
abort_startup(err, true);
throw err;
}
Module.removeRunDependency("instantiate_wasm_module");
}
-// this need to be run only after onRuntimeInitialized event, when the memory is ready
-function _instantiate_asset(asset: AssetEntry, url: string, bytes: Uint8Array) {
- if (runtimeHelpers.diagnosticTracing)
- console.debug(`MONO_WASM: Loaded:${asset.name} as ${asset.behavior} size ${bytes.length} from ${url}`);
-
- const virtualName: string = typeof (asset.virtualPath) === "string"
- ? asset.virtualPath
- : asset.name;
- let offset: VoidPtr | null = null;
-
- switch (asset.behavior) {
- case "resource":
- case "assembly":
- case "pdb":
- loaded_files.push({ url: url, file: virtualName });
- // falls through
- case "heap":
- case "icu":
- offset = mono_wasm_load_bytes_into_heap(bytes);
- loaded_assets[virtualName] = [offset, bytes.length];
- break;
-
- case "vfs": {
- // FIXME
- const lastSlash = virtualName.lastIndexOf("/");
- let parentDirectory = (lastSlash > 0)
- ? virtualName.substr(0, lastSlash)
- : null;
- let fileName = (lastSlash > 0)
- ? virtualName.substr(lastSlash + 1)
- : virtualName;
- if (fileName.startsWith("/"))
- fileName = fileName.substr(1);
- if (parentDirectory) {
- if (runtimeHelpers.diagnosticTracing)
- console.debug(`MONO_WASM: Creating directory '${parentDirectory}'`);
-
- Module.FS_createPath(
- "/", parentDirectory, true, true // fixme: should canWrite be false?
- );
- } else {
- parentDirectory = "/";
- }
-
- if (runtimeHelpers.diagnosticTracing)
- console.debug(`MONO_WASM: Creating file '${fileName}' in directory '${parentDirectory}'`);
-
- if (!mono_wasm_load_data_archive(bytes, parentDirectory)) {
- Module.FS_createDataFile(
- parentDirectory, fileName,
- bytes, true /* canRead */, true /* canWrite */, true /* canOwn */
- );
- }
- break;
- }
- default:
- throw new Error(`Unrecognized asset behavior:${asset.behavior}, for asset ${asset.name}`);
- }
-
- if (asset.behavior === "assembly") {
- const hasPpdb = cwraps.mono_wasm_add_assembly(virtualName, offset!, bytes.length);
-
- if (!hasPpdb) {
- const index = loaded_files.findIndex(element => element.file == virtualName);
- loaded_files.splice(index, 1);
- }
- }
- else if (asset.behavior === "icu") {
- if (!mono_wasm_load_icu_data(offset!))
- 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);
- }
- ++instantiated_assets_count;
-}
-
// runs just in non-blazor
async function _apply_configuration_from_args() {
try {
@@ -592,251 +460,6 @@ export function bindings_init(): void {
}
}
-function downloadResource(request: ResourceRequest): LoadingResource {
- if (typeof Module.downloadResource === "function") {
- const loading = Module.downloadResource(request);
- if (loading) return loading;
- }
- const options: any = {};
- if (request.hash) {
- options.integrity = request.hash;
- }
- const response = runtimeHelpers.fetch_like(request.resolvedUrl!, options);
- return {
- name: request.name, url: request.resolvedUrl!, response
- };
-}
-async function start_asset_download_sources(asset: AssetEntry): Promise {
- if (asset.buffer) {
- ++downloded_assets_count;
- const buffer = asset.buffer;
- asset.buffer = undefined;//GC later
- asset.pending = {
- url: "undefined://" + asset.name,
- name: asset.name,
- response: Promise.resolve({
- arrayBuffer: () => buffer,
- headers: {
- get: () => undefined,
- }
- }) as any
- };
- return Promise.resolve(asset);
- }
- if (asset.pending) {
- ++downloded_assets_count;
- return asset;
- }
-
- const sourcesList = asset.loadRemote && config.remoteSources ? config.remoteSources : [""];
- let response: Response | undefined = undefined;
- for (let sourcePrefix of sourcesList) {
- sourcePrefix = sourcePrefix.trim();
- // HACK: Special-case because MSBuild doesn't allow "" as an attribute
- if (sourcePrefix === "./")
- sourcePrefix = "";
-
- let attemptUrl;
- const assemblyRootFolder = config.assemblyRootFolder;
- if (!asset.resolvedUrl) {
- if (sourcePrefix === "") {
- if (asset.behavior === "assembly" || asset.behavior === "pdb")
- attemptUrl = assemblyRootFolder + "/" + asset.name;
- else if (asset.behavior === "resource") {
- const path = asset.culture !== "" ? `${asset.culture}/${asset.name}` : asset.name;
- attemptUrl = assemblyRootFolder + "/" + path;
- }
- else
- attemptUrl = asset.name;
- } else {
- attemptUrl = sourcePrefix + asset.name;
- }
- attemptUrl = runtimeHelpers.locateFile(attemptUrl);
- }
- else {
- attemptUrl = asset.resolvedUrl;
- }
- if (asset.name === attemptUrl) {
- if (runtimeHelpers.diagnosticTracing)
- console.debug(`MONO_WASM: Attempting to download '${attemptUrl}'`);
- } else {
- if (runtimeHelpers.diagnosticTracing)
- console.debug(`MONO_WASM: Attempting to download '${attemptUrl}' for ${asset.name}`);
- }
- try {
- const loadingResource = downloadResource({
- name: asset.name,
- resolvedUrl: attemptUrl,
- hash: asset.hash,
- behavior: asset.behavior
- });
- response = await loadingResource.response;
- if (!response.ok) {
- continue;// next source
- }
- asset.pending = loadingResource;
- ++downloded_assets_count;
- return asset;
- }
- catch (err) {
- continue; //next source
- }
- }
- throw response;
-}
-
-let throttling: PromiseAndController | undefined;
-async function start_asset_download_throttle(asset: AssetEntry): Promise {
- // we don't addRunDependency to allow download in parallel with onRuntimeInitialized event!
- while (throttling) {
- await throttling.promise;
- }
- try {
- ++parallel_count;
- if (parallel_count == runtimeHelpers.maxParallelDownloads) {
- if (runtimeHelpers.diagnosticTracing)
- console.debug("MONO_WASM: Throttling further parallel downloads");
- throttling = createPromiseController();
- }
- return await start_asset_download_sources(asset);
- }
- catch (response: any) {
- const isOkToFail = asset.isOptional || (asset.name.match(/\.pdb$/) && config.ignorePdbLoadErrors);
- if (!isOkToFail) {
- const err: any = new Error(`MONO_WASM: download '${response.url}' for ${asset.name} failed ${response.status} ${response.statusText}`);
- err.status = response.status;
- throw err;
- }
- }
- finally {
- --parallel_count;
- if (throttling && parallel_count == runtimeHelpers.maxParallelDownloads - 1) {
- if (runtimeHelpers.diagnosticTracing)
- console.debug("MONO_WASM: Resuming more parallel downloads");
- const old_throttling = throttling;
- throttling = undefined;
- old_throttling.promise_control.resolve();
- }
- }
-}
-
-async function start_asset_download(asset: AssetEntry): Promise {
- try {
- return await start_asset_download_throttle(asset);
- } catch (err: any) {
- if (err && err.status == 404) {
- throw err;
- }
- // second attempt only after all first attempts are queued
- await allDownloadsQueued.promise;
- try {
- return await start_asset_download_throttle(asset);
- } catch (err) {
- // third attempt after small delay
- await delay(100);
- return await start_asset_download_throttle(asset);
- }
- }
-}
-
-const allDownloadsQueued = createPromiseController();
-async function mono_download_assets(): Promise {
- if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: mono_download_assets");
- runtimeHelpers.maxParallelDownloads = runtimeHelpers.config.maxParallelDownloads || runtimeHelpers.maxParallelDownloads;
- try {
- const download_promises: Promise[] = [];
- // start fetching and instantiating all assets in parallel
- for (const asset of config.assets || []) {
- if (asset.behavior != "dotnetwasm") {
- download_promises.push(start_asset_download(asset));
- }
- }
- allDownloadsQueued.promise_control.resolve();
-
- const asset_promises: Promise[] = [];
- for (const downloadPromise of download_promises) {
- const downloadedAsset = await downloadPromise;
- if (downloadedAsset) {
- asset_promises.push((async () => {
- const url = downloadedAsset.pending!.url;
- const response = await downloadedAsset.pending!.response;
- downloadedAsset.pending = undefined; //GC
- const buffer = await response.arrayBuffer();
- await beforeOnRuntimeInitialized.promise;
- // this is after onRuntimeInitialized
- _instantiate_asset(downloadedAsset, url, new Uint8Array(buffer));
- })());
- }
- }
-
- // this await will get past the onRuntimeInitialized because we are not blocking via addRunDependency
- // and we are not awating it here
- all_assets_loaded_in_memory = Promise.all(asset_promises) as any;
- // OPTIMIZATION explained:
- // we do it this way so that we could allocate memory immediately after asset is downloaded (and after onRuntimeInitialized which happened already)
- // spreading in time
- // rather than to block all downloads after onRuntimeInitialized or block onRuntimeInitialized after all downloads are done. That would create allocation burst.
- } catch (err: any) {
- Module.printErr("MONO_WASM: Error in mono_download_assets: " + err);
- throw err;
- }
-}
-
-// used from Blazor
-export function mono_wasm_load_data_archive(data: Uint8Array, prefix: string): boolean {
- if (data.length < 8)
- return false;
-
- const dataview = new DataView(data.buffer);
- const magic = dataview.getUint32(0, true);
- // get magic number
- if (magic != 0x626c6174) {
- return false;
- }
- const manifestSize = dataview.getUint32(4, true);
- if (manifestSize == 0 || data.length < manifestSize + 8)
- return false;
-
- let manifest;
- try {
- const manifestContent = Module.UTF8ArrayToString(data, 8, manifestSize);
- manifest = JSON.parse(manifestContent);
- if (!(manifest instanceof Array))
- return false;
- } catch (exc) {
- return false;
- }
-
- data = data.slice(manifestSize + 8);
-
- // Create the folder structure
- // /usr/share/zoneinfo
- // /usr/share/zoneinfo/Africa
- // /usr/share/zoneinfo/Asia
- // ..
-
- const folders = new Set();
- manifest.filter(m => {
- const file = m[0];
- const last = file.lastIndexOf("/");
- const directory = file.slice(0, last + 1);
- folders.add(directory);
- });
- folders.forEach(folder => {
- Module["FS_createPath"](prefix, folder, true, true);
- });
-
- for (const row of manifest) {
- const name = row[0];
- const length = row[1];
- const bytes = data.slice(0, length);
- Module["FS_createDataFile"](prefix, name, bytes, true, true);
- data = data.slice(length);
- }
- return true;
-}
-
-let configLoaded = false;
/**
* Loads the mono config file (typically called mono-config.json) asynchroniously
* Note: the run dependencies are so emsdk actually awaits it in order.
@@ -844,8 +467,15 @@ let configLoaded = false;
* @param {string} configFilePath - relative path to the config file
* @throws Will throw an error if the config file loading fails
*/
-export async function mono_wasm_load_config(configFilePath: string): Promise {
+export async function mono_wasm_load_config(configFilePath?: string): Promise {
if (configLoaded) {
+ await afterConfigLoaded.promise;
+ return;
+ }
+ configLoaded = true;
+ if (!configFilePath) {
+ normalize();
+ afterConfigLoaded.promise_control.resolve();
return;
}
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: mono_wasm_load_config");
@@ -853,36 +483,48 @@ export async function mono_wasm_load_config(configFilePath: string): PromiseruntimeHelpers.config);
+ normalize();
}
catch (err: any) {
_print_error("MONO_WASM: onConfigLoaded() failed", err);
throw err;
}
}
- runtimeHelpers.diagnosticTracing = !!runtimeHelpers.config.diagnosticTracing;
- configLoaded = true;
+ afterConfigLoaded.promise_control.resolve();
} catch (err) {
const errMessage = `Failed to load config file ${configFilePath} ${err}`;
abort_startup(errMessage, true);
config = runtimeHelpers.config = Module.config = { message: errMessage, error: err, isError: true };
throw err;
}
+
+ function normalize() {
+ // normalize
+ config.environmentVariables = config.environmentVariables || {};
+ config.assets = config.assets || [];
+ config.runtimeOptions = config.runtimeOptions || [];
+ config.globalizationMode = config.globalizationMode || "auto";
+ if (config.debugLevel === undefined && BuildConfiguration === "Debug") {
+ config.debugLevel = -1;
+ }
+ if (config.diagnosticTracing === undefined && BuildConfiguration === "Debug") {
+ config.diagnosticTracing = true;
+ }
+ runtimeHelpers.diagnosticTracing = !!runtimeHelpers.config.diagnosticTracing;
+ }
}
export function mono_wasm_asm_loaded(assembly_name: CharPtr, assembly_ptr: number, assembly_len: number, pdb_ptr: number, pdb_len: number): void {
@@ -946,5 +588,7 @@ export async function mono_wasm_pthread_worker_init(): Promise {
export async function mono_load_runtime_and_bcl_args(cfg?: MonoConfig | MonoConfigError | undefined): Promise {
config = Module.config = runtimeHelpers.config = Object.assign(runtimeHelpers.config || {}, cfg || {}) as any;
await mono_download_assets();
- await all_assets_loaded_in_memory;
+ if (!isCustomStartup) {
+ await wait_for_all_assets();
+ }
}
diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts
index 271edd7f14595f..d9edc82f9587b6 100644
--- a/src/mono/wasm/runtime/types.ts
+++ b/src/mono/wasm/runtime/types.ts
@@ -116,7 +116,9 @@ export type AssetBehaviours =
| "heap" // store asset into the native heap
| "icu" // load asset as an ICU data archive
| "vfs" // load asset into the virtual filesystem (for fopen, File.Open, etc)
- | "dotnetwasm"; // the binary of the dotnet runtime
+ | "dotnetwasm" // the binary of the dotnet runtime
+ | "js-module-crypto" // the javascript module for subtle crypto
+ | "js-module-threads" // the javascript module for threads
export type RuntimeHelpers = {
runtime_interop_module: MonoAssembly;
@@ -128,6 +130,7 @@ export type RuntimeHelpers = {
mono_wasm_load_runtime_done: boolean;
mono_wasm_runtime_is_ready: boolean;
mono_wasm_bindings_is_ready: boolean;
+ mono_wasm_symbols_are_ready: boolean;
loaded_files: string[];
maxParallelDownloads: number;
diff --git a/src/mono/wasm/runtime/types/emscripten.ts b/src/mono/wasm/runtime/types/emscripten.ts
index 3c90efaac757a0..5ddd3f6ceb6223 100644
--- a/src/mono/wasm/runtime/types/emscripten.ts
+++ b/src/mono/wasm/runtime/types/emscripten.ts
@@ -58,7 +58,7 @@ export declare interface EmscriptenModule {
ready: Promise;
- instantiateWasm?: (imports: WebAssembly.Imports, successCallback: (instance: WebAssembly.Instance, module: WebAssembly.Module) => void) => any;
+ instantiateWasm?: InstantiateWasmCallBack;
preInit?: (() => any)[] | (() => any);
preRun?: (() => any)[] | (() => any);
onRuntimeInitialized?: () => any;
@@ -66,4 +66,7 @@ export declare interface EmscriptenModule {
onAbort?: { (error: any): void };
}
+export type InstantiateWasmSuccessCallback = (instance: WebAssembly.Instance, module: WebAssembly.Module) => void;
+export type InstantiateWasmCallBack = (imports: WebAssembly.Imports, successCallback: InstantiateWasmSuccessCallback) => any;
+
export declare type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array;
diff --git a/src/mono/wasm/runtime/workers/dotnet-crypto-worker.ts b/src/mono/wasm/runtime/workers/dotnet-crypto-worker.ts
index 6b4dcde69c12d1..59866c1c500a7e 100644
--- a/src/mono/wasm/runtime/workers/dotnet-crypto-worker.ts
+++ b/src/mono/wasm/runtime/workers/dotnet-crypto-worker.ts
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-import { setup_proxy_console } from "../debug";
+import { setup_proxy_console } from "../logging";
import type { InitCryptoMessageData } from "../crypto-worker";
import type { MonoConfig } from "../types";
diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
index de25059d66d03c..65f1a9a5b0f1ad 100644
--- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
+++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
@@ -74,7 +74,7 @@ public class WasmAppBuilder : Task
private sealed class WasmAppConfig
{
[JsonPropertyName("assemblyRootFolder")]
- public string AssemblyRoot { get; set; } = "managed";
+ public string AssemblyRootFolder { get; set; } = "managed";
[JsonPropertyName("debugLevel")]
public int DebugLevel { get; set; } = 0;
[JsonPropertyName("assets")]
@@ -96,6 +96,23 @@ protected AssetEntry (string name, string behavior)
public string Behavior { get; init; }
[JsonPropertyName("name")]
public string Name { get; init; }
+ // TODO [JsonPropertyName("hash")]
+ // TODO public string? Hash { get; set; }
+ }
+
+ private sealed class WasmEntry : AssetEntry
+ {
+ public WasmEntry(string name) : base(name, "dotnetwasm") { }
+ }
+
+ private sealed class CryptoWorkerEntry : AssetEntry
+ {
+ public CryptoWorkerEntry(string name) : base(name, "js-module-crypto") { }
+ }
+
+ private sealed class ThreadsWorkerEntry : AssetEntry
+ {
+ public ThreadsWorkerEntry(string name) : base(name, "js-module-threads") { }
}
private sealed class AssemblyEntry : AssetEntry
@@ -165,7 +182,7 @@ private bool ExecuteInternal ()
var config = new WasmAppConfig ();
// Create app
- var asmRootPath = Path.Combine(AppDir, config.AssemblyRoot);
+ var asmRootPath = Path.Combine(AppDir, config.AssemblyRootFolder);
Directory.CreateDirectory(AppDir!);
Directory.CreateDirectory(asmRootPath);
foreach (var assembly in _assemblies)
@@ -240,7 +257,7 @@ private bool ExecuteInternal ()
// FIXME: validate the culture?
string name = Path.GetFileName(fullPath);
- string directory = Path.Combine(AppDir, config.AssemblyRoot, culture);
+ string directory = Path.Combine(AppDir, config.AssemblyRootFolder, culture);
Directory.CreateDirectory(directory);
FileCopyChecked(fullPath, Path.Combine(directory, name), "SatelliteAssemblies");
config.Assets.Add(new SatelliteAssemblyEntry(name, culture));
@@ -295,6 +312,8 @@ private bool ExecuteInternal ()
config.Assets.Add(new IcuData(IcuDataFileName!) { LoadRemote = RemoteSources?.Length > 0 });
config.Assets.Add(new VfsEntry ("dotnet.timezones.blat") { VirtualPath = "/usr/share/zoneinfo/"});
+ config.Assets.Add(new WasmEntry ("dotnet.wasm") );
+ config.Assets.Add(new CryptoWorkerEntry ("dotnet-crypto-worker.js") );
if (RemoteSources?.Length > 0)
{