From d9a5256b126ef4b3e80b605a2770c138c672c1c2 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 12 Jun 2026 17:34:56 +0200 Subject: [PATCH 01/12] export memory from Module (cherry picked from commit fa8db40e4e2a6f9f2544d9f632a4086584bf9daa) --- eng/native.wasm.targets | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/eng/native.wasm.targets b/eng/native.wasm.targets index 5c1238f58dc590..a61e4863d08d6b 100644 --- a/eng/native.wasm.targets +++ b/eng/native.wasm.targets @@ -55,6 +55,16 @@ + + + + + + + + + + From a7a8e0db115d971b81d3cfefcfe0e5842cffd5ad Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 12 Jun 2026 17:34:20 +0200 Subject: [PATCH 02/12] don't node flush streams in a browser (cherry picked from commit 4113f8e52586234464f2d00d6846d62c13eb3453) --- src/mono/browser/runtime/loader/exit.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mono/browser/runtime/loader/exit.ts b/src/mono/browser/runtime/loader/exit.ts index b57d06b993cc48..bfddfbf3f29097 100644 --- a/src/mono/browser/runtime/loader/exit.ts +++ b/src/mono/browser/runtime/loader/exit.ts @@ -204,6 +204,7 @@ function set_exit_code_and_quit_now (exit_code: number, reason?: any): void { } async function flush_node_streams () { + if (!ENVIRONMENT_IS_NODE) return; try { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: From 0aea51d6c7d7c687fcb8ed3be2f5deeb910012ff Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 12 Jun 2026 19:53:10 +0200 Subject: [PATCH 03/12] WASM_BIGINT=0 for node testing (cherry picked from commit 5ad201d14a0a9d809056e05e71063ffdc127b509) --- src/coreclr/hosts/corerun/CMakeLists.txt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/coreclr/hosts/corerun/CMakeLists.txt b/src/coreclr/hosts/corerun/CMakeLists.txt index 8a7b70e7a915ac..d4a764d47317a5 100644 --- a/src/coreclr/hosts/corerun/CMakeLists.txt +++ b/src/coreclr/hosts/corerun/CMakeLists.txt @@ -98,11 +98,6 @@ else() -Wl,--error-limit=0) if (CORERUN_IN_BROWSER) - # Node.js doesn't have good support for WASM_BIGINT - # so it only is added when running in the browser. - target_link_options(corerun PRIVATE - -sWASM_BIGINT=1) - # Include the virtual file system data for the # browser scenario. set(WASM_PRELOAD_DIR "${CMAKE_INSTALL_PREFIX}/IL") @@ -111,6 +106,10 @@ else() --preload-file ${WASM_PRELOAD_DIR}@/) endif() else() + # Node.js doesn't have good support for WASM_BIGINT + # so it only is added when running in the browser. + target_link_options(corerun PRIVATE + -sWASM_BIGINT=0) # If not running in the browser, add # Node.js file system support. target_link_options(corerun PRIVATE From 3a24a58247476ebf17c8e7d5da9a94461c222a22 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 12 Jun 2026 22:22:52 +0200 Subject: [PATCH 04/12] balance runtimeKeepalivePop (cherry picked from commit 0dab167d17a670eac27902b56db8af048ad4df6c) --- src/mono/browser/runtime/startup.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts index c086c6b7c4bb6e..8f0887c90ef81a 100644 --- a/src/mono/browser/runtime/startup.ts +++ b/src/mono/browser/runtime/startup.ts @@ -291,7 +291,6 @@ async function onRuntimeInitializedAsync (userOnRuntimeInitialized: (module:Emsc await mono_wasm_after_user_runtime_initialized(); endMeasure(mark, MeasuredBlock.onRuntimeInitialized); } catch (err) { - Module.runtimeKeepalivePop(); mono_log_error("onRuntimeInitializedAsync() failed", err); loaderHelpers.mono_exit(1, err); throw err; From c9fa6124f7599708e4687fec3eecb541a6578662 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 12 Jun 2026 21:50:02 +0200 Subject: [PATCH 05/12] balance clearTimeout (cherry picked from commit 3bcf4d0407494dab5c17593ce9d8b2449e26f296) --- src/mono/browser/runtime/diagnostics/dotnet-gcdump.ts | 1 + src/mono/browser/runtime/scheduling.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mono/browser/runtime/diagnostics/dotnet-gcdump.ts b/src/mono/browser/runtime/diagnostics/dotnet-gcdump.ts index d0d503db4d5ceb..d11c2834828368 100644 --- a/src/mono/browser/runtime/diagnostics/dotnet-gcdump.ts +++ b/src/mono/browser/runtime/diagnostics/dotnet-gcdump.ts @@ -23,6 +23,7 @@ export function collectGcDump (options?:DiagnosticCommandOptions):Promise { stopSent = true; diff --git a/src/mono/browser/runtime/scheduling.ts b/src/mono/browser/runtime/scheduling.ts index 93dde2a5525281..1a374630d3d18e 100644 --- a/src/mono/browser/runtime/scheduling.ts +++ b/src/mono/browser/runtime/scheduling.ts @@ -70,18 +70,19 @@ export function SystemJS_ScheduleTimerImpl (shortestDueTimeMs: number): void { if (lastScheduledTimeoutId) { globalThis.clearTimeout(lastScheduledTimeoutId); lastScheduledTimeoutId = undefined; + Module.runtimeKeepalivePop(); } lastScheduledTimeoutId = Module.safeSetTimeout(mono_wasm_schedule_timer_tick, shortestDueTimeMs); } function mono_wasm_schedule_timer_tick () { if (WasmEnableThreads) return; + lastScheduledTimeoutId = undefined; Module.maybeExit(); forceThreadMemoryViewRefresh(); if (!loaderHelpers.is_runtime_running()) { return; } - lastScheduledTimeoutId = undefined; try { cwraps.mono_wasm_execute_timer(); } catch (ex) { From 2f3d9c274dba1027ff70561082ae94eeb9702bb5 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sat, 13 Jun 2026 20:50:49 +0200 Subject: [PATCH 06/12] disable WASM_BIGINT for corerun - NodeJS (cherry picked from commit 53a6d163992d4d016677b73e4f6561b274d6ce5a) --- docs/workflow/building/coreclr/wasm.md | 3 ++- eng/native.wasm.targets | 5 +++-- src/coreclr/runtime.proj | 12 +++++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/workflow/building/coreclr/wasm.md b/docs/workflow/building/coreclr/wasm.md index b2de062d88b066..99dd2911483441 100644 --- a/docs/workflow/building/coreclr/wasm.md +++ b/docs/workflow/building/coreclr/wasm.md @@ -146,7 +146,8 @@ In config below please replace `/path/to/runtime/` by a **absolute unix path** t "/**" ], "runtimeArgs": [ - "--stack-trace-limit=1000" + "--stack-trace-limit=10000", + "--experimental-wasm-exnref" ], "args": [ "HelloWorld.dll" diff --git a/eng/native.wasm.targets b/eng/native.wasm.targets index a61e4863d08d6b..a162ba21910ef6 100644 --- a/eng/native.wasm.targets +++ b/eng/native.wasm.targets @@ -63,8 +63,9 @@ - - + + + diff --git a/src/coreclr/runtime.proj b/src/coreclr/runtime.proj index 2c968913b26f4c..a0a11f404243b1 100644 --- a/src/coreclr/runtime.proj +++ b/src/coreclr/runtime.proj @@ -15,7 +15,17 @@ - + + + + + <_WasmDisableBigInt>true + + + From 549e9fdbd7c574c0fe8bb6475757fa619125bce6 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sun, 14 Jun 2026 12:25:06 +0200 Subject: [PATCH 07/12] Bump FireFox to 140.11.0esr (cherry picked from commit 7f3a8399ae9ea7bf42c4a6c50492cee3fcd84dbe) --- eng/testing/BrowserVersions.props | 8 ++++---- eng/testing/wasm-provisioning.targets | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/testing/BrowserVersions.props b/eng/testing/BrowserVersions.props index 31f40bbf8ba605..45640424f61f2e 100644 --- a/eng/testing/BrowserVersions.props +++ b/eng/testing/BrowserVersions.props @@ -12,9 +12,9 @@ 1625079 https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/1625123 14.9.207 - 125.0.1 - 0.34.0 - 125.0.1 - 0.34.0 + 140.11.0esr + 0.37.0 + 140.11.0esr + 0.37.0 \ No newline at end of file diff --git a/eng/testing/wasm-provisioning.targets b/eng/testing/wasm-provisioning.targets index 20e1e00ddef184..b2cb4f1b1a92d9 100644 --- a/eng/testing/wasm-provisioning.targets +++ b/eng/testing/wasm-provisioning.targets @@ -28,7 +28,7 @@ $(ArtifactsBinDir)geckodriver\ - https://ftp.mozilla.org/pub/firefox/releases/$(linux_FirefoxRevision)/linux-x86_64/en-US/firefox-$(linux_FirefoxRevision).tar.bz2 + https://ftp.mozilla.org/pub/firefox/releases/$(linux_FirefoxRevision)/linux-x86_64/en-US/firefox-$(linux_FirefoxRevision).tar.xz https://github.com/mozilla/geckodriver/releases/download/v$(linux_GeckoDriverRevision)/geckodriver-v$(linux_GeckoDriverRevision)-linux64.tar.gz firefox geckodriver From 151ecdda752c4a6af606c9de0cf7a248f8645c27 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sun, 14 Jun 2026 15:46:46 +0200 Subject: [PATCH 08/12] improve runtimeKeepalivePop() --- src/mono/browser/runtime/startup.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts index 8f0887c90ef81a..92bd64d28e5d9a 100644 --- a/src/mono/browser/runtime/startup.ts +++ b/src/mono/browser/runtime/startup.ts @@ -197,6 +197,7 @@ async function preRunAsync (userPreRun: ((module:EmscriptenModule) => void)[]) { } async function onRuntimeInitializedAsync (userOnRuntimeInitialized: (module:EmscriptenModule) => void) { + let keepAlivePushed = false; try { // wait for previous stage await runtimeHelpers.afterPreRun.promise; @@ -227,7 +228,10 @@ async function onRuntimeInitializedAsync (userOnRuntimeInitialized: (module:Emsc setTimeout(maybeSaveInterpPgoTable, (runtimeHelpers.config.interpreterPgoSaveDelay || 15) * 1000); + // this push is un-balanced for short while until runtimeReady = true. Accepted trade-off. Module.runtimeKeepalivePush(); + keepAlivePushed = true; + if (WasmEnableThreads && BuildConfiguration === "Debug" && globalThis.setInterval) globalThis.setInterval(() => { mono_log_info("UI thread is alive!"); }, 3000); @@ -291,6 +295,7 @@ async function onRuntimeInitializedAsync (userOnRuntimeInitialized: (module:Emsc await mono_wasm_after_user_runtime_initialized(); endMeasure(mark, MeasuredBlock.onRuntimeInitialized); } catch (err) { + if (keepAlivePushed && !runtimeHelpers.runtimeReady) Module.runtimeKeepalivePop(); mono_log_error("onRuntimeInitializedAsync() failed", err); loaderHelpers.mono_exit(1, err); throw err; From 10a3963b058bbe04129a69c6f23edfe22c26327a Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sun, 14 Jun 2026 15:53:45 +0200 Subject: [PATCH 09/12] WASM_BIGINT feedback --- eng/native.wasm.targets | 5 ++- src/coreclr/hosts/corerun/CMakeLists.txt | 40 +++++++++++++----------- src/coreclr/runtime.proj | 10 ------ 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/eng/native.wasm.targets b/eng/native.wasm.targets index a162ba21910ef6..a61e4863d08d6b 100644 --- a/eng/native.wasm.targets +++ b/eng/native.wasm.targets @@ -63,9 +63,8 @@ - - - + + diff --git a/src/coreclr/hosts/corerun/CMakeLists.txt b/src/coreclr/hosts/corerun/CMakeLists.txt index d4a764d47317a5..98add3094ad678 100644 --- a/src/coreclr/hosts/corerun/CMakeLists.txt +++ b/src/coreclr/hosts/corerun/CMakeLists.txt @@ -79,24 +79,6 @@ else() LINK_DEPENDS "${JS_CORE_RUN};${JS_CORE_RUN_EXTPOST};${JS_SYSTEM_NATIVE_BROWSER};${JS_SYSTEM_BROWSER_UTILS};" LINK_FLAGS "--js-library ${JS_SYSTEM_NATIVE_BROWSER} --js-library ${JS_SYSTEM_BROWSER_UTILS} --js-library ${JS_CORE_RUN} --extern-post-js ${JS_CORE_RUN_EXTPOST}" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - target_link_options(corerun PRIVATE - -sINITIAL_MEMORY=134217728 - -sMAXIMUM_MEMORY=4294967296 - -sALLOW_MEMORY_GROWTH=1 - -sALLOW_TABLE_GROWTH=1 - -sSTACK_SIZE=5MB - -sMODULARIZE=1 - -sEXPORT_ES6=1 - -sEXIT_RUNTIME=1 - -sEXPORTED_RUNTIME_METHODS=CORERUN,ENV,${CMAKE_EMCC_EXPORTED_RUNTIME_METHODS} - -sEXPORTED_FUNCTIONS=_main,${CMAKE_EMCC_EXPORTED_FUNCTIONS} - -sEXPORT_NAME=createDotnetRuntime - -sENVIRONMENT=node,shell,web - -lexports.js - -Wl,--export=__stack_pointer - -Wl,--export=__coreclr_wasm_rtlrestorecontext_tag - -Wl,--error-limit=0) - if (CORERUN_IN_BROWSER) # Include the virtual file system data for the # browser scenario. @@ -110,12 +92,34 @@ else() # so it only is added when running in the browser. target_link_options(corerun PRIVATE -sWASM_BIGINT=0) + + # remove HEAP64 and HEAPU64 from CMAKE_EMCC_EXPORTED_RUNTIME_METHODS + string(REPLACE "HEAP64," "" CMAKE_EMCC_EXPORTED_RUNTIME_METHODS "${CMAKE_EMCC_EXPORTED_RUNTIME_METHODS}") + string(REPLACE "HEAPU64," "" CMAKE_EMCC_EXPORTED_RUNTIME_METHODS "${CMAKE_EMCC_EXPORTED_RUNTIME_METHODS}") + # If not running in the browser, add # Node.js file system support. target_link_options(corerun PRIVATE -sNODERAWFS=1 -lnodefs.js) endif() + target_link_options(corerun PRIVATE + -sINITIAL_MEMORY=134217728 + -sMAXIMUM_MEMORY=4294967296 + -sALLOW_MEMORY_GROWTH=1 + -sALLOW_TABLE_GROWTH=1 + -sSTACK_SIZE=5MB + -sMODULARIZE=1 + -sEXPORT_ES6=1 + -sEXIT_RUNTIME=1 + -sEXPORTED_RUNTIME_METHODS=CORERUN,ENV,${CMAKE_EMCC_EXPORTED_RUNTIME_METHODS} + -sEXPORTED_FUNCTIONS=_main,${CMAKE_EMCC_EXPORTED_FUNCTIONS} + -sEXPORT_NAME=createDotnetRuntime + -sENVIRONMENT=node,shell,web + -lexports.js + -Wl,--export=__stack_pointer + -Wl,--export=__coreclr_wasm_rtlrestorecontext_tag + -Wl,--error-limit=0) endif() if (CORERUN_IN_BROWSER) diff --git a/src/coreclr/runtime.proj b/src/coreclr/runtime.proj index a0a11f404243b1..a9021cfde100b8 100644 --- a/src/coreclr/runtime.proj +++ b/src/coreclr/runtime.proj @@ -16,16 +16,6 @@ - - - - <_WasmDisableBigInt>true - - - From 0bc6b2292458e16bf627cc3ebd182cb363fc5f7d Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sun, 14 Jun 2026 16:32:43 +0200 Subject: [PATCH 10/12] cleanup --- eng/native.wasm.targets | 1 + src/coreclr/hosts/corerun/CMakeLists.txt | 19 ++++++------------- src/coreclr/runtime.proj | 2 +- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/eng/native.wasm.targets b/eng/native.wasm.targets index a61e4863d08d6b..42f9c581114313 100644 --- a/eng/native.wasm.targets +++ b/eng/native.wasm.targets @@ -63,6 +63,7 @@ + diff --git a/src/coreclr/hosts/corerun/CMakeLists.txt b/src/coreclr/hosts/corerun/CMakeLists.txt index 98add3094ad678..582da971ed4bcc 100644 --- a/src/coreclr/hosts/corerun/CMakeLists.txt +++ b/src/coreclr/hosts/corerun/CMakeLists.txt @@ -80,28 +80,21 @@ else() LINK_FLAGS "--js-library ${JS_SYSTEM_NATIVE_BROWSER} --js-library ${JS_SYSTEM_BROWSER_UTILS} --js-library ${JS_CORE_RUN} --extern-post-js ${JS_CORE_RUN_EXTPOST}" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") if (CORERUN_IN_BROWSER) - # Include the virtual file system data for the - # browser scenario. + # Include the virtual file system data for the browser scenario. set(WASM_PRELOAD_DIR "${CMAKE_INSTALL_PREFIX}/IL") if (EXISTS "${WASM_PRELOAD_DIR}") - target_link_options(corerun PRIVATE - --preload-file ${WASM_PRELOAD_DIR}@/) + target_link_options(corerun PRIVATE --preload-file ${WASM_PRELOAD_DIR}@/) endif() else() - # Node.js doesn't have good support for WASM_BIGINT - # so it only is added when running in the browser. - target_link_options(corerun PRIVATE - -sWASM_BIGINT=0) + # Node.js doesn't have good support for WASM_BIGINT, so disable it for the Node/shell. + target_link_options(corerun PRIVATE -sWASM_BIGINT=0) # remove HEAP64 and HEAPU64 from CMAKE_EMCC_EXPORTED_RUNTIME_METHODS string(REPLACE "HEAP64," "" CMAKE_EMCC_EXPORTED_RUNTIME_METHODS "${CMAKE_EMCC_EXPORTED_RUNTIME_METHODS}") string(REPLACE "HEAPU64," "" CMAKE_EMCC_EXPORTED_RUNTIME_METHODS "${CMAKE_EMCC_EXPORTED_RUNTIME_METHODS}") - # If not running in the browser, add - # Node.js file system support. - target_link_options(corerun PRIVATE - -sNODERAWFS=1 - -lnodefs.js) + # Add Node.js host file system support. + target_link_options(corerun PRIVATE -sNODERAWFS=1 -lnodefs.js) endif() target_link_options(corerun PRIVATE -sINITIAL_MEMORY=134217728 diff --git a/src/coreclr/runtime.proj b/src/coreclr/runtime.proj index a9021cfde100b8..2c968913b26f4c 100644 --- a/src/coreclr/runtime.proj +++ b/src/coreclr/runtime.proj @@ -15,7 +15,7 @@ - + From b65c0a6f2801ee2f43958d9107e4737a4536415c Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Sun, 14 Jun 2026 16:47:53 +0200 Subject: [PATCH 11/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/mono/browser/runtime/startup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts index 92bd64d28e5d9a..25093725766169 100644 --- a/src/mono/browser/runtime/startup.ts +++ b/src/mono/browser/runtime/startup.ts @@ -228,7 +228,7 @@ async function onRuntimeInitializedAsync (userOnRuntimeInitialized: (module:Emsc setTimeout(maybeSaveInterpPgoTable, (runtimeHelpers.config.interpreterPgoSaveDelay || 15) * 1000); - // this push is un-balanced for short while until runtimeReady = true. Accepted trade-off. + // this push is unbalanced for short while until runtimeReady = true. Accepted trade-off. Module.runtimeKeepalivePush(); keepAlivePushed = true; From 2dde4e9cba2e6dd669116aa288007e970767fcc7 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Sun, 14 Jun 2026 16:50:03 +0200 Subject: [PATCH 12/12] feedback --- src/coreclr/hosts/corerun/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/hosts/corerun/CMakeLists.txt b/src/coreclr/hosts/corerun/CMakeLists.txt index 582da971ed4bcc..3052680a13e7dc 100644 --- a/src/coreclr/hosts/corerun/CMakeLists.txt +++ b/src/coreclr/hosts/corerun/CMakeLists.txt @@ -80,6 +80,7 @@ else() LINK_FLAGS "--js-library ${JS_SYSTEM_NATIVE_BROWSER} --js-library ${JS_SYSTEM_BROWSER_UTILS} --js-library ${JS_CORE_RUN} --extern-post-js ${JS_CORE_RUN_EXTPOST}" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") if (CORERUN_IN_BROWSER) + target_link_options(corerun PRIVATE -sWASM_BIGINT=1) # Include the virtual file system data for the browser scenario. set(WASM_PRELOAD_DIR "${CMAKE_INSTALL_PREFIX}/IL") if (EXISTS "${WASM_PRELOAD_DIR}")