From e03f7edfd198f2cfe6e40cacb6097f0f43130c27 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Wed, 23 Sep 2026 16:09:21 +0200 Subject: [PATCH 1/9] --stack-first --- src/mono/mono/utils/mono-threads-wasm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/utils/mono-threads-wasm.c b/src/mono/mono/utils/mono-threads-wasm.c index 9874cb3934e8c3..8151170e0216b2 100644 --- a/src/mono/mono/utils/mono-threads-wasm.c +++ b/src/mono/mono/utils/mono-threads-wasm.c @@ -198,7 +198,7 @@ mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize) if (G_UNLIKELY (res != 0)) g_error ("%s: pthread_attr_destroy failed with \"%s\" (%d)", __func__, g_strerror (res), res); - g_assert (*staddr != NULL); + // emscripten links with --stack-first at -O0, which puts the stack at the start of the linear memory, so staddr can legitimately be NULL g_assert (*stsize != (size_t)-1); #elif defined(HOST_WASI) && !defined(DISABLE_THREADS) // TODO: this will need changes for WASI multithreading as the stack will be allocated per thread at different addresses From 01aa2d43b364ce5e0e5f648b12561962f2f541d6 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Wed, 23 Sep 2026 16:37:32 +0200 Subject: [PATCH 2/9] INCOMING_MODULE_JS_API --- src/mono/browser/browser.proj | 19 +++++++++++++++++++ .../build/BrowserWasmApp.CoreCLR.targets | 14 ++++++++++++++ src/mono/browser/build/BrowserWasmApp.targets | 16 ++++++++++++++++ .../corehost/browserhost/CMakeLists.txt | 2 ++ 4 files changed, 51 insertions(+) diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj index 710a36801a553b..e834b1f33310f8 100644 --- a/src/mono/browser/browser.proj +++ b/src/mono/browser/browser.proj @@ -208,9 +208,27 @@ + + + + + + + + + + + + + + + + + <_EmccExportedLibraryFunction>"[@(EmccExportedLibraryFunction -> '%27%(Identity)%27', ',')]" <_EmccExportedRuntimeMethods>"[@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]" + <_EmccIncomingModuleJSAPI>"[@(EmccIncomingModuleJSAPI -> '%27%(Identity)%27', ',')]" <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',') 33554432 @@ -232,6 +250,7 @@ <_EmccLinkFlags Include="-s FORCE_FILESYSTEM=1" /> <_EmccLinkFlags Condition="'$(_EmccExportedLibraryFunction)' != ''" Include="-s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$(_EmccExportedLibraryFunction)" /> <_EmccLinkFlags Include="-s EXPORTED_RUNTIME_METHODS=$(_EmccExportedRuntimeMethods)" /> + <_EmccLinkFlags Include="-s INCOMING_MODULE_JS_API=$(_EmccIncomingModuleJSAPI)" /> <_EmccLinkFlags Include="-s EXPORTED_FUNCTIONS=$(_EmccExportedFunctions)" /> <_EmccLinkFlags Include="--source-map-base http://example.com" /> <_EmccLinkFlags Include="-s WASM_BIGINT=1" /> diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index 9d4dc3b1394726..3727ff72567c49 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -856,9 +856,22 @@ <_EmccExportedRuntimeMethods>"[BROWSER_HOST,@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]" + <_EmccIncomingModuleJSAPI>"[@(EmccIncomingModuleJSAPI -> '%27%(Identity)%27', ',')]" <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',') + + + + + + + + + + + + @@ -935,6 +948,7 @@ <_EmccLinkStepArgs Include="-s EXPORTED_RUNTIME_METHODS=$(_EmccExportedRuntimeMethods)" /> + <_EmccLinkStepArgs Include="-s INCOMING_MODULE_JS_API=$(_EmccIncomingModuleJSAPI)" /> <_EmccLinkStepArgs Include="-s EXPORTED_FUNCTIONS=$(_EmccExportedFunctions)" /> diff --git a/src/mono/browser/build/BrowserWasmApp.targets b/src/mono/browser/build/BrowserWasmApp.targets index 4afa05842060c2..8fb9cc6fd30899 100644 --- a/src/mono/browser/build/BrowserWasmApp.targets +++ b/src/mono/browser/build/BrowserWasmApp.targets @@ -313,6 +313,20 @@ + + + + + + + + + + + + + + <_WasmEHLib Condition="'$(WasmEnableExceptionHandling)' == 'true'">libmono-wasm-eh-wasm.a @@ -325,6 +339,7 @@ <_WasmSIMDLibToExclude Condition="'$(WasmEnableSIMD)' == 'true'">libmono-wasm-nosimd.a <_EmccExportedLibraryFunction>"[@(EmccExportedLibraryFunction -> '%27%(Identity)%27', ',')]" <_EmccExportedRuntimeMethods>"[@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]" + <_EmccIncomingModuleJSAPI>"[@(EmccIncomingModuleJSAPI -> '%27%(Identity)%27', ',')]" <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',') @@ -373,6 +388,7 @@ <_EmccLinkStepArgs Include="-s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$(_EmccExportedLibraryFunction)" Condition="'$(_EmccExportedLibraryFunction)' != ''" /> <_EmccLinkStepArgs Include="-s EXPORTED_RUNTIME_METHODS=$(_EmccExportedRuntimeMethods)" /> + <_EmccLinkStepArgs Include="-s INCOMING_MODULE_JS_API=$(_EmccIncomingModuleJSAPI)" /> <_EmccLinkStepArgs Include="-s EXPORTED_FUNCTIONS=$(_EmccExportedFunctions)" /> <_EmccLinkStepArgs Include="$(EmccExtraLDFlags)" /> diff --git a/src/native/corehost/browserhost/CMakeLists.txt b/src/native/corehost/browserhost/CMakeLists.txt index 49876ab799114d..6bac2e100a0135 100644 --- a/src/native/corehost/browserhost/CMakeLists.txt +++ b/src/native/corehost/browserhost/CMakeLists.txt @@ -132,6 +132,8 @@ target_link_options(browserhost PRIVATE -sEXIT_RUNTIME=1 -sALLOW_TABLE_GROWTH=1 -sEXPORTED_RUNTIME_METHODS=BROWSER_HOST,${CMAKE_EMCC_EXPORTED_RUNTIME_METHODS} + # Module properties the loader sets or reads. Anything not listed here is ignored by emcc, and aborts in a debug build. + -sINCOMING_MODULE_JS_API=instantiateWasm,preInit,preRun,postRun,onAbort,onExit,print,printErr -sEXPORTED_FUNCTIONS=${CMAKE_EMCC_EXPORTED_FUNCTIONS} -sEXPORT_NAME=createDotnetRuntime -sENVIRONMENT=web,webview,worker,node,shell From 33c23b8e70f844c21bc0a322866800eea1bf982d Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Wed, 23 Sep 2026 16:45:07 +0200 Subject: [PATCH 3/9] more --- src/mono/browser/build/BrowserWasmApp.CoreCLR.targets | 1 + src/native/corehost/browserhost/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index 3727ff72567c49..e27ffb39442e41 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -863,6 +863,7 @@ + diff --git a/src/native/corehost/browserhost/CMakeLists.txt b/src/native/corehost/browserhost/CMakeLists.txt index 6bac2e100a0135..1bcc8999ed0d6f 100644 --- a/src/native/corehost/browserhost/CMakeLists.txt +++ b/src/native/corehost/browserhost/CMakeLists.txt @@ -133,7 +133,7 @@ target_link_options(browserhost PRIVATE -sALLOW_TABLE_GROWTH=1 -sEXPORTED_RUNTIME_METHODS=BROWSER_HOST,${CMAKE_EMCC_EXPORTED_RUNTIME_METHODS} # Module properties the loader sets or reads. Anything not listed here is ignored by emcc, and aborts in a debug build. - -sINCOMING_MODULE_JS_API=instantiateWasm,preInit,preRun,postRun,onAbort,onExit,print,printErr + -sINCOMING_MODULE_JS_API=instantiateWasm,locateFile,preInit,preRun,postRun,onAbort,onExit,print,printErr -sEXPORTED_FUNCTIONS=${CMAKE_EMCC_EXPORTED_FUNCTIONS} -sEXPORT_NAME=createDotnetRuntime -sENVIRONMENT=web,webview,worker,node,shell From 8370487d6ba3c5d5d0aaa475a1485f855177ed27 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Wed, 23 Sep 2026 16:53:47 +0200 Subject: [PATCH 4/9] Accept the function form of Module.preInit and Module.preRun on CoreCLR Emscripten lets preInit/preRun/postRun be either an array of callbacks or a single callback, and normalizes them itself. The browser host prepends its own hooks by spreading the user value, which throws "(Module.preRun || []) is not iterable" when a single function was supplied. Mono already normalizes this in configureEmscriptenStartup. WasmBasicTestApp passes preRun as a function, which is how Wasm.Build.Tests hit this once it moved to the standard workload. EmscriptenModuleInternal declared these as arrays only, which is what allowed the assumption in the first place, so widen them to match Mono's EmscriptenModule. postRun needs no normalization here because the host never prepends to it. Also apply npm run format, which reflows an arrow body in http.ts that was failing the brace-style lint rule. Fixes https://github.com/dotnet/runtime/issues/132555 --- src/native/libs/Common/JavaScript/types/internal.ts | 6 +++--- src/native/libs/System.Native.Browser/native/index.ts | 10 ++++++++-- .../interop/http.ts | 4 +++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/native/libs/Common/JavaScript/types/internal.ts b/src/native/libs/Common/JavaScript/types/internal.ts index 7307d336ee6772..cdf67e75aca812 100644 --- a/src/native/libs/Common/JavaScript/types/internal.ts +++ b/src/native/libs/Common/JavaScript/types/internal.ts @@ -51,9 +51,9 @@ export type EmscriptenModuleInternal = EmscriptenModule & DotnetModuleConfig & { instantiateWasm?: InstantiateWasmCallBack; onAbort?: (reason: any, extraJson?: string) => void; onExit?: (code: number) => void; - preInit?: (() => any)[]; - preRun?: (() => any)[]; - postRun?: (() => any)[]; + preInit?: (() => any)[] | (() => any); + preRun?: (() => any)[] | (() => any); + postRun?: (() => any)[] | (() => any); } export interface AssetEntryInternal extends AssetEntry { diff --git a/src/native/libs/System.Native.Browser/native/index.ts b/src/native/libs/System.Native.Browser/native/index.ts index 0a017810450e3a..78b326854960aa 100644 --- a/src/native/libs/System.Native.Browser/native/index.ts +++ b/src/native/libs/System.Native.Browser/native/index.ts @@ -61,7 +61,7 @@ export function dotnetInitializeModule(internals: InternalExchange): void { _ems_.FS.createPath("/", virtualWorkingDirectory!, true, true); _ems_.FS.chdir(virtualWorkingDirectory!); } - }, ...(_ems_.Module.preInit || [])]; + }, ...asCallbackArray(_ems_.Module.preInit)]; // preInit runs before Emscripten assigns the native WASM exports. _ems_.Module.preRun = [() => { @@ -80,6 +80,12 @@ export function dotnetInitializeModule(internals: InternalExchange): void { // silently ignore any error during shutdown } }; - }, ...(_ems_.Module.preRun || [])]; + }, ...asCallbackArray(_ems_.Module.preRun)]; } } + +// emscripten accepts a single callback or an array of them +function asCallbackArray(callbacks: undefined | (() => any) | (() => any)[]): (() => any)[] { + if (!callbacks) return []; + return typeof callbacks === "function" ? [callbacks] : callbacks; +} diff --git a/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/http.ts b/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/http.ts index 6e64216e031534..cc2ed758ba9e02 100644 --- a/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/http.ts +++ b/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/http.ts @@ -97,7 +97,9 @@ export function httpAbort(controller: HttpController): void { // Never resolves; rejects as soon as the fetch does. A failed fetch abandons the readable end of the // TransformStream, which leaves an in-flight write() pending forever, so writes race against this. function rejectWhenFetchFails(controller: HttpController): Promise { - return controller.responsePromise!.then(() => new Promise(() => { }), (err) => { throw err; }); + return controller.responsePromise!.then(() => new Promise(() => { }), (err) => { + throw err; + }); } export function httpTransformStreamWrite(controller: HttpController, bufferPtr: VoidPtr, bufferLength: number): ControllablePromise { From 0e89d249b7659bd457922e3230def7252c5168f3 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Wed, 23 Sep 2026 17:13:12 +0200 Subject: [PATCH 5/9] Declare emscripten's full default INCOMING_MODULE_JS_API The earlier list only named the properties the loaders themselves set, which rejected 16 properties emscripten accepts by default. That is reachable from user code: Blazor's prepareRuntimeConfig spreads window['Module'] into the module config, so an app author can put any emscripten property there and it would abort a debug build. Declare the default set on both flavors instead, plus wasmMemory for Mono with threads, which is the one property we set that is not a default. The setting stays explicit so an undeclared property still fails at link rather than silently, but nothing that works today changes. Also fix the ordering in BrowserWasmApp.CoreCLR.targets: the PropertyGroup computing _EmccIncomingModuleJSAPI ran before the ItemGroup defining the items, and MSBuild evaluates a target's children in document order, so the list expanded to empty and rejected every Module property on the workload relink path. --- src/mono/browser/browser.proj | 28 +++++++++++--- .../build/BrowserWasmApp.CoreCLR.targets | 37 ++++++++++++++----- src/mono/browser/build/BrowserWasmApp.targets | 28 +++++++++++--- .../corehost/browserhost/CMakeLists.txt | 4 +- 4 files changed, 73 insertions(+), 24 deletions(-) diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj index e834b1f33310f8..df8d3106091717 100644 --- a/src/mono/browser/browser.proj +++ b/src/mono/browser/browser.proj @@ -208,21 +208,37 @@ - + + + + + + - - - - + + + + + + + + + + + + + + + - + diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index e27ffb39442e41..d7e2c9fac8779b 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -854,25 +854,42 @@ - - <_EmccExportedRuntimeMethods>"[BROWSER_HOST,@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]" - <_EmccIncomingModuleJSAPI>"[@(EmccIncomingModuleJSAPI -> '%27%(Identity)%27', ',')]" - <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',') - - - + + + + + + - - - + + + + + + + + + + + + + + + + + <_EmccExportedRuntimeMethods>"[BROWSER_HOST,@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]" + <_EmccIncomingModuleJSAPI>"[@(EmccIncomingModuleJSAPI -> '%27%(Identity)%27', ',')]" + <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',') + + diff --git a/src/mono/browser/build/BrowserWasmApp.targets b/src/mono/browser/build/BrowserWasmApp.targets index 8fb9cc6fd30899..4e30bfa3502fd1 100644 --- a/src/mono/browser/build/BrowserWasmApp.targets +++ b/src/mono/browser/build/BrowserWasmApp.targets @@ -314,18 +314,34 @@ - + + + + + + - - - - + + + + + + + - + + + + + + + + + diff --git a/src/native/corehost/browserhost/CMakeLists.txt b/src/native/corehost/browserhost/CMakeLists.txt index 1bcc8999ed0d6f..7fb4cf0a07a1f4 100644 --- a/src/native/corehost/browserhost/CMakeLists.txt +++ b/src/native/corehost/browserhost/CMakeLists.txt @@ -132,8 +132,8 @@ target_link_options(browserhost PRIVATE -sEXIT_RUNTIME=1 -sALLOW_TABLE_GROWTH=1 -sEXPORTED_RUNTIME_METHODS=BROWSER_HOST,${CMAKE_EMCC_EXPORTED_RUNTIME_METHODS} - # Module properties the loader sets or reads. Anything not listed here is ignored by emcc, and aborts in a debug build. - -sINCOMING_MODULE_JS_API=instantiateWasm,locateFile,preInit,preRun,postRun,onAbort,onExit,print,printErr + # Emscripten's default INCOMING_MODULE_JS_API, kept explicit so a Module property we don't declare aborts a debug build early. + -sINCOMING_MODULE_JS_API=ENVIRONMENT,arguments,canvas,dynamicLibraries,elementPointerLock,instantiateWasm,locateFile,monitorRunDependencies,noExitRuntime,noInitialRun,onAbort,onExit,onRuntimeInitialized,postRun,preInit,preRun,print,printErr,setStatus,statusMessage,stderr,stdin,stdout,thisProgram,wasm,websocket -sEXPORTED_FUNCTIONS=${CMAKE_EMCC_EXPORTED_FUNCTIONS} -sEXPORT_NAME=createDotnetRuntime -sENVIRONMENT=web,webview,worker,node,shell From 435912e02672d1519e0661a4821287057cb41ae8 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 25 Sep 2026 12:08:46 +0200 Subject: [PATCH 6/9] Keep Module callback normalization in serialized scope Emscripten serializes dotnetInitializeModule independently of its bundle closure. Keep the callback normalization helper nested so Rollup's minified name remains available in dotnet.native.js. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/libs/System.Native.Browser/native/index.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/native/libs/System.Native.Browser/native/index.ts b/src/native/libs/System.Native.Browser/native/index.ts index 78b326854960aa..a606d1a2c532dc 100644 --- a/src/native/libs/System.Native.Browser/native/index.ts +++ b/src/native/libs/System.Native.Browser/native/index.ts @@ -54,6 +54,11 @@ export function dotnetInitializeModule(internals: InternalExchange): void { return _ems_.wasmTable; } + function asCallbackArray(callbacks: undefined | (() => any) | (() => any)[]): (() => any)[] { + if (!callbacks) return []; + return typeof callbacks === "function" ? [callbacks] : callbacks; + } + function setupEmscripten() { _ems_.Module.preInit = [() => { if (_ems_.dotnetApi.getConfig) { @@ -83,9 +88,3 @@ export function dotnetInitializeModule(internals: InternalExchange): void { }, ...asCallbackArray(_ems_.Module.preRun)]; } } - -// emscripten accepts a single callback or an array of them -function asCallbackArray(callbacks: undefined | (() => any) | (() => any)[]): (() => any)[] { - if (!callbacks) return []; - return typeof callbacks === "function" ? [callbacks] : callbacks; -} From 25b5c8ec63e80c5a7c66d1f63880e9a30dadafe2 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Fri, 25 Sep 2026 18:26:18 +0200 Subject: [PATCH 7/9] Dedupe INCOMING_MODULE_JS_API to one source per runtime flavor The explicit emscripten default INCOMING_MODULE_JS_API list was hand-copied into four files. Collapse it to a single source per flavor, matching how EXPORTED_FUNCTIONS/RUNTIME_METHODS already flow. CoreCLR: define the list in GenerateEmccExports (eng/native.wasm.targets plus its fallback in BrowserWasmApp.CoreCLR.targets). The in-tree browserhost link now consumes a CMAKE_EMCC_INCOMING_MODULE_JS_API cmake arg instead of a hardcoded string, and _CoreCLRWriteLinkRsp reads the items from its GenerateEmccExports dependency. This also removes the earlier ItemGroup-before-PropertyGroup ordering hazard, since the items are populated by a dependency target. Mono: browser.proj remains the single source and now emits EmccDefaultIncomingModuleJSAPI into wasm-props.json (incl. wasmMemory for the threads pack variant). ReadWasmProps surfaces it and the app relink in BrowserWasmApp.targets reads it instead of re-listing. CoreCLR browserhost dotnet.native.js checkIncomingModuleAPI is unchanged (rejects wasmMemory/wasmBinary, accepts the 26 defaults). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/native.wasm.targets | 32 ++++++++++ src/mono/browser/browser.proj | 1 + .../build/BrowserWasmApp.CoreCLR.targets | 60 +++++++++---------- src/mono/browser/build/BrowserWasmApp.targets | 32 +--------- src/mono/wasm/build/WasmApp.Common.targets | 2 + .../corehost/browserhost/CMakeLists.txt | 4 +- 6 files changed, 69 insertions(+), 62 deletions(-) diff --git a/eng/native.wasm.targets b/eng/native.wasm.targets index c72c24ff6df2d1..9e73454c294a27 100644 --- a/eng/native.wasm.targets +++ b/eng/native.wasm.targets @@ -147,14 +147,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_EmccExportedRuntimeMethods>@(EmccExportedRuntimeMethod -> '%(Identity)',',') <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',') + <_EmccIncomingModuleJSAPI>@(EmccIncomingModuleJSAPI -> '%(Identity)',',') + diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj index df8d3106091717..0301897f96ddd7 100644 --- a/src/mono/browser/browser.proj +++ b/src/mono/browser/browser.proj @@ -306,6 +306,7 @@ "WasmOptConfigurationFlags": [@(WasmOptConfigurationFlags -> '%22%(Identity)%22', ',')], "EmccDefaultExportedFunctions": [@(EmccExportedFunction -> '%22%(Identity)%22', ',')], "EmccDefaultExportedRuntimeMethods": [@(EmccExportedRuntimeMethod -> '%22%(Identity)%22', ',')], + "EmccDefaultIncomingModuleJSAPI": [@(EmccIncomingModuleJSAPI -> '%22%(Identity)%22', ',')], "PropertiesThatTriggerRelinking": [ { "identity": "InvariantTimezone", "defaultValueInRuntimePack": "$(InvariantTimezone)" }, { "identity": "InvariantGlobalization", "defaultValueInRuntimePack": "$(InvariantGlobalization)" }, diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index d7e2c9fac8779b..599c3000614930 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -161,6 +161,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_EmccExportedRuntimeMethods>"[BROWSER_HOST,@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]" <_EmccIncomingModuleJSAPI>"[@(EmccIncomingModuleJSAPI -> '%27%(Identity)%27', ',')]" diff --git a/src/mono/browser/build/BrowserWasmApp.targets b/src/mono/browser/build/BrowserWasmApp.targets index 4e30bfa3502fd1..24df1973d9bc93 100644 --- a/src/mono/browser/build/BrowserWasmApp.targets +++ b/src/mono/browser/build/BrowserWasmApp.targets @@ -313,36 +313,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + <_WasmEHLib Condition="'$(WasmEnableExceptionHandling)' == 'true'">libmono-wasm-eh-wasm.a diff --git a/src/mono/wasm/build/WasmApp.Common.targets b/src/mono/wasm/build/WasmApp.Common.targets index d592ec19a2a55e..3847539c6ce8bf 100644 --- a/src/mono/wasm/build/WasmApp.Common.targets +++ b/src/mono/wasm/build/WasmApp.Common.targets @@ -399,6 +399,7 @@ + @@ -408,6 +409,7 @@ + diff --git a/src/native/corehost/browserhost/CMakeLists.txt b/src/native/corehost/browserhost/CMakeLists.txt index 7fb4cf0a07a1f4..808d46280d3902 100644 --- a/src/native/corehost/browserhost/CMakeLists.txt +++ b/src/native/corehost/browserhost/CMakeLists.txt @@ -132,8 +132,8 @@ target_link_options(browserhost PRIVATE -sEXIT_RUNTIME=1 -sALLOW_TABLE_GROWTH=1 -sEXPORTED_RUNTIME_METHODS=BROWSER_HOST,${CMAKE_EMCC_EXPORTED_RUNTIME_METHODS} - # Emscripten's default INCOMING_MODULE_JS_API, kept explicit so a Module property we don't declare aborts a debug build early. - -sINCOMING_MODULE_JS_API=ENVIRONMENT,arguments,canvas,dynamicLibraries,elementPointerLock,instantiateWasm,locateFile,monitorRunDependencies,noExitRuntime,noInitialRun,onAbort,onExit,onRuntimeInitialized,postRun,preInit,preRun,print,printErr,setStatus,statusMessage,stderr,stdin,stdout,thisProgram,wasm,websocket + # Emscripten's default INCOMING_MODULE_JS_API, kept explicit so a Module property we don't declare aborts a debug build early. Value comes from GenerateEmccExports in eng/native.wasm.targets. + -sINCOMING_MODULE_JS_API=${CMAKE_EMCC_INCOMING_MODULE_JS_API} -sEXPORTED_FUNCTIONS=${CMAKE_EMCC_EXPORTED_FUNCTIONS} -sEXPORT_NAME=createDotnetRuntime -sENVIRONMENT=web,webview,worker,node,shell From 82e240c4ef5d485a570a8aa213a46b7e762ab307 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 29 Sep 2026 14:10:06 +0200 Subject: [PATCH 8/9] Drop the fallback GenerateEmccExports copy in BrowserWasmApp.CoreCLR.targets eng/native.wasm.targets is packaged into the WebAssembly SDK pack alongside this file (Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj), so both the in-tree import ($(RepositoryEngineeringDir)native.wasm.targets) and the workload import ($(MSBuildThisFileDirectory)native.wasm.targets) resolve to its authoritative GenerateEmccExports. The local fallback was already overridden by last-definition-wins in both cases since dotnet/runtime#132715 started packaging the eng file, so it was dead code that had to be kept in sync by hand. Remove it and let the single GenerateEmccExports (exported functions/methods plus the INCOMING_MODULE_JS_API allowlist) be the only definition. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../build/BrowserWasmApp.CoreCLR.targets | 92 ++----------------- 1 file changed, 6 insertions(+), 86 deletions(-) diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index 599c3000614930..8ef82796bb3fa4 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -112,95 +112,15 @@ TaskFactory="TaskHostFactory" Condition="'$(WasmAppBuilderTasksAssemblyPath)' != ''" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + From f3d0ffe35d933e9d4aebffe75bbd01284043669c Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 29 Sep 2026 18:55:06 +0200 Subject: [PATCH 9/9] Gate INCOMING_MODULE_JS_API on Debug and give the item an internal name The emscripten INCOMING_MODULE_JS_API allowlist only changes behavior when assertions are on (Debug): its whole purpose is the early "Module. was supplied but not in INCOMING_MODULE_JS_API" abort. In Release there are no assertions, so restricting the list has no benefit. Emit the -s INCOMING_MODULE_JS_API flag only for Debug and let emscripten use its default list otherwise: - browser.proj / BrowserWasmApp.targets / BrowserWasmApp.CoreCLR.targets: add Condition="'$(Configuration)' == 'Debug'" to the link-flag item. - browserhost CMakeLists.txt: move the -sINCOMING_MODULE_JS_API line into the existing if(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG) block, next to ASSERTIONS=1. The CMAKE_EMCC_INCOMING_MODULE_JS_API arg is still passed unconditionally (inert data in Release). Name the MSBuild item _EmccIncomingModuleJSAPI (each entry is one API) and the joined property _EmccIncomingModuleJSAPIs (the comma-separated list) so users don't set the item: the underscore marks both internal. The wasm-props.json task parameter keeps the EmccDefaultIncomingModuleJSAPI name to match EmccDefaultExported*. Validated: Debug clr+libs+host browser build succeeds and the generated browserhost checkIncomingModuleAPI is unchanged (rejects wasmMemory/wasmBinary, accepts the 26 defaults). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/native.wasm.targets | 56 ++++++++--------- src/mono/browser/browser.proj | 61 ++++++++++--------- .../build/BrowserWasmApp.CoreCLR.targets | 5 +- src/mono/browser/build/BrowserWasmApp.targets | 7 ++- src/mono/wasm/build/WasmApp.Common.targets | 2 +- .../corehost/browserhost/CMakeLists.txt | 6 +- 6 files changed, 71 insertions(+), 66 deletions(-) diff --git a/eng/native.wasm.targets b/eng/native.wasm.targets index 9e73454c294a27..734349ba2f1d34 100644 --- a/eng/native.wasm.targets +++ b/eng/native.wasm.targets @@ -150,43 +150,43 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + <_EmccIncomingModuleJSAPI Include="ENVIRONMENT" /> + <_EmccIncomingModuleJSAPI Include="arguments" /> + <_EmccIncomingModuleJSAPI Include="canvas" /> + <_EmccIncomingModuleJSAPI Include="dynamicLibraries" /> + <_EmccIncomingModuleJSAPI Include="elementPointerLock" /> + <_EmccIncomingModuleJSAPI Include="instantiateWasm" /> + <_EmccIncomingModuleJSAPI Include="locateFile" /> + <_EmccIncomingModuleJSAPI Include="monitorRunDependencies" /> + <_EmccIncomingModuleJSAPI Include="noExitRuntime" /> + <_EmccIncomingModuleJSAPI Include="noInitialRun" /> + <_EmccIncomingModuleJSAPI Include="onAbort" /> + <_EmccIncomingModuleJSAPI Include="onExit" /> + <_EmccIncomingModuleJSAPI Include="onRuntimeInitialized" /> + <_EmccIncomingModuleJSAPI Include="postRun" /> + <_EmccIncomingModuleJSAPI Include="preInit" /> + <_EmccIncomingModuleJSAPI Include="preRun" /> + <_EmccIncomingModuleJSAPI Include="print" /> + <_EmccIncomingModuleJSAPI Include="printErr" /> + <_EmccIncomingModuleJSAPI Include="setStatus" /> + <_EmccIncomingModuleJSAPI Include="statusMessage" /> + <_EmccIncomingModuleJSAPI Include="stderr" /> + <_EmccIncomingModuleJSAPI Include="stdin" /> + <_EmccIncomingModuleJSAPI Include="stdout" /> + <_EmccIncomingModuleJSAPI Include="thisProgram" /> + <_EmccIncomingModuleJSAPI Include="wasm" /> + <_EmccIncomingModuleJSAPI Include="websocket" /> <_EmccExportedRuntimeMethods>@(EmccExportedRuntimeMethod -> '%(Identity)',',') <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',') - <_EmccIncomingModuleJSAPI>@(EmccIncomingModuleJSAPI -> '%(Identity)',',') + <_EmccIncomingModuleJSAPIs>@(_EmccIncomingModuleJSAPI -> '%(Identity)',',') - + diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj index 0301897f96ddd7..87f40c18f6ec01 100644 --- a/src/mono/browser/browser.proj +++ b/src/mono/browser/browser.proj @@ -210,41 +210,41 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + <_EmccIncomingModuleJSAPI Include="ENVIRONMENT" /> + <_EmccIncomingModuleJSAPI Include="arguments" /> + <_EmccIncomingModuleJSAPI Include="canvas" /> + <_EmccIncomingModuleJSAPI Include="dynamicLibraries" /> + <_EmccIncomingModuleJSAPI Include="elementPointerLock" /> + <_EmccIncomingModuleJSAPI Include="instantiateWasm" /> + <_EmccIncomingModuleJSAPI Include="locateFile" /> + <_EmccIncomingModuleJSAPI Include="monitorRunDependencies" /> + <_EmccIncomingModuleJSAPI Include="noExitRuntime" /> + <_EmccIncomingModuleJSAPI Include="noInitialRun" /> + <_EmccIncomingModuleJSAPI Include="onAbort" /> + <_EmccIncomingModuleJSAPI Include="onExit" /> + <_EmccIncomingModuleJSAPI Include="onRuntimeInitialized" /> + <_EmccIncomingModuleJSAPI Include="postRun" /> + <_EmccIncomingModuleJSAPI Include="preInit" /> + <_EmccIncomingModuleJSAPI Include="preRun" /> + <_EmccIncomingModuleJSAPI Include="print" /> + <_EmccIncomingModuleJSAPI Include="printErr" /> + <_EmccIncomingModuleJSAPI Include="setStatus" /> + <_EmccIncomingModuleJSAPI Include="statusMessage" /> + <_EmccIncomingModuleJSAPI Include="stderr" /> + <_EmccIncomingModuleJSAPI Include="stdin" /> + <_EmccIncomingModuleJSAPI Include="stdout" /> + <_EmccIncomingModuleJSAPI Include="thisProgram" /> + <_EmccIncomingModuleJSAPI Include="wasm" /> + <_EmccIncomingModuleJSAPI Include="websocket" /> - + <_EmccIncomingModuleJSAPI Include="wasmMemory" /> <_EmccExportedLibraryFunction>"[@(EmccExportedLibraryFunction -> '%27%(Identity)%27', ',')]" <_EmccExportedRuntimeMethods>"[@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]" - <_EmccIncomingModuleJSAPI>"[@(EmccIncomingModuleJSAPI -> '%27%(Identity)%27', ',')]" + <_EmccIncomingModuleJSAPIs>"[@(_EmccIncomingModuleJSAPI -> '%27%(Identity)%27', ',')]" <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',') 33554432 @@ -266,7 +266,8 @@ <_EmccLinkFlags Include="-s FORCE_FILESYSTEM=1" /> <_EmccLinkFlags Condition="'$(_EmccExportedLibraryFunction)' != ''" Include="-s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$(_EmccExportedLibraryFunction)" /> <_EmccLinkFlags Include="-s EXPORTED_RUNTIME_METHODS=$(_EmccExportedRuntimeMethods)" /> - <_EmccLinkFlags Include="-s INCOMING_MODULE_JS_API=$(_EmccIncomingModuleJSAPI)" /> + + <_EmccLinkFlags Condition="'$(Configuration)' == 'Debug'" Include="-s INCOMING_MODULE_JS_API=$(_EmccIncomingModuleJSAPIs)" /> <_EmccLinkFlags Include="-s EXPORTED_FUNCTIONS=$(_EmccExportedFunctions)" /> <_EmccLinkFlags Include="--source-map-base http://example.com" /> <_EmccLinkFlags Include="-s WASM_BIGINT=1" /> @@ -306,7 +307,7 @@ "WasmOptConfigurationFlags": [@(WasmOptConfigurationFlags -> '%22%(Identity)%22', ',')], "EmccDefaultExportedFunctions": [@(EmccExportedFunction -> '%22%(Identity)%22', ',')], "EmccDefaultExportedRuntimeMethods": [@(EmccExportedRuntimeMethod -> '%22%(Identity)%22', ',')], - "EmccDefaultIncomingModuleJSAPI": [@(EmccIncomingModuleJSAPI -> '%22%(Identity)%22', ',')], + "EmccDefaultIncomingModuleJSAPI": [@(_EmccIncomingModuleJSAPI -> '%22%(Identity)%22', ',')], "PropertiesThatTriggerRelinking": [ { "identity": "InvariantTimezone", "defaultValueInRuntimePack": "$(InvariantTimezone)" }, { "identity": "InvariantGlobalization", "defaultValueInRuntimePack": "$(InvariantGlobalization)" }, diff --git a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets index 8ef82796bb3fa4..d586baabf9d99b 100644 --- a/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets +++ b/src/mono/browser/build/BrowserWasmApp.CoreCLR.targets @@ -806,7 +806,7 @@ <_EmccExportedRuntimeMethods>"[BROWSER_HOST,@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]" - <_EmccIncomingModuleJSAPI>"[@(EmccIncomingModuleJSAPI -> '%27%(Identity)%27', ',')]" + <_EmccIncomingModuleJSAPIs>"[@(_EmccIncomingModuleJSAPI -> '%27%(Identity)%27', ',')]" <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',') @@ -886,7 +886,8 @@ <_EmccLinkStepArgs Include="-s EXPORTED_RUNTIME_METHODS=$(_EmccExportedRuntimeMethods)" /> - <_EmccLinkStepArgs Include="-s INCOMING_MODULE_JS_API=$(_EmccIncomingModuleJSAPI)" /> + + <_EmccLinkStepArgs Condition="'$(Configuration)' == 'Debug'" Include="-s INCOMING_MODULE_JS_API=$(_EmccIncomingModuleJSAPIs)" /> <_EmccLinkStepArgs Include="-s EXPORTED_FUNCTIONS=$(_EmccExportedFunctions)" /> diff --git a/src/mono/browser/build/BrowserWasmApp.targets b/src/mono/browser/build/BrowserWasmApp.targets index 24df1973d9bc93..f4b70c287b7d7c 100644 --- a/src/mono/browser/build/BrowserWasmApp.targets +++ b/src/mono/browser/build/BrowserWasmApp.targets @@ -313,7 +313,7 @@ - @@ -327,7 +327,7 @@ <_WasmSIMDLibToExclude Condition="'$(WasmEnableSIMD)' == 'true'">libmono-wasm-nosimd.a <_EmccExportedLibraryFunction>"[@(EmccExportedLibraryFunction -> '%27%(Identity)%27', ',')]" <_EmccExportedRuntimeMethods>"[@(EmccExportedRuntimeMethod -> '%27%(Identity)%27', ',')]" - <_EmccIncomingModuleJSAPI>"[@(EmccIncomingModuleJSAPI -> '%27%(Identity)%27', ',')]" + <_EmccIncomingModuleJSAPIs>"[@(_EmccIncomingModuleJSAPI -> '%27%(Identity)%27', ',')]" <_EmccExportedFunctions>@(EmccExportedFunction -> '%(Identity)',',') @@ -376,7 +376,8 @@ <_EmccLinkStepArgs Include="-s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$(_EmccExportedLibraryFunction)" Condition="'$(_EmccExportedLibraryFunction)' != ''" /> <_EmccLinkStepArgs Include="-s EXPORTED_RUNTIME_METHODS=$(_EmccExportedRuntimeMethods)" /> - <_EmccLinkStepArgs Include="-s INCOMING_MODULE_JS_API=$(_EmccIncomingModuleJSAPI)" /> + + <_EmccLinkStepArgs Condition="'$(Configuration)' == 'Debug'" Include="-s INCOMING_MODULE_JS_API=$(_EmccIncomingModuleJSAPIs)" /> <_EmccLinkStepArgs Include="-s EXPORTED_FUNCTIONS=$(_EmccExportedFunctions)" /> <_EmccLinkStepArgs Include="$(EmccExtraLDFlags)" /> diff --git a/src/mono/wasm/build/WasmApp.Common.targets b/src/mono/wasm/build/WasmApp.Common.targets index 3847539c6ce8bf..8ba6b8e8302743 100644 --- a/src/mono/wasm/build/WasmApp.Common.targets +++ b/src/mono/wasm/build/WasmApp.Common.targets @@ -409,7 +409,7 @@ - + diff --git a/src/native/corehost/browserhost/CMakeLists.txt b/src/native/corehost/browserhost/CMakeLists.txt index 808d46280d3902..00403d00d3e517 100644 --- a/src/native/corehost/browserhost/CMakeLists.txt +++ b/src/native/corehost/browserhost/CMakeLists.txt @@ -117,6 +117,10 @@ if (UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG) -sASSERTIONS=1 -sLLD_REPORT_UNDEFINED -sERROR_ON_UNDEFINED_SYMBOLS=1 + # Emscripten's default INCOMING_MODULE_JS_API, kept explicit so a Module property we don't declare + # aborts the build early. This is only meaningful with ASSERTIONS=1, so it is Debug-only. The value + # comes from GenerateEmccExports in eng/native.wasm.targets. + -sINCOMING_MODULE_JS_API=${CMAKE_EMCC_INCOMING_MODULE_JS_API} ) endif () @@ -132,8 +136,6 @@ target_link_options(browserhost PRIVATE -sEXIT_RUNTIME=1 -sALLOW_TABLE_GROWTH=1 -sEXPORTED_RUNTIME_METHODS=BROWSER_HOST,${CMAKE_EMCC_EXPORTED_RUNTIME_METHODS} - # Emscripten's default INCOMING_MODULE_JS_API, kept explicit so a Module property we don't declare aborts a debug build early. Value comes from GenerateEmccExports in eng/native.wasm.targets. - -sINCOMING_MODULE_JS_API=${CMAKE_EMCC_INCOMING_MODULE_JS_API} -sEXPORTED_FUNCTIONS=${CMAKE_EMCC_EXPORTED_FUNCTIONS} -sEXPORT_NAME=createDotnetRuntime -sENVIRONMENT=web,webview,worker,node,shell