Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1190,7 +1190,8 @@ jobs:
# https://github.com/emscripten-core/emscripten/pull/11382#pullrequestreview-428902638
EMTEST_LACKS_NATIVE_CLANG: "1"
EMTEST_SKIP_V8: "1"
EMTEST_SKIP_EH: "1"
EMTEST_SKIP_WASM_LEGACY_EH: "1"
EMTEST_SKIP_WASM_EH: "1"
EMTEST_SKIP_WASM64: "1"
EMTEST_SKIP_SCONS: "1"
EMTEST_SKIP_RUST: "1"
Expand Down Expand Up @@ -1226,7 +1227,8 @@ jobs:
# We don't install d8 or modern node on the mac runner so we skip any
# tests that depend on those.
EMTEST_SKIP_V8: "1"
EMTEST_SKIP_EH: "1"
EMTEST_SKIP_WASM_LEGACY_EH: "1"
EMTEST_SKIP_WASM_EH: "1"
EMTEST_SKIP_WASM64: "1"
EMTEST_SKIP_SCONS: "1"
EMTEST_SKIP_RUST: "1"
Expand Down
2 changes: 1 addition & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ See docs/process.md for more on how version tagging works.
- Emscripten version was bumped to 4.0.0. Happy new year, happy new major
version! While version has a few interesting changes, there is nothing huge
that makes it different from any other release. (#19053)
- `-sWASM_LEAGCY_EXCEPTIONS` option is added. (#23365) If true, it will emit
- `-sWASM_LEGACY_EXCEPTIONS` option is added. (#23365) If true, it will emit
instructions for the legacy Wasm exception handling proposal
(https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/legacy/Exceptions.md),
and if false, the new standardized exception handling proposal
Expand Down
6 changes: 5 additions & 1 deletion src/settings_internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,11 @@ var CAN_ADDRESS_2GB = false;
// This has no effect if DWARF is not being emitted.
var SEPARATE_DWARF = false;

// New WebAssembly exception handling
// Target WebAssembly exception handling instead of JavaScript-side exception
// handling. Furthermore, if WASM_LEGACY_EXCEPTIONS=1, then old legacy Wasm
// exception handling is used, and if WASM_LEGACY_EXCEPTIONS=0, then Wasm
// exception handling is targeted.
// Enabled by passing -fwasm-exceptions on the command line.
var WASM_EXCEPTIONS = false;

// Set to true if the program has a main function. By default this is
Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_cxx_except_wasm.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 19555,
"a.out.js.gz": 8099,
"a.out.js": 19486,
"a.out.js.gz": 8077,
"a.out.nodebug.wasm": 144630,
"a.out.nodebug.wasm.gz": 54894,
"total": 164185,
"total_gz": 62993,
"total": 164116,
"total_gz": 62971,
"sent": [
"_abort_js",
"_tzset_js",
Expand Down
13 changes: 7 additions & 6 deletions test/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1130,8 +1130,9 @@ def require_simd(self):
self.fail('either d8 or node >= 16 required to run wasm64 tests. Use EMTEST_SKIP_SIMD to skip')

def require_wasm_legacy_eh(self):
if 'EMTEST_SKIP_EH' in os.environ:
self.skipTest('test requires node >= 17 or d8 (and EMTEST_SKIP_EH is set)')
if 'EMTEST_SKIP_WASM_LEGACY_EH' in os.environ:
self.skipTest('test requires node >= 17 or d8 (and EMTEST_SKIP_WASM_LEGACY_EH is set)')
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aheejin now I realize my root source of the confusion. What was happening is that the semantics of EMTEST_SKIP_EH=1 was to not actually skip the test, if the node.js shell was detected to support the target EH.

I would like to change the semantics to mean that if user passes EMTEST_SKIP_EH=1 (or EMTEST_SKIP_WASM_LEGACY_EH=1), then do skip the tests, independent of Node.js version. (the tests are running in the browser, so Node.js version does not matter)

So what was happening all the time on my CI tests was that I was passing EMTEST_SKIP_EH=1 and EMTEST_SKIP_WASM_LEGACY_EH=1 and running in Firefox 91, which doesn't support either; but the tests would not be skipped because I am running on Node.js 24, and they would then proceed to fail in the old browser.

Copy link
Member

@aheejin aheejin Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that's confusing. I actually wasn't aware that we don't skip the tests even if we have EMTEST_SKIP_*** set if the node version is high enough. But it looks this has been this way for years and it doesn't only apply to Wasm EH. Other methods like require_simd and require_wasm64 have the same problem. I think it would be better to change it in a separate PR for all require_***s if we are to change this. @sbc100 What do you think about changing the meaning of EMTEST_SKIP_***? Is there a reason that we don't honor these skip variables when the node version is high?

Copy link
Collaborator

@sbc100 sbc100 Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable to always skip yes.

I guess the original meaning was more like "EMTEST_ALLOW_SKIPPING_OF_XXX_IF_NEEDED", but don't see why it shouldn't be "EMTEST_ALWAYS_SKIP_XX".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree we that if we make this change it should be in its own PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


self.set_setting('WASM_LEGACY_EXCEPTIONS')
if self.try_require_node_version(17):
return
Expand All @@ -1142,11 +1143,11 @@ def require_wasm_legacy_eh(self):
self.js_engines = [v8]
return

self.fail('either d8 or node >= 17 required to run wasm-eh tests. Use EMTEST_SKIP_EH to skip')
self.fail('either d8 or node >= 17 required to run legacy wasm-eh tests. Use EMTEST_SKIP_WASM_LEGACY_EH to skip')

def require_wasm_eh(self):
if 'EMTEST_SKIP_EH' in os.environ:
self.skipTest('test requires node v24 or d8 (and EMTEST_SKIP_EH is set)')
if 'EMTEST_SKIP_WASM_EH' in os.environ:
self.skipTest('test requires node v24 or d8 (and EMTEST_SKIP_WASM_EH is set)')
self.set_setting('WASM_LEGACY_EXCEPTIONS', 0)
if self.try_require_node_version(24):
self.node_args.append('--experimental-wasm-exnref')
Expand All @@ -1162,7 +1163,7 @@ def require_wasm_eh(self):
self.v8_args.append('--experimental-wasm-exnref')
return

self.fail('either d8 or node v24 required to run wasm-eh tests. Use EMTEST_SKIP_EH to skip')
self.fail('either d8 or node v24 required to run wasm-eh tests. Use EMTEST_SKIP_WASM_EH to skip')

def require_jspi(self):
if 'EMTEST_SKIP_JSPI' in os.environ:
Expand Down
6 changes: 3 additions & 3 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -3644,7 +3644,7 @@ def test_embind_tsgen_jspi(self):
})
def test_embind_tsgen_exceptions(self, legacy):
if not legacy and shared.get_node_version(config.NODE_JS)[0] < 22:
self.skipTest('Node version needs to be 22 or greater to run tsgen with exnref')
self.skipTest('Node version needs to be 22 or greater to run tsgen with Wasm EH')
self.set_setting('WASM_LEGACY_EXCEPTIONS', legacy)

# Check that when Wasm exceptions and assertions are enabled bindings still generate.
Expand Down Expand Up @@ -14332,10 +14332,10 @@ def test_reproduce(self):

def test_min_browser_version(self):
err = self.expect_fail([EMCC, test_file('hello_world.c'), '-Wno-transpile', '-Werror', '-sWASM_BIGINT', '-sMIN_SAFARI_VERSION=130000'])
self.assertContained('emcc: error: MIN_SAFARI_VERSION=130000 is not compatible with WASM_BIGINT (150000 or above required)', err)
self.assertContained('emcc: error: MIN_SAFARI_VERSION=130000 is not compatible with WASM_BIGINT (MIN_SAFARI_VERSION=150000 or above required)', err)

err = self.expect_fail([EMCC, test_file('hello_world.c'), '-Wno-transpile', '-Werror', '-pthread', '-sMIN_FIREFOX_VERSION=65'])
self.assertContained('emcc: error: MIN_FIREFOX_VERSION=65 is not compatible with pthreads (79 or above required)', err)
self.assertContained('emcc: error: MIN_FIREFOX_VERSION=65 is not compatible with pthreads (MIN_FIREFOX_VERSION=79 or above required)', err)

def test_signext_lowering(self):
# Use `-v` to show the sub-commands being run by emcc.
Expand Down
29 changes: 28 additions & 1 deletion tools/feature_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class Feature(IntEnum):
MEMORY64 = auto()
WORKER_ES6_MODULES = auto()
OFFSCREENCANVAS_SUPPORT = auto()
WASM_LEGACY_EXCEPTIONS = auto()
WASM_EXCEPTIONS = auto()


disable_override_features = set()
Expand Down Expand Up @@ -105,6 +107,26 @@ class Feature(IntEnum):
'safari': 170000,
'node': 0, # This is a browser only feature, no requirements on Node.js
},
# Legacy Wasm exceptions was the first (now legacy) format for native
# exception handling in WebAssembly.
Feature.WASM_LEGACY_EXCEPTIONS: {
'chrome': 95,
'firefox': 100,
'safari': 150200,
'node': 170000,
},
# Wasm exceptions is a newer format for native exception handling in
# WebAssembly.
Feature.WASM_EXCEPTIONS: {
'chrome': 137,
'firefox': 131,
'safari': 180400,
# Supported with flag --experimental-wasm-exnref (TODO: Change this to
# unflagged version of Node.js 260000 that ships Wasm EH enabled, after
# Emscripten unit testing has migrated to Node.js 26, and Emsdk ships
# Node.js 26)
'node': 240000,
},
}

