From b17d4ee2af8abb85bba9da3a7a45ea0ed35c6e39 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Thu, 17 Sep 2026 12:24:34 -0700 Subject: [PATCH] Remove NODE_CODE_CACHING setting V8 removed support for serializing compiled `WebAssembly.Module` objects via `v8.serialize()` / `v8.deserialize()` over 5 years ago: https://github.com/nodejs/node/issues/18265. As a result, `-sNODE_CODE_CACHING` has not worked in any version of Node.js since Node 12, and `test_node_code_caching` has been disabled since #13489 (February 2021). Move `NODE_CODE_CACHING` to `LEGACY_SETTINGS` for backwards compatibility with `-sNODE_CODE_CACHING=0`. See: #13128 --- ChangeLog.md | 2 ++ .../docs/compiling/CrossOriginStorage.rst | 2 -- .../tools_reference/settings_reference.rst | 24 +------------ src/preamble.js | 34 +----------------- src/settings.js | 19 ---------- test/test_other.py | 35 ------------------- tools/link.py | 2 -- tools/settings.py | 3 +- 8 files changed, 5 insertions(+), 116 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index a3fe632c528d1..9d8786a4a20fb 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -39,6 +39,8 @@ See docs/process.md for more on how version tagging works. - `emscripten_clear_timeout` now releases the runtime keepalive held by the pending timeout, and both `emscripten_clear_timeout` and `emscripten_clear_immediate` are no-ops for ids that already fired. (#27720) +- The `NODE_CODE_CACHING` setting was removed, as node's support for + serializing WebAssembly modules has been missing for over 5 years now. 6.0.9 - 09/01/26 ---------------- diff --git a/site/source/docs/compiling/CrossOriginStorage.rst b/site/source/docs/compiling/CrossOriginStorage.rst index 4bc0cc7012841..8852e8ca70227 100644 --- a/site/source/docs/compiling/CrossOriginStorage.rst +++ b/site/source/docs/compiling/CrossOriginStorage.rst @@ -377,8 +377,6 @@ COS is a complement to, not a replacement for, existing browser caches: - **HTTP cache / Service Worker cache** — still used for per-origin caching. COS adds cross-origin sharing on top. -- **``NODE_CODE_CACHING``** — a Node.js-specific V8 bytecode cache; unrelated - to COS. - **IndexedDB / OPFS** — per-origin storage; COS shares across origins. See also diff --git a/site/source/docs/tools_reference/settings_reference.rst b/site/source/docs/tools_reference/settings_reference.rst index 03863398a2ffb..ed6e703d65e4e 100644 --- a/site/source/docs/tools_reference/settings_reference.rst +++ b/site/source/docs/tools_reference/settings_reference.rst @@ -1530,29 +1530,6 @@ also be controlled separately. Default value: false -.. _node_code_caching: - -NODE_CODE_CACHING -================= - -This saves the compiled wasm module in a file with name -``$WASM_BINARY_NAME.$V8_VERSION.cached`` -and loads it on subsequent runs. This caches the compiled wasm code from -v8 in node, which saves compiling on subsequent runs, making them start up -much faster. -The V8 version used in node is included in the cache name so that we don't -try to load cached code from another version, which fails silently (it seems -to load ok, but we do actually recompile). - -- The only version known to work for sure is node 12.9.1, as this has - regressed, see - https://github.com/nodejs/node/issues/18265#issuecomment-622971547 -- The default location of the .cached files is alongside the wasm binary, - as mentioned earlier. If that is in a read-only directory, you may need - to place them elsewhere. You can use the locateFile() hook to do so. - -Default value: false - .. _exported_functions: EXPORTED_FUNCTIONS @@ -3630,3 +3607,4 @@ for backwards compatibility with older versions: - ``DETERMINISTIC``: No longer supported (Valid values: [0]) - ``LEGALIZE_JS_FFI``: legacy JS FFI legalization is no longer supported (Valid values: [0]) - ``SOCKET_WEBRTC``: No longer supported (Valid values: [0]) + - ``NODE_CODE_CACHING``: No longer supported (Valid values: [0]) diff --git a/src/preamble.js b/src/preamble.js index 54b537db5e8cc..6e101a451283e 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -545,40 +545,8 @@ var splitModuleProxyHandler = { #if SPLIT_MODULE || !WASM_ASYNC_COMPILATION function instantiateSync(file, info) { - var module; var binary = getBinarySync(file); -#if NODE_CODE_CACHING - if (ENVIRONMENT_IS_NODE) { - var v8 = require('node:v8'); - // Include the V8 version in the cache name, so that we don't try to - // load cached code from another version, which fails silently (it seems - // to load ok, but we do actually recompile the binary every time). - var cachedCodeFile = '{{{ WASM_BINARY_FILE }}}.' + v8.cachedDataVersionTag() + '.cached'; - cachedCodeFile = locateFile(cachedCodeFile); - var hasCached = fs.existsSync(cachedCodeFile); - if (hasCached) { -#if RUNTIME_DEBUG - dbg('NODE_CODE_CACHING: loading module'); -#endif - try { - module = v8.deserialize(fs.readFileSync(cachedCodeFile)); - } catch (e) { - err(`NODE_CODE_CACHING: failed to deserialize, bad cache file? (${cachedCodeFile})`); - // Save the new compiled code when we have it. - hasCached = false; - } - } - } - module ||= new WebAssembly.Module(binary); - if (ENVIRONMENT_IS_NODE && !hasCached) { -#if RUNTIME_DEBUG - dbg('NODE_CODE_CACHING: saving module'); -#endif - fs.writeFileSync(cachedCodeFile, v8.serialize(module)); - } -#else // NODE_CODE_CACHING - module = new WebAssembly.Module(binary); -#endif // NODE_CODE_CACHING + var module = new WebAssembly.Module(binary); var instance = new WebAssembly.Instance(module, info); return [instance, module]; } diff --git a/src/settings.js b/src/settings.js index 5301c61fa5d80..bf95d2c0e6e8a 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1060,25 +1060,6 @@ var NODERAWFS = false; // also be controlled separately. var NODE_HOST_ENV = false; -// This saves the compiled wasm module in a file with name -// ``$WASM_BINARY_NAME.$V8_VERSION.cached`` -// and loads it on subsequent runs. This caches the compiled wasm code from -// v8 in node, which saves compiling on subsequent runs, making them start up -// much faster. -// The V8 version used in node is included in the cache name so that we don't -// try to load cached code from another version, which fails silently (it seems -// to load ok, but we do actually recompile). -// -// - The only version known to work for sure is node 12.9.1, as this has -// regressed, see -// https://github.com/nodejs/node/issues/18265#issuecomment-622971547 -// - The default location of the .cached files is alongside the wasm binary, -// as mentioned earlier. If that is in a read-only directory, you may need -// to place them elsewhere. You can use the locateFile() hook to do so. -// -// [link] -var NODE_CODE_CACHING = false; - // Symbols that are explicitly exported. These symbols are kept alive through // LLVM dead code elimination, and also made accessible outside of the // generated code even after running closure compiler (on "Module"). Native diff --git a/test/test_other.py b/test/test_other.py index e077c568b8593..360d6dfac3a3b 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -9777,41 +9777,6 @@ def test_getpass(self): self.assertEqual(returncode, 0) self.assertIn(b'done', output) - @disabled('https://github.com/nodejs/node/issues/18265') - def test_node_code_caching(self): - self.run_process([EMCC, test_file('hello_world.c'), - '-sNODE_CODE_CACHING', - '-sWASM_ASYNC_COMPILATION=0']) - - def get_cached(): - cached = glob.glob('a.out.wasm.*.cached') - if not cached: - return None - self.assertEqual(len(cached), 1) - return cached[0] - - # running the program makes it cache the code - self.assertFalse(get_cached()) - self.assertEqual('Hello, world!', self.run_js('a.out.js').strip()) - self.assertTrue(get_cached(), 'should be a cache file') - - # hard to test it actually uses it to speed itself up, but test that it - # does try to deserialize it at least - create_file(get_cached(), 'waka waka') - ERROR = 'NODE_CODE_CACHING: failed to deserialize, bad cache file?' - self.assertContained(ERROR, self.run_js('a.out.js')) - # we cached proper code after showing that error - self.assertEqual(read_binary(get_cached()).count(b'waka'), 0) - self.assertNotContained(ERROR, self.run_js('a.out.js')) - - def test_node_code_caching_incompatible_settings(self): - self.assert_fail([EMCC, test_file('hello_world.c'), '-sNODE_CODE_CACHING', '-sWASM_ASYNC_COMPILATION=0', '-sSINGLE_FILE'], - 'emcc: error: NODE_CODE_CACHING is not compatible with SINGLE_FILE (saves a file on the side)') - self.assert_fail([EMCC, test_file('hello_world.c'), '-sNODE_CODE_CACHING'], - 'emcc: error: NODE_CODE_CACHING is not compatible with WASM_ASYNC_COMPILATION') - err = self.run_process([EMCC, test_file('hello_world.c'), '-sNODE_CODE_CACHING', '-sWASM_ASYNC_COMPILATION=0', '-sENVIRONMENT=web'], stderr=PIPE).stderr - self.assertContained('warning: NODE_CODE_CACHING ignored since `node` not in `ENVIRONMENT` [-Wunused-command-line-argument]', err) - @with_env_modify({'LC_ALL': 'C'}) def test_autotools_shared_check(self): expected = ': supported targets:.* elf' diff --git a/tools/link.py b/tools/link.py index 71667330ab910..b48820fd23bb6 100644 --- a/tools/link.py +++ b/tools/link.py @@ -1803,8 +1803,6 @@ def get_full_import_name(name): diagnostics.warning('unused-command-line-argument', 'NODERAWFS ignored since `node` not in `ENVIRONMENT`') if settings.NODERAWSOCKETS: diagnostics.warning('unused-command-line-argument', 'NODERAWSOCKETS ignored since `node` not in `ENVIRONMENT`') - if settings.NODE_CODE_CACHING: - diagnostics.warning('unused-command-line-argument', 'NODE_CODE_CACHING ignored since `node` not in `ENVIRONMENT`') settings.PRE_JS_FILES = options.pre_js settings.POST_JS_FILES = options.post_js diff --git a/tools/settings.py b/tools/settings.py index dca07c8582691..5b84b58eb1554 100644 --- a/tools/settings.py +++ b/tools/settings.py @@ -167,8 +167,6 @@ ('WASM_ESM_INTEGRATION', 'WASM2JS', None), ('WASM_ESM_INTEGRATION', 'ABORT_ON_WASM_EXCEPTIONS', None), ('FORCE_FILESYSTEM', 'NO_FILESYSTEM', None), - ('NODE_CODE_CACHING', 'SINGLE_FILE', 'saves a file on the side'), - ('NODE_CODE_CACHING', 'WASM_ASYNC_COMPILATION', None), ] EXPERIMENTAL_SETTINGS = { @@ -279,6 +277,7 @@ ['DETERMINISTIC', [0], 'No longer supported'], ['LEGALIZE_JS_FFI', [0], 'legacy JS FFI legalization is no longer supported'], ['SOCKET_WEBRTC', [0], 'No longer supported'], + ['NODE_CODE_CACHING', [0], 'No longer supported'], ] user_settings: dict[str, str] = {}