Skip to content

Commit 1af44c8

Browse files
committed
Build-time error on use of emscripten_promise_any if Promise.any is not available
1 parent 309581e commit 1af44c8

File tree

5 files changed

+43
-3
lines changed

5 files changed

+43
-3
lines changed

emcc.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2432,13 +2432,16 @@ def phase_linker_setup(options, state, newargs):
24322432
settings.WASM_WORKER_FILE = unsuffixed(os.path.basename(target)) + '.ww.js'
24332433
settings.JS_LIBRARIES.append((0, shared.path_from_root('src', 'library_wasm_worker.js')))
24342434

2435+
# TODO(sbc): Find make a generic way to expose the feature matrix to JS
2436+
# compiler rather then adding them all ad-hoc as internal settings
24352437
settings.SUPPORTS_GLOBALTHIS = feature_matrix.caniuse(feature_matrix.Feature.GLOBALTHIS)
2438+
settings.SUPPORTS_PROMISE_ANY = feature_matrix.caniuse(feature_matrix.Feature.PROMISE_ANY)
24362439
if not settings.BULK_MEMORY:
24372440
settings.BULK_MEMORY = feature_matrix.caniuse(feature_matrix.Feature.BULK_MEMORY)
24382441

24392442
if settings.AUDIO_WORKLET:
24402443
if not settings.SUPPORTS_GLOBALTHIS:
2441-
exit_with_error('Must target recent enough browser versions that will support globalThis in order to target Wasm Audio Worklets!')
2444+
exit_with_error('AUDIO_WORKLET requires globalThis support, but this is disabled due to the current runtime configuration (most likely you need to build without `node` in ENVIRONMENT, or bump MIN_NODE_VERSION to >= 150000)')
24422445
if settings.AUDIO_WORKLET == 1:
24432446
settings.AUDIO_WORKLET_FILE = unsuffixed(os.path.basename(target)) + '.aw.js'
24442447
settings.JS_LIBRARIES.append((0, shared.path_from_root('src', 'library_webaudio.js')))

src/library_promise.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,13 @@ mergeInto(LibraryManager.library, {
216216
return id;
217217
},
218218

219-
emscripten_promise_any__deps: ['$promiseMap', '$idsToPromises'],
219+
220+
emscripten_promise_any__deps: [
221+
'$promiseMap', '$idsToPromises',
222+
#if !SUPPORTS_PROMISE_ANY && !INCLUDE_FULL_LIBRARY
223+
() => error("emscripten_promise_any used, but Promise.any is not supported by the current runtime configuration (run with EMCC_DEBUG=1 in the env for more details)"),
224+
#endif
225+
],
220226
emscripten_promise_any: function(idBuf, errorBuf, size) {
221227
var promises = idsToPromises(idBuf, size);
222228
#if RUNTIME_DEBUG

src/settings_internal.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ var TARGET_NOT_SUPPORTED = 0x7FFFFFFF;
175175
// Used to track whether target environment supports the 'globalThis' attribute.
176176
var SUPPORTS_GLOBALTHIS = false;
177177

178+
// Used to track whether target environment supports the 'Promise.any'.
179+
var SUPPORTS_PROMISE_ANY = false;
180+
178181
// Wasm backend symbols that are considered system symbols and don't
179182
// have the normal C symbol name mangled applied (== prefix with an underscore)
180183
// (Also implicily on this list is any function that starts with string "dynCall_")

test/test_core.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9761,6 +9761,14 @@ def test_main_reads_args(self):
97619761

97629762
@requires_node
97639763
def test_promise(self):
9764+
# This test depends on Promise.any, which in turn requires a modern target. Check that it
9765+
# fails to even build without bumping the min versions:
9766+
err = self.expect_fail([EMCC, test_file('core/test_promise.c')])
9767+
self.assertContained('error: emscripten_promise_any used, but Promise.any is not supported by the current runtime configuration', err)
9768+
self.set_setting('MIN_NODE_VERSION', '150000')
9769+
self.set_setting('MIN_SAFARI_VERSION', '150000')
9770+
self.set_setting('MIN_FIREFOX_VERSION', '79')
9771+
self.set_setting('MIN_CHROME_VERSION', '85')
97649772
self.do_core_test('test_promise.c')
97659773

97669774

tools/feature_matrix.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class Feature(IntEnum):
2222
JS_BIGINT_INTEGRATION = auto()
2323
THREADS = auto()
2424
GLOBALTHIS = auto()
25+
PROMISE_ANY = auto()
2526

2627

2728
default_features = {Feature.SIGN_EXT, Feature.MUTABLE_GLOBALS}
@@ -62,25 +63,44 @@ class Feature(IntEnum):
6263
'edge': 79,
6364
'firefox': 65,
6465
'safari': 120100,
65-
# 'node': 120000
66+
'node': 120000,
67+
},
68+
Feature.PROMISE_ANY: {
69+
'chrome': 85,
70+
'firefox': 79,
71+
'safari': 140000,
72+
'node': 150000,
6673
},
6774
}
6875

6976

7077
def caniuse(feature):
7178
min_versions = min_browser_versions[feature]
79+
80+
def report_missing(setting_name):
81+
setting_value = getattr(settings, setting_name)
82+
logger.debug(f'cannot use {feature.name} because {setting_name} is too old: {setting_value}')
83+
7284
if settings.MIN_CHROME_VERSION < min_versions['chrome']:
85+
report_missing('MIN_CHROME_VERSION')
7386
return False
7487
# For edge we just use the same version requirements as chrome since,
7588
# at least for modern versions of edge, they share version numbers.
7689
if settings.MIN_EDGE_VERSION < min_versions['chrome']:
90+
report_missing('MIN_EDGE_VERSION')
7791
return False
7892
if settings.MIN_FIREFOX_VERSION < min_versions['firefox']:
93+
report_missing('MIN_FIREFOX_VERSION')
7994
return False
8095
if settings.MIN_SAFARI_VERSION < min_versions['safari']:
96+
report_missing('MIN_SAFARI_VERSION')
8197
return False
8298
# IE don't support any non-MVP features
8399
if settings.MIN_IE_VERSION != 0x7FFFFFFF:
100+
report_missing('MIN_IE_VERSION')
101+
return False
102+
if 'node' in min_versions and settings.MIN_NODE_VERSION < min_versions['node']:
103+
report_missing('MIN_NODE_VERSION')
84104
return False
85105
return True
86106

0 commit comments

Comments
 (0)