Skip to content

Commit

Permalink
Update the minimum supported version of node (10.19.0 -> 16.20.0)
Browse files Browse the repository at this point in the history
This new minimum version matches the version that we ship with emsdk.
Critically it supports null coalescing & logical assignment needed
by emscripten-core#20549.

One side effect of this is that we no longer need to support the
`NODEJS_CATCH_REJECTION` setting, which was needed only for node
versions older than v15.
  • Loading branch information
sbc100 committed Oct 27, 2023
1 parent 1dedd1b commit dfd58d5
Show file tree
Hide file tree
Showing 11 changed files with 24 additions and 98 deletions.
12 changes: 0 additions & 12 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -721,18 +721,6 @@ jobs:
core0.test_async_ccall_promise_jspi
core0.test_async_ccall_promise_exit_runtime_jspi
core0.test_cubescript_jspi"
# Run some basic tests with the minimum version of node that we currently
# support.
- install-node-version:
node_version: "10.19.0"
- run-tests:
title: "selected subset"
test_targets: "
other.test_gen_struct_info
other.test_native_call_before_init
other.test_node_unhandled_rejection
other.test_full_js_library*
core2.test_hello_world"
# Run a few test with the most recent version of node
# In particular we have some tests that require node flags on older
# versions of node but not with the most recent version.
Expand Down
4 changes: 4 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ See docs/process.md for more on how version tagging works.

