From 021af3fec44325fdf98043b497132fbcc8864791 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 10 Jul 2023 13:58:20 +0200 Subject: [PATCH 01/19] wip --- src/libraries/tests.proj | 1 + .../sample/wasm/browser-shutdown/Makefile | 11 +++ .../sample/wasm/browser-shutdown/Program.cs | 51 +++++++++++++ .../Wasm.Browser.Shutdown.Sample.csproj | 6 ++ .../sample/wasm/browser-shutdown/index.html | 24 ++++++ src/mono/sample/wasm/browser-shutdown/main.js | 76 +++++++++++++++++++ .../wasm/browser-threads-minimal/Program.cs | 2 +- .../wasm/browser-threads-minimal/main.js | 12 ++- src/mono/wasm/runtime/assets.ts | 2 +- src/mono/wasm/runtime/cancelable-promise.ts | 2 +- src/mono/wasm/runtime/cwraps.ts | 5 +- .../runtime/diagnostics/browser/controller.ts | 2 +- src/mono/wasm/runtime/diagnostics/index.ts | 2 +- .../runtime/diagnostics/mock/environment.ts | 2 +- .../diagnostics/server_pthread/index.ts | 2 +- .../server_pthread/ipc-protocol/serializer.ts | 1 + .../server_pthread/streaming-session.ts | 1 + src/mono/wasm/runtime/dotnet.d.ts | 3 + src/mono/wasm/runtime/driver.c | 8 ++ src/mono/wasm/runtime/gc-handles.ts | 2 +- src/mono/wasm/runtime/globals.ts | 17 +++++ src/mono/wasm/runtime/http.ts | 2 +- src/mono/wasm/runtime/invoke-cs.ts | 7 +- src/mono/wasm/runtime/invoke-js.ts | 15 ++-- .../wasm/runtime/jiterpreter-interp-entry.ts | 2 +- src/mono/wasm/runtime/jiterpreter-jit-call.ts | 2 +- src/mono/wasm/runtime/jiterpreter-support.ts | 4 +- .../runtime/jiterpreter-trace-generator.ts | 17 +++-- src/mono/wasm/runtime/jiterpreter.ts | 4 +- src/mono/wasm/runtime/loader/assets.ts | 3 +- src/mono/wasm/runtime/loader/exit.ts | 57 +++++++++++++- src/mono/wasm/runtime/loader/globals.ts | 23 +++++- .../wasm/runtime/loader/promise-controller.ts | 1 + src/mono/wasm/runtime/loader/run.ts | 19 ++++- src/mono/wasm/runtime/managed-exports.ts | 8 +- src/mono/wasm/runtime/marshal-to-cs.ts | 40 +++++----- src/mono/wasm/runtime/marshal-to-js.ts | 2 +- src/mono/wasm/runtime/marshal.ts | 24 +++--- src/mono/wasm/runtime/memory.ts | 18 ++--- .../runtime/net6-legacy/method-binding.ts | 2 +- .../wasm/runtime/net6-legacy/method-calls.ts | 3 +- src/mono/wasm/runtime/net6-legacy/strings.ts | 1 + src/mono/wasm/runtime/package-lock.json | 26 +++---- src/mono/wasm/runtime/profiler.ts | 2 +- .../wasm/runtime/pthreads/browser/index.ts | 2 +- .../shared/emscripten-replacements.ts | 2 +- .../wasm/runtime/pthreads/shared/index.ts | 14 ++-- .../wasm/runtime/pthreads/worker/index.ts | 5 +- src/mono/wasm/runtime/rollup.config.js | 19 ++++- src/mono/wasm/runtime/run.ts | 1 + src/mono/wasm/runtime/scheduling.ts | 11 +++ src/mono/wasm/runtime/snapshot.ts | 1 + src/mono/wasm/runtime/startup.ts | 25 +++++- src/mono/wasm/runtime/types/consts.d.ts | 3 +- src/mono/wasm/runtime/types/emscripten.ts | 1 + src/mono/wasm/runtime/types/internal.ts | 9 ++- src/mono/wasm/runtime/web-socket.ts | 2 +- src/mono/wasm/test-main.js | 1 + src/mono/wasm/wasm.proj | 1 + 59 files changed, 487 insertions(+), 124 deletions(-) create mode 100644 src/mono/sample/wasm/browser-shutdown/Makefile create mode 100644 src/mono/sample/wasm/browser-shutdown/Program.cs create mode 100644 src/mono/sample/wasm/browser-shutdown/Wasm.Browser.Shutdown.Sample.csproj create mode 100644 src/mono/sample/wasm/browser-shutdown/index.html create mode 100644 src/mono/sample/wasm/browser-shutdown/main.js diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 87089dcc04631..f6d66341d76ba 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -43,6 +43,7 @@ + diff --git a/src/mono/sample/wasm/browser-shutdown/Makefile b/src/mono/sample/wasm/browser-shutdown/Makefile new file mode 100644 index 0000000000000..5878e499433ae --- /dev/null +++ b/src/mono/sample/wasm/browser-shutdown/Makefile @@ -0,0 +1,11 @@ +TOP=../../../../.. + +include ../wasm.mk + +ifneq ($(AOT),) +override MSBUILD_ARGS+=/p:RunAOTCompilation=true +endif + +PROJECT_NAME=Wasm.Browser.Shutdown.Sample.csproj + +run: run-browser diff --git a/src/mono/sample/wasm/browser-shutdown/Program.cs b/src/mono/sample/wasm/browser-shutdown/Program.cs new file mode 100644 index 0000000000000..06c529c9fdc7e --- /dev/null +++ b/src/mono/sample/wasm/browser-shutdown/Program.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices.JavaScript; +using System.Runtime.InteropServices; + +namespace Sample +{ + public partial class Test + { + public static int Main(string[] args) + { + return 0; + } + + [JSExport()] + public static void DoNothing () + { + Console.WriteLine("You got it, boss! Doing nothing!"); + } + + [JSExport()] + public static void ThrowManagedException () + { + throw new Exception("I'll make an exception to the rules just this once... and throw one."); + } + + [JSExport()] + public static void CallFailFast () + { + System.Environment.FailFast("User requested FailFast"); + } + + [JSImport("timerTick", "main.js")] + public static partial void TimerTick (int i); + + [JSExport()] + public static void StartTimer () + { + int i = 0; + var timer = new System.Timers.Timer(1000); + timer.Elapsed += (s, e) => { + TimerTick(i); + i += 1; + }; + timer.AutoReset = true; + timer.Enabled = true; + } + } +} diff --git a/src/mono/sample/wasm/browser-shutdown/Wasm.Browser.Shutdown.Sample.csproj b/src/mono/sample/wasm/browser-shutdown/Wasm.Browser.Shutdown.Sample.csproj new file mode 100644 index 0000000000000..365ba7276cb0b --- /dev/null +++ b/src/mono/sample/wasm/browser-shutdown/Wasm.Browser.Shutdown.Sample.csproj @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/mono/sample/wasm/browser-shutdown/index.html b/src/mono/sample/wasm/browser-shutdown/index.html new file mode 100644 index 0000000000000..4ec0a60a03a7b --- /dev/null +++ b/src/mono/sample/wasm/browser-shutdown/index.html @@ -0,0 +1,24 @@ + + + + + + + Wasm Browser Shutdown Sample + + + + + + + + + + + + +
+ Timer Value: + + + diff --git a/src/mono/sample/wasm/browser-shutdown/main.js b/src/mono/sample/wasm/browser-shutdown/main.js new file mode 100644 index 0000000000000..4e68ca70fd429 --- /dev/null +++ b/src/mono/sample/wasm/browser-shutdown/main.js @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { dotnet, exit } from './_framework/dotnet.js' + +let exports = undefined, + setenv = undefined; + +window.addEventListener("load", onLoad); + +try { + const { setModuleImports, getAssemblyExports, setEnvironmentVariable, getConfig } = await dotnet + .withElementOnExit() + .create(); + + setModuleImports("main.js", { + timerTick: (i) => { + document.querySelector("#timer-value").textContent = i; + }, + }); + + setenv = setEnvironmentVariable; + const config = getConfig(); + exports = await getAssemblyExports(config.mainAssemblyName); +} +catch (err) { + exit(2, err); +} + +function onLoad() { + document.querySelector("#throw-managed-exc").addEventListener("click", () => { + try { + exports.Sample.Test.ThrowManagedException(); + alert("No JS exception was thrown!"); + } catch (exc) { + alert(exc); + } + }); + document.querySelector("#trigger-failfast").addEventListener("click", () => { + try { + exports.Sample.Test.CallFailFast(); + alert("No JS exception was thrown!"); + } catch (exc) { + alert(exc); + } + }); + document.querySelector("#start-timer").addEventListener("click", () => { + try { + exports.Sample.Test.StartTimer(); + } catch (exc) { + alert(exc); + } + }); + document.querySelector("#trigger-native-assert").addEventListener("click", () => { + try { + setenv(null, null); + alert("No JS exception was thrown!"); + } catch (exc) { + alert(exc); + } + }); + document.querySelector("#call-jsexport").addEventListener("click", () => { + try { + exports.Sample.Test.DoNothing(); + } catch (exc) { + alert(exc); + } + }); + document.querySelector("#call-exit").addEventListener("click", () => { + try { + exit(7, "User clicked exit"); + } catch (exc) { + alert(exc); + } + }); +} diff --git a/src/mono/sample/wasm/browser-threads-minimal/Program.cs b/src/mono/sample/wasm/browser-threads-minimal/Program.cs index c6a48fa511bf3..61cf37708ff83 100644 --- a/src/mono/sample/wasm/browser-threads-minimal/Program.cs +++ b/src/mono/sample/wasm/browser-threads-minimal/Program.cs @@ -277,7 +277,7 @@ public static async Task FetchBackground(string url) Console.WriteLine($"smoke: FetchBackground 2 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); var x = JSHost.ImportAsync(fetchhelper, "../fetchhelper.js"); Console.WriteLine($"smoke: FetchBackground 3A ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); - // using var import = await x.ConfigureAwait(false); + using var import = await x; Console.WriteLine($"smoke: FetchBackground 3B ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); var r = await GlobalThisFetch(url); Console.WriteLine($"smoke: FetchBackground 4 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}"); diff --git a/src/mono/sample/wasm/browser-threads-minimal/main.js b/src/mono/sample/wasm/browser-threads-minimal/main.js index 8ff5ab2d35ccd..73ed5eadd4ad8 100644 --- a/src/mono/sample/wasm/browser-threads-minimal/main.js +++ b/src/mono/sample/wasm/browser-threads-minimal/main.js @@ -7,8 +7,6 @@ const assemblyName = "Wasm.Browser.Threads.Minimal.Sample.dll"; try { - const resolveUrl = (relativeUrl) => (new URL(relativeUrl, window.location.href)).toString() - const { getAssemblyExports, runMain } = await dotnet //.withEnvironmentVariable("MONO_LOG_LEVEL", "debug") //.withDiagnosticTracing(true) @@ -33,6 +31,7 @@ try { await exports.Sample.Test.DisposeTest(); console.log("smoke: DisposeTest done "); + /* FIXME console.log("smoke: running TestHelloWebWorker"); await exports.Sample.Test.TestHelloWebWorker(); await exports.Sample.Test.TestHelloWebWorker(); @@ -43,11 +42,13 @@ try { await exports.Sample.Test.TestHelloWebWorker(); await exports.Sample.Test.TestHelloWebWorker(); console.log("smoke: TestHelloWebWorker done"); + */ console.log("smoke: running TestCanStartThread"); await exports.Sample.Test.TestCanStartThread(); console.log("smoke: TestCanStartThread done"); + /* FIXME console.log("smoke: running TestTLS"); await exports.Sample.Test.TestTLS(); console.log("smoke: TestTLS done"); @@ -58,14 +59,17 @@ try { console.log("smoke: running TestCallSetTimeoutOnWorker"); await exports.Sample.Test.TestCallSetTimeoutOnWorker(); console.log("smoke: TestCallSetTimeoutOnWorker done"); + */ console.log("smoke: running HttpClientMain(blurst.txt)"); let t = await exports.Sample.Test.HttpClientMain(globalThis.document.baseURI + "blurst.txt"); console.log("smoke: HttpClientMain(blurst.txt) done " + t); + /* FIXME console.log("smoke: running HttpClientWorker(blurst.txt)"); let t2 = await exports.Sample.Test.HttpClientWorker(globalThis.document.baseURI + "blurst.txt"); console.log("smoke: HttpClientWorker(blurst.txt) done " + t2); + */ console.log("smoke: running HttpClientPool(blurst.txt)"); let t3 = await exports.Sample.Test.HttpClientPool(globalThis.document.baseURI + "blurst.txt"); @@ -107,8 +111,10 @@ try { } console.log("smoke: TaskRunCompute done"); + /* FIXME console.log("smoke: running StartAllocatorFromWorker"); exports.Sample.Test.StartAllocatorFromWorker(); + */ await delay(5000); @@ -120,8 +126,10 @@ try { console.log("smoke: running GCCollect"); exports.Sample.Test.GCCollect(); + /* FIXME console.log("smoke: running StopTimerFromWorker"); exports.Sample.Test.StopTimerFromWorker(); + */ let exit_code = await runMain(assemblyName, []); exit(exit_code); diff --git a/src/mono/wasm/runtime/assets.ts b/src/mono/wasm/runtime/assets.ts index 8eacc484dde5f..afccb3c91b9a1 100644 --- a/src/mono/wasm/runtime/assets.ts +++ b/src/mono/wasm/runtime/assets.ts @@ -3,7 +3,7 @@ import cwraps from "./cwraps"; import { mono_wasm_load_icu_data } from "./icu"; -import { ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, Module, loaderHelpers, runtimeHelpers } from "./globals"; +import { ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { mono_log_info, mono_log_debug, mono_log_warn, parseSymbolMapFile } from "./logging"; import { mono_wasm_load_bytes_into_heap } from "./memory"; import { endMeasure, MeasuredBlock, startMeasure } from "./profiler"; diff --git a/src/mono/wasm/runtime/cancelable-promise.ts b/src/mono/wasm/runtime/cancelable-promise.ts index c15b2e87ee53e..3b59aefe1c266 100644 --- a/src/mono/wasm/runtime/cancelable-promise.ts +++ b/src/mono/wasm/runtime/cancelable-promise.ts @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import { _lookup_js_owned_object } from "./gc-handles"; -import { createPromiseController, loaderHelpers } from "./globals"; +import { createPromiseController, loaderHelpers, mono_assert } from "./globals"; import { TaskCallbackHolder } from "./marshal-to-cs"; import { ControllablePromise, GCHandle } from "./types/internal"; diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts index 1e6dcf1daa503..56317d5f3da2a 100644 --- a/src/mono/wasm/runtime/cwraps.ts +++ b/src/mono/wasm/runtime/cwraps.ts @@ -12,6 +12,7 @@ import type { import type { VoidPtr, CharPtrPtr, Int32Ptr, CharPtr, ManagedPointer } from "./types/emscripten"; import { linkerDisableLegacyJsInterop, linkerEnableAotProfiler, linkerEnableBrowserProfiler, Module } from "./globals"; import { mono_log_error } from "./logging"; +import { mono_assert } from "./globals"; type SigLine = [lazyOrSkip: boolean | (() => boolean), name: string, returnType: string | null, argTypes?: string[], opts?: any]; @@ -76,6 +77,7 @@ const fn_signatures: SigLine[] = [ //INTERNAL [false, "mono_wasm_exit", "void", ["number"]], + [false, "mono_wasm_abort", "void", []], [true, "mono_wasm_getenv", "number", ["string"]], [true, "mono_wasm_set_main_args", "void", ["number", "number"]], [false, "mono_wasm_enable_on_demand_gc", "void", ["number"]], @@ -205,7 +207,8 @@ export interface t_Cwraps { mono_wasm_intern_string_ref(strRef: MonoStringRef): void; //INTERNAL - mono_wasm_exit(exit_code: number): number; + mono_wasm_exit(exit_code: number): void; + mono_wasm_abort(): void; mono_wasm_getenv(name: string): CharPtr; mono_wasm_enable_on_demand_gc(enable: number): void; mono_wasm_set_main_args(argc: number, argv: VoidPtr): void; diff --git a/src/mono/wasm/runtime/diagnostics/browser/controller.ts b/src/mono/wasm/runtime/diagnostics/browser/controller.ts index 91ea014ff8994..d891710b8763b 100644 --- a/src/mono/wasm/runtime/diagnostics/browser/controller.ts +++ b/src/mono/wasm/runtime/diagnostics/browser/controller.ts @@ -4,7 +4,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { diagnostics_c_functions as cwraps } from "../../cwraps"; -import { INTERNAL } from "../../globals"; +import { INTERNAL, mono_assert } from "../../globals"; import { mono_log_info, mono_log_debug, mono_log_warn } from "../../logging"; import { withStackAlloc, getI32 } from "../../memory"; import { Thread, waitForThread } from "../../pthreads/browser"; diff --git a/src/mono/wasm/runtime/diagnostics/index.ts b/src/mono/wasm/runtime/diagnostics/index.ts index 2b973f2f11aaa..1d93725a640b8 100644 --- a/src/mono/wasm/runtime/diagnostics/index.ts +++ b/src/mono/wasm/runtime/diagnostics/index.ts @@ -11,7 +11,7 @@ import type { VoidPtr } from "../types/emscripten"; import { getController, startDiagnosticServer } from "./browser/controller"; import * as memory from "../memory"; import { mono_log_warn } from "../logging"; -import { runtimeHelpers } from "../globals"; +import { mono_assert, runtimeHelpers } from "../globals"; // called from C on the main thread diff --git a/src/mono/wasm/runtime/diagnostics/mock/environment.ts b/src/mono/wasm/runtime/diagnostics/mock/environment.ts index 17dd2b3cbd9d2..bcbdf390a4dfe 100644 --- a/src/mono/wasm/runtime/diagnostics/mock/environment.ts +++ b/src/mono/wasm/runtime/diagnostics/mock/environment.ts @@ -7,7 +7,7 @@ import Serializer from "../server_pthread/ipc-protocol/base-serializer"; import { CommandSetId, EventPipeCommandId, ProcessCommandId } from "../server_pthread/ipc-protocol/types"; import { assertNever } from "../../types/internal"; import { pthread_self } from "../../pthreads/worker"; -import { createPromiseController } from "../../globals"; +import { createPromiseController, mono_assert } from "../../globals"; function expectAdvertise(data: ArrayBuffer): boolean { diff --git a/src/mono/wasm/runtime/diagnostics/server_pthread/index.ts b/src/mono/wasm/runtime/diagnostics/server_pthread/index.ts index 8bc87ea37633b..4023df9e5134e 100644 --- a/src/mono/wasm/runtime/diagnostics/server_pthread/index.ts +++ b/src/mono/wasm/runtime/diagnostics/server_pthread/index.ts @@ -7,7 +7,7 @@ import monoDiagnosticsMock from "consts:monoDiagnosticsMock"; import { PromiseAndController, assertNever } from "../../types/internal"; import { pthread_self } from "../../pthreads/worker"; -import { createPromiseController } from "../../globals"; +import { createPromiseController, mono_assert } from "../../globals"; import { diagnostics_c_functions as cwraps } from "../../cwraps"; import { EventPipeSessionIDImpl } from "../shared/types"; import { CharPtr } from "../../types/emscripten"; diff --git a/src/mono/wasm/runtime/diagnostics/server_pthread/ipc-protocol/serializer.ts b/src/mono/wasm/runtime/diagnostics/server_pthread/ipc-protocol/serializer.ts index 0a4af3544a66c..6b67488c5fdac 100644 --- a/src/mono/wasm/runtime/diagnostics/server_pthread/ipc-protocol/serializer.ts +++ b/src/mono/wasm/runtime/diagnostics/server_pthread/ipc-protocol/serializer.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 { mono_assert } from "../../../globals"; import Serializer from "./base-serializer"; import { CommandSetId, ServerCommandId } from "./types"; diff --git a/src/mono/wasm/runtime/diagnostics/server_pthread/streaming-session.ts b/src/mono/wasm/runtime/diagnostics/server_pthread/streaming-session.ts index 88fbc7d3913cd..b8830ae488b84 100644 --- a/src/mono/wasm/runtime/diagnostics/server_pthread/streaming-session.ts +++ b/src/mono/wasm/runtime/diagnostics/server_pthread/streaming-session.ts @@ -13,6 +13,7 @@ import { EventPipeCollectTracingCommandProvider, } from "./protocol-client-commands"; import { createEventPipeStreamingSession } from "../shared/create-session"; +import { mono_assert } from "../../globals"; /// The streaming session holds all the pieces of an event pipe streaming session that the /// diagnostic server knows about: the session ID, a diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts index b715912b04ef3..97dd65088a633 100644 --- a/src/mono/wasm/runtime/dotnet.d.ts +++ b/src/mono/wasm/runtime/dotnet.d.ts @@ -63,6 +63,9 @@ declare interface EmscriptenModule { onAbort?: { (error: any): void; }; + onExit?: { + (code: number): void; + }; } type InstantiateWasmSuccessCallback = (instance: WebAssembly.Instance, module: WebAssembly.Module | undefined) => void; type InstantiateWasmCallBack = (imports: WebAssembly.Imports, successCallback: InstantiateWasmSuccessCallback) => any; diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c index 551d1b4ca8709..c4e0679579c4a 100644 --- a/src/mono/wasm/runtime/driver.c +++ b/src/mono/wasm/runtime/driver.c @@ -249,6 +249,8 @@ mono_wasm_add_satellite_assembly (const char *name, const char *culture, const u EMSCRIPTEN_KEEPALIVE void mono_wasm_setenv (const char *name, const char *value) { + assert (name); + assert (value); monoeg_g_setenv (strdup (name), strdup (value), 1); } @@ -1187,6 +1189,12 @@ mono_wasm_exit (int exit_code) exit (exit_code); } +EMSCRIPTEN_KEEPALIVE int +mono_wasm_abort () +{ + abort (); +} + EMSCRIPTEN_KEEPALIVE void mono_wasm_set_main_args (int argc, char* argv[]) { diff --git a/src/mono/wasm/runtime/gc-handles.ts b/src/mono/wasm/runtime/gc-handles.ts index b5b6382ab27ad..b62ead0594ce9 100644 --- a/src/mono/wasm/runtime/gc-handles.ts +++ b/src/mono/wasm/runtime/gc-handles.ts @@ -109,7 +109,7 @@ export function teardown_managed_proxy(result: any, gc_handle: GCHandle): void { export function assert_not_disposed(result: any): GCHandle { const gc_handle = result[js_owned_gc_handle_symbol]; - mono_assert(gc_handle != GCHandleNull, "ObjectDisposedException"); + mono_check(gc_handle != GCHandleNull, "ObjectDisposedException"); return gc_handle; } diff --git a/src/mono/wasm/runtime/globals.ts b/src/mono/wasm/runtime/globals.ts index 22fef7474b2ae..d42ba56bf8a79 100644 --- a/src/mono/wasm/runtime/globals.ts +++ b/src/mono/wasm/runtime/globals.ts @@ -5,6 +5,7 @@ /// /// +import { mono_log_error } from "./logging"; import { RuntimeAPI } from "./types/index"; import type { GlobalObjects, EmscriptenInternals, RuntimeHelpers, LoaderHelpers, DotnetModuleInternal, PromiseAndController } from "./types/internal"; @@ -72,4 +73,20 @@ export function setRuntimeGlobals(globalObjects: GlobalObjects) { export function createPromiseController(afterResolve?: () => void, afterReject?: () => void): PromiseAndController { return loaderHelpers.createPromiseController(afterResolve, afterReject); +} + +// this will abort the program if the condition is false +// see src\mono\wasm\runtime\rollup.config.js +// we inline the condition, because the lambda could allocate closure on hot path otherwise +export function mono_assert(condition: unknown, messageFactory: string | (() => string)): asserts condition { + if (condition) return; + const message = "Assert failed: " + (typeof messageFactory === "function" + ? messageFactory() + : messageFactory); + const abort = runtimeHelpers.mono_wasm_abort; + if (abort) { + mono_log_error(message); + abort(); + } + throw new Error(message); } \ No newline at end of file diff --git a/src/mono/wasm/runtime/http.ts b/src/mono/wasm/runtime/http.ts index 9034494a4615c..08df91631a14e 100644 --- a/src/mono/wasm/runtime/http.ts +++ b/src/mono/wasm/runtime/http.ts @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import { wrap_as_cancelable_promise } from "./cancelable-promise"; -import { Module, loaderHelpers } from "./globals"; +import { Module, loaderHelpers, mono_assert } from "./globals"; import { MemoryViewType, Span } from "./marshal"; import type { VoidPtr } from "./types/emscripten"; diff --git a/src/mono/wasm/runtime/invoke-cs.ts b/src/mono/wasm/runtime/invoke-cs.ts index 259867305fe23..a7a8bd10d897a 100644 --- a/src/mono/wasm/runtime/invoke-cs.ts +++ b/src/mono/wasm/runtime/invoke-cs.ts @@ -4,7 +4,7 @@ import BuildConfiguration from "consts:configuration"; import MonoWasmThreads from "consts:monoWasmThreads"; -import { Module, runtimeHelpers } from "./globals"; +import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { bind_arg_marshal_to_cs } from "./marshal-to-cs"; import { marshal_exception_to_js, bind_arg_marshal_to_js } from "./marshal-to-js"; import { @@ -127,6 +127,7 @@ function bind_fn_0V(closure: BindingClosure) { (closure) = null; return function bound_fn_0V() { const mark = startMeasure(); + loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { const args = alloc_stack_frame(2); @@ -146,6 +147,7 @@ function bind_fn_1V(closure: BindingClosure) { (closure) = null; return function bound_fn_1V(arg1: any) { const mark = startMeasure(); + loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { const args = alloc_stack_frame(3); @@ -168,6 +170,7 @@ function bind_fn_1R(closure: BindingClosure) { (closure) = null; return function bound_fn_1R(arg1: any) { const mark = startMeasure(); + loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { const args = alloc_stack_frame(3); @@ -194,6 +197,7 @@ function bind_fn_2R(closure: BindingClosure) { (closure) = null; return function bound_fn_2R(arg1: any, arg2: any) { const mark = startMeasure(); + loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { const args = alloc_stack_frame(4); @@ -221,6 +225,7 @@ function bind_fn(closure: BindingClosure) { (closure) = null; return function bound_fn(...js_args: any[]) { const mark = startMeasure(); + loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { const args = alloc_stack_frame(2 + args_count); diff --git a/src/mono/wasm/runtime/invoke-js.ts b/src/mono/wasm/runtime/invoke-js.ts index 98ca18a31d8fb..abf5f078e8ce4 100644 --- a/src/mono/wasm/runtime/invoke-js.ts +++ b/src/mono/wasm/runtime/invoke-js.ts @@ -10,7 +10,7 @@ import { setI32, setI32_unchecked, receiveWorkerHeapViews } from "./memory"; import { monoStringToString, stringToMonoStringRoot } from "./strings"; import { MonoObject, MonoObjectRef, MonoString, MonoStringRef, JSFunctionSignature, JSMarshalerArguments, WasmRoot, BoundMarshalerToJs, JSFnHandle, BoundMarshalerToCs, JSHandle, MarshalerType } from "./types/internal"; import { Int32Ptr } from "./types/emscripten"; -import { INTERNAL, Module, runtimeHelpers } from "./globals"; +import { INTERNAL, Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { bind_arg_marshal_to_js } from "./marshal-to-js"; import { mono_wasm_new_external_root } from "./roots"; import { mono_log_debug, mono_wasm_symbolicate_string } from "./logging"; @@ -309,22 +309,22 @@ function mono_wasm_lookup_function(function_name: string, js_module_name: string } export function set_property(self: any, name: string, value: any): void { - mono_assert(self, "Null reference"); + mono_check(self, "Null reference"); self[name] = value; } export function get_property(self: any, name: string): any { - mono_assert(self, "Null reference"); + mono_check(self, "Null reference"); return self[name]; } export function has_property(self: any, name: string): boolean { - mono_assert(self, "Null reference"); + mono_check(self, "Null reference"); return name in self; } export function get_typeof_property(self: any, name: string): string { - mono_assert(self, "Null reference"); + mono_check(self, "Null reference"); return typeof self[name]; } @@ -336,8 +336,8 @@ export const importedModulesPromises: Map> = new Map(); export const importedModules: Map> = new Map(); export function dynamic_import(module_name: string, module_url: string): Promise { - mono_assert(module_name, "Invalid module_name"); - mono_assert(module_url, "Invalid module_name"); + mono_assert(typeof module_name === "string", "module_name must be string"); + mono_assert(typeof module_url === "string", "module_url must be string"); assert_synchronization_context(); let promise = importedModulesPromises.get(module_name); const newPromise = !promise; @@ -397,6 +397,7 @@ export function wrap_no_error_root(is_exception: Int32Ptr | null, result?: WasmR } export function assert_bindings(): void { + loaderHelpers.assert_runtime_running(); if (MonoWasmThreads) { mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready, "Please use dedicated worker for working with JavaScript interop. See https://github.com/dotnet/runtime/blob/main/src/mono/wasm/threads.md#JS-interop-on-dedicated-threads"); } else { diff --git a/src/mono/wasm/runtime/jiterpreter-interp-entry.ts b/src/mono/wasm/runtime/jiterpreter-interp-entry.ts index f15bb676d8d11..57d1e041aaba2 100644 --- a/src/mono/wasm/runtime/jiterpreter-interp-entry.ts +++ b/src/mono/wasm/runtime/jiterpreter-interp-entry.ts @@ -3,7 +3,7 @@ import { MonoMethod, MonoType } from "./types/internal"; import { NativePointer } from "./types/emscripten"; -import { Module } from "./globals"; +import { Module, mono_assert } from "./globals"; import { setI32, getU32_unaligned, _zero_region } from "./memory"; diff --git a/src/mono/wasm/runtime/jiterpreter-jit-call.ts b/src/mono/wasm/runtime/jiterpreter-jit-call.ts index 8c98c06577669..1cb2927aae677 100644 --- a/src/mono/wasm/runtime/jiterpreter-jit-call.ts +++ b/src/mono/wasm/runtime/jiterpreter-jit-call.ts @@ -3,7 +3,7 @@ import { MonoType, MonoMethod } from "./types/internal"; import { NativePointer, Int32Ptr, VoidPtr } from "./types/emscripten"; -import { Module, runtimeHelpers } from "./globals"; +import { Module, mono_assert, runtimeHelpers } from "./globals"; import { getU8, getI32_unaligned, getU32_unaligned, setU32_unchecked, receiveWorkerHeapViews } from "./memory"; diff --git a/src/mono/wasm/runtime/jiterpreter-support.ts b/src/mono/wasm/runtime/jiterpreter-support.ts index 0b8660768a903..ec935f82476db 100644 --- a/src/mono/wasm/runtime/jiterpreter-support.ts +++ b/src/mono/wasm/runtime/jiterpreter-support.ts @@ -3,7 +3,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { NativePointer, ManagedPointer, VoidPtr } from "./types/emscripten"; -import { Module, runtimeHelpers } from "./globals"; +import { Module, mono_assert, runtimeHelpers } from "./globals"; import { WasmOpcode, WasmSimdOpcode } from "./jiterpreter-opcodes"; import { MintOpcode } from "./mintops"; import cwraps from "./cwraps"; @@ -1871,7 +1871,7 @@ export function bytesFromHex(hex: string): Uint8Array { return bytes; } -let observedTaintedZeroPage : boolean | undefined; +let observedTaintedZeroPage: boolean | undefined; export function isZeroPageReserved(): boolean { // FIXME: This check will always return true on worker threads. diff --git a/src/mono/wasm/runtime/jiterpreter-trace-generator.ts b/src/mono/wasm/runtime/jiterpreter-trace-generator.ts index 671190745b1a8..2e9336ed12718 100644 --- a/src/mono/wasm/runtime/jiterpreter-trace-generator.ts +++ b/src/mono/wasm/runtime/jiterpreter-trace-generator.ts @@ -53,6 +53,7 @@ import { simdLoadTable, simdStoreTable, } from "./jiterpreter-tables"; import { mono_log_error, mono_log_info } from "./logging"; +import { mono_assert } from "./globals"; /* struct MonoVTable { @@ -3569,10 +3570,10 @@ function emit_simd_3(builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrins const lane = get_known_constant_value(builder, getArgU16(ip, 3)), laneCount = extractTup[0]; if (typeof (lane) !== "number") { - mono_log_error (`${builder.functions[0].name}: Non-constant lane index passed to ExtractLane`); + mono_log_error(`${builder.functions[0].name}: Non-constant lane index passed to ExtractLane`); return false; } else if ((lane >= laneCount) || (lane < 0)) { - mono_log_error (`${builder.functions[0].name}: ExtractLane index ${lane} out of range (0 - ${laneCount - 1})`); + mono_log_error(`${builder.functions[0].name}: ExtractLane index ${lane} out of range (0 - ${laneCount - 1})`); return false; } @@ -3718,10 +3719,10 @@ function emit_simd_4(builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrins const laneCount = rtup[0], lane = get_known_constant_value(builder, getArgU16(ip, 3)); if (typeof (lane) !== "number") { - mono_log_error (`${builder.functions[0].name}: Non-constant lane index passed to ReplaceLane`); + mono_log_error(`${builder.functions[0].name}: Non-constant lane index passed to ReplaceLane`); return false; } else if ((lane >= laneCount) || (lane < 0)) { - mono_log_error (`${builder.functions[0].name}: ReplaceLane index ${lane} out of range (0 - ${laneCount - 1})`); + mono_log_error(`${builder.functions[0].name}: ReplaceLane index ${lane} out of range (0 - ${laneCount - 1})`); return false; } @@ -3737,10 +3738,10 @@ function emit_simd_4(builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrins const laneCount = stup[0], lane = get_known_constant_value(builder, getArgU16(ip, 4)); if (typeof (lane) !== "number") { - mono_log_error (`${builder.functions[0].name}: Non-constant lane index passed to store method`); + mono_log_error(`${builder.functions[0].name}: Non-constant lane index passed to store method`); return false; } else if ((lane >= laneCount) || (lane < 0)) { - mono_log_error (`${builder.functions[0].name}: Store lane ${lane} out of range (0 - ${laneCount - 1})`); + mono_log_error(`${builder.functions[0].name}: Store lane ${lane} out of range (0 - ${laneCount - 1})`); return false; } append_ldloc(builder, getArgU16(ip, 2), WasmOpcode.i32_load); @@ -3770,13 +3771,13 @@ function emit_simd_4(builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrins case SimdIntrinsic4.ShuffleD1: { const indices = get_known_constant_value(builder, getArgU16(ip, 4)); if (typeof (indices) !== "object") { - mono_log_error (`${builder.functions[0].name}: Non-constant indices passed to PackedSimd.Shuffle`); + mono_log_error(`${builder.functions[0].name}: Non-constant indices passed to PackedSimd.Shuffle`); return false; } for (let i = 0; i < 32; i++) { const lane = indices[i]; if ((lane < 0) || (lane > 31)) { - mono_log_error (`${builder.functions[0].name}: Shuffle lane index #${i} (${lane}) out of range (0 - 31)`); + mono_log_error(`${builder.functions[0].name}: Shuffle lane index #${i} (${lane}) out of range (0 - 31)`); return false; } } diff --git a/src/mono/wasm/runtime/jiterpreter.ts b/src/mono/wasm/runtime/jiterpreter.ts index dfe1d4138a1ab..9e62073bb4b38 100644 --- a/src/mono/wasm/runtime/jiterpreter.ts +++ b/src/mono/wasm/runtime/jiterpreter.ts @@ -3,7 +3,7 @@ import { MonoMethod } from "./types/internal"; import { NativePointer } from "./types/emscripten"; -import { Module, runtimeHelpers } from "./globals"; +import { Module, mono_assert, runtimeHelpers } from "./globals"; import { getU16, getU32_unaligned, localHeapViewU8 } from "./memory"; import { WasmOpcode, getOpcodeName } from "./jiterpreter-opcodes"; import { MintOpcode } from "./mintops"; @@ -778,7 +778,7 @@ function generate_wasm( builder.generateTypeSection(); - const traceLocals : any = { + const traceLocals: any = { "disp": WasmValtype.i32, "cknull_ptr": WasmValtype.i32, "dest_ptr": WasmValtype.i32, diff --git a/src/mono/wasm/runtime/loader/assets.ts b/src/mono/wasm/runtime/loader/assets.ts index da53c3207b2fd..11c353c34ad3e 100644 --- a/src/mono/wasm/runtime/loader/assets.ts +++ b/src/mono/wasm/runtime/loader/assets.ts @@ -3,7 +3,7 @@ import type { AssetEntryInternal, PromiseAndController } from "../types/internal"; import type { AssetBehaviours, AssetEntry, LoadingResource, ResourceRequest } from "../types"; -import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, loaderHelpers, runtimeHelpers } from "./globals"; +import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { createPromiseController } from "./promise-controller"; import { mono_log_debug } from "./logging"; @@ -184,6 +184,7 @@ export async function mono_download_assets(): Promise { }).catch(e => { loaderHelpers.err("Error in mono_download_assets: " + e); loaderHelpers.abort_startup(e, true); + throw e; }); // OPTIMIZATION explained: // we do it this way so that we could allocate memory immediately after asset is downloaded (and after onRuntimeInitialized which happened already) diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index 94a6bf8ba7fd8..0ceca06f252ab 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -1,10 +1,38 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, INTERNAL, loaderHelpers, runtimeHelpers } from "./globals"; -import { mono_log_debug, consoleWebSocket, mono_log_error, mono_log_info_no_prefix } from "./logging"; +import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, INTERNAL, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; +import { mono_log_debug, consoleWebSocket, mono_log_error, mono_log_info_no_prefix, mono_log_warn } from "./logging"; + + +let ABORT = false; +let EXIT = false; +let EXIT_CODE: number | undefined = undefined; + +export function is_exited() { + return EXIT; +} + +export function is_runtime_running() { + return runtimeHelpers.runtimeReady && !EXIT && !ABORT; +} + + +export function assert_runtime_running() { + mono_assert(runtimeHelpers.runtimeReady, "mono runtime didn't start yet"); + mono_assert(!ABORT, "startup of the mono runtime was aborted"); + + const assertAfterExit = !ENVIRONMENT_IS_WEB || // always assert on shell and node + (loaderHelpers.config && loaderHelpers.config.assertAfterExit); // assert on web when enabled + + mono_assert(assertAfterExit || typeof EXIT_CODE == "undefined", () => `mono runtime already exited with ${EXIT_CODE}`); +} + +export function abort_startup(reason: any, should_exit: boolean, should_throw?: boolean): void { + if (ABORT) return; + ABORT = true; + EXIT_CODE = 1; -export function abort_startup(reason: any, should_exit: boolean): void { mono_log_debug("abort_startup"); loaderHelpers.allDownloadsQueued.promise_control.reject(reason); loaderHelpers.afterConfigLoaded.promise_control.reject(reason); @@ -25,11 +53,21 @@ export function abort_startup(reason: any, should_exit: boolean): void { if (should_exit || ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) { mono_exit(1, reason); } - throw reason; + if (should_throw) { + throw reason; + } } } +// this will also call mono_wasm_exit if available, which will call exitJS -> _proc_exit -> terminateAllThreads export function mono_exit(exit_code: number, reason?: any): void { + if (EXIT) { + mono_log_warn("mono_exit called more than once", new Error().stack); + return; + } + EXIT = true; + EXIT_CODE = exit_code; + if (loaderHelpers.config && loaderHelpers.config.asyncFlushOnExit && exit_code === 0) { // this would NOT call Node's exit() immediately, it's a hanging promise (async () => { @@ -98,6 +136,17 @@ function set_exit_code_and_quit_now(exit_code: number, reason?: any): void { } appendElementOnExit(exit_code); + if (!ABORT && !runtimeHelpers.runtimeReady) abort_startup(reason, false, false); + if (runtimeHelpers.mono_wasm_exit) { + try { + runtimeHelpers.mono_wasm_exit(exit_code); + } + catch (expected) { + if (expected && runtimeHelpers.ExitStatus && !(expected instanceof runtimeHelpers.ExitStatus)) { + throw expected; + } + } + } if (exit_code !== 0 || !ENVIRONMENT_IS_WEB) { if (ENVIRONMENT_IS_NODE && INTERNAL.process) { INTERNAL.process.exit(exit_code); diff --git a/src/mono/wasm/runtime/loader/globals.ts b/src/mono/wasm/runtime/loader/globals.ts index 6cebb54eaa428..574a8be33a340 100644 --- a/src/mono/wasm/runtime/loader/globals.ts +++ b/src/mono/wasm/runtime/loader/globals.ts @@ -3,10 +3,10 @@ import type { AssetEntryInternal, GlobalObjects, LoaderHelpers, RuntimeHelpers } from "../types/internal"; import type { MonoConfig, RuntimeAPI } from "../types"; -import { abort_startup, mono_exit } from "./exit"; +import { abort_startup, assert_runtime_running, is_exited, is_runtime_running, mono_exit } from "./exit"; import { assertIsControllablePromise, createPromiseController, getPromiseController } from "./promise-controller"; import { mono_download_assets, resolve_asset_path } from "./assets"; -import { setup_proxy_console } from "./logging"; +import { mono_log_error, setup_proxy_console } from "./logging"; export const ENVIRONMENT_IS_NODE = typeof process == "object" && typeof process.versions == "object" && typeof process.versions.node == "string"; export const ENVIRONMENT_IS_WEB = typeof window == "object"; @@ -76,6 +76,9 @@ export function setLoaderGlobals( runtimeModuleLoaded: createPromiseController(), abort_startup, + is_exited, + is_runtime_running, + assert_runtime_running, mono_exit, createPromiseController, getPromiseController, @@ -86,3 +89,19 @@ export function setLoaderGlobals( } as Partial); } + +// this will abort the program if the condition is false +// see src\mono\wasm\runtime\rollup.config.js +// we inline the condition, because the lambda could allocate closure on hot path otherwise +export function mono_assert(condition: unknown, messageFactory: string | (() => string)): asserts condition { + if (condition) return; + const message = "Assert failed: " + (typeof messageFactory === "function" + ? messageFactory() + : messageFactory); + const abort = globalObjectsRoot.runtimeHelpers.mono_wasm_abort; + if (abort) { + mono_log_error(message); + abort(); + } + throw new Error(message); +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/loader/promise-controller.ts b/src/mono/wasm/runtime/loader/promise-controller.ts index d46719c8b041f..2a0f0a7a4144f 100644 --- a/src/mono/wasm/runtime/loader/promise-controller.ts +++ b/src/mono/wasm/runtime/loader/promise-controller.ts @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import type { ControllablePromise, PromiseAndController, PromiseController } from "../types/internal"; +import { mono_assert } from "./globals"; /// a unique symbol used to mark a promise as controllable export const promise_control_symbol = Symbol.for("wasm promise_control"); diff --git a/src/mono/wasm/runtime/loader/run.ts b/src/mono/wasm/runtime/loader/run.ts index 6d8b480554bb4..0010d7fd82241 100644 --- a/src/mono/wasm/runtime/loader/run.ts +++ b/src/mono/wasm/runtime/loader/run.ts @@ -4,9 +4,9 @@ import type { MonoConfig, DotnetHostBuilder, DotnetModuleConfig, RuntimeAPI, WebAssemblyStartOptions } from "../types"; import type { MonoConfigInternal, EmscriptenModuleInternal, RuntimeModuleExportsInternal, NativeModuleExportsInternal, } from "../types/internal"; -import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, exportedRuntimeAPI, globalObjectsRoot } from "./globals"; +import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, exportedRuntimeAPI, globalObjectsRoot, mono_assert } from "./globals"; import { deep_merge_config, deep_merge_module, mono_wasm_load_config } from "./config"; -import { mono_exit } from "./exit"; +import { is_exited, mono_exit } from "./exit"; import { setup_proxy_console } from "./logging"; import { resolve_asset_path, start_asset_download } from "./assets"; import { detect_features_and_polyfill } from "./polyfills"; @@ -52,7 +52,7 @@ export class HostBuilder implements DotnetHostBuilder { const handler = function fatal_handler(event: Event, error: any) { event.preventDefault(); try { - if (!error || !error.silent) mono_exit(1, error); + if ((!error || !error.silent) && !is_exited()) mono_exit(1, error); } catch (err) { // no not re-throw from the fatal handler } @@ -109,6 +109,19 @@ export class HostBuilder implements DotnetHostBuilder { } } + // internal + withAssertAfterExit(): DotnetHostBuilder { + try { + deep_merge_config(monoConfig, { + assertAfterExit: true + }); + return this; + } catch (err) { + mono_exit(1, err); + throw err; + } + } + // internal // todo fallback later by debugLevel withWaitingForDebugger(level: number): DotnetHostBuilder { diff --git a/src/mono/wasm/runtime/managed-exports.ts b/src/mono/wasm/runtime/managed-exports.ts index 6bd65eb2ec025..a5fbbcea0971b 100644 --- a/src/mono/wasm/runtime/managed-exports.ts +++ b/src/mono/wasm/runtime/managed-exports.ts @@ -5,7 +5,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { GCHandle, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod } from "./types/internal"; import cwraps from "./cwraps"; -import { runtimeHelpers, Module } from "./globals"; +import { runtimeHelpers, Module, loaderHelpers, mono_assert } from "./globals"; import { alloc_stack_frame, get_arg, get_arg_gc_handle, set_arg_type, set_gc_handle } from "./marshal"; import { invoke_method_and_handle_exception } from "./invoke-cs"; import { marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_to_cs } from "./marshal-to-cs"; @@ -39,6 +39,7 @@ export function init_managed_exports(): void { mono_assert(get_managed_stack_trace_method, "Can't find GetManagedStackTrace method"); runtimeHelpers.javaScriptExports.call_entry_point = async (entry_point: MonoMethod, program_args?: string[]): Promise => { + loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { Module.runtimeKeepalivePush(); @@ -64,6 +65,7 @@ export function init_managed_exports(): void { }; runtimeHelpers.javaScriptExports.release_js_owned_object_by_gc_handle = (gc_handle: GCHandle) => { mono_assert(gc_handle, "Must be valid gc_handle"); + loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { const args = alloc_stack_frame(3); @@ -77,6 +79,7 @@ export function init_managed_exports(): void { }; runtimeHelpers.javaScriptExports.create_task_callback = () => { const sp = Module.stackSave(); + loaderHelpers.assert_runtime_running(); try { const args = alloc_stack_frame(2); invoke_method_and_handle_exception(create_task_callback_method, args); @@ -87,6 +90,7 @@ export function init_managed_exports(): void { } }; runtimeHelpers.javaScriptExports.complete_task = (holder_gc_handle: GCHandle, error?: any, data?: any, res_converter?: MarshalerToCs) => { + loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { const args = alloc_stack_frame(5); @@ -108,6 +112,7 @@ export function init_managed_exports(): void { } }; runtimeHelpers.javaScriptExports.call_delegate = (callback_gc_handle: GCHandle, arg1_js: any, arg2_js: any, arg3_js: any, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs) => { + loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { const args = alloc_stack_frame(6); @@ -141,6 +146,7 @@ export function init_managed_exports(): void { } }; runtimeHelpers.javaScriptExports.get_managed_stack_trace = (exception_gc_handle: GCHandle) => { + loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { const args = alloc_stack_frame(3); diff --git a/src/mono/wasm/runtime/marshal-to-cs.ts b/src/mono/wasm/runtime/marshal-to-cs.ts index 0c22c590b79df..9b2155daf397f 100644 --- a/src/mono/wasm/runtime/marshal-to-cs.ts +++ b/src/mono/wasm/runtime/marshal-to-cs.ts @@ -5,7 +5,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { isThenable } from "./cancelable-promise"; import cwraps from "./cwraps"; import { assert_not_disposed, cs_owned_js_handle_symbol, js_owned_gc_handle_symbol, mono_wasm_get_js_handle, setup_managed_proxy, teardown_managed_proxy } from "./gc-handles"; -import { Module, runtimeHelpers } from "./globals"; +import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { ManagedError, set_gc_handle, set_js_handle, set_arg_type, set_arg_i32, set_arg_f64, set_arg_i52, set_arg_f32, set_arg_i16, set_arg_u8, set_arg_b8, set_arg_date, @@ -195,7 +195,7 @@ function _marshal_date_time_to_cs(arg: JSMarshalerArgument, value: Date): void { set_arg_type(arg, MarshalerType.None); } else { - mono_assert(value instanceof Date, "Value is not a Date"); + mono_check(value instanceof Date, "Value is not a Date"); set_arg_type(arg, MarshalerType.DateTime); set_arg_date(arg, value); } @@ -206,7 +206,7 @@ function _marshal_date_time_offset_to_cs(arg: JSMarshalerArgument, value: Date): set_arg_type(arg, MarshalerType.None); } else { - mono_assert(value instanceof Date, "Value is not a Date"); + mono_check(value instanceof Date, "Value is not a Date"); set_arg_type(arg, MarshalerType.DateTimeOffset); set_arg_date(arg, value); } @@ -218,7 +218,7 @@ function _marshal_string_to_cs(arg: JSMarshalerArgument, value: string) { } else { set_arg_type(arg, MarshalerType.String); - mono_assert(typeof value === "string", "Value is not a String"); + mono_check(typeof value === "string", "Value is not a String"); _marshal_string_to_cs_impl(arg, value); } } @@ -242,7 +242,7 @@ function _marshal_function_to_cs(arg: JSMarshalerArgument, value: Function, _?: set_arg_type(arg, MarshalerType.None); return; } - mono_assert(value && value instanceof Function, "Value is not a Function"); + mono_check(value && value instanceof Function, "Value is not a Function"); // TODO: we could try to cache value -> existing JSHandle const marshal_function_to_cs_wrapper: any = (args: JSMarshalerArguments) => { @@ -302,7 +302,7 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?: set_arg_type(arg, MarshalerType.None); return; } - mono_assert(isThenable(value), "Value is not a Promise"); + mono_check(isThenable(value), "Value is not a Promise"); const gc_handle: GCHandle = runtimeHelpers.javaScriptExports.create_task_callback(); set_gc_handle(arg, gc_handle); @@ -315,6 +315,7 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?: value.then(data => { try { + loaderHelpers.assert_runtime_running(); if (MonoWasmThreads) settleUnsettledPromise(); runtimeHelpers.javaScriptExports.complete_task(gc_handle, null, data, res_converter || _marshal_cs_object_to_cs); @@ -325,6 +326,7 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?: } }).catch(reason => { try { + loaderHelpers.assert_runtime_running(); if (MonoWasmThreads) settleUnsettledPromise(); runtimeHelpers.javaScriptExports.complete_task(gc_handle, reason, null, undefined); @@ -347,7 +349,7 @@ export function marshal_exception_to_cs(arg: JSMarshalerArgument, value: any): v set_gc_handle(arg, gc_handle); } else { - mono_assert(typeof value === "object" || typeof value === "string", () => `Value is not an Error ${typeof value}`); + mono_check(typeof value === "object" || typeof value === "string", () => `Value is not an Error ${typeof value}`); set_arg_type(arg, MarshalerType.JSException); const message = value.toString(); _marshal_string_to_cs_impl(arg, message); @@ -369,8 +371,8 @@ function _marshal_js_object_to_cs(arg: JSMarshalerArgument, value: any): void { } else { // if value was ManagedObject, it would be double proxied, but the C# signature requires that - mono_assert(value[js_owned_gc_handle_symbol] === undefined, "JSObject proxy of ManagedObject proxy is not supported"); - mono_assert(typeof value === "function" || typeof value === "object", () => `JSObject proxy of ${typeof value} is not supported`); + mono_check(value[js_owned_gc_handle_symbol] === undefined, "JSObject proxy of ManagedObject proxy is not supported"); + mono_check(typeof value === "function" || typeof value === "object", () => `JSObject proxy of ${typeof value} is not supported`); set_arg_type(arg, MarshalerType.JSObject); const js_handle = mono_wasm_get_js_handle(value)!; @@ -480,7 +482,7 @@ export function marshal_array_to_cs_impl(arg: JSMarshalerArgument, value: Array< const buffer_length = element_size * length; const buffer_ptr = Module._malloc(buffer_length); if (element_type == MarshalerType.String) { - mono_assert(Array.isArray(value), "Value is not an Array"); + mono_check(Array.isArray(value), "Value is not an Array"); _zero_region(buffer_ptr, buffer_length); cwraps.mono_wasm_register_root(buffer_ptr, buffer_length, "marshal_array_to_cs"); for (let index = 0; index < length; index++) { @@ -489,7 +491,7 @@ export function marshal_array_to_cs_impl(arg: JSMarshalerArgument, value: Array< } } else if (element_type == MarshalerType.Object) { - mono_assert(Array.isArray(value), "Value is not an Array"); + mono_check(Array.isArray(value), "Value is not an Array"); _zero_region(buffer_ptr, buffer_length); cwraps.mono_wasm_register_root(buffer_ptr, buffer_length, "marshal_array_to_cs"); for (let index = 0; index < length; index++) { @@ -498,7 +500,7 @@ export function marshal_array_to_cs_impl(arg: JSMarshalerArgument, value: Array< } } else if (element_type == MarshalerType.JSObject) { - mono_assert(Array.isArray(value), "Value is not an Array"); + mono_check(Array.isArray(value), "Value is not an Array"); _zero_region(buffer_ptr, buffer_length); for (let index = 0; index < length; index++) { const element_arg = get_arg(buffer_ptr, index); @@ -506,17 +508,17 @@ export function marshal_array_to_cs_impl(arg: JSMarshalerArgument, value: Array< } } else if (element_type == MarshalerType.Byte) { - mono_assert(Array.isArray(value) || value instanceof Uint8Array, "Value is not an Array or Uint8Array"); + mono_check(Array.isArray(value) || value instanceof Uint8Array, "Value is not an Array or Uint8Array"); const targetView = localHeapViewU8().subarray(buffer_ptr, buffer_ptr + length); targetView.set(value); } else if (element_type == MarshalerType.Int32) { - mono_assert(Array.isArray(value) || value instanceof Int32Array, "Value is not an Array or Int32Array"); + mono_check(Array.isArray(value) || value instanceof Int32Array, "Value is not an Array or Int32Array"); const targetView = localHeapViewI32().subarray(buffer_ptr >> 2, (buffer_ptr >> 2) + length); targetView.set(value); } else if (element_type == MarshalerType.Double) { - mono_assert(Array.isArray(value) || value instanceof Float64Array, "Value is not an Array or Float64Array"); + mono_check(Array.isArray(value) || value instanceof Float64Array, "Value is not an Array or Float64Array"); const targetView = localHeapViewF64().subarray(buffer_ptr >> 3, (buffer_ptr >> 3) + length); targetView.set(value); } @@ -532,7 +534,7 @@ export function marshal_array_to_cs_impl(arg: JSMarshalerArgument, value: Array< function _marshal_span_to_cs(arg: JSMarshalerArgument, value: Span, element_type?: MarshalerType): void { mono_assert(!!element_type, "Expected valid element_type parameter"); - mono_assert(!value.isDisposed, "ObjectDisposedException"); + mono_check(!value.isDisposed, "ObjectDisposedException"); checkViewType(element_type, value._viewType); set_arg_type(arg, MarshalerType.Span); @@ -554,13 +556,13 @@ function _marshal_array_segment_to_cs(arg: JSMarshalerArgument, value: ArraySegm function checkViewType(element_type: MarshalerType, viewType: MemoryViewType) { if (element_type == MarshalerType.Byte) { - mono_assert(MemoryViewType.Byte == viewType, "Expected MemoryViewType.Byte"); + mono_check(MemoryViewType.Byte == viewType, "Expected MemoryViewType.Byte"); } else if (element_type == MarshalerType.Int32) { - mono_assert(MemoryViewType.Int32 == viewType, "Expected MemoryViewType.Int32"); + mono_check(MemoryViewType.Int32 == viewType, "Expected MemoryViewType.Int32"); } else if (element_type == MarshalerType.Double) { - mono_assert(MemoryViewType.Double == viewType, "Expected MemoryViewType.Double"); + mono_check(MemoryViewType.Double == viewType, "Expected MemoryViewType.Double"); } else { throw new Error(`NotImplementedException ${MarshalerType[element_type]} `); diff --git a/src/mono/wasm/runtime/marshal-to-js.ts b/src/mono/wasm/runtime/marshal-to-js.ts index c9076b1890c60..40f3f56a49d3f 100644 --- a/src/mono/wasm/runtime/marshal-to-js.ts +++ b/src/mono/wasm/runtime/marshal-to-js.ts @@ -3,7 +3,7 @@ import cwraps from "./cwraps"; import { _lookup_js_owned_object, mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle, setup_managed_proxy } from "./gc-handles"; -import { Module, createPromiseController, loaderHelpers, runtimeHelpers } from "./globals"; +import { Module, createPromiseController, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { ManagedObject, ManagedError, get_arg_gc_handle, get_arg_js_handle, get_arg_type, get_arg_i32, get_arg_f64, get_arg_i52, get_arg_i16, get_arg_u8, get_arg_f32, diff --git a/src/mono/wasm/runtime/marshal.ts b/src/mono/wasm/runtime/marshal.ts index 80fb44f7e9f96..d800a1fa95c89 100644 --- a/src/mono/wasm/runtime/marshal.ts +++ b/src/mono/wasm/runtime/marshal.ts @@ -1,8 +1,10 @@ // 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 { js_owned_gc_handle_symbol, teardown_managed_proxy } from "./gc-handles"; -import { Module, runtimeHelpers } from "./globals"; +import { Module, mono_assert, runtimeHelpers } from "./globals"; import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8, localHeapViewF64, localHeapViewI32, localHeapViewU8 } from "./memory"; import { mono_wasm_new_external_root } from "./roots"; import { GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot, MarshalerType } from "./types/internal"; @@ -198,7 +200,7 @@ export function get_arg_f64(arg: JSMarshalerArgument): number { export function set_arg_b8(arg: JSMarshalerArgument, value: boolean): void { mono_assert(arg, "Null arg"); - mono_assert(typeof value === "boolean", () => `Value is not a Boolean: ${value} (${typeof (value)})`); + mono_check(typeof value === "boolean", () => `Value is not a Boolean: ${value} (${typeof (value)})`); setU8(arg, value ? 1 : 0); } @@ -229,7 +231,7 @@ export function set_arg_intptr(arg: JSMarshalerArgument, value: VoidPtr): void { export function set_arg_i52(arg: JSMarshalerArgument, value: number): void { mono_assert(arg, "Null arg"); - mono_assert(Number.isSafeInteger(value), () => `Value is not an integer: ${value} (${typeof (value)})`); + mono_check(Number.isSafeInteger(value), () => `Value is not an integer: ${value} (${typeof (value)})`); // we know that conversion to Int64 would be done on C# side setF64(arg, value); } @@ -334,7 +336,7 @@ export class ManagedError extends Error implements IDisposable { getManageStack() { const gc_handle = (this)[js_owned_gc_handle_symbol]; - if (gc_handle) { + if (gc_handle && (!MonoWasmThreads || runtimeHelpers.jsSynchronizationContextInstalled)) { const managed_stack = runtimeHelpers.javaScriptExports.get_managed_stack_trace(gc_handle); if (managed_stack) { return managed_stack + "\n" + this.getSuperStack(); @@ -395,36 +397,36 @@ abstract class MemoryView implements IMemoryView { } set(source: TypedArray, targetOffset?: number): void { - mono_assert(!this.isDisposed, "ObjectDisposedException"); + mono_check(!this.isDisposed, "ObjectDisposedException"); const targetView = this._unsafe_create_view(); - mono_assert(source && targetView && source.constructor === targetView.constructor, () => `Expected ${targetView.constructor}`); + mono_check(source && targetView && source.constructor === targetView.constructor, () => `Expected ${targetView.constructor}`); targetView.set(source, targetOffset); // TODO consider memory write barrier } copyTo(target: TypedArray, sourceOffset?: number): void { - mono_assert(!this.isDisposed, "ObjectDisposedException"); + mono_check(!this.isDisposed, "ObjectDisposedException"); const sourceView = this._unsafe_create_view(); - mono_assert(target && sourceView && target.constructor === sourceView.constructor, () => `Expected ${sourceView.constructor}`); + mono_check(target && sourceView && target.constructor === sourceView.constructor, () => `Expected ${sourceView.constructor}`); const trimmedSource = sourceView.subarray(sourceOffset); // TODO consider memory read barrier target.set(trimmedSource); } slice(start?: number, end?: number): TypedArray { - mono_assert(!this.isDisposed, "ObjectDisposedException"); + mono_check(!this.isDisposed, "ObjectDisposedException"); const sourceView = this._unsafe_create_view(); // TODO consider memory read barrier return sourceView.slice(start, end); } get length(): number { - mono_assert(!this.isDisposed, "ObjectDisposedException"); + mono_check(!this.isDisposed, "ObjectDisposedException"); return this._length; } get byteLength(): number { - mono_assert(!this.isDisposed, "ObjectDisposedException"); + mono_check(!this.isDisposed, "ObjectDisposedException"); return this._viewType == MemoryViewType.Byte ? this._length : this._viewType == MemoryViewType.Int32 ? this._length << 2 : this._viewType == MemoryViewType.Double ? this._length << 3 diff --git a/src/mono/wasm/runtime/memory.ts b/src/mono/wasm/runtime/memory.ts index d765d09830da9..80c154e263915 100644 --- a/src/mono/wasm/runtime/memory.ts +++ b/src/mono/wasm/runtime/memory.ts @@ -50,8 +50,8 @@ export function _release_temp_frame(): void { function assert_int_in_range(value: Number, min: Number, max: Number) { - mono_assert(Number.isSafeInteger(value), () => `Value is not an integer: ${value} (${typeof (value)})`); - mono_assert(value >= min && value <= max, () => `Overflow: value ${value} is out of ${min} ${max} range`); + mono_check(Number.isSafeInteger(value), () => `Value is not an integer: ${value} (${typeof (value)})`); + mono_check(value >= min && value <= max, () => `Overflow: value ${value} is out of ${min} ${max} range`); } export function _zero_region(byteOffset: VoidPtr, sizeBytes: number): void { @@ -141,7 +141,7 @@ function autoThrowI52(error: I52Error) { * Throws for values which are not 52 bit integer. See Number.isSafeInteger() */ export function setI52(offset: MemOffset, value: number): void { - mono_assert(Number.isSafeInteger(value), () => `Value is not a safe integer: ${value} (${typeof (value)})`); + mono_check(Number.isSafeInteger(value), () => `Value is not a safe integer: ${value} (${typeof (value)})`); receiveWorkerHeapViews(); const error = cwraps.mono_wasm_f64_to_i52(offset, value); autoThrowI52(error); @@ -151,28 +151,28 @@ export function setI52(offset: MemOffset, value: number): void { * Throws for values which are not 52 bit integer or are negative. See Number.isSafeInteger(). */ export function setU52(offset: MemOffset, value: number): void { - mono_assert(Number.isSafeInteger(value), () => `Value is not a safe integer: ${value} (${typeof (value)})`); - mono_assert(value >= 0, "Can't convert negative Number into UInt64"); + mono_check(Number.isSafeInteger(value), () => `Value is not a safe integer: ${value} (${typeof (value)})`); + mono_check(value >= 0, "Can't convert negative Number into UInt64"); receiveWorkerHeapViews(); const error = cwraps.mono_wasm_f64_to_u52(offset, value); autoThrowI52(error); } export function setI64Big(offset: MemOffset, value: bigint): void { - mono_assert(typeof value === "bigint", () => `Value is not an bigint: ${value} (${typeof (value)})`); - mono_assert(value >= min_int64_big && value <= max_int64_big, () => `Overflow: value ${value} is out of ${min_int64_big} ${max_int64_big} range`); + mono_check(typeof value === "bigint", () => `Value is not an bigint: ${value} (${typeof (value)})`); + mono_check(value >= min_int64_big && value <= max_int64_big, () => `Overflow: value ${value} is out of ${min_int64_big} ${max_int64_big} range`); Module.HEAP64[offset >>> 3] = value; } export function setF32(offset: MemOffset, value: number): void { - mono_assert(typeof value === "number", () => `Value is not a Number: ${value} (${typeof (value)})`); + mono_check(typeof value === "number", () => `Value is not a Number: ${value} (${typeof (value)})`); receiveWorkerHeapViews(); Module.HEAPF32[offset >>> 2] = value; } export function setF64(offset: MemOffset, value: number): void { - mono_assert(typeof value === "number", () => `Value is not a Number: ${value} (${typeof (value)})`); + mono_check(typeof value === "number", () => `Value is not a Number: ${value} (${typeof (value)})`); receiveWorkerHeapViews(); Module.HEAPF64[offset >>> 3] = value; } diff --git a/src/mono/wasm/runtime/net6-legacy/method-binding.ts b/src/mono/wasm/runtime/net6-legacy/method-binding.ts index 5764e9afa1161..2d38c950674a9 100644 --- a/src/mono/wasm/runtime/net6-legacy/method-binding.ts +++ b/src/mono/wasm/runtime/net6-legacy/method-binding.ts @@ -4,7 +4,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { legacy_c_functions as cwraps } from "../cwraps"; -import { ENVIRONMENT_IS_PTHREAD, Module } from "../globals"; +import { ENVIRONMENT_IS_PTHREAD, Module, mono_assert } from "../globals"; import { parseFQN } from "../invoke-cs"; import { setI32, setU32, setF32, setF64, setU52, setI52, setB32, setI32_unchecked, setU32_unchecked, _zero_region, _create_temp_frame, getB32, getI32, getU32, getF32, getF64 } from "../memory"; import { mono_wasm_new_external_root, mono_wasm_new_root } from "../roots"; diff --git a/src/mono/wasm/runtime/net6-legacy/method-calls.ts b/src/mono/wasm/runtime/net6-legacy/method-calls.ts index 753c5851ab3d1..825e5e7d4107f 100644 --- a/src/mono/wasm/runtime/net6-legacy/method-calls.ts +++ b/src/mono/wasm/runtime/net6-legacy/method-calls.ts @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import { get_js_obj, mono_wasm_get_jsobj_from_js_handle } from "../gc-handles"; -import { Module, INTERNAL } from "../globals"; +import { Module, INTERNAL, loaderHelpers } from "../globals"; import { wrap_error_root, wrap_no_error_root } from "../invoke-js"; import { _release_temp_frame } from "../memory"; import { mono_wasm_new_external_root, mono_wasm_new_root } from "../roots"; @@ -76,6 +76,7 @@ export function mono_bind_assembly_entry_point(assembly: string, signature?: str const js_method = mono_bind_method(method, signature!, false, "_" + assembly + "__entrypoint"); return async function (...args: any[]) { + loaderHelpers.assert_runtime_running(); if (args.length > 0 && Array.isArray(args[0])) args[0] = js_array_to_mono_array(args[0], true, false); return js_method(...args); diff --git a/src/mono/wasm/runtime/net6-legacy/strings.ts b/src/mono/wasm/runtime/net6-legacy/strings.ts index 0a6e4d41903eb..1f6dadab26173 100644 --- a/src/mono/wasm/runtime/net6-legacy/strings.ts +++ b/src/mono/wasm/runtime/net6-legacy/strings.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 { mono_assert } from "../globals"; import { mono_wasm_new_root } from "../roots"; import { interned_string_table, monoStringToString, mono_wasm_empty_string, stringToInternedMonoStringRoot, stringToMonoStringRoot } from "../strings"; import { MonoString, MonoStringNull, is_nullish } from "../types/internal"; diff --git a/src/mono/wasm/runtime/package-lock.json b/src/mono/wasm/runtime/package-lock.json index 5894b4ec3680c..1d5853fcbd7e4 100644 --- a/src/mono/wasm/runtime/package-lock.json +++ b/src/mono/wasm/runtime/package-lock.json @@ -518,7 +518,7 @@ }, "concat-map": { "version": "0.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/concat-map/-/concat-map-0.0.1.tgz", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, @@ -781,7 +781,7 @@ }, "fast-levenshtein": { "version": "2.0.6", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, @@ -840,7 +840,7 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, @@ -968,13 +968,13 @@ }, "imurmurhash": { "version": "0.1.4", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/imurmurhash/-/imurmurhash-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, "inflight": { "version": "1.0.6", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/inflight/-/inflight-1.0.6.tgz", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { @@ -999,7 +999,7 @@ }, "is-extglob": { "version": "2.1.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/is-extglob/-/is-extglob-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, @@ -1137,7 +1137,7 @@ }, "isexe": { "version": "2.0.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/isexe/-/isexe-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, @@ -1171,7 +1171,7 @@ }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, @@ -1263,7 +1263,7 @@ }, "natural-compare": { "version": "1.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/natural-compare/-/natural-compare-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, @@ -1284,7 +1284,7 @@ }, "once": { "version": "1.4.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/once/-/once-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { @@ -1355,7 +1355,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -1615,7 +1615,7 @@ }, "text-table": { "version": "0.2.0", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/text-table/-/text-table-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, @@ -1698,7 +1698,7 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, diff --git a/src/mono/wasm/runtime/profiler.ts b/src/mono/wasm/runtime/profiler.ts index a62d0c82792c0..af4775f66fe97 100644 --- a/src/mono/wasm/runtime/profiler.ts +++ b/src/mono/wasm/runtime/profiler.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 { ENVIRONMENT_IS_WEB, linkerEnableAotProfiler, linkerEnableBrowserProfiler, runtimeHelpers } from "./globals"; +import { ENVIRONMENT_IS_WEB, linkerEnableAotProfiler, linkerEnableBrowserProfiler, mono_assert, runtimeHelpers } from "./globals"; import { MonoMethod, AOTProfilerOptions, BrowserProfilerOptions } from "./types/internal"; import { profiler_c_functions as cwraps } from "./cwraps"; import { utf8ToString } from "./strings"; diff --git a/src/mono/wasm/runtime/pthreads/browser/index.ts b/src/mono/wasm/runtime/pthreads/browser/index.ts index b9dbdaeedbb07..a4e0428812d9f 100644 --- a/src/mono/wasm/runtime/pthreads/browser/index.ts +++ b/src/mono/wasm/runtime/pthreads/browser/index.ts @@ -5,7 +5,7 @@ import { isMonoWorkerMessageChannelCreated, monoSymbol, makeMonoThreadMessageApp import { pthreadPtr } from "../shared/types"; import { MonoThreadMessage } from "../shared"; import Internals from "../shared/emscripten-internals"; -import { createPromiseController, runtimeHelpers } from "../../globals"; +import { createPromiseController, mono_assert, runtimeHelpers } from "../../globals"; import { PromiseController } from "../../types/internal"; import { MonoConfig } from "../../types"; import { mono_log_debug } from "../../logging"; diff --git a/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts b/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts index a179b5b111cd1..6487699403a73 100644 --- a/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts +++ b/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts @@ -5,7 +5,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { afterLoadWasmModuleToWorker } from "../browser"; import { afterThreadInitTLS } from "../worker"; import Internals from "./emscripten-internals"; -import { loaderHelpers } from "../../globals"; +import { loaderHelpers, mono_assert } from "../../globals"; import { PThreadReplacements } from "../../types/internal"; import { mono_log_debug } from "../../logging"; diff --git a/src/mono/wasm/runtime/pthreads/shared/index.ts b/src/mono/wasm/runtime/pthreads/shared/index.ts index 6ab053516ad00..dcced2d8cccc1 100644 --- a/src/mono/wasm/runtime/pthreads/shared/index.ts +++ b/src/mono/wasm/runtime/pthreads/shared/index.ts @@ -4,7 +4,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import BuildConfiguration from "consts:configuration"; -import { Module, runtimeHelpers } from "../../globals"; +import { Module, mono_assert, runtimeHelpers } from "../../globals"; import { MonoConfig } from "../../types"; import { pthreadPtr } from "./types"; import { mono_log_debug } from "../../logging"; @@ -136,13 +136,11 @@ export function isMonoWorkerMessagePreload(message: MonoWorkerMessage `if (!(${match[1]})) throw new Error("Assert failed: ${match[2]}"); // inlined mono_check` + }, + { + // eslint-disable-next-line quotes + pattern: 'mono_check\\(([^,]*), \\(\\) => *`([^`]*)`\\);', + replacement: (match) => `if (!(${match[1]})) throw new Error(\`Assert failed: ${match[2]}\`); // inlined mono_check` + }, { // eslint-disable-next-line quotes pattern: 'mono_assert\\(([^,]*), *"([^"]*)"\\);', // eslint-disable-next-line quotes - replacement: (match) => `if (!(${match[1]})) throw new Error("Assert failed: ${match[2]}"); // inlined mono_assert` + replacement: (match) => `if (!(${match[1]})) mono_assert(false, "${match[2]}"); // inlined mono_assert condition` }, { // eslint-disable-next-line quotes pattern: 'mono_assert\\(([^,]*), \\(\\) => *`([^`]*)`\\);', - replacement: (match) => `if (!(${match[1]})) throw new Error(\`Assert failed: ${match[2]}\`); // inlined mono_assert` + replacement: (match) => `if (!(${match[1]})) mono_assert(false, \`${match[2]}\`); // inlined mono_assert condition` } ]; const checkAssert = { - pattern: /^\s*mono_assert/gm, - failure: "previous regexp didn't inline all mono_assert statements" + pattern: /^\s*mono_check/gm, + failure: "previous regexp didn't inline all mono_check statements" }; const checkNoLoader = { diff --git a/src/mono/wasm/runtime/run.ts b/src/mono/wasm/runtime/run.ts index 3c8e46df5d80f..8e2af5e14dd31 100644 --- a/src/mono/wasm/runtime/run.ts +++ b/src/mono/wasm/runtime/run.ts @@ -40,6 +40,7 @@ export async function mono_run_main(main_assembly_name: string, args: string[]): } export function find_entry_point(assembly: string) { + loaderHelpers.assert_runtime_running(); assert_bindings(); const asm = assembly_load(assembly); if (!asm) diff --git a/src/mono/wasm/runtime/scheduling.ts b/src/mono/wasm/runtime/scheduling.ts index 1309cf8043683..080c7f2c732c0 100644 --- a/src/mono/wasm/runtime/scheduling.ts +++ b/src/mono/wasm/runtime/scheduling.ts @@ -29,12 +29,19 @@ export function prevent_timer_throttling(): void { function prevent_timer_throttling_tick() { Module.maybeExit(); + if (!loaderHelpers.is_runtime_running()) { + return; + } cwraps.mono_wasm_execute_timer(); pump_count++; mono_background_exec_until_done(); } function mono_background_exec_until_done() { + Module.maybeExit(); + if (!loaderHelpers.is_runtime_running()) { + return; + } while (pump_count > 0) { --pump_count; cwraps.mono_background_exec(); @@ -60,6 +67,10 @@ export function mono_wasm_schedule_timer(shortestDueTimeMs: number): void { } function mono_wasm_schedule_timer_tick() { + Module.maybeExit(); + if (!loaderHelpers.is_runtime_running()) { + return; + } lastScheduledTimeoutId = undefined; cwraps.mono_wasm_execute_timer(); } diff --git a/src/mono/wasm/runtime/snapshot.ts b/src/mono/wasm/runtime/snapshot.ts index abe4a8d94916f..7f6dcba52e323 100644 --- a/src/mono/wasm/runtime/snapshot.ts +++ b/src/mono/wasm/runtime/snapshot.ts @@ -167,6 +167,7 @@ async function getCacheKey(): Promise { delete inputs.forwardConsoleLogsToWS; delete inputs.diagnosticTracing; delete inputs.appendElementOnExit; + delete inputs.assertAfterExit; delete inputs.logExitCode; delete inputs.pthreadPoolSize; delete inputs.asyncFlushOnExit; diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 7faea31a50e53..6f6dbda2871a4 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -5,7 +5,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import WasmEnableLegacyJsInterop from "consts:wasmEnableLegacyJsInterop"; import { DotnetModuleInternal, CharPtrNull } from "./types/internal"; -import { linkerDisableLegacyJsInterop, ENVIRONMENT_IS_PTHREAD, exportedRuntimeAPI, INTERNAL, loaderHelpers, Module, runtimeHelpers } from "./globals"; +import { linkerDisableLegacyJsInterop, ENVIRONMENT_IS_PTHREAD, exportedRuntimeAPI, INTERNAL, loaderHelpers, Module, runtimeHelpers, createPromiseController, mono_assert } from "./globals"; import cwraps, { init_c_exports } from "./cwraps"; import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug"; import { toBase64StringImpl } from "./base64"; @@ -97,8 +97,14 @@ export function configureEmscriptenStartup(module: DotnetModuleInternal): void { // execution order == [*] == if (!module.onAbort) { module.onAbort = (error) => { - loaderHelpers.abort_startup(error, false); - loaderHelpers.mono_exit(1, error); + const is_exited = loaderHelpers.is_exited(); + if (!runtimeHelpers.runtimeReady) loaderHelpers.abort_startup(error, is_exited, false); + }; + } + if (!module.onExit) { + module.onExit = (code) => { + if (!runtimeHelpers.runtimeReady) loaderHelpers.abort_startup("exit " + code, false, false); + if (!loaderHelpers.is_exited()) loaderHelpers.mono_exit(code, null); }; } } @@ -197,6 +203,7 @@ async function preInitWorkerAsync() { export function preRunWorker() { // signal next stage + runtimeHelpers.runtimeReady = true; runtimeHelpers.afterPreRun.promise_control.resolve(); } @@ -247,7 +254,7 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { : new Error("Snapshot taken, exiting because exitAfterSnapshot was set."); reason.silent = true; - loaderHelpers.abort_startup(reason, false); + loaderHelpers.abort_startup(reason, false, true); return; } @@ -258,6 +265,7 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { bindings_init(); if (MonoWasmThreads) { runtimeHelpers.javaScriptExports.install_synchronization_context(); + runtimeHelpers.jsSynchronizationContextInstalled = true; } if (!runtimeHelpers.mono_wasm_runtime_is_ready) mono_wasm_runtime_ready(); @@ -312,6 +320,12 @@ async function postRunAsync(userpostRun: (() => void)[]) { runtimeHelpers.afterPostRun.promise_control.resolve(); } +export function postRunWorker() { + // signal next stage + runtimeHelpers.runtimeReady = false; + runtimeHelpers.afterPreRun = createPromiseController(); +} + async function mono_wasm_init_threads() { if (!MonoWasmThreads) { return; @@ -329,6 +343,8 @@ function mono_wasm_pre_init_essential(isWorker: boolean): void { mono_log_debug("mono_wasm_pre_init_essential"); init_c_exports(); + runtimeHelpers.mono_wasm_exit = cwraps.mono_wasm_exit; + runtimeHelpers.mono_wasm_abort = cwraps.mono_wasm_abort; cwraps_internal(INTERNAL); if (WasmEnableLegacyJsInterop && !linkerDisableLegacyJsInterop) { cwraps_mono_api(MONO); @@ -571,6 +587,7 @@ export function mono_wasm_load_runtime(unused?: string, debugLevel?: number): vo } catch (err: any) { _print_error("mono_wasm_load_runtime () failed", err); loaderHelpers.abort_startup(err, false); + throw err; } } diff --git a/src/mono/wasm/runtime/types/consts.d.ts b/src/mono/wasm/runtime/types/consts.d.ts index a86668b13fa8a..c7a6f740ae652 100644 --- a/src/mono/wasm/runtime/types/consts.d.ts +++ b/src/mono/wasm/runtime/types/consts.d.ts @@ -18,6 +18,7 @@ declare module "consts:monoDiagnosticsMock" { export default constant; } +// this will throw exception if the condition is false // see src\mono\wasm\runtime\rollup.config.js // inline this, because the lambda could allocate closure on hot path otherwise -declare function mono_assert(condition: unknown, messageFactory: string | (() => string)): asserts condition; +declare function mono_check(condition: unknown, messageFactory: string | (() => string)): asserts condition; diff --git a/src/mono/wasm/runtime/types/emscripten.ts b/src/mono/wasm/runtime/types/emscripten.ts index 46a2449c6cf7d..c30a588468c0f 100644 --- a/src/mono/wasm/runtime/types/emscripten.ts +++ b/src/mono/wasm/runtime/types/emscripten.ts @@ -72,6 +72,7 @@ export declare interface EmscriptenModule { onRuntimeInitialized?: () => any; postRun?: (() => any)[] | (() => any); onAbort?: { (error: any): void }; + onExit?: { (code: number): void }; } export type InstantiateWasmSuccessCallback = (instance: WebAssembly.Instance, module: WebAssembly.Module | undefined) => void; diff --git a/src/mono/wasm/runtime/types/internal.ts b/src/mono/wasm/runtime/types/internal.ts index 735e7c445b410..c6cf0b26ec3ad 100644 --- a/src/mono/wasm/runtime/types/internal.ts +++ b/src/mono/wasm/runtime/types/internal.ts @@ -72,6 +72,7 @@ export type MonoConfigInternal = MonoConfig & { browserProfilerOptions?: BrowserProfilerOptions, // dictionary-style Object. If omitted, browser profiler will not be initialized. waitForDebugger?: number, appendElementOnExit?: boolean + assertAfterExit?: boolean logExitCode?: boolean forwardConsoleLogsToWS?: boolean, asyncFlushOnExit?: boolean @@ -118,7 +119,10 @@ export type LoaderHelpers = { wasmDownloadPromise: PromiseAndController, runtimeModuleLoaded: PromiseAndController, - abort_startup: (reason: any, should_exit: boolean) => void, + is_exited: () => boolean, + is_runtime_running: () => boolean, + assert_runtime_running: () => void, + abort_startup: (reason: any, should_exit: boolean, should_throw?: boolean) => void, mono_exit: (exit_code: number, reason?: any) => void, createPromiseController: (afterResolve?: () => void, afterReject?: () => void) => PromiseAndController, getPromiseController: (promise: ControllablePromise) => PromiseController, @@ -154,12 +158,15 @@ export type RuntimeHelpers = { waitForDebugger?: number; ExitStatus: ExitStatusError; quit: Function, + mono_wasm_exit?: (code: number) => void, + mono_wasm_abort?: () => void, javaScriptExports: JavaScriptExports, storeMemorySnapshotPending: boolean, memorySnapshotCacheKey: string, subtle: SubtleCrypto | null, updateMemoryViews: () => void runtimeReady: boolean, + jsSynchronizationContextInstalled: boolean, cspPolicy: boolean, runtimeModuleUrl: string diff --git a/src/mono/wasm/runtime/web-socket.ts b/src/mono/wasm/runtime/web-socket.ts index 00ca6c8e70ebb..550b7edd4e7b8 100644 --- a/src/mono/wasm/runtime/web-socket.ts +++ b/src/mono/wasm/runtime/web-socket.ts @@ -5,7 +5,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { prevent_timer_throttling } from "./scheduling"; import { Queue } from "./queue"; -import { createPromiseController } from "./globals"; +import { createPromiseController, mono_assert } from "./globals"; import { setI32, localHeapViewU8 } from "./memory"; import { VoidPtr } from "./types/emscripten"; import { PromiseController } from "./types/internal"; diff --git a/src/mono/wasm/test-main.js b/src/mono/wasm/test-main.js index a6cd146f9652e..4c7788cef29ca 100644 --- a/src/mono/wasm/test-main.js +++ b/src/mono/wasm/test-main.js @@ -259,6 +259,7 @@ function configureRuntime(dotnet, runArgs) { .withExitOnUnhandledError() .withExitCodeLogging() .withElementOnExit() + .withAssertAfterExit() .withConfig({ loadAllSatelliteResources: true }); diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 33441e6abd505..c479a8cddf72e 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -195,6 +195,7 @@ + From 362de7df05c51d8a50cbeb69c5ecb281b1b569b8 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 10 Jul 2023 14:06:20 +0200 Subject: [PATCH 02/19] wip --- src/mono/wasm/runtime/loader/config.ts | 3 ++- src/mono/wasm/runtime/loader/exit.ts | 6 +----- src/mono/wasm/runtime/loader/globals.ts | 1 + src/mono/wasm/runtime/types/internal.ts | 1 + 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/mono/wasm/runtime/loader/config.ts b/src/mono/wasm/runtime/loader/config.ts index 4d997a11865f1..d71cbbde33cbc 100644 --- a/src/mono/wasm/runtime/loader/config.ts +++ b/src/mono/wasm/runtime/loader/config.ts @@ -4,7 +4,7 @@ import BuildConfiguration from "consts:configuration"; import type { DotnetModuleInternal, MonoConfigInternal } from "../types/internal"; import type { DotnetModuleConfig } from "../types"; -import { exportedRuntimeAPI, loaderHelpers, runtimeHelpers } from "./globals"; +import { ENVIRONMENT_IS_WEB, exportedRuntimeAPI, loaderHelpers, runtimeHelpers } from "./globals"; import { initializeBootConfig, loadBootConfig } from "./blazor/_Integration"; import { BootConfigResult } from "./blazor/BootConfig"; import { BootJsonData } from "../types/blazor"; @@ -45,6 +45,7 @@ export function normalizeConfig() { config.assets = config.assets || []; config.runtimeOptions = config.runtimeOptions || []; config.globalizationMode = config.globalizationMode || "auto"; + loaderHelpers.assertAfterExit = config.assertAfterExit = config.assertAfterExit || !ENVIRONMENT_IS_WEB; if (config.debugLevel === undefined && BuildConfiguration === "Debug") { config.debugLevel = -1; diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index 0ceca06f252ab..46bcc516eee9c 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -21,11 +21,7 @@ export function is_runtime_running() { export function assert_runtime_running() { mono_assert(runtimeHelpers.runtimeReady, "mono runtime didn't start yet"); mono_assert(!ABORT, "startup of the mono runtime was aborted"); - - const assertAfterExit = !ENVIRONMENT_IS_WEB || // always assert on shell and node - (loaderHelpers.config && loaderHelpers.config.assertAfterExit); // assert on web when enabled - - mono_assert(assertAfterExit || typeof EXIT_CODE == "undefined", () => `mono runtime already exited with ${EXIT_CODE}`); + mono_assert(!loaderHelpers.assertAfterExit || typeof EXIT_CODE == "undefined", () => `mono runtime already exited with ${EXIT_CODE}`); } export function abort_startup(reason: any, should_exit: boolean, should_throw?: boolean): void { diff --git a/src/mono/wasm/runtime/loader/globals.ts b/src/mono/wasm/runtime/loader/globals.ts index 574a8be33a340..0ad303d606b5d 100644 --- a/src/mono/wasm/runtime/loader/globals.ts +++ b/src/mono/wasm/runtime/loader/globals.ts @@ -62,6 +62,7 @@ export function setLoaderGlobals( maxParallelDownloads: 16, enableDownloadRetry: true, + assertAfterExit: !ENVIRONMENT_IS_WEB, _loaded_files: [], loadedFiles: [], diff --git a/src/mono/wasm/runtime/types/internal.ts b/src/mono/wasm/runtime/types/internal.ts index c6cf0b26ec3ad..02413b8a64fa9 100644 --- a/src/mono/wasm/runtime/types/internal.ts +++ b/src/mono/wasm/runtime/types/internal.ts @@ -100,6 +100,7 @@ export type LoaderHelpers = { maxParallelDownloads: number; enableDownloadRetry: boolean; + assertAfterExit: boolean; loadedFiles: string[], _loaded_files: { url: string, file: string }[]; From 68681354b0c453ddd2d8805b65d42417849811a2 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 10 Jul 2023 14:41:35 +0200 Subject: [PATCH 03/19] fix MT --- src/mono/wasm/runtime/startup.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 6f6dbda2871a4..6855cc9763067 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -89,7 +89,6 @@ export function configureEmscriptenStartup(module: DotnetModuleInternal): void { // - here we resolve the promise returned by createDotnetRuntime export // - any code after createDotnetRuntime is executed now runtimeHelpers.dotnetReady.promise_control.resolve(exportedRuntimeAPI); - runtimeHelpers.runtimeReady = true; }).catch(err => { runtimeHelpers.dotnetReady.promise_control.reject(err); }); @@ -263,6 +262,8 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { } bindings_init(); + runtimeHelpers.runtimeReady = true; + if (MonoWasmThreads) { runtimeHelpers.javaScriptExports.install_synchronization_context(); runtimeHelpers.jsSynchronizationContextInstalled = true; From 0ff8506f2ec5a4abffd9cff14914d2d5a2aa420f Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 10 Jul 2023 16:58:25 +0200 Subject: [PATCH 04/19] don't call managed from finalizer after exit --- src/mono/wasm/runtime/gc-handles.ts | 6 +++++- src/mono/wasm/runtime/loader/exit.ts | 28 ++++++++++++------------- src/mono/wasm/runtime/types/internal.ts | 3 +++ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/mono/wasm/runtime/gc-handles.ts b/src/mono/wasm/runtime/gc-handles.ts index b62ead0594ce9..1c807ac19e775 100644 --- a/src/mono/wasm/runtime/gc-handles.ts +++ b/src/mono/wasm/runtime/gc-handles.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 { runtimeHelpers } from "./globals"; +import { loaderHelpers, runtimeHelpers } from "./globals"; import { mono_log_warn } from "./logging"; import { GCHandle, GCHandleNull, JSHandle, JSHandleDisposed, JSHandleNull } from "./types/internal"; import { create_weak_ref } from "./weak-ref"; @@ -114,6 +114,10 @@ export function assert_not_disposed(result: any): GCHandle { } function _js_owned_object_finalized(gc_handle: GCHandle): void { + if (loaderHelpers.isAborted || loaderHelpers.exitCode !== undefined) { + // We're shutting down, so don't bother doing anything else. + return; + } teardown_managed_proxy(null, gc_handle); } diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index 46bcc516eee9c..45ecebfbc7fec 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -5,29 +5,25 @@ import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, INTERNAL import { mono_log_debug, consoleWebSocket, mono_log_error, mono_log_info_no_prefix, mono_log_warn } from "./logging"; -let ABORT = false; -let EXIT = false; -let EXIT_CODE: number | undefined = undefined; - export function is_exited() { - return EXIT; + return loaderHelpers.exitCode !== undefined; } export function is_runtime_running() { - return runtimeHelpers.runtimeReady && !EXIT && !ABORT; + return runtimeHelpers.runtimeReady && !is_exited() && !loaderHelpers.isAborted; } export function assert_runtime_running() { mono_assert(runtimeHelpers.runtimeReady, "mono runtime didn't start yet"); - mono_assert(!ABORT, "startup of the mono runtime was aborted"); - mono_assert(!loaderHelpers.assertAfterExit || typeof EXIT_CODE == "undefined", () => `mono runtime already exited with ${EXIT_CODE}`); + mono_assert(!loaderHelpers.isAborted, "startup of the mono runtime was aborted"); + mono_assert(!loaderHelpers.assertAfterExit || !is_exited(), () => `mono runtime already exited with ${loaderHelpers.exitCode}`); } export function abort_startup(reason: any, should_exit: boolean, should_throw?: boolean): void { - if (ABORT) return; - ABORT = true; - EXIT_CODE = 1; + if (loaderHelpers.isAborted) return; + loaderHelpers.isAborted = true; + loaderHelpers.exitCode = 1; mono_log_debug("abort_startup"); loaderHelpers.allDownloadsQueued.promise_control.reject(reason); @@ -57,12 +53,14 @@ export function abort_startup(reason: any, should_exit: boolean, should_throw?: // this will also call mono_wasm_exit if available, which will call exitJS -> _proc_exit -> terminateAllThreads export function mono_exit(exit_code: number, reason?: any): void { - if (EXIT) { + if (is_exited()) { mono_log_warn("mono_exit called more than once", new Error().stack); return; } - EXIT = true; - EXIT_CODE = exit_code; + + // TODO forceDisposeProxies(); here + + loaderHelpers.exitCode = exit_code; if (loaderHelpers.config && loaderHelpers.config.asyncFlushOnExit && exit_code === 0) { // this would NOT call Node's exit() immediately, it's a hanging promise @@ -132,7 +130,7 @@ function set_exit_code_and_quit_now(exit_code: number, reason?: any): void { } appendElementOnExit(exit_code); - if (!ABORT && !runtimeHelpers.runtimeReady) abort_startup(reason, false, false); + if (!loaderHelpers.isAborted && !runtimeHelpers.runtimeReady) abort_startup(reason, false, false); if (runtimeHelpers.mono_wasm_exit) { try { runtimeHelpers.mono_wasm_exit(exit_code); diff --git a/src/mono/wasm/runtime/types/internal.ts b/src/mono/wasm/runtime/types/internal.ts index 2a7f5810b20d4..9a65ed089c743 100644 --- a/src/mono/wasm/runtime/types/internal.ts +++ b/src/mono/wasm/runtime/types/internal.ts @@ -102,6 +102,9 @@ export type LoaderHelpers = { enableDownloadRetry: boolean; assertAfterExit: boolean; + exitCode: number | undefined; + isAborted: boolean; + loadedFiles: string[], _loaded_files: { url: string, file: string }[]; scriptDirectory: string From eb35a3afcf1b885957d174cb8d7b5e561c5a5449 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 11 Jul 2023 15:27:31 +0200 Subject: [PATCH 05/19] wip --- src/mono/wasm/runtime/loader/exit.ts | 123 ++++++++++++--------------- src/mono/wasm/runtime/marshal.ts | 19 +++-- src/mono/wasm/runtime/run.ts | 7 +- 3 files changed, 72 insertions(+), 77 deletions(-) diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index 45ecebfbc7fec..f18679d202f04 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -53,14 +53,34 @@ export function abort_startup(reason: any, should_exit: boolean, should_throw?: // this will also call mono_wasm_exit if available, which will call exitJS -> _proc_exit -> terminateAllThreads export function mono_exit(exit_code: number, reason?: any): void { + // make sure we have both reason and exit_code + exit_code = reason?.status || exit_code; + reason = reason || runtimeHelpers.ExitStatus + ? new runtimeHelpers.ExitStatus(exit_code) + : new Error("Exit with code " + exit_code); + reason.status = exit_code; + if (is_exited()) { - mono_log_warn("mono_exit called more than once", new Error().stack); - return; - } + if (reason.silent !== true) { + mono_log_warn("mono_exit called more than once", reason.toString()); + } + reason.silent = true; + } else { + try { + reason.stack;// force stack to be generated before we shut down managed code + logErrorOnExit(exit_code, reason); + appendElementOnExit(exit_code); + if (runtimeHelpers.jiterpreter_dump_stats) runtimeHelpers.jiterpreter_dump_stats(false); + } + catch { + // ignore + } - // TODO forceDisposeProxies(); here + // TODO forceDisposeProxies(); here - loaderHelpers.exitCode = exit_code; + loaderHelpers.exitCode = exit_code; + if (!loaderHelpers.isAborted && !runtimeHelpers.runtimeReady) abort_startup(reason, false, false); + } if (loaderHelpers.config && loaderHelpers.config.asyncFlushOnExit && exit_code === 0) { // this would NOT call Node's exit() immediately, it's a hanging promise @@ -74,16 +94,30 @@ export function mono_exit(exit_code: number, reason?: any): void { })(); // we need to throw, rather than let the caller continue the normal execution // in the middle of some code, which expects this to stop the process - throw runtimeHelpers.ExitStatus - ? new runtimeHelpers.ExitStatus(exit_code) - : reason - ? reason - : new Error("Stop with exit code " + exit_code); + throw reason; } else { set_exit_code_and_quit_now(exit_code, reason); } } +function set_exit_code_and_quit_now(exit_code: number, reason?: any): void { + if (runtimeHelpers.mono_wasm_exit) { + runtimeHelpers.mono_wasm_exit(exit_code); + } + // just in case mono_wasm_exit didn't exit or throw + if (exit_code !== 0 || !ENVIRONMENT_IS_WEB) { + if (ENVIRONMENT_IS_NODE && INTERNAL.process) { + INTERNAL.process.exit(exit_code); + throw reason; + } + else if (runtimeHelpers.quit) { + runtimeHelpers.quit(exit_code, reason); + } else { + throw reason; + } + } +} + async function flush_node_streams() { try { // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -103,57 +137,6 @@ async function flush_node_streams() { } } -function set_exit_code_and_quit_now(exit_code: number, reason?: any): void { - if (runtimeHelpers.ExitStatus) { - if (reason && !(reason instanceof runtimeHelpers.ExitStatus)) { - if (!loaderHelpers.config.logExitCode) { - if (reason instanceof Error && runtimeHelpers.stringify_as_error_with_stack) - loaderHelpers.err(runtimeHelpers.stringify_as_error_with_stack(reason)); - else if (typeof reason == "string") - loaderHelpers.err(reason); - else - loaderHelpers.err(JSON.stringify(reason)); - } - } - else if (!reason) { - reason = new runtimeHelpers.ExitStatus(exit_code); - } else if (typeof reason.status === "number") { - exit_code = reason.status; - } - } - logErrorOnExit(exit_code, reason); - try { - if (runtimeHelpers.jiterpreter_dump_stats) runtimeHelpers.jiterpreter_dump_stats(false); - } catch { - // eslint-disable-next-line @typescript-eslint/no-extra-semi - ; - } - - appendElementOnExit(exit_code); - if (!loaderHelpers.isAborted && !runtimeHelpers.runtimeReady) abort_startup(reason, false, false); - if (runtimeHelpers.mono_wasm_exit) { - try { - runtimeHelpers.mono_wasm_exit(exit_code); - } - catch (expected) { - if (expected && runtimeHelpers.ExitStatus && !(expected instanceof runtimeHelpers.ExitStatus)) { - throw expected; - } - } - } - if (exit_code !== 0 || !ENVIRONMENT_IS_WEB) { - if (ENVIRONMENT_IS_NODE && INTERNAL.process) { - INTERNAL.process.exit(exit_code); - throw reason; - } - else if (runtimeHelpers.quit) { - runtimeHelpers.quit(exit_code, reason); - } else { - throw reason; - } - } -} - function appendElementOnExit(exit_code: number) { if (ENVIRONMENT_IS_WEB && loaderHelpers.config && loaderHelpers.config.appendElementOnExit) { //Tell xharness WasmBrowserTestRunner what was the exit code @@ -165,16 +148,16 @@ function appendElementOnExit(exit_code: number) { } } -function logErrorOnExit(exit_code: number, reason?: any) { +function logErrorOnExit(exit_code: number, reason: any) { + if (exit_code !== 0 && reason) { + if (reason instanceof Error && runtimeHelpers.stringify_as_error_with_stack) + mono_log_error(runtimeHelpers.stringify_as_error_with_stack(reason)); + else if (typeof reason == "string") + mono_log_error(reason); + else + mono_log_error(JSON.stringify(reason)); + } if (loaderHelpers.config && loaderHelpers.config.logExitCode) { - if (exit_code != 0 && reason) { - if (reason instanceof Error && runtimeHelpers.stringify_as_error_with_stack) - mono_log_error(runtimeHelpers.stringify_as_error_with_stack(reason)); - else if (typeof reason == "string") - mono_log_error(reason); - else - mono_log_error(JSON.stringify(reason)); - } if (consoleWebSocket) { const stop_when_ws_buffer_empty = () => { if (consoleWebSocket.bufferedAmount == 0) { diff --git a/src/mono/wasm/runtime/marshal.ts b/src/mono/wasm/runtime/marshal.ts index d800a1fa95c89..2d79befbb9db0 100644 --- a/src/mono/wasm/runtime/marshal.ts +++ b/src/mono/wasm/runtime/marshal.ts @@ -4,7 +4,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { js_owned_gc_handle_symbol, teardown_managed_proxy } from "./gc-handles"; -import { Module, mono_assert, runtimeHelpers } from "./globals"; +import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8, localHeapViewF64, localHeapViewI32, localHeapViewU8 } from "./memory"; import { mono_wasm_new_external_root } from "./roots"; import { GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot, MarshalerType } from "./types/internal"; @@ -319,6 +319,7 @@ export class ManagedObject implements IDisposable { export class ManagedError extends Error implements IDisposable { private superStack: any; + private managed_stack: any; constructor(message: string) { super(message); this.superStack = Object.getOwnPropertyDescriptor(this, "stack"); // this works on Chrome @@ -335,11 +336,17 @@ export class ManagedError extends Error implements IDisposable { } getManageStack() { - const gc_handle = (this)[js_owned_gc_handle_symbol]; - if (gc_handle && (!MonoWasmThreads || runtimeHelpers.jsSynchronizationContextInstalled)) { - const managed_stack = runtimeHelpers.javaScriptExports.get_managed_stack_trace(gc_handle); - if (managed_stack) { - return managed_stack + "\n" + this.getSuperStack(); + if (this.managed_stack) { + return this.managed_stack; + } + if (loaderHelpers.is_runtime_running() && (!MonoWasmThreads || runtimeHelpers.jsSynchronizationContextInstalled)) { + const gc_handle = (this)[js_owned_gc_handle_symbol]; + if (gc_handle !== GCHandleNull) { + const managed_stack = runtimeHelpers.javaScriptExports.get_managed_stack_trace(gc_handle); + if (managed_stack) { + this.managed_stack = managed_stack + "\n" + this.getSuperStack(); + return this.managed_stack; + } } } return this.getSuperStack(); diff --git a/src/mono/wasm/runtime/run.ts b/src/mono/wasm/runtime/run.ts index 8e2af5e14dd31..bd57f61b8e851 100644 --- a/src/mono/wasm/runtime/run.ts +++ b/src/mono/wasm/runtime/run.ts @@ -18,10 +18,15 @@ export async function mono_run_main_and_exit(main_assembly_name: string, args: s loaderHelpers.mono_exit(result); return result; } catch (error) { + try { + loaderHelpers.mono_exit(1, error); + } + catch (e) { + // ignore + } if (error instanceof runtimeHelpers.ExitStatus) { return error.status; } - loaderHelpers.mono_exit(1, error); return 1; } } From 1fbef455f3c91263c9e17fe38305a59971c2eba8 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 11 Jul 2023 15:45:08 +0200 Subject: [PATCH 06/19] fix --- src/mono/wasm/runtime/loader/exit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index f18679d202f04..72ad72f5e3e63 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -62,7 +62,7 @@ export function mono_exit(exit_code: number, reason?: any): void { if (is_exited()) { if (reason.silent !== true) { - mono_log_warn("mono_exit called more than once", reason.toString()); + mono_log_warn("mono_exit called more than once \n" + reason.stack); } reason.silent = true; } else { From 30f4759c994dd6aa839956ac8cad07eb26a130d0 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 11 Jul 2023 17:31:45 +0200 Subject: [PATCH 07/19] wip --- src/mono/wasm/runtime/loader/exit.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index 72ad72f5e3e63..fe2e50e8e1914 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, INTERNAL, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; -import { mono_log_debug, consoleWebSocket, mono_log_error, mono_log_info_no_prefix, mono_log_warn } from "./logging"; +import { mono_log_debug, consoleWebSocket, mono_log_error, mono_log_info_no_prefix } from "./logging"; export function is_exited() { @@ -60,12 +60,7 @@ export function mono_exit(exit_code: number, reason?: any): void { : new Error("Exit with code " + exit_code); reason.status = exit_code; - if (is_exited()) { - if (reason.silent !== true) { - mono_log_warn("mono_exit called more than once \n" + reason.stack); - } - reason.silent = true; - } else { + if (!is_exited()) { try { reason.stack;// force stack to be generated before we shut down managed code logErrorOnExit(exit_code, reason); @@ -73,7 +68,7 @@ export function mono_exit(exit_code: number, reason?: any): void { if (runtimeHelpers.jiterpreter_dump_stats) runtimeHelpers.jiterpreter_dump_stats(false); } catch { - // ignore + // ignore any failures } // TODO forceDisposeProxies(); here @@ -81,6 +76,7 @@ export function mono_exit(exit_code: number, reason?: any): void { loaderHelpers.exitCode = exit_code; if (!loaderHelpers.isAborted && !runtimeHelpers.runtimeReady) abort_startup(reason, false, false); } + reason.silent = true; if (loaderHelpers.config && loaderHelpers.config.asyncFlushOnExit && exit_code === 0) { // this would NOT call Node's exit() immediately, it's a hanging promise From 9ff8180222694aa8bfa9b988bae3dbb827163244 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 11 Jul 2023 18:24:46 +0200 Subject: [PATCH 08/19] wip --- src/mono/wasm/runtime/loader/exit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index fe2e50e8e1914..ea3a312fc5cfc 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -97,7 +97,7 @@ export function mono_exit(exit_code: number, reason?: any): void { } function set_exit_code_and_quit_now(exit_code: number, reason?: any): void { - if (runtimeHelpers.mono_wasm_exit) { + if (!is_exited() && runtimeHelpers.mono_wasm_exit) { runtimeHelpers.mono_wasm_exit(exit_code); } // just in case mono_wasm_exit didn't exit or throw From 7369de8fb654332dc82a9ed233654497d9340230 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 11 Jul 2023 18:27:21 +0200 Subject: [PATCH 09/19] wip --- src/mono/sample/wasm/browser-threads-minimal/main.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mono/sample/wasm/browser-threads-minimal/main.js b/src/mono/sample/wasm/browser-threads-minimal/main.js index 73ed5eadd4ad8..216e65158e1bf 100644 --- a/src/mono/sample/wasm/browser-threads-minimal/main.js +++ b/src/mono/sample/wasm/browser-threads-minimal/main.js @@ -116,6 +116,7 @@ try { exports.Sample.Test.StartAllocatorFromWorker(); */ + /* FIXME await delay(5000); console.log("smoke: running GCCollect"); @@ -125,6 +126,8 @@ try { console.log("smoke: running GCCollect"); exports.Sample.Test.GCCollect(); + console.log("smoke: running GCCollect done"); + */ /* FIXME console.log("smoke: running StopTimerFromWorker"); From 4bd12cc7c7595d537057c3762044eef78373539b Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 11 Jul 2023 18:41:06 +0200 Subject: [PATCH 10/19] wip --- src/mono/sample/wasm/browser-threads-minimal/main.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mono/sample/wasm/browser-threads-minimal/main.js b/src/mono/sample/wasm/browser-threads-minimal/main.js index 216e65158e1bf..f7404f449268b 100644 --- a/src/mono/sample/wasm/browser-threads-minimal/main.js +++ b/src/mono/sample/wasm/browser-threads-minimal/main.js @@ -79,9 +79,11 @@ try { let t4 = await exports.Sample.Test.HttpClientThread(globalThis.document.baseURI + "blurst.txt"); console.log("smoke: HttpClientThread(blurst.txt) done " + t4); + /* FIXME console.log("smoke: running WsClientMain"); let w0 = await exports.Sample.Test.WsClientMain("wss://socketsbay.com/wss/v2/1/demo/"); console.log("smoke: WsClientMain done " + w0); + */ /* ActiveIssue https://github.com/dotnet/runtime/issues/88057 console.log("smoke: running FetchBackground(blurst.txt)"); From 1994535baa74e50508f13ed88bb54d9098ee5989 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Wed, 12 Jul 2023 12:29:52 +0200 Subject: [PATCH 11/19] fix --- src/mono/wasm/runtime/loader/exit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index ea3a312fc5cfc..0b25417dbf3f1 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -54,7 +54,7 @@ export function abort_startup(reason: any, should_exit: boolean, should_throw?: // this will also call mono_wasm_exit if available, which will call exitJS -> _proc_exit -> terminateAllThreads export function mono_exit(exit_code: number, reason?: any): void { // make sure we have both reason and exit_code - exit_code = reason?.status || exit_code; + exit_code = (reason && typeof reason.status === "number") ? reason.status : exit_code; reason = reason || runtimeHelpers.ExitStatus ? new runtimeHelpers.ExitStatus(exit_code) : new Error("Exit with code " + exit_code); From 85097f0508ab008ef3f03369d4dd1682e499dc8a Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 13 Jul 2023 10:51:44 +0200 Subject: [PATCH 12/19] feedback from @kg --- .../wasm/browser-threads-minimal/main.js | 14 ++-------- src/mono/wasm/runtime/invoke-js.ts | 4 +-- src/mono/wasm/runtime/loader/exit.ts | 2 +- src/mono/wasm/runtime/package-lock.json | 26 +++++++++---------- src/mono/wasm/runtime/types/internal.ts | 2 +- 5 files changed, 19 insertions(+), 29 deletions(-) diff --git a/src/mono/sample/wasm/browser-threads-minimal/main.js b/src/mono/sample/wasm/browser-threads-minimal/main.js index 340d0fffa35bf..d35c744ebde7b 100644 --- a/src/mono/sample/wasm/browser-threads-minimal/main.js +++ b/src/mono/sample/wasm/browser-threads-minimal/main.js @@ -7,6 +7,8 @@ const assemblyName = "Wasm.Browser.Threads.Minimal.Sample.dll"; try { + const resolveUrl = (relativeUrl) => (new URL(relativeUrl, window.location.href)).toString() + const { getAssemblyExports, runMain } = await dotnet //.withEnvironmentVariable("MONO_LOG_LEVEL", "debug") //.withDiagnosticTracing(true) @@ -31,7 +33,6 @@ try { await exports.Sample.Test.DisposeTest(); console.log("smoke: DisposeTest done "); - /* FIXME console.log("smoke: running TestHelloWebWorker"); await exports.Sample.Test.TestHelloWebWorker(); await exports.Sample.Test.TestHelloWebWorker(); @@ -42,13 +43,11 @@ try { await exports.Sample.Test.TestHelloWebWorker(); await exports.Sample.Test.TestHelloWebWorker(); console.log("smoke: TestHelloWebWorker done"); - */ console.log("smoke: running TestCanStartThread"); await exports.Sample.Test.TestCanStartThread(); console.log("smoke: TestCanStartThread done"); - /* FIXME console.log("smoke: running TestTLS"); await exports.Sample.Test.TestTLS(); console.log("smoke: TestTLS done"); @@ -59,17 +58,14 @@ try { console.log("smoke: running TestCallSetTimeoutOnWorker"); await exports.Sample.Test.TestCallSetTimeoutOnWorker(); console.log("smoke: TestCallSetTimeoutOnWorker done"); - */ console.log("smoke: running HttpClientMain(blurst.txt)"); let t = await exports.Sample.Test.HttpClientMain(globalThis.document.baseURI + "blurst.txt"); console.log("smoke: HttpClientMain(blurst.txt) done " + t); - /* FIXME console.log("smoke: running HttpClientWorker(blurst.txt)"); let t2 = await exports.Sample.Test.HttpClientWorker(globalThis.document.baseURI + "blurst.txt"); console.log("smoke: HttpClientWorker(blurst.txt) done " + t2); - */ console.log("smoke: running HttpClientPool(blurst.txt)"); let t3 = await exports.Sample.Test.HttpClientPool(globalThis.document.baseURI + "blurst.txt"); @@ -79,11 +75,9 @@ try { let t4 = await exports.Sample.Test.HttpClientThread(globalThis.document.baseURI + "blurst.txt"); console.log("smoke: HttpClientThread(blurst.txt) done " + t4); - /* FIXME console.log("smoke: running WsClientMain"); let w0 = await exports.Sample.Test.WsClientMain("wss://corefx-net-http11.azurewebsites.net/WebSocket/EchoWebSocket.ashx"); console.log("smoke: WsClientMain done " + w0); - */ /* ActiveIssue https://github.com/dotnet/runtime/issues/88057 console.log("smoke: running FetchBackground(blurst.txt)"); @@ -113,10 +107,8 @@ try { } console.log("smoke: TaskRunCompute done"); - /* FIXME console.log("smoke: running StartAllocatorFromWorker"); exports.Sample.Test.StartAllocatorFromWorker(); - */ /* ActiveIssue https://github.com/dotnet/runtime/issues/88663 await delay(5000); @@ -131,10 +123,8 @@ try { console.log("smoke: running GCCollect done"); */ - /* FIXME console.log("smoke: running StopTimerFromWorker"); exports.Sample.Test.StopTimerFromWorker(); - */ let exit_code = await runMain(assemblyName, []); exit(exit_code); diff --git a/src/mono/wasm/runtime/invoke-js.ts b/src/mono/wasm/runtime/invoke-js.ts index abf5f078e8ce4..5b762f491de8a 100644 --- a/src/mono/wasm/runtime/invoke-js.ts +++ b/src/mono/wasm/runtime/invoke-js.ts @@ -336,8 +336,8 @@ export const importedModulesPromises: Map> = new Map(); export const importedModules: Map> = new Map(); export function dynamic_import(module_name: string, module_url: string): Promise { - mono_assert(typeof module_name === "string", "module_name must be string"); - mono_assert(typeof module_url === "string", "module_url must be string"); + mono_assert(module_name && typeof module_name === "string", "module_name must be string"); + mono_assert(module_url && typeof module_url === "string", "module_url must be string"); assert_synchronization_context(); let promise = importedModulesPromises.get(module_name); const newPromise = !promise; diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index 0b25417dbf3f1..3f226c62b67c5 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -25,7 +25,7 @@ export function abort_startup(reason: any, should_exit: boolean, should_throw?: loaderHelpers.isAborted = true; loaderHelpers.exitCode = 1; - mono_log_debug("abort_startup"); + mono_log_debug("abort_startup, reason: " + reason); loaderHelpers.allDownloadsQueued.promise_control.reject(reason); loaderHelpers.afterConfigLoaded.promise_control.reject(reason); loaderHelpers.wasmDownloadPromise.promise_control.reject(reason); diff --git a/src/mono/wasm/runtime/package-lock.json b/src/mono/wasm/runtime/package-lock.json index 1d5853fcbd7e4..5894b4ec3680c 100644 --- a/src/mono/wasm/runtime/package-lock.json +++ b/src/mono/wasm/runtime/package-lock.json @@ -518,7 +518,7 @@ }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, @@ -781,7 +781,7 @@ }, "fast-levenshtein": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, @@ -840,7 +840,7 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, @@ -968,13 +968,13 @@ }, "imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { @@ -999,7 +999,7 @@ }, "is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, @@ -1137,7 +1137,7 @@ }, "isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, @@ -1171,7 +1171,7 @@ }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, @@ -1263,7 +1263,7 @@ }, "natural-compare": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, @@ -1284,7 +1284,7 @@ }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { @@ -1355,7 +1355,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -1615,7 +1615,7 @@ }, "text-table": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, @@ -1698,7 +1698,7 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, diff --git a/src/mono/wasm/runtime/types/internal.ts b/src/mono/wasm/runtime/types/internal.ts index 9a65ed089c743..77dad0927b1ff 100644 --- a/src/mono/wasm/runtime/types/internal.ts +++ b/src/mono/wasm/runtime/types/internal.ts @@ -72,7 +72,7 @@ export type MonoConfigInternal = MonoConfig & { browserProfilerOptions?: BrowserProfilerOptions, // dictionary-style Object. If omitted, browser profiler will not be initialized. waitForDebugger?: number, appendElementOnExit?: boolean - assertAfterExit?: boolean + assertAfterExit?: boolean // default true for shell/nodeJS logExitCode?: boolean forwardConsoleLogsToWS?: boolean, asyncFlushOnExit?: boolean From a1dde14860a3a089e692e609d80a68b217f9465d Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 13 Jul 2023 17:19:48 +0200 Subject: [PATCH 13/19] fix --- src/mono/wasm/runtime/loader/exit.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index 3f226c62b67c5..ff29006c4b8fb 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -55,9 +55,9 @@ export function abort_startup(reason: any, should_exit: boolean, should_throw?: export function mono_exit(exit_code: number, reason?: any): void { // make sure we have both reason and exit_code exit_code = (reason && typeof reason.status === "number") ? reason.status : exit_code; - reason = reason || runtimeHelpers.ExitStatus + reason = reason || (runtimeHelpers.ExitStatus ? new runtimeHelpers.ExitStatus(exit_code) - : new Error("Exit with code " + exit_code); + : new Error("Exit with code " + exit_code)); reason.status = exit_code; if (!is_exited()) { From b246a3cbc1b8fc4a39a51e9cdbdebe44df194fe0 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 13 Jul 2023 20:11:07 +0200 Subject: [PATCH 14/19] bad merge --- src/mono/wasm/runtime/loader/config.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mono/wasm/runtime/loader/config.ts b/src/mono/wasm/runtime/loader/config.ts index dd7e8bbaba1a4..4b331a9f65d14 100644 --- a/src/mono/wasm/runtime/loader/config.ts +++ b/src/mono/wasm/runtime/loader/config.ts @@ -42,7 +42,6 @@ export function normalizeConfig() { config.environmentVariables = config.environmentVariables || {}; config.assets = config.assets || []; config.runtimeOptions = config.runtimeOptions || []; - config.globalizationMode = config.globalizationMode || "auto"; loaderHelpers.assertAfterExit = config.assertAfterExit = config.assertAfterExit || !ENVIRONMENT_IS_WEB; if (config.debugLevel === undefined && BuildConfiguration === "Debug") { From f93a43de275f6df1b8b8c476b70099e9b8b7ca37 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 14 Jul 2023 12:22:11 +0200 Subject: [PATCH 15/19] fix abort --- src/mono/wasm/runtime/loader/exit.ts | 10 ++++++++-- src/mono/wasm/runtime/loader/run.ts | 10 ++++++++-- src/mono/wasm/runtime/polyfills.ts | 7 ------- .../wasm/testassets/WasmBasicTestApp/wwwroot/main.js | 3 +++ 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index ff29006c4b8fb..49c6d6d8146b3 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -22,9 +22,15 @@ export function assert_runtime_running() { export function abort_startup(reason: any, should_exit: boolean, should_throw?: boolean): void { if (loaderHelpers.isAborted) return; - loaderHelpers.isAborted = true; - loaderHelpers.exitCode = 1; + const exit_code = (reason && typeof reason.status === "number") ? reason.status : 1; + reason = reason || (runtimeHelpers.ExitStatus + ? new runtimeHelpers.ExitStatus(exit_code) + : new Error("Exit with code " + exit_code)); + reason.status = exit_code; + + loaderHelpers.isAborted = true; + loaderHelpers.exitCode = exit_code; mono_log_debug("abort_startup, reason: " + reason); loaderHelpers.allDownloadsQueued.promise_control.reject(reason); loaderHelpers.afterConfigLoaded.promise_control.reject(reason); diff --git a/src/mono/wasm/runtime/loader/run.ts b/src/mono/wasm/runtime/loader/run.ts index 1fc2cd789c4d2..a630b6770bf83 100644 --- a/src/mono/wasm/runtime/loader/run.ts +++ b/src/mono/wasm/runtime/loader/run.ts @@ -1,13 +1,15 @@ // 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 type { MonoConfig, DotnetHostBuilder, DotnetModuleConfig, RuntimeAPI, WebAssemblyStartOptions, LoadBootResourceCallback } from "../types"; import type { MonoConfigInternal, EmscriptenModuleInternal, RuntimeModuleExportsInternal, NativeModuleExportsInternal, } from "../types/internal"; import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, exportedRuntimeAPI, globalObjectsRoot, mono_assert } from "./globals"; import { deep_merge_config, deep_merge_module, mono_wasm_load_config } from "./config"; import { is_exited, mono_exit } from "./exit"; -import { setup_proxy_console } from "./logging"; +import { setup_proxy_console, mono_log_info } from "./logging"; import { resolve_asset_path, start_asset_download } from "./assets"; import { detect_features_and_polyfill } from "./polyfills"; import { runtimeHelpers, loaderHelpers } from "./globals"; @@ -15,7 +17,6 @@ import { init_globalization } from "./icu"; import { setupPreloadChannelToMainThread } from "./worker"; import { invokeLibraryInitializers } from "./libraryInitializers"; - const module = globalObjectsRoot.module; const monoConfig = module.config as MonoConfigInternal; @@ -388,6 +389,11 @@ export class HostBuilder implements DotnetHostBuilder { } export async function createEmscripten(moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)): Promise { + if (BuildConfiguration === "Debug") { + mono_log_info(`starting script ${loaderHelpers.scriptUrl}`); + mono_log_info(`starting in ${loaderHelpers.scriptDirectory}`); + } + // extract ModuleConfig if (typeof moduleFactory === "function") { const extension = moduleFactory(globalObjectsRoot.api) as any; diff --git a/src/mono/wasm/runtime/polyfills.ts b/src/mono/wasm/runtime/polyfills.ts index 8502962e60260..fbf3a400f136c 100644 --- a/src/mono/wasm/runtime/polyfills.ts +++ b/src/mono/wasm/runtime/polyfills.ts @@ -1,13 +1,11 @@ // 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 type { EmscriptenReplacements } from "./types/internal"; import type { TypedArray } from "./types/emscripten"; import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, INTERNAL, Module, loaderHelpers, runtimeHelpers } from "./globals"; import { replaceEmscriptenPThreadLibrary } from "./pthreads/shared/emscripten-replacements"; -import { mono_log_info } from "./logging"; const dummyPerformance = { now: function () { @@ -28,11 +26,6 @@ export function initializeReplacements(replacements: EmscriptenReplacements): vo Module.locateFile = loaderHelpers.locateFile; } - if (BuildConfiguration === "Debug") { - mono_log_info(`starting script ${loaderHelpers.scriptUrl}`); - mono_log_info(`starting in ${loaderHelpers.scriptDirectory}`); - } - // prefer fetch_like over global fetch for assets replacements.fetch = loaderHelpers.fetch_like; diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/wwwroot/main.js b/src/mono/wasm/testassets/WasmBasicTestApp/wwwroot/main.js index 127d506e3c9a6..6c1ec0913a16a 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/wwwroot/main.js +++ b/src/mono/wasm/testassets/WasmBasicTestApp/wwwroot/main.js @@ -48,6 +48,9 @@ try { exports.AppSettingsTest.Run(); exit(0); break; + default: + console.error(`Unknown test case: ${testCase}`); + exit(3); } } catch (e) { exit(1, e); From 66bfa87426e0059892aeb3e66d4bb9ab49a11f81 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 14 Jul 2023 17:03:00 +0200 Subject: [PATCH 16/19] fixx merge --- src/mono/wasm/runtime/loader/polyfills.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/runtime/loader/polyfills.ts b/src/mono/wasm/runtime/loader/polyfills.ts index cf4b5abdb5d10..d77ceabc0b79a 100644 --- a/src/mono/wasm/runtime/loader/polyfills.ts +++ b/src/mono/wasm/runtime/loader/polyfills.ts @@ -4,7 +4,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import type { DotnetModuleInternal } from "../types/internal"; -import { INTERNAL, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, loaderHelpers, ENVIRONMENT_IS_WEB } from "./globals"; +import { INTERNAL, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, loaderHelpers, ENVIRONMENT_IS_WEB, mono_assert } from "./globals"; let node_fs: any | undefined = undefined; let node_url: any | undefined = undefined; From 5c0faf079aa73bc0c75e76625e20eac93c60f010 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 14 Jul 2023 19:02:00 +0200 Subject: [PATCH 17/19] fix abort + sample --- src/mono/sample/wasm/browser-shutdown/main.js | 11 ++++ src/mono/wasm/runtime/loader/config.ts | 2 +- src/mono/wasm/runtime/loader/exit.ts | 52 +++++++++++++------ src/mono/wasm/runtime/loader/run.ts | 13 +++++ 4 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/mono/sample/wasm/browser-shutdown/main.js b/src/mono/sample/wasm/browser-shutdown/main.js index 4e68ca70fd429..d727f7932a10d 100644 --- a/src/mono/sample/wasm/browser-shutdown/main.js +++ b/src/mono/sample/wasm/browser-shutdown/main.js @@ -10,7 +10,18 @@ window.addEventListener("load", onLoad); try { const { setModuleImports, getAssemblyExports, setEnvironmentVariable, getConfig } = await dotnet + .withModuleConfig() + .withExitOnUnhandledError() + .withExitCodeLogging() .withElementOnExit() + .withAssertAfterExit() + .withOnConfigLoaded(() => { + // you can test abort of the startup by opening http://localhost:8000/?throwError=true + const params = new URLSearchParams(location.search); + if (params.get("throwError") === "true") { + throw new Error("Error thrown from OnConfigLoaded"); + } + }) .create(); setModuleImports("main.js", { diff --git a/src/mono/wasm/runtime/loader/config.ts b/src/mono/wasm/runtime/loader/config.ts index 4b331a9f65d14..7d4f41add610d 100644 --- a/src/mono/wasm/runtime/loader/config.ts +++ b/src/mono/wasm/runtime/loader/config.ts @@ -117,7 +117,7 @@ export async function mono_wasm_load_config(module: DotnetModuleInternal): Promi loaderHelpers.afterConfigLoaded.promise_control.resolve(loaderHelpers.config); } catch (err) { const errMessage = `Failed to load config file ${configFilePath} ${err} ${(err as Error)?.stack}`; - loaderHelpers.config = module.config = { message: errMessage, error: err, isError: true }; + loaderHelpers.config = module.config = Object.assign(loaderHelpers.config, { message: errMessage, error: err, isError: true }); loaderHelpers.abort_startup(errMessage, true); throw err; } diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index 49c6d6d8146b3..d7b3ad0c9a4dc 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -20,17 +20,26 @@ export function assert_runtime_running() { mono_assert(!loaderHelpers.assertAfterExit || !is_exited(), () => `mono runtime already exited with ${loaderHelpers.exitCode}`); } -export function abort_startup(reason: any, should_exit: boolean, should_throw?: boolean): void { - if (loaderHelpers.isAborted) return; - - const exit_code = (reason && typeof reason.status === "number") ? reason.status : 1; - reason = reason || (runtimeHelpers.ExitStatus - ? new runtimeHelpers.ExitStatus(exit_code) - : new Error("Exit with code " + exit_code)); +function unifyReason(exit_code: number, reason?: any) { + exit_code = (typeof reason === "object" && typeof reason.status === "number") ? reason.status : 1; + const message = (typeof reason === "object" && typeof reason.message === "number") + ? reason.message + : "" + reason; + reason = (typeof reason === "object") + ? reason + : (runtimeHelpers.ExitStatus + ? new runtimeHelpers.ExitStatus(exit_code) + : new Error("Exit with code " + exit_code + " " + message)); reason.status = exit_code; + if (!reason.message) reason.message = message; + return reason; +} +export function abort_startup(reason: any, should_exit: boolean, should_throw?: boolean): void { + if (loaderHelpers.isAborted) return; + reason = unifyReason(1, reason); loaderHelpers.isAborted = true; - loaderHelpers.exitCode = exit_code; + const exit_code = loaderHelpers.exitCode = reason.status || 1; mono_log_debug("abort_startup, reason: " + reason); loaderHelpers.allDownloadsQueued.promise_control.reject(reason); loaderHelpers.afterConfigLoaded.promise_control.reject(reason); @@ -47,7 +56,15 @@ export function abort_startup(reason: any, should_exit: boolean, should_throw?: runtimeHelpers.afterOnRuntimeInitialized.promise_control.reject(reason); runtimeHelpers.afterPostRun.promise_control.reject(reason); } - if (typeof reason !== "object" || reason.silent !== true) { + if (reason.silent !== true) { + try { + logErrorOnExit(exit_code, reason); + appendElementOnExit(exit_code); + } + catch { + // ignore any failures + } + reason.silent = true; if (should_exit || ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) { mono_exit(1, reason); } @@ -59,12 +76,8 @@ export function abort_startup(reason: any, should_exit: boolean, should_throw?: // this will also call mono_wasm_exit if available, which will call exitJS -> _proc_exit -> terminateAllThreads export function mono_exit(exit_code: number, reason?: any): void { - // make sure we have both reason and exit_code - exit_code = (reason && typeof reason.status === "number") ? reason.status : exit_code; - reason = reason || (runtimeHelpers.ExitStatus - ? new runtimeHelpers.ExitStatus(exit_code) - : new Error("Exit with code " + exit_code)); - reason.status = exit_code; + reason = unifyReason(exit_code, reason); + exit_code = reason.status || 1; if (!is_exited()) { try { @@ -152,8 +165,13 @@ function appendElementOnExit(exit_code: number) { function logErrorOnExit(exit_code: number, reason: any) { if (exit_code !== 0 && reason) { - if (reason instanceof Error && runtimeHelpers.stringify_as_error_with_stack) - mono_log_error(runtimeHelpers.stringify_as_error_with_stack(reason)); + if (reason instanceof Error) { + if (runtimeHelpers.stringify_as_error_with_stack) { + mono_log_error(runtimeHelpers.stringify_as_error_with_stack(reason)); + } else { + mono_log_error(reason.message + "\n" + reason.stack); + } + } else if (typeof reason == "string") mono_log_error(reason); else diff --git a/src/mono/wasm/runtime/loader/run.ts b/src/mono/wasm/runtime/loader/run.ts index a630b6770bf83..f32a843b0c9f2 100644 --- a/src/mono/wasm/runtime/loader/run.ts +++ b/src/mono/wasm/runtime/loader/run.ts @@ -36,6 +36,19 @@ export class HostBuilder implements DotnetHostBuilder { } } + // internal + withOnConfigLoaded(onConfigLoaded: (config: MonoConfig) => void | Promise): DotnetHostBuilder { + try { + deep_merge_module(module, { + onConfigLoaded + }); + return this; + } catch (err) { + mono_exit(1, err); + throw err; + } + } + // internal withConsoleForwarding(): DotnetHostBuilder { try { From fbbdfc74cb3c767ae05d54eb8a9640663ba26d98 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 14 Jul 2023 19:58:14 +0200 Subject: [PATCH 18/19] fix --- src/mono/wasm/runtime/loader/exit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index d7b3ad0c9a4dc..e91b0e35d0d51 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -21,7 +21,7 @@ export function assert_runtime_running() { } function unifyReason(exit_code: number, reason?: any) { - exit_code = (typeof reason === "object" && typeof reason.status === "number") ? reason.status : 1; + exit_code = (typeof reason === "object" && typeof reason.status === "number") ? reason.status : exit_code; const message = (typeof reason === "object" && typeof reason.message === "number") ? reason.message : "" + reason; From 8b430776266e66964d3dd0591379ff9d6fb173ca Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 14 Jul 2023 23:31:10 +0200 Subject: [PATCH 19/19] @maraf's feedback about just one exit method --- src/mono/wasm/runtime/gc-handles.ts | 2 +- src/mono/wasm/runtime/loader/assets.ts | 9 +- src/mono/wasm/runtime/loader/config.ts | 3 +- src/mono/wasm/runtime/loader/exit.ts | 102 ++++++++---------- src/mono/wasm/runtime/loader/globals.ts | 3 +- .../runtime/loader/libraryInitializers.ts | 9 +- src/mono/wasm/runtime/loader/logging.ts | 4 + src/mono/wasm/runtime/loader/run.ts | 19 ++-- src/mono/wasm/runtime/logging.ts | 4 + src/mono/wasm/runtime/run.ts | 4 +- src/mono/wasm/runtime/startup.ts | 60 ++++------- src/mono/wasm/runtime/types/internal.ts | 2 - 12 files changed, 97 insertions(+), 124 deletions(-) diff --git a/src/mono/wasm/runtime/gc-handles.ts b/src/mono/wasm/runtime/gc-handles.ts index 1c807ac19e775..c95e0a96b3d1c 100644 --- a/src/mono/wasm/runtime/gc-handles.ts +++ b/src/mono/wasm/runtime/gc-handles.ts @@ -114,7 +114,7 @@ export function assert_not_disposed(result: any): GCHandle { } function _js_owned_object_finalized(gc_handle: GCHandle): void { - if (loaderHelpers.isAborted || loaderHelpers.exitCode !== undefined) { + if (loaderHelpers.is_exited()) { // We're shutting down, so don't bother doing anything else. return; } diff --git a/src/mono/wasm/runtime/loader/assets.ts b/src/mono/wasm/runtime/loader/assets.ts index b68f360be1db8..c6f0ddff1cc94 100644 --- a/src/mono/wasm/runtime/loader/assets.ts +++ b/src/mono/wasm/runtime/loader/assets.ts @@ -6,6 +6,7 @@ import type { AssetBehaviours, AssetEntry, LoadingResource, ResourceRequest } fr import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { createPromiseController } from "./promise-controller"; import { mono_log_debug } from "./logging"; +import { mono_exit } from "./exit"; let throttlingPromise: PromiseAndController | undefined; @@ -181,10 +182,10 @@ export async function mono_download_assets(): Promise { // and we are not awating it here Promise.all(promises_of_asset_instantiation).then(() => { runtimeHelpers.allAssetsInMemory.promise_control.resolve(); - }).catch(e => { - loaderHelpers.err("Error in mono_download_assets: " + e); - loaderHelpers.abort_startup(e, true); - throw e; + }).catch(err => { + loaderHelpers.err("Error in mono_download_assets: " + err); + mono_exit(1, err); + throw err; }); // OPTIMIZATION explained: // we do it this way so that we could allocate memory immediately after asset is downloaded (and after onRuntimeInitialized which happened already) diff --git a/src/mono/wasm/runtime/loader/config.ts b/src/mono/wasm/runtime/loader/config.ts index 7d4f41add610d..6b1b5932a3905 100644 --- a/src/mono/wasm/runtime/loader/config.ts +++ b/src/mono/wasm/runtime/loader/config.ts @@ -10,6 +10,7 @@ import { BootConfigResult } from "./blazor/BootConfig"; import { BootJsonData } from "../types/blazor"; import { mono_log_error, mono_log_debug } from "./logging"; import { invokeLibraryInitializers } from "./libraryInitializers"; +import { mono_exit } from "./exit"; export function deep_merge_config(target: MonoConfigInternal, source: MonoConfigInternal): MonoConfigInternal { const providedConfig: MonoConfigInternal = { ...source }; @@ -118,7 +119,7 @@ export async function mono_wasm_load_config(module: DotnetModuleInternal): Promi } catch (err) { const errMessage = `Failed to load config file ${configFilePath} ${err} ${(err as Error)?.stack}`; loaderHelpers.config = module.config = Object.assign(loaderHelpers.config, { message: errMessage, error: err, isError: true }); - loaderHelpers.abort_startup(errMessage, true); + mono_exit(1, new Error(errMessage)); throw err; } } \ No newline at end of file diff --git a/src/mono/wasm/runtime/loader/exit.ts b/src/mono/wasm/runtime/loader/exit.ts index e91b0e35d0d51..57cb33e66fcbd 100644 --- a/src/mono/wasm/runtime/loader/exit.ts +++ b/src/mono/wasm/runtime/loader/exit.ts @@ -1,87 +1,55 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, INTERNAL, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; +import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, INTERNAL, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { mono_log_debug, consoleWebSocket, mono_log_error, mono_log_info_no_prefix } from "./logging"; - export function is_exited() { return loaderHelpers.exitCode !== undefined; } export function is_runtime_running() { - return runtimeHelpers.runtimeReady && !is_exited() && !loaderHelpers.isAborted; + return runtimeHelpers.runtimeReady && !is_exited(); } - export function assert_runtime_running() { mono_assert(runtimeHelpers.runtimeReady, "mono runtime didn't start yet"); - mono_assert(!loaderHelpers.isAborted, "startup of the mono runtime was aborted"); mono_assert(!loaderHelpers.assertAfterExit || !is_exited(), () => `mono runtime already exited with ${loaderHelpers.exitCode}`); } -function unifyReason(exit_code: number, reason?: any) { - exit_code = (typeof reason === "object" && typeof reason.status === "number") ? reason.status : exit_code; - const message = (typeof reason === "object" && typeof reason.message === "number") +// this will also call mono_wasm_exit if available, which will call exitJS -> _proc_exit -> terminateAllThreads +export function mono_exit(exit_code: number, reason?: any): void { + // unify shape of the reason object + const is_object = reason && typeof reason === "object"; + exit_code = (is_object && typeof reason.status === "number") ? reason.status : exit_code; + const message = (is_object && typeof reason.message === "string") ? reason.message : "" + reason; - reason = (typeof reason === "object") + reason = is_object ? reason : (runtimeHelpers.ExitStatus ? new runtimeHelpers.ExitStatus(exit_code) : new Error("Exit with code " + exit_code + " " + message)); reason.status = exit_code; - if (!reason.message) reason.message = message; - return reason; -} - -export function abort_startup(reason: any, should_exit: boolean, should_throw?: boolean): void { - if (loaderHelpers.isAborted) return; - reason = unifyReason(1, reason); - loaderHelpers.isAborted = true; - const exit_code = loaderHelpers.exitCode = reason.status || 1; - mono_log_debug("abort_startup, reason: " + reason); - loaderHelpers.allDownloadsQueued.promise_control.reject(reason); - loaderHelpers.afterConfigLoaded.promise_control.reject(reason); - loaderHelpers.wasmDownloadPromise.promise_control.reject(reason); - loaderHelpers.runtimeModuleLoaded.promise_control.reject(reason); - if (runtimeHelpers.dotnetReady) { - runtimeHelpers.dotnetReady.promise_control.reject(reason); - runtimeHelpers.memorySnapshotSkippedOrDone.promise_control.reject(reason); - runtimeHelpers.afterInstantiateWasm.promise_control.reject(reason); - runtimeHelpers.beforePreInit.promise_control.reject(reason); - runtimeHelpers.afterPreInit.promise_control.reject(reason); - runtimeHelpers.afterPreRun.promise_control.reject(reason); - runtimeHelpers.beforeOnRuntimeInitialized.promise_control.reject(reason); - runtimeHelpers.afterOnRuntimeInitialized.promise_control.reject(reason); - runtimeHelpers.afterPostRun.promise_control.reject(reason); + if (!reason.message) { + reason.message = message; } - if (reason.silent !== true) { - try { - logErrorOnExit(exit_code, reason); - appendElementOnExit(exit_code); - } - catch { - // ignore any failures - } - reason.silent = true; - if (should_exit || ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) { - mono_exit(1, reason); - } - if (should_throw) { - throw reason; - } + + // force stack property to be generated before we shut down managed code, or create current stack if it doesn't exist + if (!reason.stack) { + reason.stack = new Error().stack || ""; } -} -// this will also call mono_wasm_exit if available, which will call exitJS -> _proc_exit -> terminateAllThreads -export function mono_exit(exit_code: number, reason?: any): void { - reason = unifyReason(exit_code, reason); - exit_code = reason.status || 1; + // don't report this error twice + reason.silent = true; if (!is_exited()) { try { - reason.stack;// force stack to be generated before we shut down managed code + reason.stack; + if (!runtimeHelpers.runtimeReady) { + mono_log_debug("abort_startup, reason: " + reason); + abort_promises(reason); + } logErrorOnExit(exit_code, reason); appendElementOnExit(exit_code); if (runtimeHelpers.jiterpreter_dump_stats) runtimeHelpers.jiterpreter_dump_stats(false); @@ -93,9 +61,7 @@ export function mono_exit(exit_code: number, reason?: any): void { // TODO forceDisposeProxies(); here loaderHelpers.exitCode = exit_code; - if (!loaderHelpers.isAborted && !runtimeHelpers.runtimeReady) abort_startup(reason, false, false); } - reason.silent = true; if (loaderHelpers.config && loaderHelpers.config.asyncFlushOnExit && exit_code === 0) { // this would NOT call Node's exit() immediately, it's a hanging promise @@ -116,20 +82,18 @@ export function mono_exit(exit_code: number, reason?: any): void { } function set_exit_code_and_quit_now(exit_code: number, reason?: any): void { - if (!is_exited() && runtimeHelpers.mono_wasm_exit) { + if (is_runtime_running() && runtimeHelpers.mono_wasm_exit) { runtimeHelpers.mono_wasm_exit(exit_code); } // just in case mono_wasm_exit didn't exit or throw if (exit_code !== 0 || !ENVIRONMENT_IS_WEB) { if (ENVIRONMENT_IS_NODE && INTERNAL.process) { INTERNAL.process.exit(exit_code); - throw reason; } else if (runtimeHelpers.quit) { runtimeHelpers.quit(exit_code, reason); - } else { - throw reason; } + throw reason; } } @@ -152,6 +116,24 @@ async function flush_node_streams() { } } +function abort_promises(reason: any) { + loaderHelpers.allDownloadsQueued.promise_control.reject(reason); + loaderHelpers.afterConfigLoaded.promise_control.reject(reason); + loaderHelpers.wasmDownloadPromise.promise_control.reject(reason); + loaderHelpers.runtimeModuleLoaded.promise_control.reject(reason); + if (runtimeHelpers.dotnetReady) { + runtimeHelpers.dotnetReady.promise_control.reject(reason); + runtimeHelpers.memorySnapshotSkippedOrDone.promise_control.reject(reason); + runtimeHelpers.afterInstantiateWasm.promise_control.reject(reason); + runtimeHelpers.beforePreInit.promise_control.reject(reason); + runtimeHelpers.afterPreInit.promise_control.reject(reason); + runtimeHelpers.afterPreRun.promise_control.reject(reason); + runtimeHelpers.beforeOnRuntimeInitialized.promise_control.reject(reason); + runtimeHelpers.afterOnRuntimeInitialized.promise_control.reject(reason); + runtimeHelpers.afterPostRun.promise_control.reject(reason); + } +} + function appendElementOnExit(exit_code: number) { if (ENVIRONMENT_IS_WEB && loaderHelpers.config && loaderHelpers.config.appendElementOnExit) { //Tell xharness WasmBrowserTestRunner what was the exit code diff --git a/src/mono/wasm/runtime/loader/globals.ts b/src/mono/wasm/runtime/loader/globals.ts index 775fa5605c757..7fac0d5ac7e85 100644 --- a/src/mono/wasm/runtime/loader/globals.ts +++ b/src/mono/wasm/runtime/loader/globals.ts @@ -3,7 +3,7 @@ import type { AssetEntryInternal, GlobalObjects, LoaderHelpers, RuntimeHelpers } from "../types/internal"; import type { MonoConfig, RuntimeAPI } from "../types"; -import { abort_startup, assert_runtime_running, is_exited, is_runtime_running, mono_exit } from "./exit"; +import { assert_runtime_running, is_exited, is_runtime_running, mono_exit } from "./exit"; import { assertIsControllablePromise, createPromiseController, getPromiseController } from "./promise-controller"; import { mono_download_assets, resolve_asset_path } from "./assets"; import { mono_log_error, setup_proxy_console } from "./logging"; @@ -80,7 +80,6 @@ export function setLoaderGlobals( wasmDownloadPromise: createPromiseController(), runtimeModuleLoaded: createPromiseController(), - abort_startup, is_exited, is_runtime_running, assert_runtime_running, diff --git a/src/mono/wasm/runtime/loader/libraryInitializers.ts b/src/mono/wasm/runtime/loader/libraryInitializers.ts index f9f5b472aed5e..87007e35e98cd 100644 --- a/src/mono/wasm/runtime/loader/libraryInitializers.ts +++ b/src/mono/wasm/runtime/loader/libraryInitializers.ts @@ -5,7 +5,7 @@ import { mono_log_warn } from "./logging"; import { MonoConfig } from "../types"; import { appendUniqueQuery } from "./assets"; import { loaderHelpers } from "./globals"; -import { abort_startup } from "./exit"; +import { mono_exit } from "./exit"; export type LibraryInitializerTypes = "onRuntimeConfigLoaded" @@ -62,8 +62,9 @@ export async function invokeLibraryInitializers(functionName: string, args: any[ async function abortStartupOnError(scriptName: string, methodName: string, callback: () => Promise | undefined): Promise { try { await callback(); - } catch (error) { - mono_log_warn(`Failed to invoke '${methodName}' on library initializer '${scriptName}': ${error}`); - abort_startup(error, true); + } catch (err) { + mono_log_warn(`Failed to invoke '${methodName}' on library initializer '${scriptName}': ${err}`); + mono_exit(1, err); + throw err; } } \ No newline at end of file diff --git a/src/mono/wasm/runtime/loader/logging.ts b/src/mono/wasm/runtime/loader/logging.ts index 72a62f97487fb..eecddd2fb7654 100644 --- a/src/mono/wasm/runtime/loader/logging.ts +++ b/src/mono/wasm/runtime/loader/logging.ts @@ -25,6 +25,10 @@ export function mono_log_warn(msg: string, ...data: any) { } export function mono_log_error(msg: string, ...data: any) { + if (data && data.length > 0 && data[0] && typeof data[0] === "object" && data[0].silent) { + // don't log silent errors + return; + } console.error(prefix + msg, ...data); } export let consoleWebSocket: WebSocket; diff --git a/src/mono/wasm/runtime/loader/run.ts b/src/mono/wasm/runtime/loader/run.ts index f32a843b0c9f2..6db906ab75efe 100644 --- a/src/mono/wasm/runtime/loader/run.ts +++ b/src/mono/wasm/runtime/loader/run.ts @@ -8,7 +8,7 @@ import type { MonoConfigInternal, EmscriptenModuleInternal, RuntimeModuleExports import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WEB, exportedRuntimeAPI, globalObjectsRoot, mono_assert } from "./globals"; import { deep_merge_config, deep_merge_module, mono_wasm_load_config } from "./config"; -import { is_exited, mono_exit } from "./exit"; +import { mono_exit } from "./exit"; import { setup_proxy_console, mono_log_info } from "./logging"; import { resolve_asset_path, start_asset_download } from "./assets"; import { detect_features_and_polyfill } from "./polyfills"; @@ -67,7 +67,7 @@ export class HostBuilder implements DotnetHostBuilder { const handler = function fatal_handler(event: Event, error: any) { event.preventDefault(); try { - if ((!error || !error.silent) && !is_exited()) mono_exit(1, error); + if (!error || !error.silent) mono_exit(1, error); } catch (err) { // no not re-throw from the fatal handler } @@ -402,11 +402,6 @@ export class HostBuilder implements DotnetHostBuilder { } export async function createEmscripten(moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)): Promise { - if (BuildConfiguration === "Debug") { - mono_log_info(`starting script ${loaderHelpers.scriptUrl}`); - mono_log_info(`starting in ${loaderHelpers.scriptDirectory}`); - } - // extract ModuleConfig if (typeof moduleFactory === "function") { const extension = moduleFactory(globalObjectsRoot.api) as any; @@ -423,6 +418,12 @@ export async function createEmscripten(moduleFactory: DotnetModuleConfig | ((api throw new Error("Can't use moduleFactory callback of createDotnetRuntime function."); } + await detect_features_and_polyfill(module); + if (BuildConfiguration === "Debug") { + mono_log_info(`starting script ${loaderHelpers.scriptUrl}`); + mono_log_info(`starting in ${loaderHelpers.scriptDirectory}`); + } + return module.ENVIRONMENT_IS_PTHREAD ? createEmscriptenWorker() : createEmscriptenMain(); @@ -458,8 +459,6 @@ function initializeModules(es6Modules: [RuntimeModuleExportsInternal, NativeModu } async function createEmscriptenMain(): Promise { - await detect_features_and_polyfill(module); - if (!module.configSrc && (!module.config || Object.keys(module.config).length === 0 || !module.config.assets)) { // if config file location nor assets are provided module.configSrc = "./blazor.boot.json"; @@ -489,8 +488,6 @@ async function createEmscriptenMain(): Promise { } async function createEmscriptenWorker(): Promise { - await detect_features_and_polyfill(module); - setupPreloadChannelToMainThread(); await loaderHelpers.afterConfigLoaded.promise; diff --git a/src/mono/wasm/runtime/logging.ts b/src/mono/wasm/runtime/logging.ts index ad1da8788e396..2b24ad453dae8 100644 --- a/src/mono/wasm/runtime/logging.ts +++ b/src/mono/wasm/runtime/logging.ts @@ -27,6 +27,10 @@ export function mono_log_warn(msg: string, ...data: any) { } export function mono_log_error(msg: string, ...data: any) { + if (data && data.length > 0 && data[0] && typeof data[0] === "object" && data[0].silent) { + // don't log silent errors + return; + } console.error(prefix + msg, ...data); } diff --git a/src/mono/wasm/runtime/run.ts b/src/mono/wasm/runtime/run.ts index bd57f61b8e851..184a8d6c8d9ff 100644 --- a/src/mono/wasm/runtime/run.ts +++ b/src/mono/wasm/runtime/run.ts @@ -17,14 +17,14 @@ export async function mono_run_main_and_exit(main_assembly_name: string, args: s const result = await mono_run_main(main_assembly_name, args); loaderHelpers.mono_exit(result); return result; - } catch (error) { + } catch (error: any) { try { loaderHelpers.mono_exit(1, error); } catch (e) { // ignore } - if (error instanceof runtimeHelpers.ExitStatus) { + if (error && typeof error.status === "number") { return error.status; } return 1; diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 106a149e56c4c..0f2bf33682cd0 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -24,7 +24,7 @@ import { preAllocatePThreadWorkerPool, instantiateWasmPThreadWorkerPool } from " import { export_linker } from "./exports-linker"; import { endMeasure, MeasuredBlock, startMeasure } from "./profiler"; import { getMemorySnapshot, storeMemorySnapshot, getMemorySnapshotSize } from "./snapshot"; -import { mono_log_debug, mono_log_warn, mono_set_thread_id } from "./logging"; +import { mono_log_debug, mono_log_error, mono_log_warn, mono_set_thread_id } from "./logging"; import { getBrowserThreadID } from "./pthreads/shared"; // legacy @@ -96,14 +96,12 @@ export function configureEmscriptenStartup(module: DotnetModuleInternal): void { // execution order == [*] == if (!module.onAbort) { module.onAbort = (error) => { - const is_exited = loaderHelpers.is_exited(); - if (!runtimeHelpers.runtimeReady) loaderHelpers.abort_startup(error, is_exited, false); + loaderHelpers.mono_exit(1, error); }; } if (!module.onExit) { module.onExit = (code) => { - if (!runtimeHelpers.runtimeReady) loaderHelpers.abort_startup("exit " + code, false, false); - if (!loaderHelpers.is_exited()) loaderHelpers.mono_exit(code, null); + loaderHelpers.mono_exit(code, null); }; } } @@ -156,8 +154,8 @@ function preInit(userPreInit: (() => void)[]) { // all user Module.preInit callbacks userPreInit.forEach(fn => fn()); } catch (err) { - _print_error("user preInint() failed", err); - loaderHelpers.abort_startup(err, true); + mono_log_error("user preInint() failed", err); + loaderHelpers.mono_exit(1, err); throw err; } // this will start immediately but return on first await. @@ -174,7 +172,7 @@ function preInit(userPreInit: (() => void)[]) { endMeasure(mark, MeasuredBlock.preInit); } catch (err) { - loaderHelpers.abort_startup(err, true); + loaderHelpers.mono_exit(1, err); throw err; } // signal next stage @@ -194,8 +192,8 @@ async function preInitWorkerAsync() { runtimeHelpers.afterPreInit.promise_control.resolve(); endMeasure(mark, MeasuredBlock.preInitWorker); } catch (err) { - _print_error("user preInitWorker() failed", err); - loaderHelpers.abort_startup(err, true); + mono_log_error("user preInitWorker() failed", err); + loaderHelpers.mono_exit(1, err); throw err; } } @@ -218,8 +216,8 @@ async function preRunAsync(userPreRun: (() => void)[]) { userPreRun.map(fn => fn()); endMeasure(mark, MeasuredBlock.preRun); } catch (err) { - _print_error("user callback preRun() failed", err); - loaderHelpers.abort_startup(err, true); + mono_log_error("user callback preRun() failed", err); + loaderHelpers.mono_exit(1, err); throw err; } // signal next stage @@ -253,7 +251,7 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { : new Error("Snapshot taken, exiting because exitAfterSnapshot was set."); reason.silent = true; - loaderHelpers.abort_startup(reason, false, true); + loaderHelpers.mono_exit(0, reason); return; } @@ -283,15 +281,15 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { userOnRuntimeInitialized(); } catch (err: any) { - _print_error("user callback onRuntimeInitialized() failed", err); + mono_log_error("user callback onRuntimeInitialized() failed", err); throw err; } // finish await mono_wasm_after_user_runtime_initialized(); endMeasure(mark, MeasuredBlock.onRuntimeInitialized); } catch (err) { - _print_error("onRuntimeInitializedAsync() failed", err); - loaderHelpers.abort_startup(err, true); + mono_log_error("onRuntimeInitializedAsync() failed", err); + loaderHelpers.mono_exit(1, err); throw err; } // signal next stage @@ -313,8 +311,8 @@ async function postRunAsync(userpostRun: (() => void)[]) { userpostRun.map(fn => fn()); endMeasure(mark, MeasuredBlock.postRun); } catch (err) { - _print_error("user callback posRun() failed", err); - loaderHelpers.abort_startup(err, true); + mono_log_error("user callback posRun() failed", err); + loaderHelpers.mono_exit(1, err); throw err; } // signal next stage @@ -410,28 +408,16 @@ async function mono_wasm_after_user_runtime_initialized(): Promise { await Module.onDotnetReady(); } catch (err: any) { - _print_error("onDotnetReady () failed", err); + mono_log_error("onDotnetReady () failed", err); throw err; } } } catch (err: any) { - _print_error("Error in mono_wasm_after_user_runtime_initialized", err); + mono_log_error("mono_wasm_after_user_runtime_initialized () failed", err); throw err; } } - -function _print_error(message: string, err: any): void { - if (typeof err === "object" && err.silent) { - return; - } - Module.err(`${message}: ${JSON.stringify(err)}`); - if (typeof err === "object" && err.stack) { - Module.err("Stacktrace: \n"); - Module.err(err.stack); - } -} - // 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 { @@ -518,8 +504,8 @@ async function instantiate_wasm_module( } runtimeHelpers.afterInstantiateWasm.promise_control.resolve(); } catch (err) { - _print_error("instantiate_wasm_module() failed", err); - loaderHelpers.abort_startup(err, true); + mono_log_error("instantiate_wasm_module() failed", err); + loaderHelpers.mono_exit(1, err); throw err; } Module.removeRunDependency("instantiate_wasm_module"); @@ -586,8 +572,8 @@ export function mono_wasm_load_runtime(unused?: string, debugLevel?: number): vo endMeasure(mark, MeasuredBlock.loadRuntime); } catch (err: any) { - _print_error("mono_wasm_load_runtime () failed", err); - loaderHelpers.abort_startup(err, false); + mono_log_error("mono_wasm_load_runtime () failed", err); + loaderHelpers.mono_exit(1, err); throw err; } } @@ -610,7 +596,7 @@ export function bindings_init(): void { runtimeHelpers._i52_error_scratch_buffer = Module._malloc(4); endMeasure(mark, MeasuredBlock.bindingsInit); } catch (err) { - _print_error("Error in bindings_init", err); + mono_log_error("Error in bindings_init", err); throw err; } } diff --git a/src/mono/wasm/runtime/types/internal.ts b/src/mono/wasm/runtime/types/internal.ts index 823e0ad07eac3..acaccaf233c7a 100644 --- a/src/mono/wasm/runtime/types/internal.ts +++ b/src/mono/wasm/runtime/types/internal.ts @@ -103,7 +103,6 @@ export type LoaderHelpers = { assertAfterExit: boolean; exitCode: number | undefined; - isAborted: boolean; loadedFiles: string[], _loaded_files: { url: string, file: string }[]; @@ -127,7 +126,6 @@ export type LoaderHelpers = { is_exited: () => boolean, is_runtime_running: () => boolean, assert_runtime_running: () => void, - abort_startup: (reason: any, should_exit: boolean, should_throw?: boolean) => void, mono_exit: (exit_code: number, reason?: any) => void, createPromiseController: (afterResolve?: () => void, afterReject?: () => void) => PromiseAndController, getPromiseController: (promise: ControllablePromise) => PromiseController,