dotnet_runtime/src/mono/browser/runtime/startup.ts

676 lines
27 KiB
TypeScript

// 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 WasmEnableLegacyJsInterop from "consts:wasmEnableLegacyJsInterop";
import { DotnetModuleInternal, CharPtrNull } from "./types/internal";
import { linkerDisableLegacyJsInterop, ENVIRONMENT_IS_PTHREAD, ENVIRONMENT_IS_NODE, exportedRuntimeAPI, INTERNAL, loaderHelpers, Module, runtimeHelpers, createPromiseController, mono_assert, linkerWasmEnableSIMD, linkerWasmEnableEH, ENVIRONMENT_IS_WORKER } from "./globals";
import cwraps, { init_c_exports, threads_c_functions as tcwraps } from "./cwraps";
import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug";
import { toBase64StringImpl } from "./base64";
import { mono_wasm_init_aot_profiler, mono_wasm_init_browser_profiler } from "./profiler";
import { initialize_marshalers_to_cs } from "./marshal-to-cs";
import { initialize_marshalers_to_js } from "./marshal-to-js";
import { init_polyfills_async } from "./polyfills";
import { strings_init, utf8ToString } from "./strings";
import { init_managed_exports } from "./managed-exports";
import { cwraps_internal } from "./exports-internal";
import { CharPtr, InstantiateWasmCallBack, InstantiateWasmSuccessCallback } from "./types/emscripten";
import { wait_for_all_assets } from "./assets";
import { mono_wasm_init_diagnostics } from "./diagnostics";
import { replace_linker_placeholders } from "./exports-binding";
import { endMeasure, MeasuredBlock, startMeasure } from "./profiler";
import { checkMemorySnapshotSize, getMemorySnapshot, storeMemorySnapshot } from "./snapshot";
import { interp_pgo_load_data, interp_pgo_save_data } from "./interp-pgo";
import { mono_log_debug, mono_log_error, mono_log_warn, mono_set_thread_id } from "./logging";
// threads
import { preAllocatePThreadWorkerPool, instantiateWasmPThreadWorkerPool } from "./pthreads/browser";
import { currentWorkerThreadEvents, dotnetPthreadCreated, initWorkerThreadEvents } from "./pthreads/worker";
import { getBrowserThreadID } from "./pthreads/shared";
import { jiterpreter_allocate_tables } from "./jiterpreter-support";
// legacy
import { init_legacy_exports } from "./net6-legacy/corebindings";
import { cwraps_binding_api, cwraps_mono_api } from "./net6-legacy/exports-legacy";
import { BINDING, MONO } from "./net6-legacy/globals";
import { localHeapViewU8 } from "./memory";
import { assertNoProxies } from "./gc-handles";
export async function configureRuntimeStartup(): Promise<void> {
await init_polyfills_async();
await checkMemorySnapshotSize();
}
// 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
export function configureEmscriptenStartup(module: DotnetModuleInternal): void {
const mark = startMeasure();
if (!module.locateFile) {
// this is dummy plug so that wasmBinaryFile doesn't try to use URL class
module.locateFile = module.__locateFile = (path) => loaderHelpers.scriptDirectory + path;
}
if (!module.out) {
// eslint-disable-next-line no-console
module.out = console.log.bind(console);
}
if (!module.err) {
// eslint-disable-next-line no-console
module.err = console.error.bind(console);
}
loaderHelpers.out = module.out;
loaderHelpers.err = module.err;
module.mainScriptUrlOrBlob = loaderHelpers.scriptUrl;// this is needed by worker threads
// these all could be overridden on DotnetModuleConfig, we are chaing them to async below, as opposed to emscripten
// when user set configSrc or config, we are running our default startup sequence.
const userInstantiateWasm: undefined | ((imports: WebAssembly.Imports, successCallback: InstantiateWasmSuccessCallback) => any) = module.instantiateWasm;
const userPreInit: (() => void)[] = !module.preInit ? [] : typeof module.preInit === "function" ? [module.preInit] : module.preInit;
const userPreRun: (() => void)[] = !module.preRun ? [] : typeof module.preRun === "function" ? [module.preRun] : module.preRun as any;
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 : () => { };
// execution order == [0] ==
// - default or user Module.instantiateWasm (will start downloading dotnet.native.wasm)
module.instantiateWasm = (imports, callback) => instantiateWasm(imports, callback, userInstantiateWasm);
// execution order == [1] ==
module.preInit = [() => preInit(userPreInit)];
// execution order == [2] ==
module.preRun = [() => preRunAsync(userPreRun)];
// execution order == [4] ==
module.onRuntimeInitialized = () => onRuntimeInitializedAsync(userOnRuntimeInitialized);
// execution order == [5] ==
module.postRun = [() => postRunAsync(userpostRun)];
// execution order == [6] ==
module.ready.then(async () => {
// wait for previous stage
await runtimeHelpers.afterPostRun.promise;
// startup end
endMeasure(mark, MeasuredBlock.emscriptenStartup);
// - here we resolve the promise returned by createDotnetRuntime export
// - any code after createDotnetRuntime is executed now
runtimeHelpers.dotnetReady.promise_control.resolve(exportedRuntimeAPI);
}).catch(err => {
runtimeHelpers.dotnetReady.promise_control.reject(err);
});
module.ready = runtimeHelpers.dotnetReady.promise;
}
function instantiateWasm(
imports: WebAssembly.Imports,
successCallback: InstantiateWasmSuccessCallback,
userInstantiateWasm?: InstantiateWasmCallBack): any[] {
// this is called so early that even Module exports like addRunDependency don't exist yet
const mark = startMeasure();
if (userInstantiateWasm) {
const exports = userInstantiateWasm(imports, (instance: WebAssembly.Instance, module: WebAssembly.Module | undefined) => {
endMeasure(mark, MeasuredBlock.instantiateWasm);
runtimeHelpers.afterInstantiateWasm.promise_control.resolve();
successCallback(instance, module);
});
return exports;
}
instantiate_wasm_module(imports, successCallback);
return []; // No exports
}
async function instantiateWasmWorker(
imports: WebAssembly.Imports,
successCallback: InstantiateWasmSuccessCallback
): Promise<void> {
// wait for the config to arrive by message from the main thread
await loaderHelpers.afterConfigLoaded.promise;
replace_linker_placeholders(imports);
// Instantiate from the module posted from the main thread.
// We can just use sync instantiation in the worker.
const instance = new WebAssembly.Instance(Module.wasmModule!, imports);
successCallback(instance, undefined);
Module.wasmModule = null;
}
function preInit(userPreInit: (() => void)[]) {
Module.addRunDependency("mono_pre_init");
const mark = startMeasure();
try {
mono_wasm_pre_init_essential(false);
mono_log_debug("preInit");
runtimeHelpers.beforePreInit.promise_control.resolve();
// all user Module.preInit callbacks
userPreInit.forEach(fn => fn());
} catch (err) {
mono_log_error("user preInint() failed", err);
loaderHelpers.mono_exit(1, err);
throw err;
}
// this will start immediately but return on first await.
// It will block our `preRun` by afterPreInit promise
// It will block emscripten `userOnRuntimeInitialized` by pending addRunDependency("mono_pre_init")
(async () => {
try {
// - init the rest of the polyfills
await mono_wasm_pre_init_essential_async();
endMeasure(mark, MeasuredBlock.preInit);
} catch (err) {
loaderHelpers.mono_exit(1, err);
throw err;
}
// signal next stage
runtimeHelpers.afterPreInit.promise_control.resolve();
Module.removeRunDependency("mono_pre_init");
})();
}
async function preInitWorkerAsync() {
mono_log_debug("worker initializing essential C exports and APIs");
const mark = startMeasure();
try {
mono_log_debug("preInitWorker");
runtimeHelpers.beforePreInit.promise_control.resolve();
mono_wasm_pre_init_essential(true);
await ensureUsedWasmFeatures();
await init_polyfills_async();
runtimeHelpers.afterPreInit.promise_control.resolve();
endMeasure(mark, MeasuredBlock.preInitWorker);
} catch (err) {
mono_log_error("user preInitWorker() failed", err);
loaderHelpers.mono_exit(1, err);
throw err;
}
}
export function preRunWorker() {
// signal next stage
runtimeHelpers.runtimeReady = true;
runtimeHelpers.afterPreRun.promise_control.resolve();
}
async function preRunAsync(userPreRun: (() => void)[]) {
Module.addRunDependency("mono_pre_run_async");
// wait for previous stages
try {
await runtimeHelpers.afterInstantiateWasm.promise;
await runtimeHelpers.afterPreInit.promise;
mono_log_debug("preRunAsync");
const mark = startMeasure();
// all user Module.preRun callbacks
userPreRun.map(fn => fn());
endMeasure(mark, MeasuredBlock.preRun);
} catch (err) {
mono_log_error("user callback preRun() failed", err);
loaderHelpers.mono_exit(1, err);
throw err;
}
// signal next stage
runtimeHelpers.afterPreRun.promise_control.resolve();
Module.removeRunDependency("mono_pre_run_async");
}
async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) {
try {
// wait for previous stage
await runtimeHelpers.afterPreRun.promise;
mono_log_debug("onRuntimeInitialized");
runtimeHelpers.mono_wasm_exit = cwraps.mono_wasm_exit;
runtimeHelpers.abort = (reason: any) => {
loaderHelpers.exitReason = reason;
if (!loaderHelpers.is_exited()) {
cwraps.mono_wasm_abort();
}
throw reason;
};
const mark = startMeasure();
// signal this stage, this will allow pending assets to allocate memory
runtimeHelpers.beforeOnRuntimeInitialized.promise_control.resolve();
await wait_for_all_assets();
// Threads early are not supported with memory snapshot. See below how we enable them later.
// Please disable startupMemoryCache in order to be able to diagnose or pause runtime startup.
if (MonoWasmThreads && !runtimeHelpers.config.startupMemoryCache) {
await mono_wasm_init_threads();
}
// load runtime and apply environment settings (if necessary)
await mono_wasm_before_memory_snapshot();
if (runtimeHelpers.config.interpreterPgo) {
await interp_pgo_load_data();
}
if (runtimeHelpers.config.exitAfterSnapshot) {
const reason = runtimeHelpers.ExitStatus
? new runtimeHelpers.ExitStatus(0)
: new Error("Snapshot taken, exiting because exitAfterSnapshot was set.");
reason.silent = true;
loaderHelpers.mono_exit(0, reason);
return;
}
if (!ENVIRONMENT_IS_WORKER) {
Module.runtimeKeepalivePush();
}
runtimeHelpers.runtimeReady = true;
if (runtimeHelpers.config.virtualWorkingDirectory) {
const FS = Module.FS;
const cwd = runtimeHelpers.config.virtualWorkingDirectory;
const wds = FS.stat(cwd);
if (!wds) {
Module.FS_createPath("/", cwd, true, true);
}
mono_assert(wds && FS.isDir(wds.mode), () => `FS.chdir: ${cwd} is not a directory`);
FS.chdir(cwd);
}
if (MonoWasmThreads && runtimeHelpers.config.startupMemoryCache) {
await mono_wasm_init_threads();
}
bindings_init();
jiterpreter_allocate_tables();
runtimeHelpers.runtimeReady = true;
if (ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER) {
Module.runtimeKeepalivePush();
}
if (MonoWasmThreads) {
runtimeHelpers.javaScriptExports.install_main_synchronization_context();
}
if (!runtimeHelpers.mono_wasm_runtime_is_ready) mono_wasm_runtime_ready();
if (loaderHelpers.config.debugLevel !== 0 && loaderHelpers.config.cacheBootResources) {
loaderHelpers.logDownloadStatsToConsole();
}
setTimeout(() => {
loaderHelpers.purgeUnusedCacheEntriesAsync(); // Don't await - it's fine to run in background
}, loaderHelpers.config.cachedResourcesPurgeDelay);
// call user code
try {
userOnRuntimeInitialized();
}
catch (err: any) {
mono_log_error("user callback onRuntimeInitialized() failed", err);
throw err;
}
// finish
await mono_wasm_after_user_runtime_initialized();
endMeasure(mark, MeasuredBlock.onRuntimeInitialized);
} catch (err) {
mono_log_error("onRuntimeInitializedAsync() failed", err);
loaderHelpers.mono_exit(1, err);
throw err;
}
// signal next stage
runtimeHelpers.afterOnRuntimeInitialized.promise_control.resolve();
}
async function postRunAsync(userpostRun: (() => void)[]) {
// wait for previous stage
try {
await runtimeHelpers.afterOnRuntimeInitialized.promise;
mono_log_debug("postRunAsync");
const mark = startMeasure();
// create /usr/share folder which is SpecialFolder.CommonApplicationData
Module["FS_createPath"]("/", "usr", true, true);
Module["FS_createPath"]("/", "usr/share", true, true);
if (MonoWasmThreads) {
tcwraps.mono_wasm_init_finalizer_thread();
}
// all user Module.postRun callbacks
userpostRun.map(fn => fn());
endMeasure(mark, MeasuredBlock.postRun);
} catch (err) {
mono_log_error("user callback posRun() failed", err);
loaderHelpers.mono_exit(1, err);
throw err;
}
// signal next stage
runtimeHelpers.afterPostRun.promise_control.resolve();
}
export function postRunWorker() {
assertNoProxies();
// signal next stage
runtimeHelpers.runtimeReady = false;
runtimeHelpers.afterPreRun = createPromiseController<void>();
}
async function mono_wasm_init_threads() {
if (!MonoWasmThreads) {
return;
}
const tid = getBrowserThreadID();
mono_set_thread_id(`0x${tid.toString(16)}-main`);
await instantiateWasmPThreadWorkerPool();
await mono_wasm_init_diagnostics();
}
function mono_wasm_pre_init_essential(isWorker: boolean): void {
if (!isWorker)
Module.addRunDependency("mono_wasm_pre_init_essential");
mono_log_debug("mono_wasm_pre_init_essential");
if (loaderHelpers.gitHash !== runtimeHelpers.gitHash) {
mono_log_warn("The version of dotnet.runtime.js is different from the version of dotnet.js!");
}
if (loaderHelpers.gitHash !== runtimeHelpers.moduleGitHash) {
mono_log_warn("The version of dotnet.native.js is different from the version of dotnet.js!");
}
init_c_exports();
cwraps_internal(INTERNAL);
if (WasmEnableLegacyJsInterop && !linkerDisableLegacyJsInterop) {
cwraps_mono_api(MONO);
cwraps_binding_api(BINDING);
}
// removeRunDependency triggers the dependenciesFulfilled callback (runCaller) in
// emscripten - on a worker since we don't have any other dependencies that causes run() to get
// called too soon; and then it will get called a second time when dotnet.native.js calls it directly.
// on a worker run() short-cirtcuits and just calls readyPromiseResolve, initRuntime and postMessage.
// sending postMessage twice will break instantiateWasmPThreadWorkerPool on the main thread.
if (!isWorker)
Module.removeRunDependency("mono_wasm_pre_init_essential");
}
async function mono_wasm_pre_init_essential_async(): Promise<void> {
mono_log_debug("mono_wasm_pre_init_essential_async");
Module.addRunDependency("mono_wasm_pre_init_essential_async");
if (MonoWasmThreads) {
preAllocatePThreadWorkerPool(runtimeHelpers.config.pthreadPoolSize!);
}
Module.removeRunDependency("mono_wasm_pre_init_essential_async");
}
async function mono_wasm_after_user_runtime_initialized(): Promise<void> {
mono_log_debug("mono_wasm_after_user_runtime_initialized");
try {
if (!Module.disableDotnet6Compatibility && Module.exports) {
// Export emscripten defined in module through EXPORTED_RUNTIME_METHODS
// Useful to export IDBFS or other similar types generally exposed as
// global types when emscripten is not modularized.
const globalThisAny = globalThis as any;
for (let i = 0; i < Module.exports.length; ++i) {
const exportName = Module.exports[i];
const exportValue = (<any>Module)[exportName];
if (exportValue != undefined) {
globalThisAny[exportName] = exportValue;
}
else {
mono_log_warn(`The exported symbol ${exportName} could not be found in the emscripten module`);
}
}
}
mono_log_debug("Initializing mono runtime");
if (Module.onDotnetReady) {
try {
await Module.onDotnetReady();
}
catch (err: any) {
mono_log_error("onDotnetReady () failed", err);
throw err;
}
}
} catch (err: any) {
mono_log_error("mono_wasm_after_user_runtime_initialized () failed", err);
throw err;
}
}
// Set environment variable NAME to VALUE
// Should be called before mono_load_runtime_and_bcl () in most cases
export function mono_wasm_setenv(name: string, value: string): void {
cwraps.mono_wasm_setenv(name, value);
}
export function mono_wasm_set_runtime_options(options: string[]): void {
if (!Array.isArray(options))
throw new Error("Expected runtimeOptions to be an array of strings");
const argv = Module._malloc(options.length * 4);
let aindex = 0;
for (let i = 0; i < options.length; ++i) {
const option = options[i];
if (typeof (option) !== "string")
throw new Error("Expected runtimeOptions to be an array of strings");
Module.setValue(<any>argv + (aindex * 4), cwraps.mono_wasm_strdup(option), "i32");
aindex += 1;
}
cwraps.mono_wasm_parse_runtime_options(options.length, argv);
}
async function instantiate_wasm_module(
imports: WebAssembly.Imports,
successCallback: InstantiateWasmSuccessCallback,
): Promise<void> {
// this is called so early that even Module exports like addRunDependency don't exist yet
try {
await loaderHelpers.afterConfigLoaded;
mono_log_debug("instantiate_wasm_module");
await runtimeHelpers.beforePreInit.promise;
Module.addRunDependency("instantiate_wasm_module");
await ensureUsedWasmFeatures();
replace_linker_placeholders(imports);
const compiledModule = await loaderHelpers.wasmCompilePromise.promise;
const compiledInstance = await WebAssembly.instantiate(compiledModule, imports);
successCallback(compiledInstance, compiledModule);
mono_log_debug("instantiate_wasm_module done");
if (runtimeHelpers.loadedMemorySnapshotSize) {
try {
const wasmMemory = runtimeHelpers.getMemory();
// .grow() takes a delta compared to the previous size
wasmMemory.grow((runtimeHelpers.loadedMemorySnapshotSize! - wasmMemory.buffer.byteLength + 65535) >>> 16);
runtimeHelpers.updateMemoryViews();
} catch (err) {
mono_log_warn("failed to resize memory for the snapshot", err);
runtimeHelpers.loadedMemorySnapshotSize = undefined;
}
// now we know if the loading of memory succeeded or not, we can start loading the rest of the assets
loaderHelpers.memorySnapshotSkippedOrDone.promise_control.resolve();
}
runtimeHelpers.afterInstantiateWasm.promise_control.resolve();
} catch (err) {
mono_log_error("instantiate_wasm_module() failed", err);
loaderHelpers.mono_exit(1, err);
throw err;
}
Module.removeRunDependency("instantiate_wasm_module");
}
async function ensureUsedWasmFeatures() {
runtimeHelpers.featureWasmSimd = await loaderHelpers.simd();
runtimeHelpers.featureWasmEh = await loaderHelpers.exceptions();
if (linkerWasmEnableSIMD) {
mono_assert(runtimeHelpers.featureWasmSimd, "This browser/engine doesn't support WASM SIMD. Please use a modern version. See also https://aka.ms/dotnet-wasm-features");
}
if (linkerWasmEnableEH) {
mono_assert(runtimeHelpers.featureWasmEh, "This browser/engine doesn't support WASM exception handling. Please use a modern version. See also https://aka.ms/dotnet-wasm-features");
}
}
async function mono_wasm_before_memory_snapshot() {
const mark = startMeasure();
if (runtimeHelpers.loadedMemorySnapshotSize) {
// get the bytes after we re-sized the memory, so that we don't have too much memory in use at the same time
const memoryBytes = await getMemorySnapshot();
const heapU8 = localHeapViewU8();
mono_assert(memoryBytes!.byteLength === heapU8.byteLength, "Loaded memory is not the expected size");
heapU8.set(new Uint8Array(memoryBytes!), 0);
mono_log_debug("Loaded WASM linear memory from browser cache");
// all things below are loaded from the snapshot
return;
}
for (const k in runtimeHelpers.config.environmentVariables) {
const v = runtimeHelpers.config.environmentVariables![k];
if (typeof (v) === "string")
mono_wasm_setenv(k, v);
else
throw new Error(`Expected environment variable '${k}' to be a string but it was ${typeof v}: '${v}'`);
}
if (runtimeHelpers.config.runtimeOptions)
mono_wasm_set_runtime_options(runtimeHelpers.config.runtimeOptions);
if (runtimeHelpers.config.aotProfilerOptions)
mono_wasm_init_aot_profiler(runtimeHelpers.config.aotProfilerOptions);
if (runtimeHelpers.config.browserProfilerOptions)
mono_wasm_init_browser_profiler(runtimeHelpers.config.browserProfilerOptions);
mono_wasm_load_runtime("unused", runtimeHelpers.config.debugLevel);
// we didn't have snapshot yet and the feature is enabled. Take snapshot now.
if (runtimeHelpers.config.startupMemoryCache) {
await storeMemorySnapshot(localHeapViewU8().buffer);
runtimeHelpers.storeMemorySnapshotPending = false;
}
if (runtimeHelpers.config.interpreterPgo)
setTimeout(maybeSaveInterpPgoTable, (runtimeHelpers.config.interpreterPgoSaveDelay || 15) * 1000);
endMeasure(mark, MeasuredBlock.memorySnapshot);
}
async function maybeSaveInterpPgoTable() {
// If the application exited abnormally, don't save the table. It probably doesn't contain useful data,
// and saving would overwrite any existing table from a previous successful run.
// We treat exiting with a code of 0 as equivalent to if the app is still running - it's perfectly fine
// to save the table once main has returned, since the table can still make future runs faster.
if ((loaderHelpers.exitCode !== undefined) && (loaderHelpers.exitCode !== 0))
return;
await interp_pgo_save_data();
}
export function mono_wasm_load_runtime(unused?: string, debugLevel?: number): void {
mono_log_debug("mono_wasm_load_runtime");
try {
const mark = startMeasure();
if (debugLevel == undefined) {
debugLevel = 0;
if (runtimeHelpers.config.debugLevel) {
debugLevel = 0 + debugLevel;
}
}
cwraps.mono_wasm_load_runtime(unused || "unused", debugLevel);
endMeasure(mark, MeasuredBlock.loadRuntime);
} catch (err: any) {
mono_log_error("mono_wasm_load_runtime () failed", err);
loaderHelpers.mono_exit(1, err);
throw err;
}
}
export function bindings_init(): void {
if (runtimeHelpers.mono_wasm_bindings_is_ready) {
return;
}
mono_log_debug("bindings_init");
runtimeHelpers.mono_wasm_bindings_is_ready = true;
try {
const mark = startMeasure();
strings_init();
init_managed_exports();
if (WasmEnableLegacyJsInterop && !linkerDisableLegacyJsInterop && !ENVIRONMENT_IS_PTHREAD) {
init_legacy_exports();
}
initialize_marshalers_to_js();
initialize_marshalers_to_cs();
runtimeHelpers._i52_error_scratch_buffer = <any>Module._malloc(4);
endMeasure(mark, MeasuredBlock.bindingsInit);
} catch (err) {
mono_log_error("Error in bindings_init", err);
throw err;
}
}
export function mono_wasm_asm_loaded(assembly_name: CharPtr, assembly_ptr: number, assembly_len: number, pdb_ptr: number, pdb_len: number): void {
// Only trigger this codepath for assemblies loaded after app is ready
if (runtimeHelpers.mono_wasm_runtime_is_ready !== true)
return;
const heapU8 = localHeapViewU8();
const assembly_name_str = assembly_name !== CharPtrNull ? utf8ToString(assembly_name).concat(".dll") : "";
const assembly_data = new Uint8Array(heapU8.buffer, assembly_ptr, assembly_len);
const assembly_b64 = toBase64StringImpl(assembly_data);
let pdb_b64;
if (pdb_ptr) {
const pdb_data = new Uint8Array(heapU8.buffer, pdb_ptr, pdb_len);
pdb_b64 = toBase64StringImpl(pdb_data);
}
mono_wasm_raise_debug_event({
eventName: "AssemblyLoaded",
assembly_name: assembly_name_str,
assembly_b64,
pdb_b64
});
}
export function mono_wasm_set_main_args(name: string, allRuntimeArguments: string[]): void {
const main_argc = allRuntimeArguments.length + 1;
const main_argv = <any>Module._malloc(main_argc * 4);
let aindex = 0;
Module.setValue(main_argv + (aindex * 4), cwraps.mono_wasm_strdup(name), "i32");
aindex += 1;
for (let i = 0; i < allRuntimeArguments.length; ++i) {
Module.setValue(main_argv + (aindex * 4), cwraps.mono_wasm_strdup(allRuntimeArguments[i]), "i32");
aindex += 1;
}
cwraps.mono_wasm_set_main_args(main_argc, main_argv);
}
/// Called when dotnet.worker.js receives an emscripten "load" event from the main thread.
/// This method is comparable to configure_emscripten_startup function
///
/// Notes:
/// 1. Emscripten skips a lot of initialization on the pthread workers, Module may not have everything you expect.
/// 2. Emscripten does not run any event but preInit in the workers.
/// 3. At the point when this executes there is no pthread assigned to the worker yet.
export async function configureWorkerStartup(module: DotnetModuleInternal): Promise<void> {
initWorkerThreadEvents();
currentWorkerThreadEvents.addEventListener(dotnetPthreadCreated, (ev) => {
mono_log_debug("pthread created 0x" + ev.pthread_self.pthreadId.toString(16));
});
// these are the only events which are called on worker
module.preInit = [() => preInitWorkerAsync()];
module.instantiateWasm = instantiateWasmWorker;
await runtimeHelpers.afterPreInit.promise;
}