3.1.48 (in development)
-----------------------
- The minimum version of node required to run the compiler was updated from
10.19.0 to 16.20.0. One side effect of this is that the
`NODEJS_CATCH_REJECTION` settings no longer relevant and is will be ignored.
(#20551)
- A new top-level `bootstrap` script was added. This script is for emscripten
developers and helps take a care of post-checkout tasks such as `npm install`.
If this script needs to be run (e.g. becuase package.json was changed, emcc
Expand Down
15 changes: 5 additions & 10 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -747,8 +747,6 @@ def make_js_executable(script):
cmd = config.NODE_JS
if settings.MEMORY64 == 1:
cmd += shared.node_memory64_flags()
elif settings.WASM_BIGINT:
cmd += shared.node_bigint_flags()
if len(cmd) > 1 or not os.path.isabs(cmd[0]):
# Using -S (--split-string) here means that arguments to the executable are
# correctly parsed. We don't do this by default because old versions of env
Expand Down Expand Up @@ -2276,23 +2274,20 @@ def phase_linker_setup(options, state, newargs):
if settings.MIN_CHROME_VERSION <= 37:
settings.WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG = 1

# 10.19.0 is the oldest version of node that we do any testing with.
# 16.20.0 is the oldest version of node that we do any testing with.
# Keep this in sync with the test-node-compat in .circleci/config.yml
# and MINIMUM_NODE_VERSION in tools/shared.py
if settings.MIN_NODE_VERSION:
if settings.MIN_NODE_VERSION < 101900:
exit_with_error('targeting node older than 10.19.00 is not supported')
if settings.MIN_NODE_VERSION >= 150000:
default_setting('NODEJS_CATCH_REJECTION', 0)
if settings.MIN_NODE_VERSION < 162000:
exit_with_error('targeting node older than v16.2 is not supported')

# Do not catch rejections or exits in modularize mode, as these options
# are for use when running emscripten modules standalone
# see https://github.com/emscripten-core/emscripten/issues/18723#issuecomment-1429236996
if settings.MODULARIZE:
default_setting('NODEJS_CATCH_REJECTION', 0)
default_setting('NODEJS_CATCH_EXIT', 0)
if settings.NODEJS_CATCH_REJECTION or settings.NODEJS_CATCH_EXIT:
exit_with_error('Cannot use -sNODEJS_CATCH_REJECTION or -sNODEJS_CATCH_EXIT with -sMODULARIZE')
if settings.NODEJS_CATCH_EXIT:
exit_with_error('Cannot use -sNODEJS_CATCH_EXIT with -sMODULARIZE')

setup_environment_settings()

Expand Down
13 changes: 2 additions & 11 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -756,16 +756,6 @@ var EXCEPTION_STACK_TRACES = false;
// [link]
var NODEJS_CATCH_EXIT = true;

// Catch unhandled rejections in node. This only effect versions of node older
// than 15. Without this, old version node will print a warning, but exit
// with a zero return code. With this setting enabled, we handle any unhandled
// rejection and throw an exception, which will cause the process exit
// immediately with a non-0 return code.
// This not needed in Node 15+ so this setting will default to false if
// MIN_NODE_VERSION is 150000 or above.
// [link]
var NODEJS_CATCH_REJECTION = true;

// Whether to support async operations in the compiled code. This makes it
// possible to call JS functions from synchronous-looking code in C/C++.
// 1: Run binaryen's Asyncify pass to transform the code using asyncify. This
Expand Down Expand Up @@ -1819,7 +1809,7 @@ var MIN_CHROME_VERSION = 75;
// distinct from the minimum version required run the emscripten compiler.
// This version aligns with the current Ubuuntu TLS 20.04 (Focal).
// Version is encoded in MMmmVV, e.g. 1814101 denotes Node 18.14.01.
var MIN_NODE_VERSION = 160000;
var MIN_NODE_VERSION = 162000;

// Tracks whether we are building with errno support enabled. Set to 0
// to disable compiling errno support in altogether. This saves a little
Expand Down Expand Up @@ -2167,4 +2157,5 @@ var LEGACY_SETTINGS = [
['USES_DYNAMIC_ALLOC', [1], 'No longer supported. Use -sMALLOC=none'],
['REVERSE_DEPS', ['auto', 'all', 'none'], 'No longer needed'],
['RUNTIME_LOGGING', 'RUNTIME_DEBUG'],
['NODEJS_CATCH_REJECTION', [0, 1], 'No longer applies to modern node versions'],
];
12 changes: 0 additions & 12 deletions src/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,18 +245,6 @@ if (ENVIRONMENT_IS_NODE) {
});
#endif

#if NODEJS_CATCH_REJECTION
// Without this older versions of node (< v15) will log unhandled rejections
// but return 0, which is not normally the desired behaviour. This is
// not be needed with node v15 and about because it is now the default
// behaviour:
// See https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode
var nodeMajor = process.versions.node.split(".")[0];
if (nodeMajor < 15) {
process.on('unhandledRejection', (reason) => { throw reason; });
}
#endif

quit_ = (status, toThrow) => {
process.exitCode = status;
throw toThrow;
Expand Down
2 changes: 0 additions & 2 deletions test/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@ def metafunc(self, with_bigint):
if self.get_setting('WASM_BIGINT') is not None:
self.skipTest('redundant in bigint test config')
self.set_setting('WASM_BIGINT')
self.node_args += shared.node_bigint_flags()
f(self)
else:
f(self)
Expand Down Expand Up @@ -381,7 +380,6 @@ def metafunc(self, standalone):
# if we are impure, disallow all wasm engines
if impure:
self.wasm_engines = []
self.node_args += shared.node_bigint_flags()
func(self)

metafunc._parameterize = {'': (False,),
Expand Down
10 changes: 2 additions & 8 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,6 @@ def test_i64_varargs(self):
def test_i64_invoke_bigint(self):
self.set_setting('WASM_BIGINT')
self.emcc_args += ['-fexceptions']
self.node_args += shared.node_bigint_flags()
self.do_core_test('test_i64_invoke_bigint.cpp')

def test_vararg_copy(self):
Expand Down Expand Up @@ -2287,7 +2286,6 @@ def test_em_js_i64(self):
self.assertContained('emcc: error: using 64-bit arguments in EM_JS function without WASM_BIGINT is not yet fully supported: `foo`', err)

self.set_setting('WASM_BIGINT')
self.node_args += shared.node_bigint_flags()
self.do_core_test('test_em_js_i64.c')

def test_em_js_address_taken(self):
Expand Down Expand Up @@ -7867,14 +7865,12 @@ def test_embind_dynamic_initialization(self):
def test_embind_i64_val(self):
self.set_setting('WASM_BIGINT')
self.emcc_args += ['-lembind']
self.node_args += shared.node_bigint_flags()
self.do_run_in_out_file_test('embind/test_i64_val.cpp', assert_identical=True)

@no_wasm2js('wasm_bigint')
def test_embind_i64_binding(self):
self.set_setting('WASM_BIGINT')
self.emcc_args += ['-lembind']
self.node_args += shared.node_bigint_flags()
self.do_run_in_out_file_test('embind/test_i64_binding.cpp', assert_identical=True)

def test_embind_no_rtti(self):
Expand Down Expand Up @@ -10038,8 +10034,7 @@ def setUp(self):
require_wasm64=True)
# MEMORY64=2, or "lowered"
wasm64l = make_run('wasm64l', emcc_args=['-O1', '-Wno-experimental', '--profiling-funcs'],
settings={'MEMORY64': 2},
node_args=shared.node_bigint_flags())
settings={'MEMORY64': 2})

lto0 = make_run('lto0', emcc_args=['-flto', '-O0'])
lto1 = make_run('lto1', emcc_args=['-flto', '-O1'])
Expand Down Expand Up @@ -10075,8 +10070,7 @@ def setUp(self):
core2s = make_run('core2s', emcc_args=['-O2'], settings={'SAFE_HEAP': 1})
core2ss = make_run('core2ss', emcc_args=['-O2'], settings={'STACK_OVERFLOW_CHECK': 2})

bigint = make_run('bigint', emcc_args=['--profiling-funcs'], settings={'WASM_BIGINT': 1},
node_args=shared.node_bigint_flags())
bigint = make_run('bigint', emcc_args=['--profiling-funcs'], settings={'WASM_BIGINT': 1})

# Add DEFAULT_TO_CXX=0
strict = make_run('strict', emcc_args=[], settings={'STRICT': 1})
Expand Down
28 changes: 3 additions & 25 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -1010,12 +1010,6 @@ def test_failure_error_code(self):
self.expect_fail([compiler, test_file('hello_world.c'), 'this_file_is_missing.c', '-o', 'out.js'])
self.assertFalse(os.path.exists('out.js'))

def test_failure_modularize_and_catch_rejection(self):
for compiler in [EMCC, EMXX]:
# Test that if sMODULARIZE and sNODEJS_CATCH_REJECTION are both enabled, then emcc shouldn't succeed, and shouldn't produce an output file.
self.expect_fail([compiler, test_file('hello_world.c'), '-sMODULARIZE', '-sNODEJS_CATCH_REJECTION', '-o', 'out.js'])
self.assertFalse(os.path.exists('out.js'))

def test_failure_modularize_and_catch_exit(self):
for compiler in [EMCC, EMXX]:
# Test that if sMODULARIZE and sNODEJS_CATCH_EXIT are both enabled, then emcc shouldn't succeed, and shouldn't produce an output file.
Expand Down Expand Up @@ -7478,7 +7472,6 @@ def test_i64_return_value(self, args, bind_js):
''')

# Run the test and confirm the output is as expected.
self.node_args += shared.node_bigint_flags()
out = self.run_js('testrun.js')
self.assertContained('''\
input = 0xaabbccdd11223344
Expand Down Expand Up @@ -12676,7 +12669,7 @@ def test_config_closure_compiler(self):
self.assertContained(sys.executable, err)
self.assertContained('not execute properly!', err)

def test_node_unhandled_rejection(self):
def test_unhandled_rejection(self):
create_file('pre.js', '''
async function foo() {
var a = missing;
Expand All @@ -12695,24 +12688,11 @@ def test_node_unhandled_rejection(self):
}
''')

# With NODEJS_CATCH_REJECTION we expect the unhandled rejection to cause a non-zero
# exit code and log the stack trace correctly.
# We expect the unhandled rejection to cause a non-zero exit code and log
# the stack trace correctly.
self.build('main.c', emcc_args=['--pre-js=pre.js', '-sNODEJS_CATCH_REJECTION'])
output = self.run_js('main.js', assert_returncode=NON_ZERO)
self.assertContained('unhandledRejection', read_file('main.js'))
self.assertContained('ReferenceError: missing is not defined', output)
self.assertContained('at foo (', output)

# Without NODEJS_CATCH_REJECTION we expect node to log the unhandled rejection
# but return 0.
self.node_args = [a for a in self.node_args if '--unhandled-rejections' not in a]
self.build('main.c', emcc_args=['--pre-js=pre.js', '-sNODEJS_CATCH_REJECTION=0'])
self.assertNotContained('unhandledRejection', read_file('main.js'))

if shared.check_node_version()[0] >= 15:
self.skipTest('old behaviour of node JS cannot be tested on node v15 or above')

output = self.run_js('main.js')
self.assertContained('ReferenceError: missing is not defined', output)
self.assertContained('at foo (', output)

Expand Down Expand Up @@ -12907,7 +12887,6 @@ def test_wasmfs_readfile(self):
def test_wasmfs_readfile_bigint(self):
self.set_setting('FORCE_FILESYSTEM')
self.set_setting('WASM_BIGINT')
self.node_args += shared.node_bigint_flags()
self.do_run_in_out_file_test(test_file('wasmfs/wasmfs_readfile.c'))

def test_wasmfs_jsfile(self):
Expand Down Expand Up @@ -13671,7 +13650,6 @@ def test_missing_struct_info(self):
def run_wasi_test_suite_test(self, name):
if not os.path.exists(path_from_root('test/third_party/wasi-test-suite')):
self.fail('wasi-testsuite not found; run `git submodule update --init`')
self.node_args += shared.node_bigint_flags()
wasm = path_from_root('test', 'third_party', 'wasi-test-suite', name + '.wasm')
with open(path_from_root('test', 'third_party', 'wasi-test-suite', name + '.json')) as f:
config = json.load(f)
Expand Down
4 changes: 2 additions & 2 deletions test/test_sanity.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,8 @@ def test_node(self):
for version, succeed in [('v0.8.0', False),
('v4.1.0', False),
('v10.18.0', False),
('v10.19.0', True),
('v10.19.1-pre', True),
('v16.20.0', True),
('v16.20.1-pre', True),
('cheez', False)]:
print(version, succeed)
delete_file(SANITY_FILE)
Expand Down
2 changes: 0 additions & 2 deletions tools/gen_struct_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,6 @@ def inspect_headers(headers, cflags):
# Run the compiled program.
show('Calling generated program... ' + js_file[1])
args = []
if settings.MEMORY64:
args += shared.node_bigint_flags()
info = shared.run_js_tool(js_file[1], node_args=args, stdout=shared.PIPE).splitlines()

if not DEBUG:
Expand Down
20 changes: 6 additions & 14 deletions tools/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@
PRINT_SUBPROCS = int(os.getenv('EMCC_VERBOSE', '0'))
SKIP_SUBPROCS = False

# Minimum node version required to run the emscripten compiler. This is distinct
# from the minimum version required to execute the generated code. This is not an
# exact requirement, but is the oldest version of node that we do any testing with.
# This version aligns with the current Ubuuntu TLS 20.04 (Focal).
MINIMUM_NODE_VERSION = (10, 19, 0)
# Minimum node version required to run the emscripten compiler. This is
# distinct from the minimum version required to execute the generated code
# (settings.MIN_NODE_VERSION).
# This version currently matches the node version that we ship with emsdk
# which means that we can say for sure that this version is well supported.
MINIMUM_NODE_VERSION = (16, 20, 0)
EXPECTED_LLVM_VERSION = 18

# These get set by setup_temp_dirs
Expand Down Expand Up @@ -351,15 +352,6 @@ def check_node_version():
return version


def node_bigint_flags():
node_version = check_node_version()
# wasm bigint was enabled by default in node v16.
if node_version and node_version < (16, 0, 0):
return ['--experimental-wasm-bigint']
else:
return []


def node_reference_types_flags():
node_version = check_node_version()
# reference types were enabled by default in node v18.
Expand Down

0 comments on commit dfd58d5

Please sign in to comment.