# Static assertion to check that we actually need each of the above feature flags
Expand Down Expand Up @@ -165,7 +187,7 @@ def enable_feature(feature, reason, override=False):
diagnostics.warning(
'compatibility',
f'{name}={user_settings[name]} is not compatible with {reason} '
f'({min_version} or above required)')
f'({name}={min_version} or above required)')
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A drive-by improvement to help error message

MIN_FIREFOX_VERSION=91 is not compatible with -sWASM_LEGACY_EXCEPTIONS=0 (131 or above required)

which one might at first glance read as needing to pass -sWASM_LEGACY_EXCEPTIONS=131 to fix.

else:
# If no conflict, bump the minimum version to accommodate the feature.
logger.debug(f'Enabling {name}={min_version} to accommodate {reason}')
Expand Down Expand Up @@ -199,3 +221,8 @@ def apply_min_browser_versions():
enable_feature(Feature.WORKER_ES6_MODULES, 'EXPORT_ES6 with -sWASM_WORKERS')
if settings.OFFSCREENCANVAS_SUPPORT:
enable_feature(Feature.OFFSCREENCANVAS_SUPPORT, 'OFFSCREENCANVAS_SUPPORT')
if settings.WASM_EXCEPTIONS or settings.SUPPORT_LONGJMP == 'wasm': # Wasm longjmp support will lean on Wasm (Legacy) EH
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks fine, but Wasm longjmp support does not particularly require the legacy EH. It should work fine with both legacy and non-legacy EH.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, how do you mean? Is the comment wrong, or? Should I update to something else?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry, I missed the parentheses, and read that sentence as "Wasm longjmp support will lean on Wasm legacy EH". So I said "No Wasm longjmp supports works fine with exnref too". Now I understand what you meant.

if settings.WASM_LEGACY_EXCEPTIONS:
enable_feature(Feature.WASM_LEGACY_EXCEPTIONS, 'Wasm Legacy exceptions (-fwasm-exceptions with -sWASM_LEGACY_EXCEPTIONS=1)')
else:
enable_feature(Feature.WASM_EXCEPTIONS, 'Wasm exceptions (-fwasm-exceptions with -sWASM_LEGACY_EXCEPTIONS=0)')